Skip to content

Release v1.1.0 — PDF/A Latin embedding, BiDi isolates, Arabic harakat, emoji#40

Merged
Nizoka merged 10 commits into
mainfrom
release/v1.1.0
Apr 29, 2026
Merged

Release v1.1.0 — PDF/A Latin embedding, BiDi isolates, Arabic harakat, emoji#40
Nizoka merged 10 commits into
mainfrom
release/v1.1.0

Conversation

@Nizoka

@Nizoka Nizoka commented Apr 29, 2026

Copy link
Copy Markdown
Owner

Summary

Maximalist minor release. Closes the two largest open epics — issue
#28 (PDF/A Latin font
embedding) and issue #25
(full UAX #9 BiDi isolates + GPOS MarkBasePos for Arabic harakat) — and
adds first-class monochrome emoji support, auto-fit table columns, and
per-cell clipping. Folds the alpha.1 / alpha.2 medium-term items into a
single stable cut.

100% backward-compatible. All new features are opt-in and gated on
font registration or explicit table flags. Pre-existing PDFs are
byte-identical when the new font modules are not registered.

Tests 1726 / 1726 green across 48 files (was 1674)
Coverage ~95% statements, ~88% branches, ~98% functions
Build ESM + CJS + 105.82 KB .d.ts, tree-shakeable
veraPDF blocking in CI (was continue-on-error pre-v1.0.5)
Sample PDFs 154 across 26 categories (incl. new pdfa-latin/ and emoji/)
Public API additive only — zero breaking changes

What's new

PDF/A — full veraPDF conformance ✅

  • Latin font embedding (#28)
    via Noto Sans VF (OFL-1.1). Opt-in with one line:
    registerFont('latin', () => import('pdfnative/fonts/noto-sans-data.js'));
  • Type0 redirector dicts for /F1 / /F2 under PDF/A — Object 3
    and Object 4 now reference the embedded CIDFontType2 chain instead
    of standard-14 Helvetica (ISO 19005-1 §6.3.4 / ISO 19005-2 §6.2.11.4.1).
  • XMP parity<dc:title>, <dc:description>, <pdf:Keywords>
    mirror /Info /Title, /Subject, /Keywords byte-for-byte
    (ISO 19005-1 §6.7.3 t1 / t4 / t5). Achieved via
    • new utf8EncodeBinaryString() helper that preserves chars > U+00FF
      through toBytes()'s & 0xFF masking, and
    • extending buildXMPMetadata() with optional subject / keywords
      parameters.
  • veraPDF CI is now blocking. scripts/validate-pdfa.ts already
    auto-detects PDF/A claims via XMP pdfaid:part, so non-PDF/A samples
    never trigger CI failures.

BiDi & shaping (#25)

  • UAX chore(deps): bump typescript-eslint from 8.57.2 to 8.58.1 in the dev-dependencies group #9 isolates — LRI / RLI / FSI / PDI (U+2066–U+2069) handled
    via three-tier dispatcher: resolveBidiRunsresolveBidiRunsForced
    resolveBidiCore. Nested and unmatched isolates supported.
  • Arabic GPOS MarkBasePos — transparent marks (harakat: fatha,
    kasra, damma, sukun, shadda, …) now anchor on the preceding base
    glyph using font-provided GPOS anchor data. Tracks lastBaseGid
    through the shaping pipeline including lam-alef ligatures.
  • Shared GSUB / GPOS drivers — new
    src/shaping/gsub-driver.ts
    (tryLigature) and
    src/shaping/gpos-positioner.ts
    (positionMarkOnBase). Bengali / Tamil / Devanagari / Arabic shapers
    now route through a single lookup helper instead of four duplicated
    implementations.

Emoji

  • Monochrome emoji via Noto Emoji (OFL-1.1, 1891 glyphs). Opt-in:
    registerFont('emoji', () => import('pdfnative/fonts/noto-emoji-data.js'));
  • Detection covers BMP/SMP emoji ranges, Fitzpatrick modifiers, ZWJ,
    and VS-15 / VS-16. Multi-font run splitting routes emoji codepoints
    to the registered 'emoji' font automatically.
  • COLRv1 colour emoji deferred to v1.2.

Tables

  • TableBlock.autoFitColumns: true — column widths derived from
    measured content. Honours per-column minWidth / maxWidth clamping.
  • TableBlock.clipCells: true (default) — every header and data cell
    wrapped in q <rect> re W n … Q so variable-width content cannot
    escape its column rectangle.

Backward compatibility — no breaking changes

Surface v1.1.0 behaviour
Public API additive only — registerFont('latin', …), registerFont('emoji', …), TableBlock.autoFitColumns, TableBlock.clipCells, utf8EncodeBinaryString are new exports
buildPDFBytes byte-identical output when no 'latin' / 'emoji' font is registered
buildDocumentPDF byte-identical output when no 'latin' / 'emoji' font is registered
buildXMPMetadata new optional subject / keywords params default to undefined — pre-existing call sites unchanged
createEncodingContext new optional pdfA param defaults to false — pre-existing call sites unchanged
Type signatures only widenings (readonly number[]); no narrowing changes
Removed APIs none

Migration

No code changes required. To opt into stricter PDF/A or emoji rendering:

import { registerFont } from 'pdfnative';

registerFont('latin', () => import('pdfnative/fonts/noto-sans-data.js'));
registerFont('emoji', () => import('pdfnative/fonts/noto-emoji-data.js'));

Validation

  • npm run typecheck:all — 3 tsconfigs, clean
  • npm run lint — clean (ESLint 9 + typescript-eslint strict)
  • npm test1726 / 1726 green across 48 files
  • npm run build — ESM + CJS + d.ts, tree-shakeable
  • npm run test:generate — 154 sample PDFs across 26 categories
  • npm run validate:pdfa — every PDF/A-claiming sample passes veraPDF
    (1b / 2b / 2u / 3b)

Documentation refreshed

Issues closed

Deferred to v1.2.0

  • UAX chore(deps): bump typescript-eslint from 8.57.2 to 8.58.1 in the dev-dependencies group #9 embeddings (LRE / RLE / LRO / RLO / PDF — rare in practice,
    require deeper level-stack refactor)
  • True page-by-page constant-memory streaming
    (buildDocumentPDFStreamPageByPage())
  • COLRv1 colour emoji (this release ships monochrome only)
  • Noto Sans Bold separate font module (~250 KB) for true bold under PDF/A
  • Noto Sans Math integration for U+2200–U+22FF math operators block

Credits

Checklist

  • Tests added / updated (1726 / 1726 green)
  • npm run typecheck:all clean
  • npm run lint clean
  • npm run build clean
  • npm run test:generate regenerates 154 PDFs without errors
  • CHANGELOG updated under [1.1.0]
  • Release notes updated in release-notes/v1.1.0.md
  • No breaking changes
  • veraPDF reference validation passes on all PDF/A samples
  • Documentation refreshed (README, ROADMAP, docs/guides/pdfa.md)

Closes #28, closes #25

Nizoka added 8 commits April 29, 2026 21:06
Carries forward alpha.1 work: watermark auto-fit, Unicode ellipsis, ColumnDef min/max, ASCII85/LZW/ASCIIHex/RunLength decoders, live version widget. 1665/1665 tests, ready for v1.1.0 stable expansion.
Phase 5 - Cell clipping (ISO 32000-1 §8.5.4):

- TableBlock.clipCells (default true) wraps each cell in 'q <rect> re W n ... Q'

- TableBlock.autoFitColumns option scaffolded (Phase 4 wiring next)

- 2 new tests in pdf-document.test.ts (1667/1667 total)

Phase 7 - UX docs polish:

- Added pdfnative-mcp badge in hero badge strip

- New compact version strip mounted directly under <nav> (.pn-version-strip)

- versions.js refactored to dual-mode (compact|detailed) supporting multiple mounts

- Strip propagated to docs/playgrounds/{cli,mcp}.html
Phase 4 - Auto-fit column widths:

- src/core/pdf-column-fit.ts (NEW): content-derived f-fraction computation

- TableBlock.autoFitColumns option (opt-in, default false)

- Wired into renderTable via computeAutoFitColumns()

- 7 new tests in tests/core/pdf-column-fit.test.ts

Total: 1674/1674 tests green (alpha.1 baseline 1665 + 9 new)

Version bump: 1.1.0-alpha.1 -> 1.1.0-alpha.2

Deferred to v1.1.0 stable: PDF/A Latin embedding (#28), full UAX #9 (#25), emoji

Deferred to v1.2.0: true page-by-page constant-memory streaming
Bake NotoSans-VF.ttf (OFL-1.1) as fonts/noto-sans-data.js (4515 glyphs,

3094 cmap entries, unitsPerEm=1000) so consumers can embed a PDF/A-conforming

Latin font through the existing fontEntries pipeline:

  import * as notoSans from 'pdfnative/fonts/noto-sans-data.js';

  buildDocumentPDF({ ..., fontEntries: [{ fontRef: '/F1', fontData: notoSans }] },

                   { tagged: 'pdfa2b' });

Closes #28. Adds 4 tests (1678 total). No public API change.
…B/GPOS drivers (#25)

Phase 2 of v1.1.0. Three coordinated improvements to text shaping:

1. UAX #9 isolate support (LRI U+2066, RLI U+2067, FSI U+2068, PDI U+2069)

   in src/shaping/bidi.ts. Inner content of matched isolate pairs is

   resolved as a sealed sub-paragraph with its own forced (LRI/RLI) or

   auto-detected (FSI) paragraph level; nested isolates recurse. Texts

   without isolate codepoints behave byte-identically to v1.0.

2. Arabic GPOS MarkBasePos: harakat (U+064B-U+0652) and other transparent

   marks are now anchored on the preceding base glyph using the GPOS

   anchor data already extracted by tools/build-font-data.cjs. Falls back

   to (0,0) when the font lacks anchors, preserving v1.0 behaviour.

3. New shared modules src/shaping/gsub-driver.ts and gpos-positioner.ts

   centralise tryLigature() (was duplicated 3x in Bengali/Tamil/Devanagari)

   and getBaseAnchor/getMarkAnchor/positionMarkOnBase (was duplicated in

   Devanagari and missing from Arabic). Pure refactor, zero behaviour

   change for the Indic shapers.

Adds 24 new tests (1702 total), full typecheck + lint clean.

Closes the BiDi/GPOS portion of #25; remaining work (full embeddings

LRE/RLE/LRO/RLO and embedding levels > 2) deferred to v1.2.
Phase 3 of v1.1.0. Adds first-class emoji rendering through the existing

multi-font fallback pipeline:

- Bundle fonts/noto-emoji-data.js (Noto Emoji OFL-1.1, 1891 glyphs,

  1489 cmap entries, 2.6 MB module)

- Add EMOJI_RANGES + isEmojiCodepoint() + containsEmoji() in

  src/shaping/script-registry.ts; export ZWJ / VS-15 / VS-16 / Fitzpatrick

  range constants

- Update src/shaping/script-detect.ts: detectCharLang() returns 'emoji',

  needsUnicodeFont('emoji') === true, detectFallbackLangs() picks up

  emoji codepoints

- Wire NotoEmoji-Regular.ttf into scripts/download-fonts.ts manifest

Usage:

  registerFont('emoji', () => import('pdfnative/fonts/noto-emoji-data.js'));

Adds 15 tests (1717 total), full typecheck + lint clean.
- Bump package.json version 1.1.0-alpha.2 -> 1.1.0 stable

- Add 'emoji' to package.json keywords

- New release-notes/v1.1.0.md (full feature list, upgrade notes, deferred items)

- CHANGELOG: new [1.1.0] section above alpha sections

- ROADMAP: move #28, #25, emoji, auto-fit, clipping, UX docs to Released

- ROADMAP: new 'Planned v1.2.0' section (streaming, embeddings, COLRv1, USE-lite)

- README: register 'latin' / 'emoji' fonts in example + supported languages table

- copilot-instructions: shared GSUB/GPOS drivers, BiDi isolates, emoji, Latin VF

- 1717 / 1717 tests green across 48 files; build + lint + typecheck:all clean
Hardens the v1.1.0 release after #25 + #28 closeout:
- Closes veraPDF rules 6.7.3-4/5 (dc:description/pdf:Keywords parity)
- Reactivates veraPDF blocking in CI
- Updates all docs and release notes
@Nizoka Nizoka self-assigned this Apr 29, 2026
@Nizoka Nizoka added bug Something isn't working enhancement New feature or request release Tracks a versioned release — implementation, quality gates, and publish workflow labels Apr 29, 2026
@Nizoka Nizoka merged commit 62f8c2e into main Apr 29, 2026
6 checks passed
@Nizoka Nizoka deleted the release/v1.1.0 branch April 29, 2026 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request release Tracks a versioned release — implementation, quality gates, and publish workflow

Projects

None yet

1 participant