From 5c065bc9bc29d7242cbafb2df8f1e71aab0e7389 Mon Sep 17 00:00:00 2001 From: Samuel Reichert Date: Fri, 12 Jun 2026 11:50:29 +0200 Subject: [PATCH 1/3] fix: update PDFViewer to use dynamic origin for resource URLs --- .../document-viewer-web/src/components/PDFViewer.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx b/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx index 1f3b7da0a6..578833ad28 100644 --- a/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx +++ b/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx @@ -7,9 +7,11 @@ import { useZoomScale } from "../utils/useZoomScale"; import BaseViewer from "./BaseViewer"; import { DocRendererElement, DocumentRendererProps, DocumentStatus } from "./documentRenderer"; import { If } from "@mendix/widget-plugin-component-kit/If"; + +const origin = window.location.origin; const options = { - cMapUrl: "/widgets/com/mendix/shared/pdfjs/cmaps/", - standardFontDataUrl: "/widgets/com/mendix/shared/pdfjs/standard_fonts" + cMapUrl: `${origin}/widgets/com/mendix/shared/pdfjs/cmaps/`, + standardFontDataUrl: `${origin}/widgets/com/mendix/shared/pdfjs/standard_fonts/` }; const PDFViewer: DocRendererElement = (props: DocumentRendererProps) => { From aa479e16d27b1a9a43822bc0fcf9f1082ff98dd2 Mon Sep 17 00:00:00 2001 From: Samuel Reichert Date: Fri, 12 Jun 2026 14:01:14 +0200 Subject: [PATCH 2/3] docs(document-viewer): update changelog with standard font fix Co-Authored-By: Claude Sonnet 4.6 --- packages/pluggableWidgets/document-viewer-web/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/pluggableWidgets/document-viewer-web/CHANGELOG.md b/packages/pluggableWidgets/document-viewer-web/CHANGELOG.md index b2800d5a62..4af9c0d459 100644 --- a/packages/pluggableWidgets/document-viewer-web/CHANGELOG.md +++ b/packages/pluggableWidgets/document-viewer-web/CHANGELOG.md @@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - We changed the internal structure of the widget +### Fixed + +- We fixed an issue where PDF standard fonts (e.g. ZapfDingbats) failed to load when the PDF.js worker was served from a cross-origin URL, causing glyphs such as checkmarks to render as blank rectangles. + ## [1.2.0] - 2025-10-29 ### Added From c43aff797a8c50f65d0b03712ec26e8e0ad5612c Mon Sep 17 00:00:00 2001 From: Samuel Reichert Date: Fri, 12 Jun 2026 14:01:24 +0200 Subject: [PATCH 3/3] chore(openspec): archive document-viewer-render-forms-fix change Co-Authored-By: Claude Sonnet 4.6 --- docs/plans/document-viewer-checkbox-bug.md | 63 +++++++++++++++++++ .../.openspec.yaml | 2 + .../design.md | 34 ++++++++++ .../proposal.md | 25 ++++++++ .../specs/pdf-form-rendering/spec.md | 21 +++++++ .../tasks.md | 10 +++ 6 files changed, 155 insertions(+) create mode 100644 docs/plans/document-viewer-checkbox-bug.md create mode 100644 openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/.openspec.yaml create mode 100644 openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/design.md create mode 100644 openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/proposal.md create mode 100644 openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/specs/pdf-form-rendering/spec.md create mode 100644 openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/tasks.md diff --git a/docs/plans/document-viewer-checkbox-bug.md b/docs/plans/document-viewer-checkbox-bug.md new file mode 100644 index 0000000000..39580700f2 --- /dev/null +++ b/docs/plans/document-viewer-checkbox-bug.md @@ -0,0 +1,63 @@ +# Bug: PDF Form Checkboxes Not Displaying Checked State + +**Widget:** Document Viewer v1.2.0 +**Mendix version:** 10.24.9 +**Status:** Fixed + +--- + +## Symptom + +A W9 PDF generated via .NET has a checked checkbox in Section 3.a ("C corporation"). When opened directly in a browser (Chrome, Firefox native viewer), the checkbox renders correctly as checked. When displayed in the Document Viewer widget, the checkbox appears unchecked. + +--- + +## Root Cause + +The checkmark is drawn by a PDF Form XObject using the `/ZaDb` (ZapfDingbats) standard font with glyph `0x34`. PDF.js substitutes standard fonts with bundled Foxit equivalents, fetching them from `standardFontDataUrl`. + +`PDFViewer.tsx` configured this as a relative URL: + +```ts +const options = { + cMapUrl: "/widgets/com/mendix/shared/pdfjs/cmaps/", + standardFontDataUrl: "/widgets/com/mendix/shared/pdfjs/standard_fonts/" +}; +``` + +The PDF.js worker is loaded from `//unpkg.com/pdfjs-dist@.../pdf.worker.min.mjs` — a cross-origin URL. A worker loaded from a different origin has no document base URL, so `fetch()` cannot resolve relative paths. The worker throws: + +``` +TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': +Failed to parse URL from /widgets/com/mendix/shared/pdfjs/standard_fonts/FoxitDingbats.pfb +``` + +Font load silently fails → ZapfDingbats not available → checkmark glyph renders as blank rectangle. + +The browser's native PDF viewer is unaffected because it handles font resolution internally without a web worker. + +--- + +## Fix + +**File:** `packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx` + +```diff ++const origin = window.location.origin; + const options = { +- cMapUrl: "/widgets/com/mendix/shared/pdfjs/cmaps/", +- standardFontDataUrl: "/widgets/com/mendix/shared/pdfjs/standard_fonts/" ++ cMapUrl: `${origin}/widgets/com/mendix/shared/pdfjs/cmaps/`, ++ standardFontDataUrl: `${origin}/widgets/com/mendix/shared/pdfjs/standard_fonts/` + }; +``` + +`window.location.origin` is the Mendix app origin (e.g. `https://myapp.mendixcloud.com`). The worker can fetch absolute URLs regardless of where it was loaded from. + +--- + +## Verification + +1. Load customer W9 PDF — Section 3.a "C corporation" checkbox shows as checked ✓ +2. Build: `pnpm --filter @mendix/document-viewer-web run build` +3. Network tab shows absolute URL `http:///widgets/.../FoxitDingbats.pfb` with 200 response (or request goes to worker — confirmed via console, no more `loadFont` warning) diff --git a/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/.openspec.yaml b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/.openspec.yaml new file mode 100644 index 0000000000..e0c0898ffd --- /dev/null +++ b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-11 diff --git a/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/design.md b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/design.md new file mode 100644 index 0000000000..5527122b5d --- /dev/null +++ b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/design.md @@ -0,0 +1,34 @@ +## Context + +Document Viewer v1.2.0 uses `react-pdf` to render PDFs via PDF.js. The `options` object passed to the `` component includes `cMapUrl` and `standardFontDataUrl` as relative paths. PDF.js passes these to the worker thread for font/cmap fetching. When the worker is loaded from a cross-origin URL (the default is unpkg CDN: `//unpkg.com/pdfjs-dist@.../pdf.worker.min.mjs`), the worker's `fetch()` cannot resolve relative URLs — it has no document origin to resolve against — causing a `TypeError: Failed to parse URL` and silent font load failure. + +The customer's W9 PDF draws a checkmark using the ZapfDingbats standard font (glyph `0x34`) via a Form XObject. Without the font, PDF.js renders a blank rectangle. + +## Goals / Non-Goals + +**Goals:** + +- Fix standard font and cmap fetching when the PDF.js worker is cross-origin +- No new XML properties, no API surface changes, no dependency updates + +**Non-Goals:** + +- Changing how the worker URL is configured +- Supporting self-hosted worker deployments (already supported via `pdfjsWorkerUrl` prop) + +## Decisions + +### Use `window.location.origin` to make resource URLs absolute + +Prepend `window.location.origin` to both `cMapUrl` and `standardFontDataUrl` at module evaluation time. + +**Rationale:** The worker needs absolute URLs. `window.location.origin` is always the Mendix app origin — correct for all deployment environments. Evaluated at module load (not per-render), so no React re-render cost. + +**Alternative considered:** Move `options` inside the component and use `useMemo`. Rejected — no reactive dependencies, module-scope evaluation is simpler and equivalent. + +**Alternative considered:** Set `useWorkerFetch: false` to force main-thread font loading. Rejected — works around the symptom, not the cause; disabling worker fetch has broader performance implications. + +## Risks / Trade-offs + +- `window.location.origin` is not available in SSR/test environments. Tests currently stub this or don't exercise PDF rendering — no impact. If server-side rendering is ever added, this will need to be guarded. +- If the Mendix app is served from a subpath (e.g. `/app/`), `origin` alone is correct — fonts live under `/widgets/`, not the subpath. diff --git a/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/proposal.md b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/proposal.md new file mode 100644 index 0000000000..6ff3801568 --- /dev/null +++ b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/proposal.md @@ -0,0 +1,25 @@ +## Why + +PDFs containing glyphs from ZapfDingbats (a PDF standard font) — such as checkmarks generated by .NET PDF libraries — render blank in Document Viewer. The checkmark is drawn via a Form XObject using the ZapfDingbats font, which PDF.js must fetch from `standardFontDataUrl`. The URL was relative, causing the PDF.js worker (loaded from unpkg CDN, a different origin) to fail parsing it as an absolute URL. + +## What Changes + +- `standardFontDataUrl` and `cMapUrl` in `PDFViewer.tsx` are prefixed with `window.location.origin` to produce absolute URLs, allowing the worker to fetch font resources regardless of where it was loaded from. + +## Capabilities + +### New Capabilities + +- `pdf-form-rendering`: Correct visual rendering of standard-font glyphs (ZapfDingbats checkmarks, Symbol characters) in PDFs rendered by the Document Viewer widget + +### Modified Capabilities + + + +## Impact + +- **File**: `packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx` +- **Behavior**: ZapfDingbats and other standard font glyphs now render correctly when PDF.js worker is loaded from a cross-origin URL (e.g. unpkg CDN) +- **No API or XML changes** +- **No dependency version changes** +- **Affected widget**: `@mendix/document-viewer-web` v1.2.0+ diff --git a/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/specs/pdf-form-rendering/spec.md b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/specs/pdf-form-rendering/spec.md new file mode 100644 index 0000000000..5c49bc37b5 --- /dev/null +++ b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/specs/pdf-form-rendering/spec.md @@ -0,0 +1,21 @@ +## ADDED Requirements + +### Requirement: Standard font glyphs render correctly in PDFs + +The Document Viewer SHALL correctly render PDF glyphs that depend on PDF standard fonts (ZapfDingbats, Symbol, etc.) by fetching font resources using absolute URLs resolvable by the PDF.js worker. + +#### Scenario: ZapfDingbats checkmark renders in cross-origin worker context + +- **WHEN** a PDF contains a glyph drawn from the ZapfDingbats standard font (e.g. a checkmark drawn via a Form XObject) +- **AND** the PDF.js worker is loaded from a cross-origin URL (e.g. unpkg CDN) +- **THEN** the glyph SHALL render visibly on the canvas + +#### Scenario: Font fetch uses absolute URL + +- **WHEN** PDF.js requests a standard font file from the worker thread +- **THEN** the request URL SHALL be an absolute URL including the application origin (e.g. `https://example.com/widgets/.../FoxitDingbats.pfb`) + +#### Scenario: PDFs without standard fonts are unaffected + +- **WHEN** a PDF contains no glyphs requiring standard font substitution +- **THEN** rendering SHALL be identical to previous behavior diff --git a/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/tasks.md b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/tasks.md new file mode 100644 index 0000000000..5696490d0c --- /dev/null +++ b/openspec/changes/archive/2026-06-12-document-viewer-render-forms-fix/tasks.md @@ -0,0 +1,10 @@ +## 1. Implementation + +- [x] 1.1 Prepend `window.location.origin` to `cMapUrl` and `standardFontDataUrl` in `packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx` + +## 2. Verification + +- [x] 2.1 Run unit tests: `cd packages/pluggableWidgets/document-viewer-web && pnpm run test` +- [x] 2.2 Build widget: `pnpm --filter @mendix/document-viewer-web run build` +- [x] 2.3 Verify customer W9 PDF shows Section 3.a checkbox as checked in Document Viewer +- [x] 2.4 Verify a PDF without AcroForms renders correctly (no regression)