Add IPdfPage/extractPdfPageData: a Worker-clonable slice for PDF assembly - #42
Merged
Conversation
…mbly addPdfPage()/addTextOnlyPdfPage() needed a full IPage (recognition data plus a bunch of protocol/address metadata fields neither actually reads), which isn't structured-clone-safe in the way it's normally obtained (straight off a parsed SupernoteX). Widened their signature to accept IPdfPage - IRenderablePage (already used for rasterization, see extractPageRenderData) plus recognitionElements, the one extra field addPdfPage/addTextOnlyPdfPage actually read off IPage. IPage still satisfies IPdfPage structurally, so this is purely additive - existing callers passing a full parsed IPage are unaffected. extractPdfPageData() mirrors extractPageRenderData() to produce this slice, so a whole PDF (rasterize + embed + save) can now be built entirely inside a Worker from data sent in one structured-clone message, without reconstructing a fake IPage there - see the supernote-obsidian- plugin plugin's buildPdfInWorker() for the motivating use (pdf-lib's own embedPng() is expensive enough that keeping it off the host app's UI thread matters for a many-page note).
philips-clanker
added a commit
to philips/supernote-obsidian-plugin
that referenced
this pull request
Aug 1, 2026
pdf-lib's own embedPng() is genuinely expensive - confirmed via real-device testing to still peak ~1.7GB and take 10+ seconds for a 100-page note even after dropping each page's alpha channel, since it always fully decodes every embedded PNG to raw pixels and retains that until pdfDoc.save(). All of that is inherent to pdf-lib itself, not fixable from this plugin's side short of not using it - so instead of trying to shrink it further, move the whole rasterize-embed-save sequence off the main thread entirely. Bumps the supernote-typescript submodule (branch pdf-page-data-for-worker, PR philips/supernote-typescript#42) for IPdfPage/extractPdfPageData - a structured-clone-safe page slice that carries recognition text alongside rasterization data, letting a whole note's pages be sent to a Worker in one message. - myworker.worker.ts: new 'buildPdf' message does createPdfContext() through pdfDoc.save() in one call (pdf-lib's objects aren't structured-clone-safe, so this can't be split across round trips) - same per-batch alpha-flatten and PDF_ASSEMBLY_BATCH_SIZE batching as before, just relocated, plus the same heap/timing diagnostics logged from the Worker's own console instead. - main.ts: assemblePdfFromNote() replaced by buildPdfInWorker(), which spins up a *dedicated* Worker (not the shared pool used for on-screen rendering - tying up one of its 2 workers for 10+ seconds would stall page/thumbnail loading for just as long) and terminates it once done. Removed the now-dead `flatten` plumbing from the shared pool's 'convert' path, since PDF building no longer goes through it at all.
…annel
A prior fix dropped the alpha channel before PDF-embedding a page
(convertColor('RGB')), reasoning that background/unwritten pixels were
already packed as opaque white regardless of alpha. That was wrong: per
RattaRLEDecoder.buildPackedTranslation(), a fully-transparent pixel is
packed with its color channels at *black* (0,0,0) and alpha 0 -
specifically because nothing looks at the color while alpha is
respected. Dropping alpha instead of compositing revealed that stored
black as if it were solid ink, producing a black-background PDF with
the actual strokes barely visible - confirmed via a real exported PDF.
flattenToWhite() properly alpha-composites each pixel toward white
(standard flatten-onto-a-background-color blending) instead, giving
the correct result for fully-transparent (-> white), fully-opaque (->
unchanged), and anti-aliased in-between pixels alike. Added tests
covering all three cases plus the no-op path for an already-alpha-free
image.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Motivation
Building on this repo's supernote-obsidian-plugin: pdf-lib's own `embedPng()` fully decodes every embedded PNG to raw pixels and retains that until `pdfDoc.save()` - confirmed via real-device testing to peak ~1.7GB and take 10+ seconds for a 100-page note, entirely inherent to pdf-lib itself. Moving PDF assembly into a Worker keeps that off the host app's UI thread, but doing so needs a way to hand a whole note's pages (rasterization data and recognition text) to the Worker in one structured-clone message - this is that.
Test plan