Word → PDF: true 1:1 render + Color/B&W option - #2
Merged
Conversation
The Document converter's docx → pdf path ran the file through mammoth, a semantic converter that discards table borders, column widths, cell fills, fonts, and rotated text. For a form-style document (e.g. a maintenance shift log) that collapsed the entire layout — the PDF looked nothing like Word. Add a dedicated high-fidelity path: renderDocxPdf() in pdfrender.js renders the real OOXML with docx-preview inside an offscreen Chromium window, then prints to PDF. It's a genuine 1:1 conversion, still fully offline, and needs no LibreOffice. Page geometry (and thus pagination) comes from the document's own w:pgSz via parseDocxPageSize, so it matches Word. On any parse failure it falls back to the mammoth intermediate so the conversion never hard-fails. Add a "PDF colour" option (Color / Black & White). B&W applies a filter:grayscale(100%) at print time; it also works for the md/html/txt → pdf paths. Implementation notes: - docx-preview needs JavaScript enabled, so pdfrender now runs two pools: htmlPool (javascript:false, untrusted converted HTML) and docxPool (javascript:true). Both share the same private session that cancels every non-local request, so the offline guarantee is unchanged (test/electron-offline.js still passes). - Windows are pooled and reused, not created/destroyed per file: on Electron 43, destroying a window and immediately creating another in the same session deadlocks the second (first docx converts, next hangs). Reuse + park on about:blank before release, same discipline as the HTML pool. - docx-preview + jszip are pure JS and stay in the asar; their browser builds are read with fs and inlined into the render page. Tests: new test/pdfrender.test.js unit-tests parseDocxPageSize under plain node; test/electron-pdf.js gains a docx → pdf case (color + B&W, sequential renders). Verified end-to-end against the real reported document (2 pages, matching Word) and under a concurrent 6-file batch (pool caps at POOL_SIZE, no hang). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GoZVDGfDjZwmGi3HNWda9B
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GoZVDGfDjZwmGi3HNWda9B
AxialForge
marked this pull request as ready for review
July 24, 2026 14:56
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.
The problem
A user reported that converting a Word document to PDF (a maintenance shift-log form) produced output that "does not look right." The
docx → pdfpath ran the file through mammoth, which is a semantic converter — it deliberately discards table borders, column widths, cell fills, fonts, and rotated text. For a form-style document that collapses the entire layout, so the PDF looked nothing like the original.Before → After (the actual reported document, rendered through the real app code):
The fix
renderDocxPdf()inpdfrender.jsrenders the real OOXML with docx-preview (pure JS) inside an offscreen Chromium window, thenprintToPDF. Fully offline, no LibreOffice required. Page geometry — and therefore pagination — is read from the document's ownw:pgSz(parseDocxPageSize), so it matches Word. If docx-preview can't parse a given file, it falls back to the mammoth intermediate so the conversion never hard-fails.filter:grayscale(100%)at print time and also works for the md/html/txt → pdf paths.Implementation notes
pdfrender.jsrunshtmlPool(javascript:false, for untrusted converted HTML) anddocxPool(javascript:true). Both share the same private session that cancels every non-local request, so the offline guarantee is unchanged (test/electron-offline.jsstill passes).about:blankbefore release, the same discipline the HTML pool already follows. Reproduced the hang and confirmed the fix.fsand inlined into the render page.CLAUDE.md.Testing
test/pdfrender.test.js— unit-testsparseDocxPageSize(Letter/A4/landscape/malformed) under plainnode.test/electron-pdf.js— adocx → pdfcase that builds a fixture withhtml-to-docx, converts it Color and B&W, and asserts both are valid PDFs and that greyscale changed the output.POOL_SIZE, no hang).Version
Bumped to
1.2.0;CHANGELOG.md,CLAUDE.md, andFORMATS.mdupdated.🤖 Generated with Claude Code
https://claude.ai/code/session_01GoZVDGfDjZwmGi3HNWda9B
Generated by Claude Code