From 5c065bc9bc29d7242cbafb2df8f1e71aab0e7389 Mon Sep 17 00:00:00 2001 From: Samuel Reichert Date: Fri, 12 Jun 2026 11:50:29 +0200 Subject: [PATCH 1/7] 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/7] 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/7] 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) From 17f77790b28d69d625532937c4558b7982096abb Mon Sep 17 00:00:00 2001 From: Samuel Reichert Date: Tue, 16 Jun 2026 10:45:09 +0200 Subject: [PATCH 4/7] fix: update PDFViewer to use dynamic origin for resource URLs Use mx.appUrl (with window.location.origin fallback) for cMapUrl and standardFontDataUrl so the PDF.js worker can resolve font and cmap resources when loaded from a cross-origin URL. Co-Authored-By: Claude Sonnet 4.6 --- packages/pluggableWidgets/document-viewer-web/CHANGELOG.md | 2 +- .../document-viewer-web/src/components/PDFViewer.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pluggableWidgets/document-viewer-web/CHANGELOG.md b/packages/pluggableWidgets/document-viewer-web/CHANGELOG.md index 4af9c0d459..712e4c27b2 100644 --- a/packages/pluggableWidgets/document-viewer-web/CHANGELOG.md +++ b/packages/pluggableWidgets/document-viewer-web/CHANGELOG.md @@ -12,7 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### 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. +- We fixed an issue where checkmarks and other special characters in PDFs were not displayed correctly. ## [1.2.0] - 2025-10-29 diff --git a/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx b/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx index 578833ad28..7677bdc97f 100644 --- a/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx +++ b/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx @@ -8,7 +8,7 @@ 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 origin: string = (window as any).mx?.appUrl ?? window.location.origin; const options = { cMapUrl: `${origin}/widgets/com/mendix/shared/pdfjs/cmaps/`, standardFontDataUrl: `${origin}/widgets/com/mendix/shared/pdfjs/standard_fonts/` From 259e81284fb6c8ebf1c8031dc202cf6c114580d3 Mon Sep 17 00:00:00 2001 From: Samuel Reichert Date: Tue, 16 Jun 2026 11:08:48 +0200 Subject: [PATCH 5/7] fix: strip trailing slash from mx.appUrl and add Window type declaration Co-Authored-By: Claude Sonnet 4.6 --- docs/plans/document-viewer-checkbox-bug.md | 63 ------------------- .../src/components/PDFViewer.tsx | 2 +- .../document-viewer-web/typings/global.d.ts | 7 +++ 3 files changed, 8 insertions(+), 64 deletions(-) delete mode 100644 docs/plans/document-viewer-checkbox-bug.md create mode 100644 packages/pluggableWidgets/document-viewer-web/typings/global.d.ts diff --git a/docs/plans/document-viewer-checkbox-bug.md b/docs/plans/document-viewer-checkbox-bug.md deleted file mode 100644 index 39580700f2..0000000000 --- a/docs/plans/document-viewer-checkbox-bug.md +++ /dev/null @@ -1,63 +0,0 @@ -# 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/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx b/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx index 7677bdc97f..a492de0f76 100644 --- a/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx +++ b/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx @@ -8,7 +8,7 @@ import BaseViewer from "./BaseViewer"; import { DocRendererElement, DocumentRendererProps, DocumentStatus } from "./documentRenderer"; import { If } from "@mendix/widget-plugin-component-kit/If"; -const origin: string = (window as any).mx?.appUrl ?? window.location.origin; +const origin: string = (window.mx?.appUrl ?? window.location.origin).replace(/\/$/, ""); const options = { cMapUrl: `${origin}/widgets/com/mendix/shared/pdfjs/cmaps/`, standardFontDataUrl: `${origin}/widgets/com/mendix/shared/pdfjs/standard_fonts/` diff --git a/packages/pluggableWidgets/document-viewer-web/typings/global.d.ts b/packages/pluggableWidgets/document-viewer-web/typings/global.d.ts new file mode 100644 index 0000000000..3c83dddd5d --- /dev/null +++ b/packages/pluggableWidgets/document-viewer-web/typings/global.d.ts @@ -0,0 +1,7 @@ +declare global { + interface Window { + mx?: { appUrl?: string }; + } +} + +export {}; From b6679c140f91b9e8de956cd7f91dc384d9324a81 Mon Sep 17 00:00:00 2001 From: Samuel Reichert Date: Tue, 16 Jun 2026 15:27:56 +0200 Subject: [PATCH 6/7] fix: reorganize imports in PDFViewer and add CSS module declaration --- .../document-viewer-web/src/components/PDFViewer.tsx | 6 +++--- .../document-viewer-web/typings/modules.d.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 packages/pluggableWidgets/document-viewer-web/typings/modules.d.ts diff --git a/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx b/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx index a492de0f76..e7b93313a2 100644 --- a/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx +++ b/packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx @@ -1,12 +1,12 @@ import { ChangeEvent, FormEvent, Fragment, KeyboardEvent, useCallback, useEffect, useMemo, useState } from "react"; import { Document, Page, pdfjs } from "react-pdf"; +import { If } from "@mendix/widget-plugin-component-kit/If"; import "react-pdf/dist/Page/AnnotationLayer.css"; import "react-pdf/dist/Page/TextLayer.css"; -import { downloadFile } from "../utils/helpers"; -import { useZoomScale } from "../utils/useZoomScale"; import BaseViewer from "./BaseViewer"; import { DocRendererElement, DocumentRendererProps, DocumentStatus } from "./documentRenderer"; -import { If } from "@mendix/widget-plugin-component-kit/If"; +import { downloadFile } from "../utils/helpers"; +import { useZoomScale } from "../utils/useZoomScale"; const origin: string = (window.mx?.appUrl ?? window.location.origin).replace(/\/$/, ""); const options = { diff --git a/packages/pluggableWidgets/document-viewer-web/typings/modules.d.ts b/packages/pluggableWidgets/document-viewer-web/typings/modules.d.ts new file mode 100644 index 0000000000..cbe652dbe0 --- /dev/null +++ b/packages/pluggableWidgets/document-viewer-web/typings/modules.d.ts @@ -0,0 +1 @@ +declare module "*.css"; From 863b0726f48dc2196aed545ca8995394cef8c7e3 Mon Sep 17 00:00:00 2001 From: Samuel Reichert Date: Tue, 16 Jun 2026 15:46:02 +0200 Subject: [PATCH 7/7] fix: use absolute origin for resource URLs in PDFViewer and add supporting typings --- .../design.md | 20 ++++++++++++++----- .../proposal.md | 10 ++++++++-- .../specs/pdf-form-rendering/spec.md | 2 +- .../tasks.md | 3 ++- 4 files changed, 26 insertions(+), 9 deletions(-) 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 index 5527122b5d..59251490e0 100644 --- 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 @@ -18,17 +18,27 @@ The customer's W9 PDF draws a checkmark using the ZapfDingbats standard font (gl ## Decisions -### Use `window.location.origin` to make resource URLs absolute +### Use an absolute `origin` to make resource URLs absolute -Prepend `window.location.origin` to both `cMapUrl` and `standardFontDataUrl` at module evaluation time. +Compute a module-scope `origin` and prepend it 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. +```ts +const origin: string = (window.mx?.appUrl ?? window.location.origin).replace(/\/$/, ""); +``` + +**Rationale:** The worker needs absolute URLs. `window.mx.appUrl` is the canonical Mendix application URL and is correct even when the app is served from a non-origin base path or behind a reverse proxy; `window.location.origin` is the fallback when `mx.appUrl` is unset (e.g. tests). The trailing-slash strip prevents `//` in the joined resource URLs. Evaluated at module load (not per-render), so no React re-render cost. + +**Alternative considered:** Use `window.location.origin` alone. Rejected — does not account for apps served under a base path that `mx.appUrl` encodes. **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. +### Supporting typings + +`window.mx` is not in the widget's ambient types, and CSS imports (`react-pdf/dist/Page/*.css`) need a module declaration. Added `typings/global.d.ts` (declares `Window.mx?: { appUrl?: string }`) and `typings/modules.d.ts` (`declare module "*.css"`). + ## 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. +- Neither `window.mx?.appUrl` nor `window.location.origin` is guaranteed in SSR. Tests don't exercise PDF rendering and `mx.appUrl` falls back to `location.origin` — 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/`), `mx.appUrl` already encodes it correctly; fonts resolve under the app's `/widgets/` path. 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 index 6ff3801568..0eb7d1377b 100644 --- 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 @@ -4,7 +4,10 @@ PDFs containing glyphs from ZapfDingbats (a PDF standard font) — such as check ## 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. +- `standardFontDataUrl` and `cMapUrl` in `PDFViewer.tsx` are prefixed with an absolute `origin` so the worker can fetch font resources regardless of where it was loaded from. +- `origin` resolves to `window.mx.appUrl` (the Mendix app URL) when available, falling back to `window.location.origin`. A trailing slash is stripped to avoid double slashes in the resulting resource URLs. +- A trailing slash is added to `standard_fonts/` so the font directory URL is well-formed. +- Supporting typings added: `window.mx` declaration (`typings/global.d.ts`) and a CSS module declaration (`typings/modules.d.ts`). ## Capabilities @@ -18,7 +21,10 @@ PDFs containing glyphs from ZapfDingbats (a PDF standard font) — such as check ## Impact -- **File**: `packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx` +- **Files**: + - `packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx` + - `packages/pluggableWidgets/document-viewer-web/typings/global.d.ts` (new — `window.mx` type) + - `packages/pluggableWidgets/document-viewer-web/typings/modules.d.ts` (new — `*.css` module type) - **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** 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 index 5c49bc37b5..13c77430d3 100644 --- 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 @@ -13,7 +13,7 @@ The Document Viewer SHALL correctly render PDF glyphs that depend on PDF standar #### 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`) +- **THEN** the request URL SHALL be an absolute URL rooted at the Mendix application URL (`window.mx.appUrl`, falling back to `window.location.origin`, with any trailing slash stripped) — e.g. `https://example.com/widgets/.../FoxitDingbats.pfb` #### Scenario: PDFs without standard fonts are unaffected 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 index 5696490d0c..383f0392a8 100644 --- 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 @@ -1,6 +1,7 @@ ## 1. Implementation -- [x] 1.1 Prepend `window.location.origin` to `cMapUrl` and `standardFontDataUrl` in `packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx` +- [x] 1.1 Prepend an absolute `origin` (`window.mx?.appUrl ?? window.location.origin`, trailing slash stripped) to `cMapUrl` and `standardFontDataUrl` in `packages/pluggableWidgets/document-viewer-web/src/components/PDFViewer.tsx` +- [x] 1.2 Add `typings/global.d.ts` (`window.mx`) and `typings/modules.d.ts` (`*.css`) ## 2. Verification