예제 #1
0
// Callers delete the ToC tree, so we re-create it from prerecorded
// values (which is faster than re-creating it from html every time)
DocTocItem *ChmModel::GetTocTree()
{
    DocTocItem *root = NULL, **nextChild = &root;
    Vec<DocTocItem *> levels;
    int idCounter = 0;

    for (ChmTocTraceItem *ti = tocTrace->IterStart(); ti; ti = tocTrace->IterNext()) {
        ChmTocItem *item = new ChmTocItem(ti->title, ti->pageNo, ti->url);
        item->id = ++idCounter;
        // append the item at the correct level
        CrashIf(ti->level < 1);
        if ((size_t)ti->level <= levels.Count()) {
            levels.RemoveAt(ti->level, levels.Count() - ti->level);
            levels.Last()->AddSibling(item);
        }
        else {
            (*nextChild) = item;
            levels.Append(item);
        }
        nextChild = &item->child;
    }

    if (root)
        root->OpenSingleNode();
    return root;
}
예제 #2
0
int Pdfsync::DocToSource(UINT pageNo, PointI pt, ScopedMem<WCHAR>& filename, UINT *line, UINT *col)
{
    if (IsIndexDiscarded())
        if (RebuildIndex() != PDFSYNCERR_SUCCESS)
            return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED;

    // find the entry in the index corresponding to this page
    if (pageNo <= 0 || pageNo >= sheetIndex.Count() || pageNo > (UINT)engine->PageCount())
        return PDFSYNCERR_INVALID_PAGE_NUMBER;

    // PdfSync coordinates are y-inversed
    RectI mbox = engine->PageMediabox(pageNo).Round();
    pt.y = mbox.dy - pt.y;

    // distance to the closest pdf location (in the range <PDFSYNC_EPSILON_SQUARE)
    UINT closest_xydist = UINT_MAX;
    UINT selected_record = UINT_MAX;
    // If no record is found within a distance^2 of PDFSYNC_EPSILON_SQUARE
    // (selected_record == -1) then we pick up the record that is closest
    // vertically to the hit-point.
    UINT closest_ydist = UINT_MAX; // vertical distance between the hit point and the vertically-closest record
    UINT closest_xdist = UINT_MAX; // horizontal distance between the hit point and the vertically-closest record
    UINT closest_ydist_record = UINT_MAX; // vertically-closest record

    // read all the sections of 'p' declarations for this pdf sheet
    for (size_t i = sheetIndex.At(pageNo); i < points.Count() && points.At(i).page == pageNo; i++) {
        // check whether it is closer than the closest point found so far
        UINT dx = abs(pt.x - (int)SYNC_TO_PDF_COORDINATE(points.At(i).x));
        UINT dy = abs(pt.y - (int)SYNC_TO_PDF_COORDINATE(points.At(i).y));
        UINT dist = dx * dx + dy * dy;
        if (dist < PDFSYNC_EPSILON_SQUARE && dist < closest_xydist) {
            selected_record = points.At(i).record;
            closest_xydist = dist;
        }
        else if ((closest_xydist == UINT_MAX) && dy < PDFSYNC_EPSILON_Y &&
                 (dy < closest_ydist || (dy == closest_ydist && dx < closest_xdist))) {
            closest_ydist_record = points.At(i).record;
            closest_ydist = dy;
            closest_xdist = dx;
        }
    }

    if (selected_record == UINT_MAX)
        selected_record = closest_ydist_record;
    if (selected_record == UINT_MAX)
        return PDFSYNCERR_NO_SYNC_AT_LOCATION; // no record was found close enough to the hit point

    // We have a record number, we need to find its declaration ('l ...') in the syncfile
    PdfsyncLine cmp; cmp.record = selected_record;
    PdfsyncLine *found = (PdfsyncLine *)bsearch(&cmp, lines.LendData(), lines.Count(), sizeof(PdfsyncLine), cmpLineRecords);
    assert(found);
    if (!found)
        return PDFSYNCERR_NO_SYNC_AT_LOCATION;

    filename.Set(str::Dup(srcfiles.At(found->file)));
    *line = found->line;
    *col = found->column;

    return PDFSYNCERR_SUCCESS;
}
// Paint windows in z-order by first collecting the windows
// and then painting consecutive layers with the same z-order,
// starting with the lowest z-order.
// We don't sort because we want to preserve the order of
// containment of windows with the same z-order and non-stable
// sort could change it.
static void PaintWindowsInZOrder(Graphics *g, Control *c)
{
    Vec<CtrlAndOffset>  toPaint;
    WndFilter           wndFilter;
    CtrlAndOffset *     coff;
    Pen                 debugPen(Color(255, 0, 0), 1);

    CollectWindowsBreathFirst(c, 0, 0, &wndFilter, &toPaint);
    size_t paintedCount = 0;
    int16 lastPaintedZOrder = INT16_MIN;
    for (;;) {
        // find which z-order should we paint now
        int16 minUnpaintedZOrder = INT16_MAX;
        for (coff = toPaint.IterStart(); coff; coff = toPaint.IterNext()) {
            int16 zOrder = coff->c->zOrder;
            if ((zOrder > lastPaintedZOrder) && (zOrder < minUnpaintedZOrder))
                minUnpaintedZOrder = zOrder;
        }
        for (coff = toPaint.IterStart(); coff; coff = toPaint.IterNext()) {
            if (minUnpaintedZOrder == coff->c->zOrder) {
                coff->c->Paint(g, coff->offX, coff->offY);
                if (IsDebugPaint()) {
                    Rect bbox(coff->offX, coff->offY, coff->c->pos.Width, coff->c->pos.Height);
                    g->DrawRectangle(&debugPen, bbox);
                }
                ++paintedCount;
            }
        }
        if (paintedCount == toPaint.Count())
            return;
        CrashIf(paintedCount > toPaint.Count());
        lastPaintedZOrder = minUnpaintedZOrder;
    }
}
예제 #4
0
bool SaveFileModifictions(const WCHAR *filePath, Vec<PageAnnotation> *list)
{
    if (!list)
        return false;

    ScopedMem<WCHAR> modificationsPath(str::Join(filePath, SMX_FILE_EXT));
    str::Str<char> data;
    size_t offset = 0;

    ScopedMem<char> prevData(file::ReadAll(modificationsPath, NULL));
    Vec<PageAnnotation> *prevList = ParseFileModifications(prevData);
    if (prevList) {
        // in the case of an update, append changed annotations to the existing ones
        // (don't rewrite the existing ones in case they're by a newer version which
        // added annotation types and properties this version doesn't know anything about)
        for (; offset < prevList->Count() && prevList->At(offset) == list->At(offset); offset++);
        CrashIf(offset != prevList->Count());
        data.AppendAndFree(prevData.StealData());
        delete prevList;
    }
    else {
        data.AppendFmt("# SumatraPDF: modifications to \"%S\"\r\n", path::GetBaseName(filePath));
    }
    data.Append("\r\n");

    if (list->Count() == offset)
        return true; // nothing (new) to save

    data.AppendFmt("[@%s]\r\n", prevList ? "update" : "meta");
    data.AppendFmt("version = %s\r\n", SMX_CURR_VERSION);
    int64 size = file::GetSize(filePath);
    if (0 <= size && size <= UINT_MAX)
        data.AppendFmt("filesize = %u\r\n", (UINT)size);
    SYSTEMTIME time;
    GetSystemTime(&time);
    data.AppendFmt("timestamp = %04d-%02d-%02dT%02d:%02d:%02dZ\r\n",
        time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);
    data.Append("\r\n");

    for (size_t i = offset; i < list->Count(); i++) {
        PageAnnotation& annot = list->At(i);
        switch (annot.type) {
        case Annot_Highlight: data.Append("[highlight]\r\n"); break;
        case Annot_Underline: data.Append("[underline]\r\n"); break;
        case Annot_StrikeOut: data.Append("[strikeout]\r\n"); break;
        case Annot_Squiggly:  data.Append("[squiggly]\r\n");  break;
        default: continue;
        }
        data.AppendFmt("page = %d\r\n", annot.pageNo);
        data.AppendFmt("rect = %g %g %g %g\r\n", annot.rect.x, annot.rect.y, annot.rect.dx, annot.rect.dy);
        data.AppendFmt("color = #%02x%02x%02x\r\n", annot.color.r, annot.color.g, annot.color.b);
        data.AppendFmt("opacity = %g\r\n", annot.color.a / 255.f);
        data.Append("\r\n");
    }
    data.RemoveAt(data.Size() - 2, 2);

    FileTransaction trans;
    return trans.WriteAll(modificationsPath, data.LendData(), data.Size()) && trans.Commit();
}
예제 #5
0
void DumpPageContent(BaseEngine *engine, int pageNo, bool fullDump)
{
    // ensure that the page is loaded
    engine->BenchLoadPage(pageNo);

    Out("\t<Page Number=\"%d\"\n", pageNo);
    if (engine->HasPageLabels()) {
        ScopedMem<char> label(Escape(engine->GetPageLabel(pageNo)));
        Out("\t\tLabel=\"%s\"\n", label.Get());
    }
    RectI bbox = engine->PageMediabox(pageNo).Round();
    Out("\t\tMediaBox=\"%d %d %d %d\"\n", bbox.x, bbox.y, bbox.dx, bbox.dy);
    RectI cbox = engine->PageContentBox(pageNo).Round();
    if (cbox != bbox)
        Out("\t\tContentBox=\"%d %d %d %d\"\n", cbox.x, cbox.y, cbox.dx, cbox.dy);
    if (!engine->HasClipOptimizations(pageNo))
        Out("\t\tHasClipOptimizations=\"no\"\n");
    Out("\t>\n");

    if (fullDump) {
        ScopedMem<char> text(Escape(engine->ExtractPageText(pageNo, L"\n")));
        if (text)
            Out("\t\t<TextContent>\n%s\t\t</TextContent>\n", text.Get());
    }

    Vec<PageElement *> *els = engine->GetElements(pageNo);
    if (els && els->Count() > 0) {
        Out("\t\t<PageElements>\n");
        for (size_t i = 0; i < els->Count(); i++) {
            RectD rect = els->At(i)->GetRect();
            Out("\t\t\t<Element Type=\"%s\"\n\t\t\t\tRect=\"%.0f %.0f %.0f %.0f\"\n",
                ElementTypeToStr(els->At(i)), rect.x, rect.y, rect.dx, rect.dy);
            PageDestination *dest = els->At(i)->AsLink();
            if (dest) {
                if (dest->GetDestType() != Dest_None)
                    Out("\t\t\t\tLinkType=\"%s\"\n", PageDestToStr(dest->GetDestType()));
                ScopedMem<char> value(Escape(dest->GetDestValue()));
                if (value)
                    Out("\t\t\t\tLinkTarget=\"%s\"\n", value.Get());
                if (dest->GetDestPageNo())
                    Out("\t\t\t\tLinkedPage=\"%d\"\n", dest->GetDestPageNo());
                ScopedMem<char> rectStr(DestRectToStr(engine, dest));
                if (rectStr)
                    Out("\t\t\t\tLinked%s\n", rectStr.Get());
            }
            ScopedMem<char> name(Escape(els->At(i)->GetValue()));
            if (name)
                Out("\t\t\t\tLabel=\"%s\"\n", name.Get());
            Out("\t\t\t/>\n");
        }
        Out("\t\t</PageElements>\n");
        DeleteVecMembers(*els);
    }
    delete els;

    Out("\t</Page>\n");
}
예제 #6
0
UINT RenderCache::Paint(HDC hdc, RectI bounds, DisplayModel *dm, int pageNo,
                        PageInfo *pageInfo, bool *renderOutOfDateCue)
{
    assert(pageInfo->shown && 0.0 != pageInfo->visibleRatio);

    int rotation = dm->Rotation();
    float zoom = dm->ZoomReal();
    USHORT targetRes = GetTileRes(dm, pageNo);
    USHORT maxRes = GetMaxTileRes(dm, pageNo, rotation);
    if (maxRes < targetRes)
        maxRes = targetRes;

    Vec<TilePosition> queue;
    queue.Append(TilePosition(0, 0, 0));
    UINT renderDelayMin = RENDER_DELAY_UNDEFINED;
    bool neededScaling = false;

    while (queue.Count() > 0) {
        TilePosition tile = queue.At(0);
        queue.RemoveAt(0);
        RectI tileOnScreen = GetTileOnScreen(dm->engine, pageNo, rotation, zoom, tile, pageInfo->pageOnScreen);
        tileOnScreen = pageInfo->pageOnScreen.Intersect(tileOnScreen);
        RectI isect = bounds.Intersect(tileOnScreen);
        if (isect.IsEmpty())
            continue;

        bool isTargetRes = tile.res == targetRes;
        UINT renderDelay = PaintTile(hdc, isect, dm, pageNo, tile, tileOnScreen, isTargetRes,
                                     renderOutOfDateCue, isTargetRes ? &neededScaling : NULL);
        if (!(isTargetRes && 0 == renderDelay) && tile.res < maxRes) {
            queue.Append(TilePosition(tile.res + 1, tile.row * 2, tile.col * 2));
            queue.Append(TilePosition(tile.res + 1, tile.row * 2, tile.col * 2 + 1));
            queue.Append(TilePosition(tile.res + 1, tile.row * 2 + 1, tile.col * 2));
            queue.Append(TilePosition(tile.res + 1, tile.row * 2 + 1, tile.col * 2 + 1));
        }
        if (isTargetRes && renderDelay > 0)
            neededScaling = true;
        renderDelayMin = min(renderDelay, renderDelayMin);
        // paint tiles from left to right from top to bottom
        if (tile.res > 0 && queue.Count() > 0 && tile.res < queue.At(0).res)
            queue.Sort(cmpTilePosition);
    }

#ifdef CONSERVE_MEMORY
    if (!neededScaling) {
        if (renderOutOfDateCue)
            *renderOutOfDateCue = false;
        // free tiles with different resolution
        TilePosition tile(targetRes, (USHORT)-1, 0);
        FreePage(dm, pageNo, &tile);
    }
    FreeNotVisible();
#endif

    return renderDelayMin;
}
예제 #7
0
void DjVuEngineImpl::AddUserAnnots(RenderedBitmap *bmp, int pageNo, float zoom, int rotation, RectI screen)
{
    if (!bmp || userAnnots.Count() == 0)
        return;

    HDC hdc = CreateCompatibleDC(NULL);
    HGDIOBJ prevBmp = SelectObject(hdc, bmp->GetBitmap());
    {
        using namespace Gdiplus;
        Graphics g(hdc);
        g.SetCompositingQuality(CompositingQualityHighQuality);
        g.SetPageUnit(UnitPixel);

        for (size_t i = 0; i < userAnnots.Count(); i++) {
            PageAnnotation& annot = userAnnots.At(i);
            if (annot.pageNo != pageNo)
                continue;
            RectD arect;
            switch (annot.type) {
            case Annot_Highlight:
                arect = Transform(annot.rect, pageNo, zoom, rotation);
                arect.Offset(-screen.x, -screen.y);
                g.FillRectangle(&SolidBrush(Unblend(annot.color, 119)), arect.ToGdipRectF());
                break;
            case Annot_Underline:
            case Annot_StrikeOut:
                arect = RectD(annot.rect.x, annot.rect.BR().y, annot.rect.dx, 0);
                if (Annot_StrikeOut == annot.type)
                    arect.y -= annot.rect.dy / 2;
                arect = Transform(arect, pageNo, zoom, rotation);
                arect.Offset(-screen.x, -screen.y);
                g.DrawLine(&Pen(FromColor(annot.color), zoom), (float)arect.x,
                           (float)arect.y, (float)arect.BR().x, (float)arect.BR().y);
                break;
            case Annot_Squiggly:
                {
                    Pen p(FromColor(annot.color), 0.5f * zoom);
                    REAL dash[2] = { 2, 2 };
                    p.SetDashPattern(dash, dimof(dash));
                    p.SetDashOffset(1);
                    arect = Transform(RectD(annot.rect.x, annot.rect.BR().y - 0.25f, annot.rect.dx, 0), pageNo, zoom, rotation);
                    arect.Offset(-screen.x, -screen.y);
                    g.DrawLine(&p, (float)arect.x, (float)arect.y, (float)arect.BR().x, (float)arect.BR().y);
                    p.SetDashOffset(3);
                    arect = Transform(RectD(annot.rect.x, annot.rect.BR().y + 0.25f, annot.rect.dx, 0), pageNo, zoom, rotation);
                    arect.Offset(-screen.x, -screen.y);
                    g.DrawLine(&p, (float)arect.x, (float)arect.y, (float)arect.BR().x, (float)arect.BR().y);
                }
                break;
            }
        }
    }
    SelectObject(hdc, prevBmp);
    DeleteDC(hdc);
}
예제 #8
0
bool CbxEngineImpl::LoadCbrFile(const WCHAR *file)
{
    if (!file)
        return false;
    fileName = str::Dup(file);
    fileExt = L".cbr";

    RAROpenArchiveDataEx  arcData = { 0 };
    arcData.ArcNameW = (WCHAR *)file;
    arcData.OpenMode = RAR_OM_EXTRACT;

    HANDLE hArc = RAROpenArchiveEx(&arcData);
    if (!hArc || arcData.OpenResult != 0)
        return false;

    // UnRAR does not seem to support extracting a single file by name,
    // so lazy image loading doesn't seem possible

    Vec<ImagesPage *> found;
    for (;;) {
        RARHeaderDataEx rarHeader;
        int res = RARReadHeaderEx(hArc, &rarHeader);
        if (0 != res)
            break;

        const WCHAR *fileName = rarHeader.FileNameW;
        if (ImageEngine::IsSupportedFile(fileName)) {
            ImagesPage *page = LoadCurrentCbrPage(hArc, rarHeader);
            if (page)
                found.Append(page);
        }
        else if (str::EqI(fileName, L"ComicInfo.xml")) {
            ScopedMem<char> xmlData(LoadCurrentCbrFile(hArc, rarHeader, NULL));
            if (xmlData)
                ParseComicInfoXml(xmlData);
        }
        else
            RARProcessFile(hArc, RAR_SKIP, NULL, NULL);

    }
    RARCloseArchive(hArc);

    if (found.Count() == 0)
        return false;
    found.Sort(ImagesPage::cmpPageByName);

    for (size_t i = 0; i < found.Count(); i++) {
        pages.Append(found.At(i)->bmp);
        found.At(i)->bmp = NULL;
    }
    mediaboxes.AppendBlanks(pages.Count());

    DeleteVecMembers(found);
    return true;
}
예제 #9
0
// Select random files to test. We want to test each file type equally, so
// we first group them by file extension and then select up to maxPerType
// for each extension, randomly, and inter-leave the files with different
// extensions, so their testing is evenly distributed.
// Returns result in <files>.
static void RandomizeFiles(WStrVec& files, int maxPerType)
{
    WStrVec fileExts;
    Vec<WStrVec *> filesPerType;

    for (size_t i = 0; i < files.Count(); i++) {
        const WCHAR *file = files.At(i);
        const WCHAR *ext = path::GetExt(file);
        CrashAlwaysIf(!ext);
        int typeNo = fileExts.FindI(ext);
        if (-1 == typeNo) {
            fileExts.Append(str::Dup(ext));
            filesPerType.Append(new WStrVec());
            typeNo = (int)filesPerType.Count() - 1;
        }
        filesPerType.At(typeNo)->Append(str::Dup(file));
    }

    for (size_t j = 0; j < filesPerType.Count(); j++) {
        WStrVec *all = filesPerType.At(j);
        WStrVec *random = new WStrVec();

        for (int n = 0; n < maxPerType && all->Count() > 0; n++) {
            int idx = rand() % all->Count();
            WCHAR *file = all->At(idx);
            random->Append(file);
            all->RemoveAtFast(idx);
        }

        filesPerType.At(j) = random;
        delete all;
    }

    files.Reset();

    bool gotAll = false;
    while (!gotAll) {
        gotAll = true;
        for (size_t j = 0; j < filesPerType.Count(); j++) {
            WStrVec *random = filesPerType.At(j);
            if (random->Count() > 0) {
                gotAll = false;
                WCHAR *file = random->At(0);
                files.Append(file);
                random->RemoveAtFast(0);
            }
        }
    }

    for (size_t j = 0; j < filesPerType.Count(); j++) {
        delete filesPerType.At(j);
    }
}
예제 #10
0
// For easy access, we try to show favorites in the menu, similar to a list of
// recently opened files.
// The first menu items are for currently opened file (up to MAX_FAV_MENUS), based
// on the assumption that user is usually interested in navigating current file.
// Then we have a submenu for each file for which there are bookmarks (up to
// MAX_FAV_SUBMENUS), each having up to MAX_FAV_MENUS menu items.
// If not all favorites can be shown, we also enable "Show all favorites" menu which
// will provide a way to see all favorites.
// Note: not sure if that's the best layout. Maybe we should always use submenu and
// put the submenu for current file as the first one (potentially named as "Current file"
// or some such, to make it stand out from other submenus)
static void AppendFavMenus(HMENU m, const WCHAR* currFilePath) {
    // To minimize mouse movement when navigating current file via favorites
    // menu, put favorites for current file first
    DisplayState* currFileFav = nullptr;
    if (currFilePath) {
        currFileFav = gFavorites.GetFavByFilePath(currFilePath);
    }

    // sort the files with favorites by base file name of file path
    Vec<const WCHAR*> filePathsSorted;
    if (HasPermission(Perm_DiskAccess)) {
        // only show favorites for other files, if we're allowed to open them
        GetSortedFilePaths(filePathsSorted, currFileFav);
    }
    if (currFileFav && currFileFav->favorites->Count() > 0) {
        filePathsSorted.InsertAt(0, currFileFav->filePath);
    }

    if (filePathsSorted.Count() == 0) {
        return;
    }

    AppendMenu(m, MF_SEPARATOR, 0, nullptr);

    gFavorites.ResetMenuIds();
    UINT menuId = IDM_FAV_FIRST;

    size_t menusCount = filePathsSorted.Count();
    if (menusCount > MAX_FAV_MENUS) {
        menusCount = MAX_FAV_MENUS;
    }

    for (size_t i = 0; i < menusCount; i++) {
        const WCHAR* filePath = filePathsSorted.At(i);
        DisplayState* f = gFavorites.GetFavByFilePath(filePath);
        CrashIf(!f);
        HMENU sub = m;
        bool combined = (f->favorites->Count() == 1);
        if (!combined) {
            sub = CreateMenu();
        }
        AppendFavMenuItems(sub, f, menuId, combined, f == currFileFav);
        if (!combined) {
            if (f == currFileFav) {
                AppendMenu(m, MF_POPUP | MF_STRING, (UINT_PTR)sub, _TR("Current file"));
            } else {
                ScopedMem<WCHAR> tmp;
                const WCHAR* fileName = win::menu::ToSafeString(path::GetBaseName(filePath), tmp);
                AppendMenu(m, MF_POPUP | MF_STRING, (UINT_PTR)sub, fileName);
            }
        }
    }
}
PageDestination *EbookEngine::GetNamedDest(const WCHAR *name)
{
    ScopedMem<char> name_utf8(str::conv::ToUtf8(name));
    const char *id = name_utf8;
    if (str::FindChar(id, '#'))
        id = str::FindChar(id, '#') + 1;

    // if the name consists of both path and ID,
    // try to first skip to the page with the desired
    // path before looking for the ID to allow
    // for the same ID to be reused on different pages
    DrawInstr *baseAnchor = NULL;
    int basePageNo = 0;
    if (id > name_utf8 + 1) {
        size_t base_len = id - name_utf8 - 1;
        for (size_t i = 0; i < baseAnchors.Count(); i++) {
            DrawInstr *anchor = baseAnchors.At(i);
            if (anchor && base_len == anchor->str.len &&
                str::EqNI(name_utf8, anchor->str.s, base_len)) {
                baseAnchor = anchor;
                basePageNo = (int)i + 1;
                break;
            }
        }
    }

    size_t id_len = str::Len(id);
    for (size_t i = 0; i < anchors.Count(); i++) {
        PageAnchor *anchor = &anchors.At(i);
        if (baseAnchor) {
            if (anchor->instr == baseAnchor)
                baseAnchor = NULL;
            continue;
        }
        // note: at least CHM treats URLs as case-independent
        if (id_len == anchor->instr->str.len &&
            str::EqNI(id, anchor->instr->str.s, id_len)) {
            RectD rect(0, anchor->instr->bbox.Y + pageBorder, pageRect.dx, 10);
            rect.Inflate(-pageBorder, 0);
            return new SimpleDest2(anchor->pageNo, rect);
        }
    }

    // don't fail if an ID doesn't exist in a merged document
    if (basePageNo != 0) {
        RectD rect(0, pageBorder, pageRect.dx, 10);
        rect.Inflate(-pageBorder, 0);
        return new SimpleDest2(basePageNo, rect);
    }

    return NULL;
}
예제 #12
0
// don't emit multiple spaces and don't emit spaces
// at the beginning of the line
static bool CanEmitElasticSpace(float currX, float NewLineX, float maxCurrX, Vec<DrawInstr>& currLineInstr)
{
    if (NewLineX == currX || 0 == currLineInstr.Count())
        return false;
    // prevent elastic spaces from being flushed to the
    // beginning of the next line
    if (currX > maxCurrX)
        return false;
    DrawInstr& di = currLineInstr.Last();
    // don't add a space if only an anchor would be in between them
    if (InstrAnchor == di.type && currLineInstr.Count() > 1)
        di = currLineInstr.At(currLineInstr.Count() - 2);
    return (InstrElasticSpace != di.type) && (InstrFixedSpace != di.type);
}
예제 #13
0
void StressTest::Start(TestFileProvider *fileProvider, int cycles)
{
    GetSystemTime(&stressStartTime);

    this->fileProvider = fileProvider;
    this->cycles = cycles;

    if (pageRanges.Count() == 0)
        pageRanges.Append(PageRange());
    if (fileRanges.Count() == 0)
        fileRanges.Append(PageRange());

    TickTimer();
}
예제 #14
0
// For easy access, we try to show favorites in the menu, similar to a list of
// recently opened files.
// The first menu items are for currently opened file (up to MAX_FAV_MENUS), based
// on the assumption that user is usually interested in navigating current file.
// Then we have a submenu for each file for which there are bookmarks (up to
// MAX_FAV_SUBMENUS), each having up to MAX_FAV_MENUS menu items.
// If not all favorites can be shown, we also enable "Show all favorites" menu which
// will provide a way to see all favorites.
// Note: not sure if that's the best layout. Maybe we should always use submenu and
// put the submenu for current file as the first one (potentially named as "Current file"
// or some such, to make it stand out from other submenus)
static void AppendFavMenus(HMENU m, const TCHAR *currFilePath)
{
    // To minimize mouse movement when navigating current file via favorites
    // menu, put favorites for current file first
    FileFavs *currFileFav = NULL;
    if (NULL != currFilePath) {
        currFileFav = gFavorites->GetFavByFilePath(currFilePath);
    }

    // sort the files with favorites by base file name of file path
    Vec<TCHAR*> filePathsSorted;
    if (HasPermission(Perm_DiskAccess)) {
        // only show favorites for other files, if we're allowed to open them
        GetSortedFilePaths(gFavorites, filePathsSorted, currFileFav);
    }
    if (currFileFav)
        filePathsSorted.InsertAt(0, currFileFav->filePath);

    if (filePathsSorted.Count() == 0)
        return;

    AppendMenu(m, MF_SEPARATOR, 0, NULL);

    gFavorites->ResetMenuIds();
    UINT menuId = IDM_FAV_FIRST;

    size_t menusCount = filePathsSorted.Count();
    if (menusCount > MAX_FAV_MENUS)
        menusCount = MAX_FAV_MENUS;
    for (size_t i = 0; i < menusCount; i++) {
        TCHAR *filePath = filePathsSorted.At(i);
        const TCHAR *fileName = path::GetBaseName(filePath);
        FileFavs *f = gFavorites->GetFavByFilePath(filePath);
        HMENU sub = m;
        bool combined = (f->favNames.Count() == 1);
        if (!combined)
            sub = CreateMenu();
        AppendFavMenuItems(sub, f, menuId, combined, f == currFileFav);
        if (!combined) {
            if (f == currFileFav) {
                AppendMenu(m, MF_POPUP | MF_STRING, (UINT_PTR)sub, _TR("Current file"));
            } else {
                ScopedMem<TCHAR> s(win::menu::ToSafeString(fileName));
                AppendMenu(m, MF_POPUP | MF_STRING, (UINT_PTR)sub, s);
            }
        }
    }
}
예제 #15
0
// Show the result of a PDF forward-search synchronization (initiated by a DDE command)
void ShowForwardSearchResult(WindowInfo *win, const WCHAR *fileName, UINT line, UINT col, UINT ret, UINT page, Vec<RectI> &rects)
{
    win->fwdSearchMark.rects.Reset();
    const PageInfo *pi = win->dm->GetPageInfo(page);
    if ((ret == PDFSYNCERR_SUCCESS) && (rects.Count() > 0) && (NULL != pi)) {
        // remember the position of the search result for drawing the rect later on
        win->fwdSearchMark.rects = rects;
        win->fwdSearchMark.page = page;
        win->fwdSearchMark.show = true;
        win->fwdSearchMark.hideStep = 0;
        if (!gUserPrefs->forwardSearch.highlightPermanent)
            SetTimer(win->hwndCanvas, HIDE_FWDSRCHMARK_TIMER_ID, HIDE_FWDSRCHMARK_DELAY_IN_MS, NULL);

        // Scroll to show the overall highlighted zone
        int pageNo = page;
        RectI overallrc = rects.At(0);
        for (size_t i = 1; i < rects.Count(); i++)
            overallrc = overallrc.Union(rects.At(i));
        TextSel res = { 1, &pageNo, &overallrc };
        if (!win->dm->PageVisible(page))
            win->dm->GoToPage(page, 0, true);
        if (!win->dm->ShowResultRectToScreen(&res))
            win->RepaintAsync();
        if (IsIconic(win->hwndFrame))
            ShowWindowAsync(win->hwndFrame, SW_RESTORE);
        return;
    }

    ScopedMem<WCHAR> buf;
    if (ret == PDFSYNCERR_SYNCFILE_NOTFOUND)
        ShowNotification(win, _TR("No synchronization file found"));
    else if (ret == PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED)
        ShowNotification(win, _TR("Synchronization file cannot be opened"));
    else if (ret == PDFSYNCERR_INVALID_PAGE_NUMBER)
        buf.Set(str::Format(_TR("Page number %u inexistant"), page));
    else if (ret == PDFSYNCERR_NO_SYNC_AT_LOCATION)
        ShowNotification(win, _TR("No synchronization info at this position"));
    else if (ret == PDFSYNCERR_UNKNOWN_SOURCEFILE)
        buf.Set(str::Format(_TR("Unknown source file (%s)"), fileName));
    else if (ret == PDFSYNCERR_NORECORD_IN_SOURCEFILE)
        buf.Set(str::Format(_TR("Source file %s has no synchronization point"), fileName));
    else if (ret == PDFSYNCERR_NORECORD_FOR_THATLINE)
        buf.Set(str::Format(_TR("No result found around line %u in file %s"), line, fileName));
    else if (ret == PDFSYNCERR_NOSYNCPOINT_FOR_LINERECORD)
        buf.Set(str::Format(_TR("No result found around line %u in file %s"), line, fileName));
    if (buf)
        ShowNotification(win, buf);
}
WCHAR *DjVuEngineImpl::ExtractPageText(int pageNo, WCHAR *lineSep, RectI **coords_out, RenderTarget target)
{
    ScopedCritSec scope(&gDjVuContext.lock);

    miniexp_t pagetext;
    while ((pagetext = ddjvu_document_get_pagetext(doc, pageNo-1, NULL)) == miniexp_dummy)
        gDjVuContext.SpinMessageLoop();
    if (miniexp_nil == pagetext)
        return NULL;

    str::Str<WCHAR> extracted;
    Vec<RectI> coords;
    bool success = ExtractPageText(pagetext, lineSep, extracted, coords);
    ddjvu_miniexp_release(doc, pagetext);
    if (!success)
        return NULL;
    if (extracted.Count() > 0 && !str::EndsWith(extracted.Get(), lineSep))
        AppendNewline(extracted, coords, lineSep);

    assert(str::Len(extracted.Get()) == coords.Count());
    if (coords_out) {
        ddjvu_status_t status;
        ddjvu_pageinfo_t info;
        while ((status = ddjvu_document_get_pageinfo(doc, pageNo-1, &info)) < DDJVU_JOB_OK)
            gDjVuContext.SpinMessageLoop();
        float dpiFactor = 1.0;
        if (DDJVU_JOB_OK == status)
            dpiFactor = GetFileDPI() / info.dpi;

        // TODO: the coordinates aren't completely correct yet
        RectI page = PageMediabox(pageNo).Round();
        for (size_t i = 0; i < coords.Count(); i++) {
            if (coords.At(i) != RectI()) {
                if (dpiFactor != 1.0) {
                    geomutil::RectT<float> pageF = coords.At(i).Convert<float>();
                    pageF.x *= dpiFactor; pageF.dx *= dpiFactor;
                    pageF.y *= dpiFactor; pageF.dy *= dpiFactor;
                    coords.At(i) = pageF.Round();
                }
                coords.At(i).y = page.dy - coords.At(i).y - coords.At(i).dy;
            }
        }
        CrashIf(coords.Count() != extracted.Count());
        *coords_out = coords.StealData();
    }

    return extracted.StealData();
}
예제 #17
0
// the assumption here is that the data was either built by Deserialize()
// or was created by application code in a way that observes our rule: each
// struct and string was separately allocated with malloc()
void FreeStruct(uint8_t *structStart, const StructMetadata *def)
{
    if (!structStart)
        return;
    Type type;
    const FieldMetadata *fieldDef = NULL;
    for (int i = 0; i < def->nFields; i++) {
        fieldDef = def->fields + i;
        uint8_t *data = structStart + fieldDef->offset;
        type = (Type)(fieldDef->type & TYPE_MASK);
        if (TYPE_STRUCT_PTR ==  type) {
            uint8_t **p = (uint8_t**)data;
            FreeStruct(*p, GetStructDef(fieldDef));
        } else if (TYPE_ARRAY == type) {
            Vec<uint8_t*> **vecPtr = (Vec<uint8_t*> **)data;
            Vec<uint8_t*> *vec = *vecPtr;
            for (size_t j = 0; j < vec->Count(); j++) {
                FreeStruct(vec->At(j), GetStructDef(fieldDef));
            }
            delete vec;
        } else if ((TYPE_STR == type) || (TYPE_WSTR == type)) {
            char **sp = (char**)data;
            char *s = *sp;
            free(s);
        }
    }
    free(structStart);
}
예제 #18
0
static BencArray *SerializeFileHistory(FileHistory& fileHistory, bool globalPrefsOnly)
{
    BencArray *arr = new BencArray();

    // Don't save more file entries than will be useful
    int minOpenCount = 0;
    if (globalPrefsOnly) {
        Vec<DisplayState *> frequencyList;
        fileHistory.GetFrequencyOrder(frequencyList);
        if (frequencyList.Count() > FILE_HISTORY_MAX_RECENT)
            minOpenCount = frequencyList.At(FILE_HISTORY_MAX_FREQUENT)->openCount / 2;
    }

    DisplayState *state;
    for (int index = 0; (state = fileHistory.Get(index)); index++) {
        // never forget pinned documents and documents we've remembered a password for
        bool forceSave = state->isPinned || state->decryptionKey != NULL;
        if (index >= MAX_REMEMBERED_FILES && !forceSave)
            continue;
        if (state->openCount < minOpenCount && index > FILE_HISTORY_MAX_RECENT && !forceSave)
            continue;
        BencDict *obj = DisplayState_Serialize(state, globalPrefsOnly);
        if (!obj)
            goto Error;
        arr->Add(obj);
    }
    return arr;

Error:
    delete arr;
    return NULL;
}
    char *GetHtml() {
        // first add the homepage
        const char *index = doc->GetHomePath();
        ScopedMem<WCHAR> url(doc->ToStr(index));
        Visit(NULL, url, 0);

        // then add all pages linked to from the table of contents
        doc->ParseToc(this);

        // finally add all the remaining HTML files
        Vec<char *> *paths = doc->GetAllPaths();
        for (size_t i = 0; i < paths->Count(); i++) {
            char *path = paths->At(i);
            if (str::EndsWithI(path, ".htm") || str::EndsWithI(path, ".html")) {
                if (*path == '/')
                    path++;
                url.Set(str::conv::FromUtf8(path));
                Visit(NULL, url, -1);
            }
        }
        FreeVecMembers(*paths);
        delete paths;

        return html.StealData();
    }
예제 #20
0
RectF MeasureTextQuick(Graphics *g, Font *f, const WCHAR *s, size_t len)
{
    static Vec<Font *> fontCache;
    static Vec<bool> fixCache;

    RectF bbox;
    g->MeasureString(s, len, f, PointF(0, 0), &bbox);
    int idx = fontCache.Find(f);
    if (-1 == idx) {
        LOGFONTW lfw;
        Status ok = f->GetLogFontW(g, &lfw);
        bool isItalicOrMonospace = Ok != ok || lfw.lfItalic ||
                                   str::Eq(lfw.lfFaceName, L"Courier New") ||
                                   str::Find(lfw.lfFaceName, L"Consol") ||
                                   str::EndsWith(lfw.lfFaceName, L"Mono") ||
                                   str::EndsWith(lfw.lfFaceName, L"Typewriter");
        fontCache.Append(f);
        fixCache.Append(isItalicOrMonospace);
        idx = (int)fontCache.Count() - 1;
    }
    // most documents look good enough with these adjustments
    if (!fixCache.At(idx)) {
        REAL correct = 0;
        for (size_t i = 0; i < len; i++) {
            switch (s[i]) {
            case 'i': case 'l': correct += 0.2f; break;
            case 't': case 'f': case 'I': correct += 0.1f; break;
            case '.': case ',': case '!': correct += 0.1f; break;
            }
        }
        bbox.Width *= (1.0f - correct / len) * 0.99f;
    }
    bbox.Height *= 0.95f;
    return bbox;
}
void UnregisterEventHandler(EventHandler* h)
{
    bool removed = allEventHandlers.Remove(h);
    CrashIf(!removed);
    if (0 == allEventHandlers.Count())
        PostQuitMessage(0);
}
예제 #22
0
inline bool IsInRange(Vec<PageRange>& ranges, int pageNo)
{
    for (size_t i = 0; i < ranges.Count(); i++)
        if (ranges.At(i).start <= pageNo && pageNo <= ranges.At(i).end)
            return true;
    return false;
}
예제 #23
0
Vec<SelectionOnPage> *SelectionOnPage::FromRectangle(DisplayModel *dm, RectI rect)
{
    Vec<SelectionOnPage> *sel = new Vec<SelectionOnPage>();

    for (int pageNo = dm->PageCount(); pageNo >= 1; --pageNo) {
        PageInfo *pageInfo = dm->GetPageInfo(pageNo);
        assert(!pageInfo || 0.0 == pageInfo->visibleRatio || pageInfo->shown);
        if (!pageInfo || !pageInfo->shown)
            continue;

        RectI intersect = rect.Intersect(pageInfo->pageOnScreen);
        if (intersect.IsEmpty())
            continue;

        /* selection intersects with a page <pageNo> on the screen */
        RectD isectD = dm->CvtFromScreen(intersect, pageNo);
        sel->Append(SelectionOnPage(pageNo, &isectD));
    }
    sel->Reverse();

    if (sel->Count() == 0) {
        delete sel;
        return NULL;
    }
    return sel;
}
예제 #24
0
static RectD BoundSelectionOnPage(const Vec<SelectionOnPage> &sel, int pageNo) {
    RectD bounds;
    for (size_t i = 0; i < sel.Count(); i++) {
        if (sel.At(i).pageNo == pageNo)
            bounds = bounds.Union(sel.At(i).rect);
    }
    return bounds;
}
예제 #25
0
static bool ParseSimple(ParserState *state)
{
    Vec<NestingInfo> stack;
    int       parentNodeIdx = -1;
    char *s = state->curr;
    for (;;) {
        if (!*s)
            break;

        ParsedLine p;
        s = ParseSimpleLine(s, p);
        if (!s)
            return false;
        if (p.isComment)
            continue;

        while (stack.Count() > 0) {
            size_t pos = stack.Count() - 1;
            NestingInfo it = stack.At(pos);
            if (it.indent >= p.indent) {
                stack.RemoveAt(pos);
            } else {
                parentNodeIdx = it.nodeIdx;
                break;
            }
        }

        if (parentNodeIdx != -1 && 0 == stack.Count())
            return false;

        int nodeIdx;
        MarkupNode *node = state->AllocNode(nodeIdx, parentNodeIdx);
        node->name = p.name;
        node->attributes = p.attributes;
        node->user = NULL;
        state->cb->NewNode(node);

        NestingInfo it;
        it.indent = p.indent;
        it.nodeIdx = nodeIdx;
        stack.Append(it);
    }

    return 0 == stack.Count();
}
예제 #26
0
static PropertiesLayout* FindPropertyWindowByHwnd(HWND hwnd)
{
    for (size_t i = 0; i < gPropertiesWindows.Count(); i++) {
        PropertiesLayout *pl = gPropertiesWindows.At(i);
        if (pl->hwnd == hwnd)
            return pl;
    }
    return NULL;
}
예제 #27
0
static void BenchFile(const WCHAR *filePath, const WCHAR *pagesSpec)
{
    if (!file::Exists(filePath)) {
        return;
    }

    // ad-hoc: if enabled times layout instead of rendering and does layout
    // using all text rendering methods, so that we can compare and find
    // docs that take a long time to load

    if (Doc::IsSupportedFile(filePath) && !gGlobalPrefs->ebookUI.useFixedPageUI) {
        BenchEbookLayout(filePath);
        return;
    }

    if (ChmModel::IsSupportedFile(filePath) && !gGlobalPrefs->chmUI.useFixedPageUI) {
        BenchChmLoadOnly(filePath);
        return;
    }

    Timer total;
    logbench(L"Starting: %s", filePath);

    Timer t;
    BaseEngine *engine = EngineManager::CreateEngine(filePath);
    if (!engine) {
        logbench(L"Error: failed to load %s", filePath);
        return;
    }

    double timeMs = t.Stop();
    logbench(L"load: %.2f ms", timeMs);
    int pages = engine->PageCount();
    logbench(L"page count: %d", pages);

    if (nullptr == pagesSpec) {
        for (int i = 1; i <= pages; i++) {
            BenchLoadRender(engine, i);
        }
    }

    assert(!pagesSpec || IsBenchPagesInfo(pagesSpec));
    Vec<PageRange> ranges;
    if (ParsePageRanges(pagesSpec, ranges)) {
        for (size_t i = 0; i < ranges.Count(); i++) {
            for (int j = ranges.At(i).start; j <= ranges.At(i).end; j++) {
                if (1 <= j && j <= pages)
                    BenchLoadRender(engine, j);
            }
        }
    }

    delete engine;
    total.Stop();

    logbench(L"Finished (in %.2f ms): %s", total.GetTimeInMs(), filePath);
}
예제 #28
0
static PropertiesLayout* FindPropertyWindowByParent(HWND hwndParent)
{
    for (size_t i = 0; i < gPropertiesWindows.Count(); i++) {
        PropertiesLayout *pl = gPropertiesWindows.At(i);
        if (pl->hwndParent == hwndParent)
            return pl;
    }
    return nullptr;
}
// returns a numeric DjVu link to a named page (if the name resolves)
// caller needs to free() the result
char *DjVuEngineImpl::ResolveNamedDest(const char *name)
{
    if (!str::StartsWith(name, "#"))
        return NULL;
    for (size_t i = 0; i < fileInfo.Count(); i++)
        if (fileInfo.At(i).pageno >= 0 && str::EqI(name + 1, fileInfo.At(i).id))
            return str::Format("#%d", fileInfo.At(i).pageno + 1);
    return NULL;
}
예제 #30
0
파일: ChmEngine.cpp 프로젝트: Livit/moonpdf
ChmCacheEntry *ChmEngineImpl::FindDataForUrl(const WCHAR *url)
{
    for (size_t i = 0; i < urlDataCache.Count(); i++) {
        ChmCacheEntry *e = urlDataCache.At(i);
        if (str::Eq(url, e->url))
            return e;
    }
    return NULL;
}