Appearance
Changelog
All notable changes to @barrierbreak/a11ydocs-pdf are documented here. This project follows Semantic Versioning.
[1.7.0] - 2026-06-16
Changed
renderTemplatenow wraps all content under a singleDocumentstructure root by default. A template produces a complete document, but without an explicitstructureRootits tagged content (headings, paragraphs, figures, tables) was emitted as a flat list of top-level structure elements directly underStructTreeRoot— PDF/UA-1 expects a single top-levelDocumentelement.renderTemplatenow defaults the structure root toDocumentwhen the document was created without one. PassstructureRoot: falsetocreateDocumentto opt out and keep bare roots; an explicitstructureRootis still respected. The low-leveladdPage/manual-structure API is unaffected (no implicit wrapper).
[1.6.2] - 2026-06-16
Fixed
- Template running header drawn last in the content stream.
1.6.1hoisted a running header's structure roots to the front of the tag tree, but the body was still drawn first in the page content, so the header logoFigurereceived the highest marked-content id (MCID). Reading order and content order disagreed — content-order tools and PDF/UA validators saw the logo last. The header's content operators are now hoisted to the front of the page content stream alongside its structure roots, so the header is drawn first (lowest MCID) and reading order matches content order. Footers/page numbers are unaffected (they correctly remain last). Safe because text operators are self-contained (BT/ETwith an absoluteTm) and image operators are wrapped in their ownq/Q. richParagraphthrew on non-string run text. A run whosetextwasnullorundefined(e.g.localStorage.getItem(...)returningnullon a miss) crashednormalizeRichRunswithCannot read properties of null (reading 'length'). Such runs are now dropped like empty runs, so bad input degrades to nothing instead of aborting the whole render.
[1.6.1] - 2026-06-15
Fixed
- Tagged links never referenced from the structure tree. A link annotation was only tied to a
Linkstructure element when the caller passedtag: "Link"explicitly. Inline links —flow.richParagraphruns, page-levelrichTextruns, table-cell links, and paragraph text-annotation links — and the publicpage.link()omitted it, so the annotation was emitted with no OBJR back-reference (an untagged annotation, a PDF/UA-1 failure). An unspecified link tag now defaults toLink; passtag: "Artifact"to opt out for decorative links. - Inline link text was stranded outside its
Linkelement.flow.richParagraphplaced the visible link glyphs under the surroundingPand left theLinkelement holding only the annotation. The link's text is now grouped inside theLinkstructure element together with its OBJR. - Marked content read after child elements. A structure element's
/Karray always listed child elements before the node's own marked content, so a paragraph like"Page URL: " + linkwas announced as the link first, then the leading text./Know interleaves a node's own marked content with its child elements in content (reading) order; pure containers still keep their children's insertion order, so the hoisted running header is unaffected. - Per-cell header cells emitted no
/Scope. A table cell promoted toTHpurely viacell.header(outside anyheaderRows/headerColumnsband — e.g. a row-label column) was written without a/Scope, which auditors flag as a missing row header. Such cells now default to/Scope /Row(still overridable viacell.scope). - Template running header read last in the tag tree. Headers/footers render after the body, so a header logo
Figurelanded at the end of the page's structure order instead of the start. Header structure roots are now hoisted to the front of the reading order; footers remain last (their correct final-appearance position).
[1.6.0] - 2026-06-10
Added
- WOFF2 decoding — new
@barrierbreak/a11ydocs-pdf/woff2subpath export.decodeWoff2(bytes)converts a.woff2file (the format browsers and font CDNs serve) into standard TTF/OTF bytes ready forembedTrueTypeFont()/registerFontFamily(). Implements a zero-dependency Brotli (RFC 7932) decompressor — full prefix codes, context modeling, block switching, and the 122 KB static dictionary with all 121 word transforms — plus WOFF2 glyf/loca triplet reconstruction and hmtx transform reversal. Ships as a separate subpath so the embedded dictionary never loads with the core library.isWoff2(bytes)andbrotliDecompress(bytes)are also exported. loadGoogleFont()now works in browsers: when Google serves a woff2 file, the decoder is loaded on demand and the result is returned as TTF bytes. Node continues to receive a plain.ttf/.otfwith no decoder loaded.- Table cell styling: per-cell
font,color,align, andbackgroundoverrides onTableCellDefinition(joiningbold/italic). Cell fonts resolve registered families and built-in variants. - Table decoration options:
borderColor/borderWidthdraw the cell grid,headerBackgroundfills header cells, andzebra(boolean or color) stripes alternate body rows. All decoration is emitted as tagged-PDF artifacts. - Rich text in table cells: cells accept inline
runsinstead oftext— mixed fonts, sizes, colors, bold/italic, underline/strike, and links wrap together inside the cell, with row heights tracking the tallest line.
Fixed
fallbackFontsentries naming a registered font family never matched (the family name was passed through as if it were a built-in font, so fallback glyph coverage silently failed). Family names infallbackFontsnow resolve to the matching face, honoring the call'sbold/italicflags and case-insensitive matching.
[1.5.5] - 2026-06-10
Added
- Table cells accept
boldanditalicflags ({ text: "Results", header: true, bold: true }). The flags resolve against the table font: a registered family picks the matching face, a built-in base family picks its standard-14 variant. Works inpage.table(),flow.table(), structure-container tables, and templatetableblocks; row heights account for the styled face.
Fixed
- Registered font families did not resolve in
flow.table(),flow.list(), and their height estimators — a template withpage: { font: "Roboto" }and atableorlistblock threwUnknown font "Roboto"even though the family was registered. All table/list entry points now resolve families like the text APIs do.
[1.5.4] - 2026-06-10
Changed
- Font family names are now matched case-insensitively (like CSS
font-family). Registering a font as"Roboto"and referencing it as"roboto"or"ROBOTO"in text/template APIs now works correctly. This applies toregisterFontFamily(),renderTemplate({ fonts }), and all text APIs.
Fixed
loadExtensionFontBytes()now strips"./"prefixes from paths before resolving viachrome.runtime.getURL(), which would otherwise produce URLs that fail to fetch.loadExtensionFontBytes()now accepts absolute extension URLs (chrome-extension://...,moz-extension://...,https://...), passing them through without callinggetURL.loadExtensionFontBytes()wrapsfetchfailures with a detailed diagnostic message mentioningweb_accessible_resources, file path, and case-sensitivity — saving debugging time when a content script can't reach a bundled font.
[1.5.3] - 2026-06-10
Added
renderTemplate()now accepts afontsoption — aRecord<string, FontFamilyInput>of family-name → faces that are registered before the template renders. Faces accept raw bytes, embedded handles, or built-in names.loadExtensionFontFaces(paths)— loads bundled extension font files and returnsFontFamilyInputwithout registering, so it composes directly withrenderTemplate({ fonts }).
[1.5.2] - 2026-06-10
Added
loadExtensionFont(doc, family, paths)andloadExtensionFontBytes(path)— load font files bundled with a Chrome extension viachrome.runtime.getURL()and register them as a font family in one call. Fonts are fetched in parallel and passed toregisterFontFamily(). Exported as@barrierbreak/a11ydocs-pdf/chrome-extension-fonts.
[1.5.1] - 2026-06-10
Fixed
PdfFlow.richParagraph()andPdfPage.richText()produced text ops without atag, so the tagged PDF structure tree showed thePelement with empty marked-content identifiers. The block-level tag now flows through to each run's options so the structure tree carries the correct MCIDs.
[1.5.0] - 2026-06-06
Added
renderDocumentJson(document)andrenderDocumentJsonSync(document)— render a PDF from a single, fully serializablePdfDocumentJsonvalue. Fonts are declared by source (base64/ filepath/url/ Google Fonts) and referenced by name; images carry asrcinstead of raw bytes; headers/footers are static block arrays with///tokens; document options (encryption, conformance, language, tagged, metadata) live on the root.renderDocumentJsonis async;renderDocumentJsonSynchandles fully inline (base64) documents.- Absolute positioning in
PdfDocumentJson: add aposition({ x, y, width?, height?, page?, origin? }) to a block to draw it at exact PDF-point coordinates instead of flowing. Positionable types:paragraph, the image formats,rect,textField,checkBox,signatureField. PdfDocument.getPage(index, origin?)andPdfDocument.pageCount— return aPdfPagebuilder for an already-created page so finished pages can be drawn on with the absolute-coordinate APIs.PdfErrorCode.INVALID_DOCUMENT_JSONfor a malformedPdfDocumentJson.
Fixed
ParsedPdfDocument.extractText()returned empty text for pages whose content stream was FlateDecode-compressed (common once a page carried a table, list, or header/footer). The extractor decoded the already-inflated lazy-stream bytes a second time and discarded the page; text andToUnicodedecoding now tolerate the pre-decoded bytes.
[1.3.0] - 2026-06-02
Added
- Shape helpers on
PdfPage:rect(),line(),circle(),ellipse()with a simplifiedShapeStyle(inferred fill/stroke paint mode). - Text measurement:
PdfDocument.measureText(),measureTextBlock(), andmeasureRichText()return width/height/line-count without drawing. PdfPage.image()auto-detects the raster format (PNG, JPEG, GIF, BMP, TIFF, WebP, JPEG 2000) from the data.addPage({ origin: "top-left" })flips the y-axis so coordinates grow downward from the top edge — applied across the page drawing, annotation, and form-field methods.underlineandstrikeoptions on text and inline runs, drawn as decorative lines.- Named colors: text/shape
coloroptions accept names like"red", plus acolor()helper that resolves named and#hexcolors. - Shape and image helpers on
PdfStructureContainer(rect,line,circle,ellipse,image,path) andellipse/lineonPdfFlow. registerFontFamily()accepts raw font bytes per face (embedded automatically) in addition to handles and built-in names (FontFamilyInput).- Document-wide shape defaults via
createDocument({ shapeDefaults }), merged beneath per-call styles by the page shape helpers. - Layout-feedback methods
PdfPage.placeText()andplaceTextBlock()that draw and return{ width, height, (lineCount,) endY }for manual stacking. - "Choosing an API" guide page mapping common goals to the right API.
Changed
- The
fontoption now reports a clear "did you mean" error (INVALID_FONT) for an unknown string font instead of silently falling back, suggesting the closest standard-14 name. Unknown page-size names throwINVALID_PAGE_SIZEwith the same suggestion treatment. PdfStructureContainerbuilder methods (text,textBlock,list,table, image,link) now returnthisfor chaining, consistent withPdfPageandPdfFlow.
[1.2.1] - 2026-06-02
Fixed
- The
fonttext option now accepts a font-family name registered withregisterFontFamily()(and anystring-typed value), instead of rejecting it at compile time.PdfFontwidened toFontName | PdfEmbeddedFont | (string & {}), preserving built-in name autocomplete.
[1.2.0] - 2026-06-02
Added
- Inline rich text:
PdfPage.richText()andPdfFlow.richParagraph()lay out an array ofInlineTextRunsegments that flow on the same line and wrap together, each with its own font, weight, size, color, and optionallink. richParagraphtemplate block (TemplateRichParagraphBlock) for inline rich text inrenderTemplate().- Inline rich text supports
align: "justify", line-leveldirection("ltr" | "rtl" | "auto"), andwritingMode: "vertical"(single-column stacking); flow and template rich paragraphs wrap and split across columns and pages. - Vertical alignment:
verticalAlign("top" | "middle" | "bottom") on table cells (TableOptionsdefault and per-TableCellDefinition). - Vertical alignment within a fixed-height box via
height+verticalAlignonTextBlockOptionsandRichTextOptions. - New exported types:
InlineTextRun,RichTextOptions,FlowRichTextOptions,TemplateRichParagraphBlock,VerticalAlign.
[1.1.0] - 2026-06-02
Added
boldanditalictext options that resolve to the matching font face for the standard-14 base families (Helvetica, Times, Courier)PdfDocument.registerFontFamily()to group font faces (built-in or embedded) under a name, selectable with thebold/italicflags- Document-wide text defaults via
createDocument({ defaults })(font, bold, italic, fontSize, color, kerning, direction) - Unit helpers
mm(),cm(),inch(), andpt()for authoring layouts in physical units PdfDocument.save(path)convenience alias forwriteToFile()
Changed
PdfPagebuilder methods (text, images, annotations, form fields, graphics state, transforms) now returnthisfor fluent chainingbold/italic, registered font families, and document defaults resolve uniformly acrosspage.text()/textBlock(), thePdfFlowcursor API,renderTemplate()blocks, and structure containers
[1.0.0] - 2026-05-11
Added
- Zero-dependency PDF 1.4+ generation engine in TypeScript
- PDF 1.5 object streams and 2.0 encryption support
- Text rendering with 14 standard fonts plus embedded TrueType (Unicode, kerning, ligatures, RTL/Arabic)
- Image support: JPEG, PNG (transparency/alpha)
- Tagged PDF for accessibility: PDF/UA-1, structure trees, MCID-based marked content
- Flow-based coordinate-free layout engine
- Template-based report/document rendering with headers, footers, page numbers, and auto-outline
- Forms (AcroForm): text, checkbox, radio, choice, push button, signature
- Annotations: URI links, highlights, notes, free-text
- Encryption: rc4-40, rc4-128, AES-128, AES-256
- PDF parsing and incremental editing: metadata, pages, forms, overlays
- Page transfer utilities: extract, split, merge, append
- Streaming output via ByteSink abstraction
- XMP metadata support
- Linearized (Fast Web View) output for single-page documents
- PDF version auto-detection and conformance validation