Skip to content

fix: resource defaults and URLs, region locales, navigation state, viewer options, forced colors - #63

Merged
alekswebnet merged 9 commits into
masterfrom
fix/resolve-urls-locales-hash
Oct 24, 2026
Merged

alekswebnet merged 9 commits into
masterfrom
fix/resolve-urls-locales-hash

Conversation

@alekswebnet

@alekswebnet alekswebnet commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes the component bugs the docs site currently has to warn about or work around. There's one commit per fix, and each passes the suite on its own.

Fixes

  1. Relative resource URLs (0cb38d4). c-map-url, standard-font-data-url, wasm-url and icc-url went to PDF.js as written. PDF.js fetches these folders from its worker, which runs from a blob: URL, so relative paths never resolved, and a folder URL without a trailing / stopped the document from loading. They're now resolved against the page and get the trailing slash.
  2. Region locales and relative locale templates (345af19). locale="pt-BR" was silently dropped because the keys in locale.json are lowercase. pt-br passed that check, but the template then got pt-br while the folders are named pt-BR/. A relative locale-src-template blanked every toolbar label. Locales now match ignoring case, {locale} gets the folder name, and the template is resolved against the page.
  3. Navigation resets (cf3b67a). The iframe hash listed every parameter, set or not, so changing zoom, search or locale sent the reader back to page 1 and closed their sidebar. Unset parameters are now left out, and pagemode=none applies only when the viewer is built.
  4. debugger-src removed (33615e9). PDF.js only loads its debugger when pdfBugEnabled is set and the hash has a pdfbug parameter. The element never writes that parameter, and debugger.mjs isn't in dist, so the attribute never did anything.
  5. Resource defaults (ee4d579). The defaults pointed at PDF.js source-tree paths for folders the package didn't ship, and image-resources-path defaulted to ./images/ next to the host page. dist now ships cmaps/, iccs/, standard_fonts/, wasm/ and pdf.sandbox.mjs. Each default sits next to viewer.mjs (the sandbox next to pdf.mjs), so CDN script tags, assets-base and custom runtime URLs bring them along. image-resources-path is resolved and gets its slash like the others.
  6. image-resources-path after the viewer is built (645023b). PDF.js copies this option into its page viewer once, at creation, so later changes were ignored. The element now updates that copy, and the next document uses the new path.
  7. setViewerOptions across rebuilds (203dfca). Options were applied once and then cleared, so a locale change that rebuilt the viewer dropped them. They're now kept for every build, and later calls merge with earlier ones.
  8. Toolbar icons in forced colors mode (7a0b21c). Paper & Ink's fixed colors overrode the system colors viewer.css switches to in forced colors mode. The browser then repainted them, which hid every toolbar icon. The theme now sits in a not (forced-colors: active) block, so PDF.js's own high-contrast styles apply. Custom themes loaded with theme-css-src are unaffected.
  9. Cross-origin resources and the viewer CSP (6b0397e). The viewer document's CSP listed only the runtime file origins, so a sandbox-bundle-src or wasm-url on another origin was blocked by script-src, and annotation icons from a cross-origin image-resources-path by img-src. Those origins are now included. Found while reviewing this PR; before it, the sandbox never loaded at all, so it didn't show.

Behavior changes

  • Package size: packed 0.73 → 2.66 MB, unpacked 2.5 → 6.0 MB, 94 → 295 files. The new files load only for documents that need them.
  • PDF JavaScript now runs. It uses PDF.js's sandbox (QuickJS in WASM), and enableScripting is on by default in PDF.js. Before, the sandbox never loaded, so scripts silently didn't run. Use setViewerOptions({ enableScripting: false }) to opt out.
  • Locale templates: {locale} in a custom locale-src-template is now the folder name with its original case (pt-BR). Copies of pdfjs-dist/web/locale/ and the default CDN work as they are. A copy renamed to lowercase folders (the workaround the docs currently suggest) needs its original names back on case-sensitive hosts.
  • Viewer CSP: the resource attributes are read into the CSP once per build, like the runtime files. Changing sandbox-bundle-src, wasm-url or image-resources-path to a new origin after the viewer is built isn't covered; set them before connection or rebuild with locale.
  • Iframe hash: it lists only set parameters. On load it's #pagemode=none, not #page=&zoom=&pagemode=none&search=&phrase=&locale=.
  • setViewerOptions merges with earlier calls instead of replacing them.
  • Forced colors mode shows the stock PDF.js look instead of Paper & Ink.
  • Docs in the repo: debugger-src is gone from observed attributes. README, ACCESSIBILITY.md and demo/asset-attributes.html are updated; the demo no longer needs image-resources-path.

Not covered

Bundled apps (Vite, webpack) still need assets-base or the individual attributes for the resource folders. Bundlers emit the runtime files referenced with new URL('./file', import.meta.url), but they can't emit folders.

Verification

  • pnpm test -- --run: 29 passed. The new or changed tests cover resource URL resolution, default resources reachable next to the runtime files, assets-base, pt-BR from a relative template (checks the translated label), the page surviving a zoom change, the live image-resources-path, options surviving a rebuild, the forced-colors gate, and the cross-origin CSP entries. Each one fails on master, and the suite passes at every commit.
  • tsc and pnpm build are clean.
  • The built dist, served statically and checked in Chromium against 4.3.0:
Check 4.3.0 This PR
CJK PDF, c-map-url="/pdfjs/cmaps" doesn't load: "must include trailing slash" loads, CMaps from /pdfjs/cmaps/
CJK PDF, no attributes CMap fetch fails CMaps from dist/cmaps/
locale="pt-BR", relative template English "Página anterior"
locale="zh-CN", default CDN template English "上一页"
Page 5 + open sidebar, then set zoom page 1, sidebar closed page 5, sidebar open
Then set search page 1, sidebar closed page 5, sidebar open
page="9" and pagemode="none" still apply yes yes
PDF with an app.alert OpenAction sandbox 404, no alert alert shown (stock PDF.js also shows it twice for this file)
Note annotation icon, no attributes 404 from /images/ loads from dist/images/
image-resources-path set after build, new document old path new path
Toolbar icon pixels, forced colors 0 on every button 24–132 per button
Theme in normal mode applies applies
Sandbox, WASM and icons from another origin (CORS server) sandbox 404 alert shown, icon loads, no CSP violations
CMaps and fonts from another origin fetch fails loads

After release

The docs site can drop its "must be absolute", trailing-slash, region-locale and navigation-reset notes, and stop telling people to copy the resource folders unless they use a bundler.

🤖 Generated with Claude Code

alekswebnet and others added 8 commits October 25, 2026 01:58
PDF.js fetches CMaps, standard fonts, WASM decoders and ICC profiles from
its worker, which runs from a blob: URL, so a relative c-map-url,
standard-font-data-url, wasm-url or icc-url never loaded. The element now
resolves these attributes, plus sandbox-bundle-src and debugger-src,
against the page the same way it resolves src and the runtime files, and
adds the trailing slash PDF.js requires on folder URLs ("Invalid factory
url ... must include trailing slash"). Unset attributes keep their
defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The locale check compared the attribute with the lowercase keys of
locale.json, so locale="pt-BR" was dropped without a message. A lowercase
"pt-br" passed, but went into the template as-is, and the locale folders
keep their case (pt-BR/viewer.ftl), so the file 404ed. The element now
matches the locale ignoring case, fills {locale} with the folder name from
locale.json, and keys the resource by the lowercase code PDF.js looks up.

The filled template is also resolved against the page. PDF.js resolves it
against the blob: URL of the l10n resource link, so a relative template
failed and the viewer showed no interface text at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The iframe hash listed every navigation parameter, set or not. PDF.js reads
an empty page= as page 1 and the default pagemode=none closes the sidebar,
so changing zoom, search, phrase or locale on a viewer without a page
attribute sent the reader back to page 1 and closed their sidebar. Unset
parameters are now left out, and pagemode falls back to none only when the
viewer is built, so the sidebar still starts closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
debugger-src set PDF.js's debuggerSrc option, but PDF.js only loads its
debugger when pdfBugEnabled is set and the viewer hash has a pdfbug
parameter, which the element never writes, and debugger.mjs isn't in dist.
The attribute never had an effect, so it's removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…files

The defaults for c-map-url, icc-url, standard-font-data-url, wasm-url and
sandbox-bundle-src were paths in PDF.js's source tree, for folders the
package didn't ship, and image-resources-path defaulted to ./images/ next
to the host page. CJK text without embedded fonts, non-embedded standard
fonts, JPEG 2000 and JBIG2 images, ICC colors, PDF JavaScript and
annotation icons failed unless every attribute was set.

dist now includes cmaps/, iccs/, standard_fonts/, wasm/ and
pdf.sandbox.mjs. The packed tarball grows from about 0.7 to 2.7 MB; the
files load only for documents that need them. Each resource defaults to
its folder next to viewer.mjs, and the sandbox to pdf.sandbox.mjs next to
pdf.mjs, so CDN script tags, assets-base and the individual runtime URLs
carry them along. Bundlers emit the runtime files but not folders, so
bundled apps still set assets-base or the attributes.

image-resources-path is now resolved and gets a trailing slash like the
other folder attributes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PDF.js copies imageResourcesPath into its page viewer when the viewer is
created, so changing the attribute later only updated an option that
nothing read again. The element now updates that copy too, so the next
document uses the new path, like the other resource attributes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Options were applied once and then cleared, so a locale change that
rebuilt the viewer dropped them. They are now kept and applied on every
build, and later calls merge with earlier ones instead of replacing them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
viewer.css switches its colors to system colors in forced colors mode,
and the browser keeps author system colors while replacing fixed ones.
Paper & Ink loads after viewer.css with fixed colors, so its values won,
the browser replaced them, and every toolbar icon (a mask filled with a
background color) disappeared. The theme now sits in a
not (forced-colors: active) block and steps aside in that mode, so
PDF.js's own high-contrast styles apply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alekswebnet alekswebnet changed the title fix: resolve resource and locale URLs, load region locales, keep the reader's place fix: resource defaults and URLs, region locales, navigation state, viewer options, forced colors Oct 24, 2026
… viewer CSP

The viewer document's CSP listed only the runtime file origins, so a
sandbox-bundle-src or wasm-url on another origin was blocked by
script-src (the sandbox is a dynamic import, and so is the QuickJS module
it loads from the WASM folder), and annotation icons from an
image-resources-path on another origin were blocked by img-src. Those
origins are now added, read once per build like the runtime files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@alekswebnet
alekswebnet merged commit 15d3dd8 into master Oct 24, 2026
5 checks passed
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.

1 participant