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; }
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; }
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; }
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); }
// 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; }
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"); }
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; }
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; }
// 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); } }
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; }
RectD ImageDirEngineImpl::PageMediabox(int pageNo) { assert(1 <= pageNo && pageNo <= PageCount()); if (!mediaboxes.At(pageNo - 1).IsEmpty()) return mediaboxes.At(pageNo - 1); size_t len; ScopedMem<char> bmpData(file::ReadAll(pageFileNames.At(pageNo - 1), &len)); if (bmpData) { Size size = BitmapSizeFromData(bmpData, len); mediaboxes.At(pageNo - 1) = RectD(0, 0, size.Width, size.Height); } return mediaboxes.At(pageNo - 1); }
// 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); }
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(); }
Font *GetFont(const WCHAR *name, float size, FontStyle style) { int idx = cache.Find(Entry((WCHAR *)name, size, style)); if (idx != -1) return cache.At(idx).font; Font *font = ::new Font(name, size, style); if (!font) { // fall back to the default font, if a desired font can't be created font = ::new Font(L"Times New Roman", size, style); if (!font) { return cache.Count() > 0 ? cache.At(0).font : NULL; } } cache.Append(Entry(str::Dup(name), size, style, font)); return font; }
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; }
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; }
// 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); }
// Find a record corresponding to the given source file, line number and optionally column number. // (at the moment the column parameter is ignored) // // If there are several *consecutively declared* records for the same line then they are all returned. // The list of records is added to the vector 'records' // // If there is no record for that line, the record corresponding to the nearest line is selected // (within a range of EPSILON_LINE) // // The function returns PDFSYNCERR_SUCCESS if a matching record was found. UINT Pdfsync::SourceToRecord(const WCHAR* srcfilename, UINT line, UINT col, Vec<size_t> &records) { if (!srcfilename) return PDFSYNCERR_INVALID_ARGUMENT; ScopedMem<WCHAR> srcfilepath; // convert the source file to an absolute path if (PathIsRelative(srcfilename)) srcfilepath.Set(PrependDir(srcfilename)); else srcfilepath.Set(str::Dup(srcfilename)); if (!srcfilepath) return PDFSYNCERR_OUTOFMEMORY; // find the source file entry size_t isrc; for (isrc = 0; isrc < srcfiles.Count(); isrc++) if (path::IsSame(srcfilepath, srcfiles.At(isrc))) break; if (isrc == srcfiles.Count()) return PDFSYNCERR_UNKNOWN_SOURCEFILE; if (fileIndex.At(isrc).start == fileIndex.At(isrc).end) return PDFSYNCERR_NORECORD_IN_SOURCEFILE; // there is not any record declaration for that particular source file // look for sections belonging to the specified file // starting with the first section that is declared within the scope of the file. UINT min_distance = EPSILON_LINE; // distance to the closest record size_t lineIx = (size_t)-1; // closest record-line index for (size_t isec = fileIndex.At(isrc).start; isec < fileIndex.At(isrc).end; isec++) { // does this section belong to the desired file? if (lines.At(isec).file != isrc) continue; UINT d = abs((int)lines.At(isec).line - (int)line); if (d < min_distance) { min_distance = d; lineIx = isec; if (0 == d) break; // We have found a record for the requested line! } } if (lineIx == (size_t)-1) return PDFSYNCERR_NORECORD_FOR_THATLINE; // we read all the consecutive records until we reach a record belonging to another line for (size_t i = lineIx; i < lines.Count() && lines.At(i).line == lines.At(lineIx).line; i++) records.Push(lines.At(i).record); return PDFSYNCERR_SUCCESS; }
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(); }
char *CbxEngineImpl::GetImageData(int pageNo, size_t& len) { if (cbzFile) { ScopedCritSec scope(&fileAccess); return cbzFile->GetFileData(fileIdxs.At(pageNo - 1), &len); } return NULL; }
static void BenchFile(TCHAR *filePath, const TCHAR *pagesSpec) { if (!file::Exists(filePath)) { return; } Timer total(true); logbench("Starting: %s", filePath); Timer t(true); BaseEngine *engine = EngineManager(!gUseEbookUI).CreateEngine(filePath); t.Stop(); if (!engine) { logbench("Error: failed to load %s", filePath); return; } double timems = t.GetTimeInMs(); logbench("load: %.2f ms", timems); int pages = engine->PageCount(); logbench("page count: %d", pages); if (NULL == 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("Finished (in %.2f ms): %s", total.GetTimeInMs(), filePath); }
EventHandler *FindEventHandler(HWND hwnd) { for (size_t i = 0; i < allEventHandlers.Count(); i++) { EventHandler *h = allEventHandlers.At(i); if (hwnd == h->hwnd) return h; } return NULL; }
PageElement *EbookEngine::GetElementAtPos(int pageNo, PointD pt) { Vec<PageElement *> *els = GetElements(pageNo); if (!els) return NULL; PageElement *el = NULL; for (size_t i = 0; i < els->Count() && !el; i++) if (els->At(i)->GetRect().Contains(pt)) el = els->At(i); if (el) els->Remove(el); DeleteVecMembers(*els); delete els; return el; }
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; }
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; }
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; }
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); }
// 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); } } } }
PageElement *DjVuEngineImpl::GetElementAtPos(int pageNo, PointD pt) { Vec<PageElement *> *els = GetElements(pageNo); if (!els) return NULL; // elements are extracted bottom-to-top but are accessed // in top-to-bottom order, so reverse the list first els->Reverse(); PageElement *el = NULL; for (size_t i = 0; i < els->Count() && !el; i++) if (els->At(i)->GetRect().Contains(pt)) el = els->At(i); if (el) els->Remove(el); DeleteVecMembers(*els); delete els; return el; }
static BencDict *SerializeStruct(PrefInfo *info, size_t count, const void *structBase, BencDict *prefs=NULL, uint32_t bitmask=-1) { if (!prefs) prefs = new BencDict(); const char *base = (const char *)structBase; for (size_t i = 0; i < count; i++) { PrefInfo& meta = info[i]; if ((meta.bitfield & bitmask) != meta.bitfield) continue; switch (meta.type) { case Pref_Bool: prefs->Add(meta.name, (int64_t)*(bool *)(base + meta.offset)); break; case Pref_Int: prefs->Add(meta.name, (int64_t)*(int *)(base + meta.offset)); break; case Pref_Str: case Pref_UILang: if (*(const char **)(base + meta.offset)) prefs->AddRaw(meta.name, *(const char **)(base + meta.offset)); else delete prefs->Remove(meta.name); break; case Pref_WStr: if (*(const WCHAR **)(base + meta.offset)) prefs->Add(meta.name, *(const WCHAR **)(base + meta.offset)); else delete prefs->Remove(meta.name); break; case Pref_DisplayMode: prefs->Add(meta.name, DisplayModeConv::NameFromEnum(*(DisplayMode *)(base + meta.offset))); break; case Pref_Float: prefs->AddRaw(meta.name, ScopedMem<char>(str::Format("%.4f", *(float *)(base + meta.offset)))); break; case Pref_IntVec: Vec<int> *intVec = *(Vec<int> **)(base + meta.offset); if (intVec) { BencArray *array = new BencArray(); for (size_t idx = 0; idx < intVec->Count(); idx++) { array->Add(intVec->At(idx)); } prefs->Add(meta.name, array); } else delete prefs->Remove(meta.name); break; } } return prefs; }