@awacloud/pdf 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +609 -0
- package/LICENSE +661 -0
- package/NOTICE +77 -0
- package/README.md +363 -2
- package/dist/build/index.js +21 -0
- package/dist/build/pdf-full-rw.js +10972 -0
- package/dist/build/pdf-full-rw.meta.json +105 -0
- package/dist/build/pdf-full-rw.min.js +53 -0
- package/dist/build/pdf-full.js +6078 -0
- package/dist/build/pdf-full.meta.json +90 -0
- package/dist/build/pdf-full.min.js +32 -0
- package/dist/build/pdf-large-rw.js +10367 -0
- package/dist/build/pdf-large-rw.meta.json +99 -0
- package/dist/build/pdf-large-rw.min.js +53 -0
- package/dist/build/pdf-large.js +5473 -0
- package/dist/build/pdf-large.meta.json +84 -0
- package/dist/build/pdf-large.min.js +32 -0
- package/dist/build/pdf-legacy-rw.js +12402 -0
- package/dist/build/pdf-legacy-rw.meta.json +110 -0
- package/dist/build/pdf-legacy-rw.min.js +53 -0
- package/dist/build/pdf-legacy.js +7508 -0
- package/dist/build/pdf-legacy.meta.json +95 -0
- package/dist/build/pdf-legacy.min.js +32 -0
- package/dist/build/pdf-rw.js +7578 -0
- package/dist/build/pdf-rw.meta.json +77 -0
- package/dist/build/pdf-rw.min.js +53 -0
- package/dist/build/pdf.js +2684 -0
- package/dist/build/pdf.meta.json +62 -0
- package/dist/build/pdf.min.js +32 -0
- package/dist/standalone/pdf-full-rw.js +16798 -0
- package/dist/standalone/pdf-full-rw.meta.json +78 -0
- package/dist/standalone/pdf-full-rw.min.js +56 -0
- package/dist/standalone/pdf-full.js +11904 -0
- package/dist/standalone/pdf-full.meta.json +63 -0
- package/dist/standalone/pdf-full.min.js +35 -0
- package/dist/standalone/pdf-large-rw.js +16193 -0
- package/dist/standalone/pdf-large-rw.meta.json +72 -0
- package/dist/standalone/pdf-large-rw.min.js +56 -0
- package/dist/standalone/pdf-large.js +11299 -0
- package/dist/standalone/pdf-large.meta.json +57 -0
- package/dist/standalone/pdf-large.min.js +35 -0
- package/dist/standalone/pdf-legacy-rw.js +18228 -0
- package/dist/standalone/pdf-legacy-rw.meta.json +83 -0
- package/dist/standalone/pdf-legacy-rw.min.js +56 -0
- package/dist/standalone/pdf-legacy.js +13334 -0
- package/dist/standalone/pdf-legacy.meta.json +68 -0
- package/dist/standalone/pdf-legacy.min.js +35 -0
- package/dist/standalone/pdf-rw.js +13404 -0
- package/dist/standalone/pdf-rw.meta.json +50 -0
- package/dist/standalone/pdf-rw.min.js +56 -0
- package/dist/standalone/pdf.js +8510 -0
- package/dist/standalone/pdf.meta.json +35 -0
- package/dist/standalone/pdf.min.js +35 -0
- package/docs/README.md +53 -0
- package/docs/api/README.md +38 -0
- package/docs/api/_shared/README.md +91 -0
- package/docs/api/action/README.md +29 -0
- package/docs/api/action/action.md +81 -0
- package/docs/api/action/goTo.md +66 -0
- package/docs/api/action/launch.md +58 -0
- package/docs/api/action/named.md +55 -0
- package/docs/api/action/uri.md +54 -0
- package/docs/api/annot/README.md +53 -0
- package/docs/api/annot/annot.md +114 -0
- package/docs/api/annot/fileAttach.md +53 -0
- package/docs/api/annot/freeText.md +68 -0
- package/docs/api/annot/ink.md +69 -0
- package/docs/api/annot/link.md +74 -0
- package/docs/api/annot/markup.md +83 -0
- package/docs/api/annot/popup.md +52 -0
- package/docs/api/annot/projection.md +56 -0
- package/docs/api/annot/redact.md +67 -0
- package/docs/api/annot/square.md +87 -0
- package/docs/api/annot/stamp.md +54 -0
- package/docs/api/annot/text.md +69 -0
- package/docs/api/annot/widget.md +69 -0
- package/docs/api/associatedFiles/README.md +9 -0
- package/docs/api/associatedFiles/associatedFiles.md +78 -0
- package/docs/api/bundles/README.md +68 -0
- package/docs/api/bundles/dist-matrix.md +165 -0
- package/docs/api/bundles/pdf-full.md +148 -0
- package/docs/api/bundles/pdf-large.md +144 -0
- package/docs/api/bundles/pdf-legacy.md +169 -0
- package/docs/api/content/README.md +29 -0
- package/docs/api/content/color.md +99 -0
- package/docs/api/content/graphics.md +114 -0
- package/docs/api/content/images.md +124 -0
- package/docs/api/content/ops.md +100 -0
- package/docs/api/content/stream.md +107 -0
- package/docs/api/content/text.md +98 -0
- package/docs/api/crypto/README.md +29 -0
- package/docs/api/crypto/aesGcm.md +72 -0
- package/docs/api/crypto/permissions.md +79 -0
- package/docs/api/crypto/security.md +98 -0
- package/docs/api/crypto/standardV4.md +104 -0
- package/docs/api/crypto/standardV5.md +84 -0
- package/docs/api/crypto/standardV6.md +93 -0
- package/docs/api/destination/README.md +9 -0
- package/docs/api/destination/destination.md +79 -0
- package/docs/api/document/README.md +29 -0
- package/docs/api/document/builder.md +281 -0
- package/docs/api/document/catalog.md +98 -0
- package/docs/api/document/document.md +187 -0
- package/docs/api/document/encryptedWriter.md +149 -0
- package/docs/api/document/incrementalWriter.md +148 -0
- package/docs/api/document/page.md +99 -0
- package/docs/api/document/pages.md +82 -0
- package/docs/api/document/resources.md +102 -0
- package/docs/api/document/writer.md +157 -0
- package/docs/api/document/xrefStreamWriter.md +122 -0
- package/docs/api/embedded/README.md +13 -0
- package/docs/api/embedded/collection.md +80 -0
- package/docs/api/embedded/embeddedFile.md +86 -0
- package/docs/api/embedded/fileSpec.md +87 -0
- package/docs/api/errors.md +110 -0
- package/docs/api/extra/3d-richmedia.md +76 -0
- package/docs/api/extra/README.md +99 -0
- package/docs/api/extra/annot-extended.md +71 -0
- package/docs/api/extra/associated-files.md +70 -0
- package/docs/api/extra/ccitt-fax-decoder.md +74 -0
- package/docs/api/extra/color-spaces-extended.md +72 -0
- package/docs/api/extra/content-ops-extended.md +82 -0
- package/docs/api/extra/document-parts.md +69 -0
- package/docs/api/extra/embedded-files-portfolio.md +87 -0
- package/docs/api/extra/font-cid-typed.md +77 -0
- package/docs/api/extra/font-color-tagging.md +76 -0
- package/docs/api/extra/form-actions-extended.md +75 -0
- package/docs/api/extra/info-dict-deprecated.md +72 -0
- package/docs/api/extra/jbig2-read.md +80 -0
- package/docs/api/extra/legacy-deprecated-annots.md +89 -0
- package/docs/api/extra/legacy-deprecated-filters.md +78 -0
- package/docs/api/extra/legacy-rc4-read.md +74 -0
- package/docs/api/extra/legacy-xfa-read.md +65 -0
- package/docs/api/extra/linearization-write.md +71 -0
- package/docs/api/extra/misc.md +93 -0
- package/docs/api/extra/optional-content-extended.md +83 -0
- package/docs/api/extra/pdf-a-output-intent.md +65 -0
- package/docs/api/extra/pdf-sandbox.md +76 -0
- package/docs/api/extra/pdf-ua-tagged.md +63 -0
- package/docs/api/extra/pdf-x-prepress.md +65 -0
- package/docs/api/extra/redaction-iso32005.md +65 -0
- package/docs/api/extra/shading-typed.md +73 -0
- package/docs/api/extra/sig-aes-gcm.md +69 -0
- package/docs/api/extra/sig-pades.md +103 -0
- package/docs/api/extra/tagged-pdf-typed.md +78 -0
- package/docs/api/extra/transparency-typed.md +74 -0
- package/docs/api/extra/well-tagged-pdf.md +61 -0
- package/docs/api/extra/xmp-extended.md +65 -0
- package/docs/api/font/README.md +25 -0
- package/docs/api/font/embed.md +157 -0
- package/docs/api/font/encoding.md +95 -0
- package/docs/api/font/font.md +97 -0
- package/docs/api/font/type3.md +89 -0
- package/docs/api/form/README.md +35 -0
- package/docs/api/form/acroform.md +88 -0
- package/docs/api/form/appearance.md +87 -0
- package/docs/api/form/button.md +97 -0
- package/docs/api/form/choice.md +96 -0
- package/docs/api/form/fieldTree.md +93 -0
- package/docs/api/form/signature.md +90 -0
- package/docs/api/form/text.md +88 -0
- package/docs/api/linearization/README.md +11 -0
- package/docs/api/linearization/linearization.md +81 -0
- package/docs/api/main.md +116 -0
- package/docs/api/metadata/README.md +10 -0
- package/docs/api/metadata/info.md +70 -0
- package/docs/api/metadata/xmp.md +62 -0
- package/docs/api/ocg/README.md +23 -0
- package/docs/api/ocg/config.md +95 -0
- package/docs/api/ocg/ocg.md +77 -0
- package/docs/api/outline/README.md +11 -0
- package/docs/api/outline/outline.md +107 -0
- package/docs/api/pdf.md +152 -0
- package/docs/api/prepress/README.md +10 -0
- package/docs/api/prepress/outputIntent.md +79 -0
- package/docs/api/prepress/pageBoundary.md +75 -0
- package/docs/api/sig/README.md +32 -0
- package/docs/api/sig/byteRange.md +120 -0
- package/docs/api/sig/certChain.md +84 -0
- package/docs/api/sig/dss.md +111 -0
- package/docs/api/sig/oids.md +76 -0
- package/docs/api/sig/sha1.md +72 -0
- package/docs/api/sig/sign.md +317 -0
- package/docs/api/sig/signature.md +178 -0
- package/docs/api/sig/timestamp.md +84 -0
- package/docs/api/syntax/README.md +29 -0
- package/docs/api/syntax/crossRefStream.md +115 -0
- package/docs/api/syntax/filters/README.md +50 -0
- package/docs/api/syntax/filters/ascii85.md +76 -0
- package/docs/api/syntax/filters/asciiHex.md +73 -0
- package/docs/api/syntax/filters/dispatch.md +125 -0
- package/docs/api/syntax/filters/flate.md +134 -0
- package/docs/api/syntax/filters/runLength.md +78 -0
- package/docs/api/syntax/objStream.md +88 -0
- package/docs/api/syntax/parser-obj.md +97 -0
- package/docs/api/syntax/parser.md +151 -0
- package/docs/api/syntax/serializer.md +109 -0
- package/docs/api/syntax/tokenizer.md +104 -0
- package/docs/api/syntax/trailer.md +85 -0
- package/docs/api/syntax/xref.md +139 -0
- package/docs/api/tagged/README.md +25 -0
- package/docs/api/tagged/classMap.md +67 -0
- package/docs/api/tagged/markedContent.md +62 -0
- package/docs/api/tagged/parentTree.md +67 -0
- package/docs/api/tagged/roleMap.md +67 -0
- package/docs/api/tagged/structElement.md +76 -0
- package/docs/api/tagged/structTree.md +75 -0
- package/docs/guide/coverage.md +113 -0
- package/docs/guide/crypto.md +121 -0
- package/docs/guide/extending.md +76 -0
- package/docs/guide/getting-started.md +75 -0
- package/docs/guide/legacy-1.7.md +42 -0
- package/docs/guide/pades-integration.md +579 -0
- package/docs/guide/read-pdf.md +89 -0
- package/package.json +97 -4
- package/src/_shared/index.js +179 -0
- package/src/action/action.js +119 -0
- package/src/action/goTo.js +89 -0
- package/src/action/launch.js +61 -0
- package/src/action/named.js +54 -0
- package/src/action/uri.js +51 -0
- package/src/annot/annot.js +212 -0
- package/src/annot/fileAttach.js +55 -0
- package/src/annot/freeText.js +82 -0
- package/src/annot/ink.js +77 -0
- package/src/annot/link.js +77 -0
- package/src/annot/markup.js +91 -0
- package/src/annot/popup.js +53 -0
- package/src/annot/projection.js +52 -0
- package/src/annot/redact.js +87 -0
- package/src/annot/square.js +132 -0
- package/src/annot/stamp.js +48 -0
- package/src/annot/text.js +54 -0
- package/src/annot/widget.js +61 -0
- package/src/associatedFiles/associatedFiles.js +86 -0
- package/src/bundles/pdf-full.js +91 -0
- package/src/bundles/pdf-large.js +81 -0
- package/src/bundles/pdf-legacy.js +107 -0
- package/src/content/color.js +114 -0
- package/src/content/graphics.js +192 -0
- package/src/content/images.js +160 -0
- package/src/content/ops.js +137 -0
- package/src/content/stream.js +154 -0
- package/src/content/text.js +125 -0
- package/src/crypto/aesGcm.js +123 -0
- package/src/crypto/permissions.js +112 -0
- package/src/crypto/security.js +327 -0
- package/src/crypto/standardV4.js +443 -0
- package/src/crypto/standardV5.js +306 -0
- package/src/crypto/standardV6.js +334 -0
- package/src/destination/destination.js +183 -0
- package/src/document/builder.js +618 -0
- package/src/document/catalog.js +100 -0
- package/src/document/document.js +472 -0
- package/src/document/encryptedWriter.js +554 -0
- package/src/document/incrementalWriter.js +514 -0
- package/src/document/page.js +131 -0
- package/src/document/pages.js +103 -0
- package/src/document/resources.js +146 -0
- package/src/document/writer.js +211 -0
- package/src/document/xrefStreamWriter.js +353 -0
- package/src/embedded/collection.js +102 -0
- package/src/embedded/embeddedFile.js +99 -0
- package/src/embedded/fileSpec.js +137 -0
- package/src/errors.js +78 -0
- package/src/extra/3d-richmedia.js +171 -0
- package/src/extra/annot-extended.js +200 -0
- package/src/extra/associated-files.js +131 -0
- package/src/extra/ccitt-fax-decoder.js +776 -0
- package/src/extra/color-spaces-extended.js +196 -0
- package/src/extra/content-ops-extended.js +153 -0
- package/src/extra/document-parts.js +149 -0
- package/src/extra/embedded-files-portfolio.js +234 -0
- package/src/extra/font-cid-typed.js +185 -0
- package/src/extra/font-color-tagging.js +144 -0
- package/src/extra/form-actions-extended.js +196 -0
- package/src/extra/info-dict-deprecated.js +137 -0
- package/src/extra/jbig2-read.js +169 -0
- package/src/extra/legacy-deprecated-annots.js +198 -0
- package/src/extra/legacy-deprecated-filters.js +167 -0
- package/src/extra/legacy-rc4-read.js +235 -0
- package/src/extra/legacy-xfa-read.js +104 -0
- package/src/extra/linearization-write.js +97 -0
- package/src/extra/misc.js +217 -0
- package/src/extra/optional-content-extended.js +142 -0
- package/src/extra/pdf-a-output-intent.js +112 -0
- package/src/extra/pdf-sandbox.js +88 -0
- package/src/extra/pdf-ua-tagged.js +116 -0
- package/src/extra/pdf-x-prepress.js +114 -0
- package/src/extra/redaction-iso32005.js +136 -0
- package/src/extra/shading-typed.js +222 -0
- package/src/extra/sig-aes-gcm.js +135 -0
- package/src/extra/sig-pades.js +242 -0
- package/src/extra/tagged-pdf-typed.js +203 -0
- package/src/extra/transparency-typed.js +135 -0
- package/src/extra/well-tagged-pdf.js +138 -0
- package/src/extra/xmp-extended.js +190 -0
- package/src/font/embed.js +480 -0
- package/src/font/encoding.js +92 -0
- package/src/font/font.js +101 -0
- package/src/font/type3.js +75 -0
- package/src/form/acroform.js +94 -0
- package/src/form/appearance.js +90 -0
- package/src/form/button.js +105 -0
- package/src/form/choice.js +152 -0
- package/src/form/fieldTree.js +120 -0
- package/src/form/signature.js +100 -0
- package/src/form/text.js +101 -0
- package/src/linearization/linearization.js +107 -0
- package/src/main.js +411 -0
- package/src/metadata/info.js +87 -0
- package/src/metadata/xmp.js +62 -0
- package/src/ocg/config.js +156 -0
- package/src/ocg/ocg.js +124 -0
- package/src/outline/outline.js +157 -0
- package/src/pdf.js +133 -0
- package/src/prepress/outputIntent.js +118 -0
- package/src/prepress/pageBoundary.js +108 -0
- package/src/sig/byteRange.js +306 -0
- package/src/sig/certChain.js +247 -0
- package/src/sig/dss.js +317 -0
- package/src/sig/oids.js +157 -0
- package/src/sig/sha1.js +142 -0
- package/src/sig/sign.js +1899 -0
- package/src/sig/signature.js +1441 -0
- package/src/sig/timestamp.js +236 -0
- package/src/syntax/crossRefStream.js +133 -0
- package/src/syntax/filters/ascii85.js +122 -0
- package/src/syntax/filters/asciiHex.js +83 -0
- package/src/syntax/filters/dispatch.js +176 -0
- package/src/syntax/filters/flate.js +316 -0
- package/src/syntax/filters/runLength.js +96 -0
- package/src/syntax/objStream.js +99 -0
- package/src/syntax/parser-obj.js +52 -0
- package/src/syntax/parser.js +321 -0
- package/src/syntax/serializer.js +221 -0
- package/src/syntax/tokenizer.js +290 -0
- package/src/syntax/trailer.js +76 -0
- package/src/syntax/xref.js +341 -0
- package/src/tagged/classMap.js +81 -0
- package/src/tagged/markedContent.js +123 -0
- package/src/tagged/parentTree.js +126 -0
- package/src/tagged/roleMap.js +107 -0
- package/src/tagged/structElement.js +138 -0
- package/src/tagged/structTree.js +94 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
---
|
|
2
|
+
module: pdfBuilder
|
|
3
|
+
category: pdf/document
|
|
4
|
+
dependencies: [pdfErrors, pdfParserObj, pdfWriter]
|
|
5
|
+
returns: object
|
|
6
|
+
worker-safe: true
|
|
7
|
+
status: complete
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# pdfBuilder
|
|
11
|
+
|
|
12
|
+
> Chainable constructive DSL for assembling a PDF document from scratch.
|
|
13
|
+
|
|
14
|
+
**Module** `pdfBuilder` | **Source** `packages/front/office/pdf/src/document/builder.js` | **Deps** `pdfErrors`, `pdfParserObj`, `pdfWriter` | **Worker-safe** yes
|
|
15
|
+
|
|
16
|
+
An ergonomic layer over `pdfWriter.writeDocument`: instead of hand-assembling
|
|
17
|
+
a typed indirect-object graph, `builder()` returns a chainable object
|
|
18
|
+
(`addPage` → `addContent`/`addFont`/`addImage` → … → `build()`) that tracks
|
|
19
|
+
pages, content streams, fonts (referenced **or** [embedded](#embedded-fonts)),
|
|
20
|
+
[image XObjects](#images) and the Info dict internally, then
|
|
21
|
+
emits `Uint8Array` bytes on `.build()`. The returned chain is closure-based
|
|
22
|
+
(no `class`, no `this`), so it stays worker-transportable.
|
|
23
|
+
|
|
24
|
+
## Resolve
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const b = runtime.resolve('pdfBuilder');
|
|
28
|
+
// Returns: { builder }
|
|
29
|
+
const doc = b.builder();
|
|
30
|
+
// doc: { addPage, addContent, addFont, addImage, addMetadata, setVersion, setId, build }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API
|
|
34
|
+
|
|
35
|
+
| Method | Signature | Returns |
|
|
36
|
+
|--------|-----------|---------|
|
|
37
|
+
| `builder` | `() => Builder` | Creates a fresh chainable builder (own closure state — call once per document). |
|
|
38
|
+
|
|
39
|
+
### `Builder` chain
|
|
40
|
+
|
|
41
|
+
| Method | Signature | Notes |
|
|
42
|
+
|--------|-----------|-------|
|
|
43
|
+
| `addPage` | `(opts?: { mediaBox?: number[4], cropBox?: number[4], rotate?: number, resources?: DictObj }) => Builder` | Starts a new page (default `mediaBox` is US Letter `[0,0,612,792]`); becomes the target of subsequent `addContent`/`addFont` calls. |
|
|
44
|
+
| `addContent` | `(data: string \| Uint8Array) => Builder` | Appends one content stream to the **current** page. |
|
|
45
|
+
| `addFont` | `(spec: { name: string, baseFont: string, subtype?: string, encoding?: 'WinAnsiEncoding' \| 'MacRomanEncoding' \| 'StandardEncoding' }) => Builder` | **Legacy shape** — registers a non-embedded font reference (`subtype` defaults to `Type1`) into the current page's `/Resources /Font`. The font dictionary is allocated once per distinct `(baseFont, subtype, encoding)` and shared by every page that registers it. Without `encoding` the emitted bytes are frozen. |
|
|
46
|
+
| `addFont` | `(spec: { name: string, embedded: SimpleEmbed \| CidEmbed }) => Builder` | **Embedded shape** — takes a [`pdfFontEmbed`](../font/embed.md) result and allocates every indirect it needs (see [Embedded fonts](#embedded-fonts)). Exactly one of `baseFont` / `embedded` must be given. |
|
|
47
|
+
| `addImage` | `(spec: { name, width, height, colorSpace, bitsPerComponent, data, filter?, decodeParms?, sMask? }) => Builder` | Allocates an **image XObject** as an indirect stream and registers it into the current page's `/Resources /XObject` (see [Images](#images)). Decodes nothing; emits no content operator. |
|
|
48
|
+
| `addMetadata` | `(meta: object) => Builder` | Merges Info-dict fields. `Title`/`Author`/`Subject`/`Keywords`/`Creator`/`Producer`/`CreationDate`/`ModDate` are always emitted as PDF strings; other keys are kept only if their value is already a string. |
|
|
49
|
+
| `setVersion` | `(v: string) => Builder` | Overrides the PDF header version (default `'2.0'`); must match `/^\d\.\d$/`. |
|
|
50
|
+
| `setId` | `(a: string \| Uint8Array, b?: string \| Uint8Array) => Builder` | Sets the `/ID` pair — hex string or raw bytes; `b` defaults to `a`. |
|
|
51
|
+
| `build` | `() => Uint8Array` | Assembles Catalog + Pages tree + page objects + Info (if any), then calls `pdfWriter.writeDocument`. |
|
|
52
|
+
|
|
53
|
+
Every chain method except `build` returns the same `Builder` instance.
|
|
54
|
+
|
|
55
|
+
### `addFont` spec — non-embedded shape
|
|
56
|
+
|
|
57
|
+
| Key | Type | Notes |
|
|
58
|
+
|-----|------|-------|
|
|
59
|
+
| `name` | `string` | Resource name registered under the page's `/Resources /Font` (e.g. `F1`). Required. |
|
|
60
|
+
| `baseFont` | `string` | `/BaseFont` name (e.g. `Helvetica`). Exactly one of `baseFont` / `embedded`. |
|
|
61
|
+
| `subtype` | `string` | `/Subtype`; defaults to `Type1`. |
|
|
62
|
+
| `encoding` | `'WinAnsiEncoding' \| 'MacRomanEncoding' \| 'StandardEncoding'` | Optional. One of the three predefined simple-font encodings of ISO 32000-1 § 9.6.6 / Annex D, emitted as `/Encoding /<name>` after the existing keys: `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>`. Absent (or `undefined`) emits no `/Encoding` key and the bytes are identical to a spec without the option — the default is unchanged. Any other value, or `encoding` combined with `embedded`, throws `pdf/builder/bad-font` with `{ name, encoding }` in its `context`. No `/Differences` or custom encoding dictionaries. |
|
|
63
|
+
|
|
64
|
+
**Shared dictionary.** A non-embedded font dictionary is allocated once per
|
|
65
|
+
distinct `(baseFont, subtype, encoding)` and shared by every page that
|
|
66
|
+
registers it: `subtype` is the defaulted value, so `{ baseFont: 'Helvetica' }`
|
|
67
|
+
and `{ baseFont: 'Helvetica', subtype: 'Type1' }` share one object. Each page
|
|
68
|
+
(and each resource name) still gets its own `/Resources /Font` entry; only the
|
|
69
|
+
referenced object is shared. A 3-page document that registers the same four
|
|
70
|
+
faces on every page therefore carries 4 font dictionaries, not 12. A document
|
|
71
|
+
that never repeats a `(baseFont, subtype, encoding)` triple is byte-identical to
|
|
72
|
+
one built without sharing.
|
|
73
|
+
|
|
74
|
+
## Examples
|
|
75
|
+
|
|
76
|
+
### Minimal one-page document
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
const { builder } = runtime.resolve('pdfBuilder');
|
|
80
|
+
const bytes = builder()
|
|
81
|
+
.addPage({ mediaBox: [0, 0, 612, 792] })
|
|
82
|
+
.build();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Page with a font and content stream
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
const bytes = builder()
|
|
89
|
+
.addPage({ mediaBox: [0, 0, 612, 792] })
|
|
90
|
+
.addFont({ name: 'F1', baseFont: 'Helvetica', subtype: 'Type1' })
|
|
91
|
+
.addContent('BT /F1 12 Tf 100 700 Td (Hello) Tj ET')
|
|
92
|
+
.build();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Metadata + explicit `/ID`
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
const bytes = builder()
|
|
99
|
+
.addPage()
|
|
100
|
+
.addMetadata({ Title: 'Demo', Author: 'awa' })
|
|
101
|
+
.setId('0102030405060708090a0b0c0d0e0f10')
|
|
102
|
+
.build();
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Embedded fonts
|
|
106
|
+
|
|
107
|
+
`addFont({ name, embedded })` accepts a [`pdfFontEmbed`](../font/embed.md)
|
|
108
|
+
result — `embedSimple` (a `SimpleEmbed`: `/TrueType` + `/WinAnsiEncoding`) or
|
|
109
|
+
`embedCid` (a `CidEmbed`: `/Type0` + `/Identity-H` + `/CIDFontType2`). The
|
|
110
|
+
adapter deliberately returns **inline** dicts and leaves every indirect to its
|
|
111
|
+
consumer; the builder is that consumer.
|
|
112
|
+
|
|
113
|
+
An `embedded` value must carry `fontFile` (`Uint8Array`), `descriptor`,
|
|
114
|
+
`toUnicodeStream` and either `fontDict` **or** `type0Dict` + `cidFontDict` —
|
|
115
|
+
never both — else `pdf/builder/bad-font`.
|
|
116
|
+
|
|
117
|
+
### What gets allocated
|
|
118
|
+
|
|
119
|
+
Per **embed result object**, not per `addFont` call:
|
|
120
|
+
|
|
121
|
+
| # | Indirect | Content | Route |
|
|
122
|
+
|---|----------|---------|-------|
|
|
123
|
+
| 1 | font-program stream | `<< /Length1 <fontFile.length> >>` (plus `/Subtype /OpenType` when `fontFileKey` is `FontFile3`) over `embedded.fontFile` | both |
|
|
124
|
+
| 2 | `FontDescriptor` | `embedded.descriptor` **cloned**, with `[fontFileKey] → 1 0 R` added | both |
|
|
125
|
+
| 3 | `/ToUnicode` | `embedded.toUnicodeStream` (the serializer refuses an inline stream) | both |
|
|
126
|
+
| 4 | `CIDFont` | `embedded.cidFontDict` cloned, `/FontDescriptor → 2 0 R` | CID only |
|
|
127
|
+
| 5 | font object | simple: `embedded.fontDict` cloned, `/FontDescriptor → 2 0 R`, `/ToUnicode → 3 0 R` · CID: `embedded.type0Dict` cloned, `/DescendantFonts [4 0 R]`, `/ToUnicode → 3 0 R` | both |
|
|
128
|
+
|
|
129
|
+
So a simple embedding costs **4** indirects and a composite one **5** — i.e.
|
|
130
|
+
**+3** and **+4** over the single object a legacy `addFont` allocates. Only
|
|
131
|
+
the last one lands in the page's `/Resources /Font`.
|
|
132
|
+
|
|
133
|
+
**Identity cache.** The builder keeps a `WeakMap` keyed by the `embedded`
|
|
134
|
+
object, so registering the *same* result on several pages (under any resource
|
|
135
|
+
names) allocates the graph **once** and every page references the same font
|
|
136
|
+
object number. Two distinct results — even from the same face — are two
|
|
137
|
+
graphs.
|
|
138
|
+
|
|
139
|
+
**No mutation.** Every patched dict is shallow-cloned; the caller's
|
|
140
|
+
`descriptor` / `fontDict` / `type0Dict` / `cidFontDict` come back exactly as
|
|
141
|
+
`pdfFontEmbed` returned them, so one result can be reused across builders.
|
|
142
|
+
|
|
143
|
+
### Example — `pdfFontEmbed` → `addFont` → `addContent`
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
const fontsMod = runtime.resolve('fonts');
|
|
147
|
+
const embed = runtime.resolve('pdfFontEmbed');
|
|
148
|
+
const { builder } = runtime.resolve('pdfBuilder');
|
|
149
|
+
|
|
150
|
+
const face = fontsMod.read(ttfBytes);
|
|
151
|
+
const text = 'Unicode fi ⁄ ⁴';
|
|
152
|
+
const cps = [...new Set([...text].map(c => c.codePointAt(0)))];
|
|
153
|
+
|
|
154
|
+
const e = embed.embedCid(face, cps); // or embedSimple for WinAnsi
|
|
155
|
+
const hex = [...e.encode(text)]
|
|
156
|
+
.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
157
|
+
|
|
158
|
+
const bytes = builder()
|
|
159
|
+
.addPage({ mediaBox: [0, 0, 612, 792] })
|
|
160
|
+
.addFont({ name: 'F1', embedded: e })
|
|
161
|
+
.addContent(`BT /F1 12 Tf 72 700 Td <${hex}> Tj ET`)
|
|
162
|
+
.build();
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`e.encode(text)` yields the character codes the written font expects — WinAnsi
|
|
166
|
+
bytes for the simple route, big-endian 2-byte CIDs for `Identity-H` — and
|
|
167
|
+
`e.widthOf(codePoint)` gives the 1000/em advance for layout.
|
|
168
|
+
|
|
169
|
+
## Images
|
|
170
|
+
|
|
171
|
+
`addImage({ name, … })` is the image counterpart of `addFont`'s embedded
|
|
172
|
+
route: it allocates the image as an **indirect stream object** — the
|
|
173
|
+
serializer refuses an inline one — and maps `name` to it in the current
|
|
174
|
+
page's `/Resources /XObject`.
|
|
175
|
+
|
|
176
|
+
**The seam decodes nothing.** The caller supplies data that is *already*
|
|
177
|
+
encoded plus the parameters that describe it. This module does not parse a
|
|
178
|
+
PNG `IHDR`, a JPEG `SOF`, or anything else; it does not transcode, resample
|
|
179
|
+
or colour-manage, and it never inspects `data` — the bytes reach the file
|
|
180
|
+
verbatim. Choosing `/Filter`, `/ColorSpace` and `/BitsPerComponent`
|
|
181
|
+
consistently with those bytes is the caller's job.
|
|
182
|
+
|
|
183
|
+
**And it places no ink.** `addImage` makes the resource *reachable*; drawing
|
|
184
|
+
it is a content-stream matter, so the caller emits the operators itself (see
|
|
185
|
+
the snippet below).
|
|
186
|
+
|
|
187
|
+
### Spec
|
|
188
|
+
|
|
189
|
+
| Key | Type | `/Key` | Notes |
|
|
190
|
+
|-----|------|--------|-------|
|
|
191
|
+
| `name` | `string` | — | Resource name **without** the leading slash (e.g. `'Im0'`); non-empty. |
|
|
192
|
+
| `width` | `number` | `/Width` | Positive safe integer. |
|
|
193
|
+
| `height` | `number` | `/Height` | Positive safe integer. |
|
|
194
|
+
| `colorSpace` | `string` | `/ColorSpace` | Emitted as a **name** (e.g. `'DeviceRGB'`, `'DeviceGray'`); non-empty. |
|
|
195
|
+
| `bitsPerComponent` | `number` | `/BitsPerComponent` | Positive safe integer. |
|
|
196
|
+
| `data` | `Uint8Array` | stream body | The encoded bytes, verbatim; `/Length` is added by the serializer. |
|
|
197
|
+
| `filter` | `string?` | `/Filter` | Emitted as a name (e.g. `'DCTDecode'`, `'FlateDecode'`). Omit for unfiltered data — then no `/Filter` is written. |
|
|
198
|
+
| `decodeParms` | `DictObj?` | `/DecodeParms` | **Passthrough** — must already be a typed `obj.dict`, exactly like `addPage`'s `resources`. |
|
|
199
|
+
| `sMask` | `object?` | `/SMask` | A soft mask: the same spec shape **minus `name`**, and with **no nested `sMask`**. |
|
|
200
|
+
|
|
201
|
+
The emitted dict is
|
|
202
|
+
`<< /Type /XObject /Subtype /Image /Width … /Height … /ColorSpace … /BitsPerComponent … >>`,
|
|
203
|
+
plus `/Filter`, `/DecodeParms` and `/SMask` when those were given.
|
|
204
|
+
|
|
205
|
+
### Soft masks
|
|
206
|
+
|
|
207
|
+
An `sMask` is allocated **first**, as its own image XObject, and the parent's
|
|
208
|
+
`/SMask` holds a reference to it. It is *referenced, never named*: it does
|
|
209
|
+
**not** appear in the page's `/XObject` dict, so no content operator can draw
|
|
210
|
+
it directly. A masked image therefore costs **2** indirects instead of 1.
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
builder().addPage()
|
|
214
|
+
.addImage({
|
|
215
|
+
name: 'Im0', width: w, height: h,
|
|
216
|
+
colorSpace: 'DeviceRGB', bitsPerComponent: 8,
|
|
217
|
+
filter: 'FlateDecode', data: rgbDeflated,
|
|
218
|
+
sMask: { // no `name` here
|
|
219
|
+
width: w, height: h,
|
|
220
|
+
colorSpace: 'DeviceGray', bitsPerComponent: 8,
|
|
221
|
+
filter: 'FlateDecode', data: alphaDeflated
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
.build();
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Resource precedence
|
|
228
|
+
|
|
229
|
+
`/XObject` follows the pre-existing `/Font` rule exactly: `addPage`'s
|
|
230
|
+
`resources` passthrough is merged **after** the built resource classes and
|
|
231
|
+
only fills keys the builder did not produce. So on a page that called
|
|
232
|
+
`addImage`, a caller-supplied `/XObject` is dropped; on a page that did not,
|
|
233
|
+
it passes through untouched. Nothing about that rule changed.
|
|
234
|
+
|
|
235
|
+
### Example — place a JPEG on the page
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
const bytes = builder()
|
|
239
|
+
.addPage({ mediaBox: [0, 0, 612, 792] })
|
|
240
|
+
.addImage({
|
|
241
|
+
name: 'Im0',
|
|
242
|
+
width: 800, height: 600, // the image's own pixel size
|
|
243
|
+
colorSpace: 'DeviceRGB',
|
|
244
|
+
bitsPerComponent: 8,
|
|
245
|
+
filter: 'DCTDecode', // the bytes ARE a JPEG already
|
|
246
|
+
data: jpegBytes
|
|
247
|
+
})
|
|
248
|
+
// The seam placed the resource; the caller places the ink.
|
|
249
|
+
// `cm` is width height 0 0 x y in USER SPACE units, not pixels:
|
|
250
|
+
// 400x300 pt with its lower-left corner at (100, 400).
|
|
251
|
+
.addContent('q 400 0 0 300 100 400 cm /Im0 Do Q')
|
|
252
|
+
.build();
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`q … Q` brackets the transform so the CTM is restored afterwards; the image
|
|
256
|
+
XObject's own space is the unit square, which is why the `cm` matrix carries
|
|
257
|
+
the on-page size directly.
|
|
258
|
+
|
|
259
|
+
## Errors
|
|
260
|
+
|
|
261
|
+
| Code | Class | When |
|
|
262
|
+
|------|-------|------|
|
|
263
|
+
| `pdf/builder/no-page` | `RenderError` | `addContent`, `addFont` or `addImage` called before any `addPage`. |
|
|
264
|
+
| `pdf/builder/bad-bytes` | `RenderError` | `addContent` data is neither `string` nor `Uint8Array`. |
|
|
265
|
+
| `pdf/builder/bad-font` | `RenderError` | `addFont` spec has no `name`; or neither / both of `baseFont` and `embedded`; or an `embedded` value that is not a well-formed `pdfFontEmbed` result; or an `encoding` that is not one of the three predefined names, or is combined with `embedded`. |
|
|
266
|
+
| `pdf/builder/bad-image` | `RenderError` | `addImage` spec is not an object, or one of `name` / `width` / `height` / `colorSpace` / `bitsPerComponent` / `data` / `filter` / `decodeParms` is missing or ill-typed, or an `sMask` carries a nested `sMask`. `err.context.keys` lists the offending keys; `err.context.sMask` is `true` when the failure is on the mask leg. |
|
|
267
|
+
| `pdf/builder/bad-metadata` | `RenderError` | `addMetadata` argument is not an object. |
|
|
268
|
+
| `pdf/builder/bad-version` | `RenderError` | `setVersion` value doesn't match `/^\d\.\d$/`. |
|
|
269
|
+
| `pdf/builder/bad-id` | `RenderError` | `setId` part is neither hex string nor `Uint8Array`. |
|
|
270
|
+
| `pdf/builder/bad-id-hex` | `RenderError` | `setId` hex string has odd length. |
|
|
271
|
+
| `pdf/builder/bad-box` | `RenderError` | `mediaBox`/`cropBox` is not a 4-element array. |
|
|
272
|
+
| `pdf/builder/no-pages` | `RenderError` | `build()` called with zero pages added. |
|
|
273
|
+
|
|
274
|
+
It also propagates every code from [`pdfWriter`](./writer.md) (raised inside `writeDocument`).
|
|
275
|
+
|
|
276
|
+
## See also
|
|
277
|
+
|
|
278
|
+
- [`pdfWriter`](./writer.md) — the underlying emitter.
|
|
279
|
+
- [`pdfFontEmbed`](../font/embed.md) — produces the `embedded` value (`embedSimple` / `embedCid`).
|
|
280
|
+
- [`pdfParserObj`](../syntax/parser-obj.md) — typed-object constructors (`obj.dict`, `obj.ref`, …) used internally.
|
|
281
|
+
- [`pdfIncrementalWriter`](./incrementalWriter.md) · [`pdfEncryptedWriter`](./encryptedWriter.md)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
module: pdfCatalog
|
|
3
|
+
category: pdf/document
|
|
4
|
+
dependencies: [pdfErrors, pdfParser]
|
|
5
|
+
returns: object
|
|
6
|
+
worker-safe: true
|
|
7
|
+
status: complete
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# pdfCatalog
|
|
11
|
+
|
|
12
|
+
> Typing of the `/Type /Catalog` dictionary, ISO 32000-2 §7.7.2 — root of the document tree.
|
|
13
|
+
|
|
14
|
+
**Module** `pdfCatalog` | **Source** `packages/front/office/pdf/src/document/catalog.js` | **Deps** `pdfErrors`, `pdfParser` | **Worker-safe** yes
|
|
15
|
+
|
|
16
|
+
Extracts the canonical entries of the root dictionary referenced by
|
|
17
|
+
`trailer.root`. Unrecognised entries are preserved in `_extras` — useful for
|
|
18
|
+
extensions and non-destructive round trips. `/Type` is validated strictly (when
|
|
19
|
+
present it must be `/Catalog`) so that a bad xref offset cannot silently type an
|
|
20
|
+
arbitrary dictionary as the catalog.
|
|
21
|
+
|
|
22
|
+
## Resolve
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
const cat = runtime.resolve('pdfCatalog');
|
|
26
|
+
// Returns: { typeCatalog }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## API
|
|
30
|
+
|
|
31
|
+
| Method | Signature | Returns |
|
|
32
|
+
|--------|-----------|---------|
|
|
33
|
+
| `typeCatalog` | `(dict: PdfDict) => TypedCatalog` | Typed record. |
|
|
34
|
+
|
|
35
|
+
### `TypedCatalog` shape
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
{
|
|
39
|
+
pages: { num, gen }, // /Pages — required
|
|
40
|
+
version?: string, // /Version (overrides the header)
|
|
41
|
+
pageLayout?: string, // /PageLayout
|
|
42
|
+
pageMode?: string, // /PageMode
|
|
43
|
+
lang?: string, // /Lang
|
|
44
|
+
outlines?: PdfRef, // /Outlines
|
|
45
|
+
metadata?: PdfRef, // /Metadata (XMP stream)
|
|
46
|
+
structTreeRoot?: PdfRef, // /StructTreeRoot (tagged PDF)
|
|
47
|
+
acroForm?: PdfObject, // /AcroForm
|
|
48
|
+
names?: PdfObject, // /Names
|
|
49
|
+
dests?: PdfObject, // /Dests
|
|
50
|
+
viewerPrefs?: PdfObject, // /ViewerPreferences
|
|
51
|
+
pageLabels?: PdfObject, // /PageLabels
|
|
52
|
+
markInfo?: PdfObject, // /MarkInfo
|
|
53
|
+
ocProperties?: PdfObject, // /OCProperties
|
|
54
|
+
outputIntents?: PdfObject, // /OutputIntents
|
|
55
|
+
raw: PdfDict,
|
|
56
|
+
_extras: Object<string, PdfObject>
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Examples
|
|
61
|
+
|
|
62
|
+
### Reaching the Catalog
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
const doc = api.read(bytes);
|
|
66
|
+
doc.catalog.pages; // { num, gen } — page-tree root
|
|
67
|
+
doc.catalog.metadata; // reference to the XMP stream, when present
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Following the Outlines reference
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
if (doc.catalog.outlines) {
|
|
74
|
+
const root = doc._raw.resolve(doc.catalog.outlines);
|
|
75
|
+
const items = runtime.resolve('pdfOutline').walkOutline(root, doc._raw.resolve);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Inspecting `_extras`
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
Object.keys(doc.catalog._extras);
|
|
83
|
+
// e.g. ['MyProprietaryKey'] when the PDF carries an entry outside §7.7.2
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Errors
|
|
87
|
+
|
|
88
|
+
| Code | Class | When |
|
|
89
|
+
|------|-------|------|
|
|
90
|
+
| `pdf/catalog/not-dict` | `ParseError` | Argument is not a dictionary. |
|
|
91
|
+
| `pdf/catalog/bad-type` | `ParseError` | `/Type` present but not `/Catalog`. |
|
|
92
|
+
| `pdf/catalog/missing-pages` | `ParseError` | `/Pages` missing or not a reference. |
|
|
93
|
+
|
|
94
|
+
## See also
|
|
95
|
+
|
|
96
|
+
- [`pdfPages`](./pages.md) — consumes `catalog.pages` as its root.
|
|
97
|
+
- [`pdfDocument`](./document.md)
|
|
98
|
+
- [`pdfErrors`](../errors.md)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
module: pdfDocument
|
|
3
|
+
category: pdf/document
|
|
4
|
+
dependencies: [pdfErrors, pdfTokenizer, pdfParser, pdfXref, pdfTrailer, pdfCatalog, pdfPage, pdfPages, pdfCrossRefStream, pdfObjStream, pdfFilterDispatch]
|
|
5
|
+
returns: object
|
|
6
|
+
worker-safe: true
|
|
7
|
+
status: complete
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# pdfDocument
|
|
11
|
+
|
|
12
|
+
> Top-level read orchestrator — `Uint8Array` → navigable typed model.
|
|
13
|
+
|
|
14
|
+
**Module** `pdfDocument` | **Source** `packages/front/office/pdf/src/document/document.js` | **Deps** `pdfErrors`, `pdfTokenizer`, `pdfParser`, `pdfXref`, `pdfTrailer`, `pdfCatalog`, `pdfPage`, `pdfPages`, `pdfCrossRefStream`, `pdfObjStream`, `pdfFilterDispatch` | **Worker-safe** yes
|
|
15
|
+
|
|
16
|
+
Full pipeline: header → xref (chaining `/Prev`, at most 32 sections) → trailer →
|
|
17
|
+
catalog → pages. It builds an **indirect resolver** (`doc._raw.resolve`) backed
|
|
18
|
+
by a `Map<'num:gen'>` cache.
|
|
19
|
+
|
|
20
|
+
Both cross-reference forms are read automatically. At each `startxref` /
|
|
21
|
+
`/Prev` offset the walk looks at the bytes: an `xref` keyword takes the
|
|
22
|
+
classical table path, anything else is parsed as a `/Type /XRef`
|
|
23
|
+
cross-reference stream through [`pdfCrossRefStream`](../syntax/crossRefStream.md)
|
|
24
|
+
(§7.5.8), so mixed chains — a signed file whose classical incremental section
|
|
25
|
+
chains back into an xref-stream base, or the reverse — resolve end to end. A
|
|
26
|
+
classical trailer carrying `/XRefStm` (hybrid-reference file, §7.5.8.4) also
|
|
27
|
+
has its companion stream merged, without overriding the entries the table
|
|
28
|
+
itself provides. Objects stored inside a `/Type /ObjStm` container (§7.5.7)
|
|
29
|
+
are materialised on demand through [`pdfObjStream`](../syntax/objStream.md),
|
|
30
|
+
each container being decoded once per document.
|
|
31
|
+
|
|
32
|
+
The cross-reference stream is decoded through
|
|
33
|
+
[`pdfFilterDispatch`](../syntax/filters/dispatch.md), which hands its
|
|
34
|
+
`/DecodeParms` to the decoder as plain values, so a `/Predictor 12` xref stream
|
|
35
|
+
(the shape most PDF 1.5+ producers emit) is un-predicted before its entries
|
|
36
|
+
are read.
|
|
37
|
+
|
|
38
|
+
**Trailer merge across sections.** The trailer `readDocument`
|
|
39
|
+
returns is the merge of every section's trailer dict, newest first. The
|
|
40
|
+
newest dict is kept whole. Each document key it lacks (`/Root`, `/Info`,
|
|
41
|
+
`/ID`, `/Encrypt`, `/Size`) comes from the newest older section that carries
|
|
42
|
+
it. Section-local keys are never inherited: `/Prev`, `/XRefStm`, and a
|
|
43
|
+
cross-reference stream's `/Type`, `/W`, `/Index`, `/Length` and filter
|
|
44
|
+
entries. Each section's own `/Prev` drives the walk. So a linearized file
|
|
45
|
+
whose main xref stream omits `/Root` reads, and so does an incremental update
|
|
46
|
+
that omits it. A chain where **no** section supplies `/Root` still throws
|
|
47
|
+
`pdf/trailer/missing-root`.
|
|
48
|
+
|
|
49
|
+
**Free entries.** Sometimes the winning (newest) xref entry of a
|
|
50
|
+
referenced object is free. The resolver then uses the newest section that
|
|
51
|
+
still defines the object, and records `pdf/document/free-entry-fallback` in
|
|
52
|
+
`doc.losses`. When every section marks the object free, the resolver records
|
|
53
|
+
`pdf/document/free-object` and the reference reads as the null object
|
|
54
|
+
(ISO 32000-2 §7.3.10). A page-tree kid in that state contributes no page. A
|
|
55
|
+
`/Root` that is free in every section is still refused with
|
|
56
|
+
`pdf/document/free-object`.
|
|
57
|
+
|
|
58
|
+
## Resolve
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
const docMod = runtime.resolve('pdfDocument');
|
|
62
|
+
// Returns: { readDocument, readHeader }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API
|
|
66
|
+
|
|
67
|
+
| Method | Signature | Returns |
|
|
68
|
+
|--------|-----------|---------|
|
|
69
|
+
| `readDocument` | `(bytes: Uint8Array, opts?: { allowEncrypted?: boolean }) => Document` | Full model. |
|
|
70
|
+
| `readHeader` | `(bytes: Uint8Array) => { version, end }` | Header only. |
|
|
71
|
+
|
|
72
|
+
### `Document` shape
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
{
|
|
76
|
+
version: string, // '2.0', '1.7', …
|
|
77
|
+
catalog: TypedCatalog, // see pdfCatalog
|
|
78
|
+
pages: TypedPage[], // already resolved and typed
|
|
79
|
+
trailer: TypedTrailer, // see pdfTrailer
|
|
80
|
+
xref: {
|
|
81
|
+
// classical entries: { offset, gen, free }
|
|
82
|
+
// xref-stream entries also carry `type` (0 | 1 | 2); a type-2
|
|
83
|
+
// entry is { type: 2, objStm, index, offset: 0, gen: 0, free }
|
|
84
|
+
entries: { [num]: { offset, gen, free, type? } },
|
|
85
|
+
sections: Array<{ at, kind, entries }> // kind: 'table' | 'stream'
|
|
86
|
+
},
|
|
87
|
+
// Read-path degradations, e.g. 'pdf/document/free-entry-fallback',
|
|
88
|
+
// 'pdf/document/free-object'. Live: later _raw.resolve calls append to it.
|
|
89
|
+
losses: Array<{ code, message, context }>,
|
|
90
|
+
_raw: {
|
|
91
|
+
resolve: (ref) => PdfObject, // generic cached resolver
|
|
92
|
+
bytes: Uint8Array,
|
|
93
|
+
headerEnd: number,
|
|
94
|
+
// an object materialised from an object stream carries
|
|
95
|
+
// `objStm` (the container's object number) and `offset: 0`
|
|
96
|
+
indirects: Map<'num:gen', { value, offset, objStm? }>
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Examples
|
|
102
|
+
|
|
103
|
+
### Full read
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
const docMod = runtime.resolve('pdfDocument');
|
|
107
|
+
const doc = docMod.readDocument(bytes);
|
|
108
|
+
console.log(doc.version, doc.pages.length);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Header only
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
docMod.readHeader(bytes);
|
|
115
|
+
// → { version: '2.0', end: 9 }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Resolve an arbitrary reference
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
if (doc.catalog.metadata) {
|
|
122
|
+
const xmp = doc._raw.resolve(doc.catalog.metadata);
|
|
123
|
+
// xmp.type === 'stream'; xmp.raw === XMP Uint8Array
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Encrypted documents
|
|
128
|
+
|
|
129
|
+
`readDocument` fails loud on an encrypted input: when the
|
|
130
|
+
trailer carries `/Encrypt`, it throws `pdf/document/encrypted` instead
|
|
131
|
+
of silently returning a model whose strings and streams are still
|
|
132
|
+
ciphertext. Pass `{ allowEncrypted: true }` to opt into today's raw
|
|
133
|
+
behaviour (the object graph is returned unmodified — no decrypt is
|
|
134
|
+
performed):
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
try {
|
|
138
|
+
docMod.readDocument(bytes);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
if (e.code === 'pdf/document/encrypted') {
|
|
141
|
+
// bytes are encrypted; no decrypt path is composed here.
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Explicit opt-out — returns the raw (still-encrypted) container:
|
|
146
|
+
const raw = docMod.readDocument(bytes, { allowEncrypted: true });
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The full compose-decrypt read path (password API, V4/V5/V6 handler
|
|
150
|
+
selection, per-object decrypt through `resolveByKey`) is **not provided**
|
|
151
|
+
by this module — it only fails loud on an encrypted input.
|
|
152
|
+
|
|
153
|
+
## Errors
|
|
154
|
+
|
|
155
|
+
| Code | Class | When |
|
|
156
|
+
|------|-------|------|
|
|
157
|
+
| `pdf/document/short` | `ParseError` | Input shorter than 8 bytes. |
|
|
158
|
+
| `pdf/document/bad-header` | `ParseError` | No `%PDF-` in the first 1024 bytes. |
|
|
159
|
+
| `pdf/document/bad-input` | `ParseError` | Argument is not a `Uint8Array`. |
|
|
160
|
+
| `pdf/document/no-startxref` | `ParseError` | No `startxref` at the end of the file. |
|
|
161
|
+
| `pdf/document/no-trailer` | `ParseError` | No usable trailer. |
|
|
162
|
+
| `pdf/document/encrypted` | `ParseError` | Trailer carries `/Encrypt` and `opts.allowEncrypted` is not `true`. |
|
|
163
|
+
| `pdf/document/missing-xref` | `ParseError` | Reference absent from the table. |
|
|
164
|
+
| `pdf/document/free-object` | `ParseError` | The catalog (`/Root`) is free in every xref section. Any other free reference is recorded in `doc.losses` instead of thrown. |
|
|
165
|
+
| `pdf/document/bad-offset` | `ParseError` | Xref offset out of range. |
|
|
166
|
+
| `pdf/document/xref-mismatch` | `ParseError` | The definition at the offset does not match the expected `num gen`. |
|
|
167
|
+
| `pdf/document/bad-xref-section` | `ParseError` | The bytes at a `startxref` / `/Prev` / `/XRefStm` offset are neither an `xref` table nor a `/Type /XRef` stream. |
|
|
168
|
+
| `pdf/document/xrefstm-indirect-length` | `ParseError` | A cross-reference stream uses an indirect `/Length`, which cannot be resolved before the xref exists. |
|
|
169
|
+
| `pdf/document/xref-stream-unwired` | `ParseError` | The instance was built without `pdfCrossRefStream`, `pdfObjStream` and `pdfFilterDispatch`, and the input needs the stream path. |
|
|
170
|
+
| `pdf/document/objstm-nested` | `ParseError` | An object stream is itself stored inside another object stream. |
|
|
171
|
+
| `pdf/document/objstm-not-stream` | `ParseError` | The object a type-2 entry names as its container is not a stream. |
|
|
172
|
+
| `pdf/document/objstm-mismatch` | `ParseError` | The container member at the entry's index carries another object number. |
|
|
173
|
+
| `pdf/document/objstm-encrypted` | `ParseError` | A compressed object was reached under `allowEncrypted` — no decrypt path is composed. |
|
|
174
|
+
|
|
175
|
+
It also propagates every code from [`pdfXref`](../syntax/xref.md),
|
|
176
|
+
[`pdfTrailer`](../syntax/trailer.md), [`pdfCatalog`](./catalog.md),
|
|
177
|
+
[`pdfPages`](./pages.md), [`pdfPage`](./page.md),
|
|
178
|
+
[`pdfCrossRefStream`](../syntax/crossRefStream.md),
|
|
179
|
+
[`pdfObjStream`](../syntax/objStream.md) and
|
|
180
|
+
[`pdfFilterDispatch`](../syntax/filters/dispatch.md).
|
|
181
|
+
|
|
182
|
+
## See also
|
|
183
|
+
|
|
184
|
+
- [`pdf`](../pdf.md) — wraps `readDocument` behind `.read()`.
|
|
185
|
+
- [Read pipeline](../../guide/read-pdf.md)
|
|
186
|
+
- [Coverage](../../guide/coverage.md)
|
|
187
|
+
- [`pdfErrors`](../errors.md)
|