Skip to content

Add Windows PDFium system-renderer fallback for render_pdf_page/region - #195

Open
dancarloni wants to merge 10 commits into
Open-Document-Alliance:masterfrom
dancarloni:feat/windows-pdfium-renderer
Open

dancarloni wants to merge 10 commits into
Open-Document-Alliance:masterfrom
dancarloni:feat/windows-pdfium-renderer

Conversation

@dancarloni

Copy link
Copy Markdown

Summary

render_pdf_page and render_pdf_region have never had a Windows fallback: pdfjsRendererPolicy(), systemRenderPage/Region, and runSystemCommand's spawn whitelist were all darwin-only, so any host whose native @napi-rs/canvas binding is blocked or unavailable on win32 gets "No PDF page renderer is available in this host" with no recourse — including Claude Desktop's embedded Electron UtilityProcess, which already has a proven, deliberate native-canvas block with its own crash latch and no fallback to catch it on Windows.

  • Adds a Windows counterpart to the macOS qlmanage/sips fallback, using Google Chromium's official PDFium prebuilt library (via bblanchon/pdfium-binaries) called through koffi FFI, with PNG encoding done in pure JavaScript (server/png-encoder.js) since PDFium's C API renders to a raw bitmap and has no image encoder.
  • Isolation is hybrid, because a single mechanism fails differently in each host that matters:
    • Ordinary Windows hosts (Cursor, a plain CLI run): the PDFium render runs in a disposable child process, spawned through the same runSystemCommand sandboxing already proven for qlmanage/sips on darwin (whitelisted command, shell:false, strict argument-shape validation, a hard timeout, one system-render child at a time).
    • Claude Desktop's embedded Electron UtilityProcess: relaunching process.execPath as a child process is not reliable there (its process.execPath is the Electron/Claude binary, not a plain node.exe). The render instead runs on a dedicated worker_threads.Worker, terminated on the same deadline, mirroring the pattern already proven in this codebase for the QPDF WebAssembly decryption worker and for pdf-lib mutations inside this exact host.
  • pdfjsRendererPolicy() now returns native_with_system_fallback on win32 when the vendored pdfium.dll exists for the host's architecture; output-schemas.js's renderer enum gains "windows-pdfium".
  • Vendors the official win-x64 and win-arm64 PDFium builds (bblanchon/pdfium-binaries tag chromium/8044, PDFium 155.0.8044.0, built without V8/XFA) under vendor/pdfium/, verified against pinned SHA-256 hashes in sources.lock.json and reproducible via scripts/vendor-pdfium-runtime.mjs. Every bundled component is permissively licensed (BSD/MIT/zlib/Apache-2.0/FTL/Unicode), no copyleft, all compatible with this project's MIT license — see vendor/pdfium/README.md and runtime.provenance.json.
  • Adds koffi as a dependency (prebuilt per-platform bindings, no compilation) and test/pdfium-runtime-artifact.test.js, which binds the committed DLLs to their pinned hashes and (gated to win32) actually loads pdfium.dll and renders a page. .github/workflows/windows-render.yml and its probe script now also exercise this path on the existing windows-latest workflow_dispatch job, including the scenario this fallback was built for: the embedded native-canvas block in force with the system renderer available.
  • Regenerates the npm license provenance record for koffi's locked packages and the layout-occurrence-oracle.v1.json fixture (both stale after this change touched package.json/package-lock.json and output-schemas.js); fixes test/mcp-contract.test.js's share-mirror file count/list and pinned tool-contract hash; adds renderer_label to two hand-built system_command test fixtures. Mirrors all server-side changes into pdf-toolkit-mcp-share/ per this project's existing convention.
  • Does not touch the macOS qlmanage/sips renderer path, its error messages' default wording, or its tests: shared error-message helpers now take an optional rendererLabel (default "macOS", unused by every existing call site).
  • Wires vendor/pdfium/ and koffi into scripts/build-mcpb.mjs's per-target staging (stagePdfiumDllsForTarget, installKoffiForTarget, win32 targets only), extends the staged-vendor-inventory assertion and its provenance check (verifyPdfiumRuntime), and updates the CycloneDX SBOM (scripts/pdfium-sbom.mjs) so a built .mcpb now actually ships the DLLs and platform-specific koffi bindings for win32-x64/arm64. The Cursor share ZIP intentionally excludes PDFium (Windows-only, MCPB-specific).

Test plan

  • npm test — clean except for pre-existing, environment-specific failures unrelated to this change (confirmed against a clean baseline checkout of the same base commit): process-group/chmod-dependent and network-dependent tests that don't behave correctly in a sandboxed CI-like container running as root.
  • npm run build:mcpb — reproducible, byte-identical build; staged MCPB verified to contain vendor/pdfium/runtime/win-{x64,arm64}/pdfium.dll and the platform-specific koffi packages.
  • npm run smoke:mcpb — passes against the built artifact.
  • npm run test:contract:share — passes; share package correctly excludes the Windows-only PDFium runtime.
  • test/pdfium-runtime-artifact.test.js — 6/7 pass on Linux; the 7th (real pdfium.dll load + render) is gated to win32 and will run for real on windows-latest.
  • Manual render_pdf_page/render_pdf_region run on a real Windows host — requested from the PR author; not yet performed.

This PR has already been developed, tested, and verified on a fork (dancarloni#1), where it is green on CI and mergeable. This request proposes it as an upstream contribution, as a draft pending review from the Open Document Alliance maintainers.

render_pdf_page and render_pdf_region have never had a Windows fallback:
pdfjsRendererPolicy(), systemRenderPage/Region, and runSystemCommand's
spawn whitelist were all darwin-only, so any host whose native
@napi-rs/canvas binding is blocked or unavailable on win32 gets
"No PDF page renderer is available in this host" with no recourse. This
is exactly the shape of Claude Desktop's embedded Electron UtilityProcess,
which already has a proven, deliberate native-canvas block with its own
crash latch (see nativeCanvasBlockRemediation and the embedded-host
detection in pdfjs-subprocess.js) and no fallback to catch it on Windows.

Add a Windows counterpart to the macOS qlmanage/sips fallback, using
Google Chromium's official PDFium prebuilt library (via
bblanchon/pdfium-binaries) called through koffi FFI, with PNG encoding
done in pure JavaScript (server/png-encoder.js) since PDFium's C API
renders to a raw bitmap and has no image encoder.

Isolation is hybrid rather than a single mechanism, because the two
plausible designs each fail differently in exactly one of the two hosts
that matter:

- Ordinary Windows hosts (Cursor, a plain CLI run): the PDFium render
  runs in a disposable child process, spawned through the same
  runSystemCommand sandboxing already proven for qlmanage/sips on darwin
  (whitelisted command, shell:false, strict argument-shape validation, a
  hard timeout, one system-render child at a time). A hang or crash in
  pdfium.dll only takes down that child.
- Claude Desktop's embedded Electron UtilityProcess: relaunching
  process.execPath as a child process is not reliable there (its
  process.execPath is the Electron/Claude binary, not a plain node.exe;
  see the selectPdfjsIsolationMode comment in pdfjs-subprocess.js
  documenting the same class of failure for the PDF.js render worker
  itself). The render instead runs on a dedicated worker_threads.Worker,
  terminated on the same deadline, mirroring the pattern already proven
  in this codebase for the QPDF WebAssembly decryption worker and for
  pdf-lib mutations inside this exact host.

pdfjsRendererPolicy() now returns native_with_system_fallback on win32
when the vendored pdfium.dll exists for the host's architecture, mirroring
the darwin check; output-schemas.js's renderer enum gains
"windows-pdfium".

Vendors the official win-x64 and win-arm64 PDFium builds
(bblanchon/pdfium-binaries tag chromium/8044, pdfium 155.0.8044.0, built
without V8/XFA) under vendor/pdfium/, verified against pinned SHA-256
hashes in sources.lock.json and reproducible via
scripts/vendor-pdfium-runtime.mjs. Every bundled component (PDFium
itself plus FreeType, libpng, zlib, lcms, libjpeg-turbo, OpenJPEG, ICU,
Abseil, LLVM libc, simdutf, fast_float, agg23) is permissively licensed
(BSD/MIT/zlib/Apache-2.0/FTL/Unicode) with no copyleft component, all
compatible with this project's MIT license; see vendor/pdfium/README.md
and runtime.provenance.json for the component-by-license breakdown.

Adds koffi as a dependency (prebuilt per-platform bindings, no
compilation, consistent with this project's existing constraints) and
test/pdfium-runtime-artifact.test.js, which binds the committed DLLs to
their pinned hashes and (gated to win32, since koffi cannot load a
Windows PE binary elsewhere) actually loads pdfium.dll and renders a
page. .github/workflows/windows-render.yml and its probe script now also
exercise this path on the existing windows-latest workflow_dispatch job,
including the scenario this bug report was actually about: the embedded
native-canvas block in force with the system renderer available.

Regenerates the npm license provenance record for koffi's locked
packages and the layout-occurrence-oracle.v1.json fixture, both stale
after this change touched package.json/package-lock.json and
output-schemas.js; fixes test/mcp-contract.test.js's share-mirror file
count/list (it only matched *.js, missing the new *.mjs file) and its
pinned tool-contract hash (moved by the legitimate renderer enum
addition); adds renderer_label to two hand-built system_command test
fixtures in test/pdfjs-subprocess-boundary.test.js that predate that
field. Mirrors all server-side changes into pdf-toolkit-mcp-share/ per
this project's existing convention.

Does not touch the macOS qlmanage/sips renderer path, its error
messages' default wording, or its tests: the shared error-message
helpers now take an optional rendererLabel (default "macOS", unused by
every existing call site) purely to word Windows failures correctly
without duplicating them.

Not yet done: vendor/pdfium/ and koffi are not wired into
scripts/build-mcpb.mjs's MCPB packaging or the CycloneDX SBOM, so a
built .mcpb or share ZIP does not yet ship the DLLs — the existing
staged-vendor-inventory assertion in build-mcpb.mjs will fail loudly
rather than ship an incomplete extension until that follow-up lands.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
package-for-friend.js derives the share package's package.json
dependencies from a hardcoded list rather than copying package.json
verbatim, and koffi was missing from it: verifyShareLock() then found
pdf-toolkit-mcp-share/package-lock.json's root record disagreeing with
the derived manifest (the lock has koffi, the derived manifest didn't),
failing npm run test:contract:share with "package-lock.json is stale at
packages[\"\"].dependencies".

Caught by the real CI run on PR #1 once GitHub Actions was enabled on
the fork (this repo's git history has no CI runner, so this is the
first time this specific script has actually executed against this
change) — both matrix legs (node 20.19, node 22.12) failed identically
at the same step for the same reason, with the "Test suite" step
passing cleanly on both (2814/2814, no environment-specific noise, on
real hardware). Verified locally with `npm run test:contract:share`
before pushing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
- validatePdfiumRenderRequest compared realpath(args[0]) against the
  pinned expectedDllPath, but args[0] is already checked with strict
  string equality against that same constant a few lines above, and
  lstat(args[0]) already rejects a symlinked DLL path. If the checkout
  itself is reached through a symlinked path component (a real
  possibility: bind mounts, npm workspace layouts, /tmp on macOS),
  realpath() resolves to a different string than the literal constant
  and a correctly-pinned request is wrongly rejected. Drop the redundant
  realpath comparison; the whitelist is unweakened by the two checks
  that remain.

- The embedded-host PDFium render path (runPdfiumWorkerRender, a
  worker_threads.Worker) never enforced the one-system-renderer-at-a-time
  cap that the subprocess path already enforces via systemChildren.size
  in pdfjs-subprocess.js, so concurrent renders inside the Electron host
  had no bound. Add the same guard, reusing the existing
  system_renderer_concurrency_limit reason.

Mirrored into pdf-toolkit-mcp-share/. Verified with the targeted test
files (pdfjs-subprocess-boundary, pdfjs-worker-contract, render-pdf-page,
mcp-contract): no new failures beyond the one pre-existing,
environment-specific flake already documented on this PR (process-group
tracking under this sandbox's root container).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
- Create scripts/pdfium-runtime.mjs: exports runtime assets, files list, and verification function
- Create scripts/pdfium-sbom.mjs: derives CycloneDX SBOM components from provenance
- Update scripts/build-mcpb.mjs: stage PDFium DLLs and koffi packages per target platform
- Update package-for-friend.js: include PDFium components in share package SBOM
- Verify PDFium runtime against provenance during build validation
- Platform-conditional staging: Windows targets only receive pdfium.dll and koffi
- Reproducible builds: byte-identical verification across isolated builds

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
The share package contract test creates an isolated build root and
copies only specific script sources to it. With the new pdfium-runtime.mjs
and pdfium-sbom.mjs scripts now imported by package-for-friend.js, they
must be included in ISOLATED_PACKAGER_SCRIPT_SOURCES so the test can
create a complete package build in an isolated checkout.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
The share package contract test creates an isolated build root with
only the necessary files for packaging. Since package-for-friend.js now
imports pdfium-runtime.mjs which reads vendor/pdfium/runtime.provenance.json,
and since pdfium-sbom.mjs references license files in vendor/pdfium/licenses/,
those files must be copied to the isolated build root just like the QPDF
runtime files are.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
The share contract test derives the expected SBOM component count from
the files actually shipped in the archive. The function expectedNativeSbomShape
only accounted for QPDF WASM runtime components, but now also includes
PDFium runtime components (runtime + bundled libraries + build recipe).
Update the function to read PDFium's runtime.provenance.json and count
its components.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
PDFium is a Windows-only runtime included in the MCPB platform-specific
packaging, not in the cross-platform share package for Cursor. The share
package contains only QPDF WASM components, not PDFium. Remove:

- PDFIUM_SBOM_DEPENDENCIES from share package SBOM dependencies
- PDFIUM_SBOM_COMPONENTS from share package SBOM components
- PDFium component validation from validateCycloneDxSbom
- PDFium provenance/licenses from test build root population
- PDFium component counting from test SBOM shape calculation

The PDFIUM imports and exports remain for use by build-mcpb.mjs.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
…oading

pdfium-runtime.mjs reads vendor/pdfium/runtime.provenance.json at module
import time (top-level code), so the file must exist in the isolated build
root even though PDFium runtime files are not included in the share package.
Copy only the provenance file (not the runtime directory or licenses) to
allow package-for-friend.js to import and load the module successfully.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
… list

build-mcpb.mjs now imports it, which is part of build-agent-plugin.mjs's
static import closure. test/plugin-freshness-coverage.test.js caught the
gap: the freshness gate would have missed changes to it and reported a
stale published plugin as fresh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HK9BXJsKMD4rzNAkJpR7J
Copilot AI lite review requested due to automatic review settings September 13, 2026 04:14
@dancarloni dancarloni changed the title Feat/windows pdfium renderer Add Windows PDFium system-renderer fallback for render_pdf_page/region Sep 13, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical and moderate findings remain in Windows compatibility, rendering isolation, correctness, and MCPB/SBOM packaging.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds a Windows PDFium fallback for PDF page and region rendering when native canvas is unavailable.

Changes:

  • Adds PDFium/koffi rendering, hybrid isolation, and pure-JavaScript PNG encoding.
  • Vendors Windows runtimes and updates packaging, SBOM, provenance, licensing, and dependencies.
  • Adds mirrored share-package updates, contract changes, artifact tests, and Windows CI coverage.
File summaries
File Reviewed changes / final comments
vendor/pdfium/sources.lock.json Pins PDFium sources and hashes.
vendor/pdfium/runtime.provenance.json Runtime provenance; nit (3 votes): stale integration status and incorrect share ZIP claim.
vendor/pdfium/README.md Runtime documentation; nit (2 votes): documented generation does not match the promotion script.
vendor/pdfium/licenses/zlib.txt Bundled license notice.
vendor/pdfium/licenses/simdutf.txt Bundled license notice.
vendor/pdfium/licenses/pdfium.txt Bundled license notice.
vendor/pdfium/licenses/pdfium-binaries-build-scripts.txt Bundled license notice.
vendor/pdfium/licenses/manifest.json License manifest.
vendor/pdfium/licenses/llvm-libc.txt Bundled license notice.
vendor/pdfium/licenses/libpng.txt Bundled license notice.
vendor/pdfium/licenses/libopenjpeg.txt Bundled license notice.
vendor/pdfium/licenses/libjpeg_turbo.md Bundled license notice.
vendor/pdfium/licenses/libjpeg_turbo.ijg Bundled license notice.
vendor/pdfium/licenses/lcms.txt Bundled license notice.
vendor/pdfium/licenses/freetype.txt Bundled license notice.
vendor/pdfium/licenses/fast_float.txt Bundled license notice.
vendor/pdfium/licenses/agg23.txt Bundled license notice.
vendor/pdfium/licenses/abseil.txt Bundled license notice.
vendor/npm-licenses/npm-license-provenance.json Regenerated npm license provenance.
test/pdfjs-subprocess-boundary.test.js Updates system-command fixtures.
test/pdfium-runtime-artifact.test.js Verifies runtime hashes and Windows loading/rendering.
test/mcp-contract.test.js Updates share-package contract data.
test/fixtures/eval/extraction/phase1/layout-occurrence-oracle.v1.json Regenerated evaluation fixture.
server/png-encoder.js Pure-JavaScript PNG encoding; nit (1 vote): add decoded pixel validation.
server/pdfjs-worker.js Windows rendering and isolation; critical (1 vote): worker termination cannot contain native crashes; nits (2 and 3 votes): subprocess and region paths lack CI coverage.
server/pdfjs-subprocess.js Windows command validation and subprocess support.
server/pdfium-render-host.mjs PDFium FFI rendering; moderate (1 vote): pass page rotation to PDFium.
server/output-schemas.js Adds the Windows renderer enum; critical (1 vote): update trajectory-grader renderer allowlists.
server/index.js Enables Windows fallback selection; critical (2 votes): update the Windows renderer-policy test assertion.
scripts/vendor-pdfium-runtime.mjs Promotes pinned runtime assets; moderate (1 vote): rebuild runtime and license directories from scratch.
scripts/test-share-contract.mjs Share-package contract support.
scripts/plugin-shipped-paths.mjs Shipped-path inventory support.
scripts/pdfium-sbom.mjs PDFium SBOM generation; moderate findings (1, 2, 1, and 1 votes): fix notice URLs and hashes, add DLL references, and use valid SPDX identifiers.
scripts/pdfium-runtime.mjs Defines and verifies PDFium runtime assets.
scripts/build-mcpb.mjs Stages Windows runtime assets; moderate (3 votes): include PDFium license notices and synchronize inventory/hash checks.
pdf-toolkit-mcp-share/server/png-encoder.js Mirrored PNG encoder.
pdf-toolkit-mcp-share/server/pdfjs-worker.js Mirrored Windows rendering worker.
pdf-toolkit-mcp-share/server/pdfjs-subprocess.js Mirrored subprocess support.
pdf-toolkit-mcp-share/server/pdfium-render-host.mjs Mirrored PDFium rendering host.
pdf-toolkit-mcp-share/server/output-schemas.js Mirrored output schema updates.
pdf-toolkit-mcp-share/server/index.js Mirrored renderer policy updates.
pdf-toolkit-mcp-share/package.json Mirrored package metadata.
package.json Adds koffi dependency.
package-lock.json Locks dependency updates.
package-for-friend.js Packaging and SBOM integration; moderate (3 votes): add PDFium records to the generated MCPB SBOM.
.github/workflows/windows-render.yml Adds Windows rendering workflow coverage.
.github/workflows/windows-render-probe.mjs Adds embedded-host rendering probe.
.gitattributes Tracks vendored runtime assets appropriately.
Review details

Files not reviewed (1)

  • pdf-toolkit-mcp-share/package-lock.json: Generated file

Suppressed comments (6)

scripts/pdfium-sbom.mjs:113

  • Even after the notices are staged, this reference drops the licenses/ directory: the files live at vendor/pdfium/licenses/<name>, so every CycloneDX license URL currently points to a nonexistent path. Include licenses/ in the URL so SBOM consumers can resolve the notices.
  const assetPath = `vendor/pdfium/${notice.file}`;

scripts/pdfium-sbom.mjs:162

  • The PDFium runtime component has no hash or distribution reference for either architecture's pdfium.dll, even though the provenance record carries both digests. The generated SBOM therefore cannot bind the shipped native payload to the reviewed bytes, unlike the existing QPDF runtime component; add architecture-specific DLL references and hashes.
  return {
    type: "library",
    "bom-ref": RUNTIME_BOM_REF,
    group: "pdf-tools",
    name: "pdfium-runtime",
    version,
    scope: "required",
    description:
      `Official Google Chromium PDFium prebuilt binaries from bblanchon/pdfium-binaries, `
      + `shipping Windows platform binaries (win-x64 and win-arm64) at ${RUNTIME_DIRECTORY}/ `
      + `in the MCPB for Windows targets only.`,
    purl: `pkg:generic/pdf-tools/pdfium-runtime@${version}`

scripts/pdfium-sbom.mjs:200

  • PDFIUM_SBOM_COMPONENTS copies each provenance spdx value directly into a CycloneDX license expression, but the new provenance uses AGG for agg23 and the repository's pinned SPDX identifier record contains no AGG identifier. Once this graph is included, the component and composite runtime license emit an invalid SPDX expression; use a valid SPDX identifier or an explicit LicenseRef-* representation and validate it.
  return bundledAndRecipe.map(notice => ({
    type: "library",
    "bom-ref": `urn:pdf-tools:pdfium-bundled:${notice.component}`,
    name: notice.component,
    version: notice.component.includes(" ") ? notice.component.split(" ").pop() : "embedded",
    scope: "required",
    description: `${notice.component}; statically linked into ${RUNTIME_DIRECTORY}/pdfium.dll (Windows platforms only).`,
    licenses: [{ expression: notice.spdx }],
    externalReferences: [
      noticeReference(provenance, notice),
    ],

scripts/vendor-pdfium-runtime.mjs:81

  • The promotion script writes into existing runtime and license directories without clearing them first. If a future pinned release removes a DLL or notice, rerunning this generator leaves the old file behind and writeLicenseManifest() records it as shipped, so the promoted tree is no longer an exact representation of the pinned archives. Rebuild the promoted directories from scratch before copying the new assets, as the QPDF promotion script does.
  const runtimeDir = path.join(VENDOR_DIR, "runtime", asset.arch);
  await mkdir(runtimeDir, { recursive: true });
  await writeFile(path.join(runtimeDir, "pdfium.dll"), await readFile(dllPath));

server/pdfium-render-host.mjs:105

  • The PDFium render call hard-codes its rotate argument to 0, while systemRenderPageWindows() sizes and crops the bitmap from PDF.js's already-rotated page view. PDFium's render API takes page rotation explicitly, so a page with /Rotate is rendered in unrotated page space and region crops can be misplaced or distorted. Pass the page rotation (or an equivalent transform) to PDFium and cover a rotated page in the Windows system-renderer path.
    fn.FPDFBitmap_FillRect(bitmap, 0, 0, widthPx, heightPx, 0xffffffff);
    fn.FPDF_RenderPageBitmap(bitmap, page, 0, 0, widthPx, heightPx, 0, FPDF_ANNOT);

server/png-encoder.js:81

  • The new encoder's current runtime check only verifies the PNG signature; the Windows artifact test does not decode the image or verify pixel values. A BGRA channel, row-order, or stride regression could therefore pass the Windows gate while returning a corrupted render. Add a focused test with known RGBA bytes and assert decoded dimensions and pixels.
  const raw = Buffer.alloc((stride + 1) * height);
  const rgbaBuffer = Buffer.isBuffer(rgba) ? rgba : Buffer.from(rgba);
  for (let row = 0; row < height; row += 1) {
    const rawOffset = row * (stride + 1);
    raw[rawOffset] = 0; // filter type: None
  • Files reviewed: 28/52 changed files
  • Comments generated: 8
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread server/index.js
if (forced) return "system";
if (disabled || process.platform !== "darwin") return "native";
if (disabled || !available) return "native";
return "native_with_system_fallback";
Comment thread server/output-schemas.js
rendered_height_px: integer,
scale: number,
renderer: enumString(["native-canvas", "macos-sips", "macos-quicklook"]),
renderer: enumString(["native-canvas", "macos-sips", "macos-quicklook", "windows-pdfium"]),
Comment thread server/pdfjs-worker.js
Comment on lines +2128 to +2130
const worker = new Worker(PDFIUM_RENDER_HOST_URL, {
workerData: { pdf_tools_worker: "pdfium_render", dllPath, heightPx, outputPngPath, sourcePdfPath, widthPx },
});
Comment thread package-for-friend.js
Comment on lines +20 to +26
PDFIUM_RUNTIME_FILES,
} from "./scripts/pdfium-runtime.mjs";
import {
PDFIUM_RUNTIME_COMPONENT_BOM_REF,
PDFIUM_SBOM_COMPONENTS,
PDFIUM_SBOM_DEPENDENCIES,
} from "./scripts/pdfium-sbom.mjs";
Comment thread scripts/build-mcpb.mjs
if (target.os !== "win32") return; // Only Windows targets get PDFium
const archDir = target.cpu === "x64" ? "win-x64" : "win-arm64";
const dllPath = `${PDFIUM_RUNTIME_DIRECTORY}/${archDir}/pdfium.dll`;
copyRegularFile(dllPath, dllPath, stagingDir);
Comment thread scripts/pdfium-sbom.mjs
Comment on lines +124 to +129
// For shipped notices, include the hash
return {
type: "license",
url: assetPath,
comment: `${notice.component} (${notice.spdx})`,
hashes: [{ alg: "SHA-256", content: notice.sha256 }],
Comment thread server/pdfjs-worker.js
Comment on lines +2108 to +2112
async function runPdfiumSubprocessRender({ dllPath, sourcePdfPath, widthPx, heightPx, outputPngPath }) {
await runSystemCommand(process.execPath, [
PDFIUM_RENDER_HOST_PATH,
dllPath,
sourcePdfPath,
Comment thread server/pdfjs-worker.js
Comment on lines +2242 to +2246
async function systemRenderRegionWindows(bytes, password, options) {
const page = await systemRenderPageWindows(bytes, password, {
page: options.page,
max_dimension_px: null,
renderer_policy: "system",
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants