Skip to content
This repository was archived by the owner on Aug 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ const bytes = writePdf(layout);

`LayoutDocument` and its whole item family — every item/page/image-asset type and schema, plus `LAYOUT_FORMAT_VERSION` — are this package's own exports, ported from `document-schema.js` (which dropped them) so a codec's native model lives in the codec, the same family pattern as `ooxml.js`'s `Package`/`XmlElement` and `markdown-codec`'s AST. `readPdf`/`writePdf` keep their signatures; callers see the same names from a new home. `documents.js` re-exports the family onward from its own barrel — those re-exports now source from `pdf-codec` rather than `document-schema.js`, same names, new source.

There is deliberately **no `DocumentPackage`-returning read or `DocumentPackage`-accepting write here**, and `readPdf`/`writePdf` are this package's primary API precisely because of that. Only `markdown-codec`'s own read entry point returns `document-schema.js`'s flat `ContentDocument` directly — `ooxml.js`'s `readDocx`/`readPptx` return `DocxDocument`/`PptxDocument` and `odf.js`'s `readOdt`/`readOds` return `OdtDocument`/`OdsDocument`, each codec's own native model, with only `ooxml.js`'s separate `readXlsxContent` producing a `ContentDocument` outright. What lets `documents.js` offer one tree-native entry point uniformly across those formats (`decompose`/`assemblePackage` outward, `flattenPackage` back) is its own `readXContent` wrapper layer (`readDocxContent`, `readOdtContent`, and siblings), projecting each codec's native model into the flat `ContentDocument` — a step the codecs themselves don't take. PDF has no such wrapper to project through: `readPdf` yields *layout* cheaply, because positioned glyphs and paths are all the format actually states, and semantic content only through a separate, expensive, lossy reconstruction pass that infers paragraphs, headings, tables, and shapes back out of geometry. That inference is semantic policy rather than codec business, so it lives in `documents.js` — a caller wanting a PDF as a `DocumentPackage` passes `onDocument` to a named conversion function or to `convertDocument` itself and reads the tree off that callback (`convertDocument` on its own returns only bytes), or uses `createLocalDocumentConverter()`'s `DocumentConverter` port, whose `ConversionResult.package` is populated by wiring that same callback internally. The write direction is asymmetric for a different reason than it might look: turning a `DocumentPackage` into PDF bytes is not itself a layout pass — `documents.js`'s `layoutDocumentFromPackage` is a mechanical inverse that walks the positions a *prior* layout pass already stamped onto the package's own content nodes as `frames`, and it only works at all when those frames exist (a bridge conversion's own dump, e.g. `odt-to-docx`, carries no `pages` and cannot reach PDF this way). The actual font-measuring, line-breaking engine runs earlier, wherever the package first passed through an X-to-PDF or PDF-to-X conversion — both the frame-stamping and the frame-walking are `documents.js`'s. Keeping both edges out of this package is still what makes the item layer an honest record of what a file says, separate from what any consumer thinks it means.

An encrypted PDF that opens without a password decrypts transparently — no extra option, no password parameter; one that genuinely needs a user password throws `PdfPasswordRequiredError`. See [Gotchas](#gotchas-and-quirks) for exactly which encryption is supported.

Both accept an optional `signal` (`AbortSignal`); `readPdf` additionally takes a `sink` (`PdfDiagnosticSink`, called once per recoverable parse diagnostic — see the three-tier failure policy under [Conventions](#conventions)), and `writePdf` an `onSubstitution` callback (called once per character not representable in a standard-14 font — see [Fidelity](#fidelity)).
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// pdf-codec's public surface: a curated barrel export, no subpath exports, matching document-schema.js/odf.js/ooxml.js's own precedent. What's exported here is every symbol a real external consumer needs -- headline read/write/codec entry points, the Layout item family this package owns outright as its native document model (src/layout.ts, exported wholesale below), the formula/math port documents.js's own MathML layout engine passes real values through, the text-layout and font-resolution helpers every layout engine built on this codec needs, and the full bytes/image surface (this package owns src/bytes/ and src/image/ outright; nothing duplicates them upstream). Internal plumbing (objects.ts, serialize.ts, lexer.ts, parse.ts, xref.ts, document.ts, interpret.ts, content-read.ts, content-write.ts, filters.ts, predictors.ts, images-read.ts, cmap.ts, font-read.ts, font-style.ts, and the cmap-table/hmtx-table/font-tables/glyf/sfnt/sfnt-subset/cff/cff-probe/cff-bounds/tounicode/ot-layout-common/gpos-table/math-font-write/math-content-write/embedded-font-write font-parsing, font-subsetting, and font-embedding internals) stays unexported -- math-table.ts is a partial exception, exporting its MathVariants types alone (see below), and glyph-bounds.ts another, exporting the GlyphInkBounds shape those outline readers report through -- nothing outside this package's own src/ consumes it today. embedded-font.ts is the one partial exception: its EmbeddedFace is the type ResolvedFace's own 'embedded' variant carries, and its EmbeddedFaceSubstitution is what WritePdfOptions.onMissingGlyph reports, so both must be nameable by an external caller even though nothing else in that module is exported.

// Headline: read/write/diagnostics/codec.
// Headline: read/write/diagnostics/codec. readPdf/writePdf speak LayoutDocument and there is deliberately no DocumentPackage-native pair beside them. On read, PDF states positions, not structure -- readPdf yields layout cheaply, and semantic content (paragraphs, headings, tables) only through a separate, lossy reconstruction pass that infers structure from geometry, genuine semantic policy that lives in documents.js (src/layout/reconstruct.ts). On write, a DocumentPackage reaches PDF bytes only once a prior layout pass has already stamped frames onto it (a package with no pages, e.g. a bridge conversion's own odt-to-docx dump, cannot reach PDF at all): documents.js's layoutDocumentFromPackage walks those already-stamped frames back into a LayoutDocument, a mechanical inverse rather than a font-measuring, line-breaking pass of its own -- the actual layout engine runs earlier, wherever the package first passed through an X-to-PDF or PDF-to-X conversion. ExaDev/pdf-codec#65 scoped the frames-mapping half of this boundary to this package ("readPdf/writePdf ... gain mappings to and from package frames at the edge"); that half was never implemented here and instead lives in documents.js as layoutDocumentFromPackage -- a deliberate reassignment, not an oversight, but one #65 itself doesn't record. Adding a DocumentPackage-facing wrapper here would mean this package owning either a reconstruction heuristic or a frame-walking pass that depends on a layout stage documents.js runs, and would make pdf-codec depend on the package that depends on it.
export type { ReadPdfOptions } from './read';
export { readPdf } from './read';
export type { WritePdfOptions } from './write';
Expand Down
Loading