Repository navigation
v0.1.10
PDFMathReader v0.1.10
This release includes 16 new features and 6 fixes and improvements since v0.1.9. See the highlights below.
0.1.10
New features
- reopen documents with their cached translation version and scope annotations to it — Record which kernel and language pairs hold cached translations for each document, in the document's cache scope so clearing it discards the record. Expose them through GET /api/documents/:id/translation-versions. When a document opens and any usable kernel and language version is cached, open that version (the current setting first) with translations shown, and let the normal reading-scope logic translate the remaining pages. A document without a cached version is treated as new and follows the open-mode setting. Translation highlights and comments now remember the version that drew them. Other versions show only the note marker on the source text instead of misplaced translated rectangles, and jumping to such an annotation or quick link falls back to the source view. (808359a)
- add experimental watermark removal by abnormal font and image size — Add an experimental, off-by-default setting (Settings > Experimental) that hides likely watermarks while rendering pages. The PDF file is never modified. Detection runs on the pdf.js operator list and is applied through the page.render operationsFilter, so only the watermark paint operations are skipped: - Text runs are removed when much larger than the body text and also diagonal, translucent, pale, or repeated at the same position across sampled pages. Diagonal translucent text is removed at a lower size threshold. - Images covering 8-85% of the page are removed when translucent, pale or repeated across pages. Full-page images (scans, backgrounds) are kept. - Size alone never removes anything, so titles, running footers and margin stamps stay. - Optional content layers named watermark are hidden. Render caches are keyed by the filter, and toggling the setting re-renders the visible pages. Adds analysis unit tests on real pdf.js operator lists and an Electron canvas pixel smoke test (--smoke-test=watermark). (848c530)
- hide removed watermark text from the text layer and translation layout — When experimental watermark removal is on, the text of the runs it removes from the canvas is also left out of the places that read page text: - The reading text layer drops spans that match a removed run, so the invisible watermark can no longer be selected or copied. The layer redraws when the setting is toggled. - Ultra fast layout drops paragraphs that only hold removed watermark text, so they are neither covered by a translation nor sent to be translated. Matching is by normalized text: a short piece must equal a whole removed run, a longer piece may also be part of one. If the page cannot be analysed, the layout is used unchanged. Adds unit tests for the matcher, the repeated-position trait and the layout filter, and extends the Electron smoke test to check the text layer. (7bfb5c3)
- add experimental dock-style magic zoom for recent document covers — Covers in the Recents gallery grow under the pointer and neighbours make room, like the macOS Dock. Off by default in Settings > Experimental; gallery view only; skipped for touch and reduced motion. (c66394a)
- zoom nearby recent covers like the macOS Dock — Widen the magic zoom falloff to scale with cover spacing so about three covers on each side grow, and add side padding so outer covers are not clipped. (d6f4e90)
- remove translucent page-sized watermark images — A tiled stamp is often a single image drawn several times larger than the page with a low fill alpha, so only a part of it shows. The image rule kept every image covering 85% of the page or more, which let those stamps through. Images at that size are now removed when they are translucent; opaque ones (scans, backgrounds) are still kept. Checked against a real research report whose every page carries one 25% alpha image of 15 times the page area: the stamp is gone from the canvas and returns when the setting is switched off. Across about 500 PDFs, only genuine stamps (Peer Review, Do Not Copy, Retracted, a faded map) are matched. (1aa136b)
- add experimental automatic table of contents generation — Add an experimental, off-by-default setting (Settings > Experimental) that adds Edit > Generate Table of Contents for documents without bookmarks. Generation runs locally and its progress is shown in the Contents sidebar; nothing is sent anywhere. - A printed contents page is parsed first, including dot leaders and right-aligned page numbers, and its page numbers are mapped to physical pages. - Without one, headings are inferred from font size, numbering and spacing. - Scanned pages without a text layer are not supported, and the generated contents are not saved with the document. Adds unit tests for the generator and the new preference. (3f33f3a)
- refine recents magic zoom with smoother easing and edge reveal — Focal cover reaches 2.4x with a two-cover reach, drawn over the Open PDF button on an opaque fill. Animation is script-driven with fade in/out, avoids per-frame layout reads, and slides partially visible covers into view on hover. Experimental labels drop the experimental suffix. (a09bc6c)
- redesign kernel mode switcher with symbol icons and sliding selection ring — Replace the screenshot-style mode previews with bolt.fill, Lucide scale and sparkles icons tinted via CSS mask. Tiles are now 56x56 squircles, and a single selection ring slides between them (disabled for reduced motion). (09923cd)
- verify Edit menu rotate current page is selectable — Assert that Rotate Current Page and the page-width/height alignment items are enabled once a document is open. Programmatic menu clicks bypass the enabled state, so the smoke test previously could not catch a greyed-out item. (aa6e05f)
- add Finder-style tab mode switcher to reading behavior settings — Reading Behavior gains a Tabs / Window setting (default Window). In tab mode reader windows join one group, only the active tab is shown, and a Finder-style tab bar sits full width beneath the header, above the sidebar and pages. New Window (Cmd-N) and New Tab (Cmd-T) open the start page as a tab; Ctrl-Tab cycles tabs. Switching to Tabs merges open windows; switching back splits tabs into separate windows. The tabs desktop smoke covers grouping, layout, selection, reorder, close, Cmd-N and mode switching. (231bf7c)
- scale sidebar thumbnails with sidebar width — Thumbnails grow and shrink with a resized sidebar relative to its default width while each page keeps its aspect ratio. The list stays anchored on the visible page and redraws sharply once a drag ends. (a96c772)
- open a page gallery by double-clicking a sidebar thumbnail — Double-clicking a sidebar thumbnail opens a full-window contact sheet of every page that covers both the sidebar and the reader. The chosen page flies from the sidebar into its grid tile while the remaining tiles ripple in. Double-clicking a tile, or pressing Enter, flies that page back and resumes reading there; Esc closes the gallery without changing the page. Arrow keys, Home and End move the selection. Tiles use AppKit-style paper shadows with a lifted shadow during flight and a dark-mode variant. (f51d80e)
- offer Open Link for URLs detected in selected text (a189949)
- offer Copy Email for addresses detected in selected text (6aedde8)
- add experimental side-by-side translation compare view — Translation > Compare… opens a two-column view: each side picks its own kernel and service, the left starting from the reader's current choice and the right from an empty setup card. Each lane renders and translates asynchronously. Disabled by default under Experimental settings. (eb5c7bd)
Fixes and improvements
- keep visible pages from staying blank after a failed paint — A paint that threw aborted its lane and the rest of the pass, and nothing repainted the page until the next scroll or zoom. Contain each page paint, log the error, and retry a visible empty canvas up to three times with a growing pause. A cached frame no longer counts as a hit when its canvas was emptied. Release the pdf.js page data of watermark-sample pages so the scan no longer holds tens of megabytes per page. (37ad4ea)
- render translated style tags and formula placeholders as upstream parses them — Precise (BabelDOC) only parses <style id='N'>...</style> and {vN}. When the LLM translated the tag name (<样式 id='1'>) or requoted it, the tag was typeset as literal text. Rewrite any numeric-id tag to the canonical form and collapse {{vN}} before BabelDOC parses the output, and take the text layer from the parsed composition instead of the raw output. Fast (pdf2zh) only typesets \{\s*v([\d\s]+)\}. Collapse {{vN}} and resolve {v*} when the source paragraph has exactly one formula; otherwise keep the source. The frontend restore now drops unknown formula IDs, matching upstream. Sidecar schema is bumped to 2; cached pages from older schemas whose raw output contains placeholders upstream cannot parse are retranslated on next open. (e8e3df2)
- stop idle reader re-render loop from visibility reset — viewportPages() reset every visible page's `visible` ratio to 0 and then restored it, producing two reactive writes per page on each call. For translated (compacted) pages, pageLayout reads `visible`, so the pageLayout watcher re-scheduled the viewport, which re-ran viewportPages() on the next frame. The reader re-rendered all mounted pages and rebuilt emphasis overlays at display refresh rate while idle (~170% CPU in the packaged app). - Only reset pages that actually left the viewport. - Skip replacing compaction results on a cache hit when the cached entry is already current. - Add a regression test asserting that an unchanged viewport triggers no reactive updates. Idle CPU on the same translated document drops from ~170% to ~6%. (328641c)
- keep compacted pages and text layers stable while scrolling — Scrolling a translated document with paragraph-gap compaction spent about 45% of the renderer main thread on work unrelated to the scroll itself: - The compacted page view spread the reactive page, so it read `visible`. Every scroll frame rebuilt the view for each compacted page, the full-document layout, and each mounted page's paragraph boxes, which re-ran text-layer emphasis (per-span geometry reads) and redrew the topic-sentence overlay. Build the view once per page in a cached computed and forward non-geometry fields through accessors, so readers still track the source page. - ReaderPage rendered from the raw visibility ratio; render from a shown/hidden computed instead. - ReadingAnnotations bumped its layout version on every scroll event, re-measuring every text span for the page-relative note rail. Scroll now updates only popup positions. In the packaged app, scrolling the same document drops main-thread busy time from ~45% to ~10%, DOM mutations from ~3,100 to ~1,100 per 4 s, and the worst frame from 42 ms to 23 ms. (66ade65)
- draw topic emphasis as boxes and stop double-rendering scroll previews — Topic-sentence emphasis used a page-sized canvas per page (~16 MB each), and stale text-layer observers kept old ones alive. Draw it as positioned boxes instead, so it costs no raster memory. Scroll previews and prefetches of a fit-width Retina page rendered at ~1.95x and were redrawn at 2x once settled, caching both. Raise the off-screen budget to fit the full ratio, let a cached sharp frame serve later previews, and drop the preview frame once the sharp one is cached. Scroll benchmark on a 24-page PDF: page renders 89 -> 28, detached canvases 127.6 MB -> 52.1 MB, GPU process footprint after scrolling 2230 MB -> 1688 MB. (c905818)
- release text layers and pages once they leave the viewport — Inactive reading text layers cleared their host but kept module-level references to the detached topic and information overlays, which an <html style> MutationObserver kept reachable. Clear the overlays wherever the host is emptied and drop the observer; both colors already follow var(--accent) in CSS. The shared document selection listeners were created inside the first bindTextLayerSelection call, so their closure context retained that layer's host and its whole detached page until every layer unbound. Install them from a module-level function that does not capture a host. After the Alawode scroll bench and GC: detached DOM roots 52 -> 24, nodes 305 -> 244, with no detached page-wrap, information-highlights or endOfContent left. The remaining detached canvases belong to the 64 MB-capped bitmap cache. Frames avg 8.4 ms, max 17.4 ms, none over 20 ms. Drag selection across runs still works. (ae095a0)
Update
Quit PDFMathReader, download the package for your system below, replace the existing application, and reopen it. Your documents and settings are preserved.