@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,290 @@
|
|
|
1
|
+
// Copyright (c) 2026 AwaCloud SAS
|
|
2
|
+
// Author: Matthieu Bouilloux
|
|
3
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
4
|
+
// Dual-licensed; see the NOTICE file for licensing and any additional terms.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @fileoverview PDF tokenizer — splits a `Uint8Array` of PDF source into
|
|
8
|
+
* a stream of tokens per ISO 32000-2:2020 §7.2 (Lexical conventions).
|
|
9
|
+
*
|
|
10
|
+
* Tokens emitted (kind → payload):
|
|
11
|
+
*
|
|
12
|
+
* | kind | payload |
|
|
13
|
+
* |--------------|------------------------------------------------|
|
|
14
|
+
* | `'ws'` | whitespace run (only when `keepWhitespace`) |
|
|
15
|
+
* | `'comment'` | comment body (bytes between `%` and EOL) |
|
|
16
|
+
* | `'name'` | name string, `#xx` hex-escapes decoded |
|
|
17
|
+
* | `'int'` | parsed JS number (safe integer) |
|
|
18
|
+
* | `'real'` | parsed JS number (float) |
|
|
19
|
+
* | `'string'` | literal string `(...)` decoded → Uint8Array |
|
|
20
|
+
* | `'hex'` | hex string `<...>` decoded → Uint8Array |
|
|
21
|
+
* | `'open_arr'` | `[` |
|
|
22
|
+
* | `'close_arr'`| `]` |
|
|
23
|
+
* | `'open_dict'`| `<<` |
|
|
24
|
+
* | `'close_dict'`| `>>` |
|
|
25
|
+
* | `'kw'` | keyword bytes as ASCII string |
|
|
26
|
+
* | `'eof_marker'`| `%%EOF` |
|
|
27
|
+
*
|
|
28
|
+
* @module pdf/syntax/tokenizer
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Module factory — worker-safe, self-contained.
|
|
33
|
+
*/
|
|
34
|
+
import { pdfErrors } from '../errors.js';
|
|
35
|
+
import { pdfShared } from '../_shared/index.js';
|
|
36
|
+
|
|
37
|
+
export const pdfTokenizer = {
|
|
38
|
+
name: 'pdfTokenizer',
|
|
39
|
+
dependencies: ['pdfErrors', 'pdfShared'],
|
|
40
|
+
deps: [pdfErrors, pdfShared],
|
|
41
|
+
factory(errors, shared) {
|
|
42
|
+
const { ParseError } = errors;
|
|
43
|
+
const { ASCII, isWs, isEol, isDigit, isRegular, hexNibble } = shared;
|
|
44
|
+
const {
|
|
45
|
+
HT, LF, CR, FF,
|
|
46
|
+
LPAREN, RPAREN, LANGLE, RANGLE, LBRACK, RBRACK,
|
|
47
|
+
SLASH, PERCENT, BACKSLASH,
|
|
48
|
+
PLUS, MINUS, DOT,
|
|
49
|
+
ZERO,
|
|
50
|
+
HASH
|
|
51
|
+
} = ASCII;
|
|
52
|
+
|
|
53
|
+
function tokenize(bytes, opts) {
|
|
54
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
55
|
+
throw new ParseError('pdf/tokenizer/bad-input',
|
|
56
|
+
'tokenize() expects a Uint8Array', { context: { typeof: typeof bytes } });
|
|
57
|
+
}
|
|
58
|
+
const keepWs = !!(opts && opts.keepWhitespace);
|
|
59
|
+
let i = (opts && opts.start | 0) || 0;
|
|
60
|
+
const N = (opts && Number.isFinite(opts.end))
|
|
61
|
+
? Math.min(opts.end | 0, bytes.length) : bytes.length;
|
|
62
|
+
let lookahead = null;
|
|
63
|
+
|
|
64
|
+
function pos() { return i; }
|
|
65
|
+
function seek(n) {
|
|
66
|
+
if (n < 0 || n > bytes.length) {
|
|
67
|
+
throw new ParseError('pdf/tokenizer/bad-seek',
|
|
68
|
+
'seek out of range', { context: { offset: n, total: bytes.length } });
|
|
69
|
+
}
|
|
70
|
+
i = n; lookahead = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function readName() {
|
|
74
|
+
const start = i;
|
|
75
|
+
i++;
|
|
76
|
+
const out = [];
|
|
77
|
+
while (i < N) {
|
|
78
|
+
const b = bytes[i];
|
|
79
|
+
if (!isRegular(b)) break;
|
|
80
|
+
if (b === HASH) {
|
|
81
|
+
if (i + 2 >= N) {
|
|
82
|
+
throw new ParseError('pdf/tokenizer/bad-name-escape',
|
|
83
|
+
'truncated #xx escape in name', { context: { offset: i } });
|
|
84
|
+
}
|
|
85
|
+
const hi = hexNibble(bytes[i + 1]);
|
|
86
|
+
const lo = hexNibble(bytes[i + 2]);
|
|
87
|
+
if (hi < 0 || lo < 0) {
|
|
88
|
+
throw new ParseError('pdf/tokenizer/bad-name-escape',
|
|
89
|
+
'invalid hex digits in name escape', { context: { offset: i } });
|
|
90
|
+
}
|
|
91
|
+
out.push((hi << 4) | lo);
|
|
92
|
+
i += 3;
|
|
93
|
+
} else {
|
|
94
|
+
out.push(b); i++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const value = new TextDecoder('utf-8', { fatal: false }).decode(Uint8Array.from(out));
|
|
98
|
+
return { kind: 'name', value, offset: start, end: i };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function readNumber() {
|
|
102
|
+
const start = i;
|
|
103
|
+
let isReal = false;
|
|
104
|
+
if (bytes[i] === PLUS || bytes[i] === MINUS) i++;
|
|
105
|
+
while (i < N) {
|
|
106
|
+
const b = bytes[i];
|
|
107
|
+
if (isDigit(b)) { i++; continue; }
|
|
108
|
+
if (b === DOT && !isReal) { isReal = true; i++; continue; }
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
const raw = new TextDecoder('latin1').decode(bytes.subarray(start, i));
|
|
112
|
+
if (raw === '' || raw === '+' || raw === '-' || raw === '.') {
|
|
113
|
+
throw new ParseError('pdf/tokenizer/bad-number',
|
|
114
|
+
'empty number token', { context: { offset: start } });
|
|
115
|
+
}
|
|
116
|
+
const n = Number(raw);
|
|
117
|
+
if (!Number.isFinite(n)) {
|
|
118
|
+
throw new ParseError('pdf/tokenizer/bad-number',
|
|
119
|
+
'cannot parse number', { context: { raw, offset: start } });
|
|
120
|
+
}
|
|
121
|
+
return { kind: isReal ? 'real' : 'int', value: n, offset: start, end: i, raw };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function readLiteralString() {
|
|
125
|
+
const start = i;
|
|
126
|
+
i++;
|
|
127
|
+
const out = [];
|
|
128
|
+
let depth = 1;
|
|
129
|
+
while (i < N && depth > 0) {
|
|
130
|
+
const b = bytes[i];
|
|
131
|
+
if (b === LPAREN) { depth++; out.push(b); i++; continue; }
|
|
132
|
+
if (b === RPAREN) { depth--; if (depth === 0) { i++; break; } out.push(b); i++; continue; }
|
|
133
|
+
if (b === BACKSLASH) {
|
|
134
|
+
if (i + 1 >= N) {
|
|
135
|
+
throw new ParseError('pdf/tokenizer/bad-string',
|
|
136
|
+
'trailing backslash in literal string', { context: { offset: i } });
|
|
137
|
+
}
|
|
138
|
+
const nxt = bytes[i + 1];
|
|
139
|
+
if (nxt === LF) { i += 2; continue; }
|
|
140
|
+
if (nxt === CR) {
|
|
141
|
+
i += 2;
|
|
142
|
+
if (i < N && bytes[i] === LF) i++;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (nxt === 0x6E) { out.push(LF); i += 2; continue; }
|
|
146
|
+
if (nxt === 0x72) { out.push(CR); i += 2; continue; }
|
|
147
|
+
if (nxt === 0x74) { out.push(HT); i += 2; continue; }
|
|
148
|
+
if (nxt === 0x62) { out.push(0x08); i += 2; continue; }
|
|
149
|
+
if (nxt === 0x66) { out.push(FF); i += 2; continue; }
|
|
150
|
+
if (nxt === LPAREN || nxt === RPAREN || nxt === BACKSLASH) {
|
|
151
|
+
out.push(nxt); i += 2; continue;
|
|
152
|
+
}
|
|
153
|
+
if (nxt >= ZERO && nxt <= ZERO + 7) {
|
|
154
|
+
let v = 0, k = 0;
|
|
155
|
+
i++;
|
|
156
|
+
while (k < 3 && i < N && bytes[i] >= ZERO && bytes[i] <= ZERO + 7) {
|
|
157
|
+
v = (v << 3) | (bytes[i] - ZERO);
|
|
158
|
+
i++; k++;
|
|
159
|
+
}
|
|
160
|
+
out.push(v & 0xFF);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
i++;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (b === CR) {
|
|
167
|
+
out.push(LF);
|
|
168
|
+
i++;
|
|
169
|
+
if (i < N && bytes[i] === LF) i++;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
out.push(b); i++;
|
|
173
|
+
}
|
|
174
|
+
if (depth !== 0) {
|
|
175
|
+
throw new ParseError('pdf/tokenizer/unterminated-string',
|
|
176
|
+
'literal string was not closed', { context: { offset: start } });
|
|
177
|
+
}
|
|
178
|
+
return { kind: 'string', value: Uint8Array.from(out), offset: start, end: i };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function readHexString() {
|
|
182
|
+
const start = i;
|
|
183
|
+
i++;
|
|
184
|
+
const nibbles = [];
|
|
185
|
+
while (i < N) {
|
|
186
|
+
const b = bytes[i];
|
|
187
|
+
if (b === RANGLE) { i++; break; }
|
|
188
|
+
if (isWs(b)) { i++; continue; }
|
|
189
|
+
const v = hexNibble(b);
|
|
190
|
+
if (v < 0) {
|
|
191
|
+
throw new ParseError('pdf/tokenizer/bad-hex',
|
|
192
|
+
'non-hex byte in hex string', { context: { offset: i, byte: b } });
|
|
193
|
+
}
|
|
194
|
+
nibbles.push(v); i++;
|
|
195
|
+
}
|
|
196
|
+
if (nibbles.length % 2 === 1) nibbles.push(0);
|
|
197
|
+
const out = new Uint8Array(nibbles.length / 2);
|
|
198
|
+
for (let k = 0; k < out.length; k++)
|
|
199
|
+
out[k] = (nibbles[2 * k] << 4) | nibbles[2 * k + 1];
|
|
200
|
+
return { kind: 'hex', value: out, offset: start, end: i };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function readKeyword() {
|
|
204
|
+
const start = i;
|
|
205
|
+
while (i < N && isRegular(bytes[i])) i++;
|
|
206
|
+
const raw = new TextDecoder('latin1').decode(bytes.subarray(start, i));
|
|
207
|
+
if (raw === '') {
|
|
208
|
+
throw new ParseError('pdf/tokenizer/empty-keyword',
|
|
209
|
+
'empty keyword', { context: { offset: start } });
|
|
210
|
+
}
|
|
211
|
+
return { kind: 'kw', value: raw, offset: start, end: i };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function next() {
|
|
215
|
+
if (lookahead) { const t = lookahead; lookahead = null; return t; }
|
|
216
|
+
while (i < N) {
|
|
217
|
+
const b = bytes[i];
|
|
218
|
+
if (isWs(b)) {
|
|
219
|
+
if (keepWs) {
|
|
220
|
+
const start = i;
|
|
221
|
+
while (i < N && isWs(bytes[i])) i++;
|
|
222
|
+
return { kind: 'ws', offset: start, end: i };
|
|
223
|
+
}
|
|
224
|
+
i++; continue;
|
|
225
|
+
}
|
|
226
|
+
if (b === PERCENT) {
|
|
227
|
+
if (i + 4 < N
|
|
228
|
+
&& bytes[i + 1] === PERCENT
|
|
229
|
+
&& bytes[i + 2] === 0x45
|
|
230
|
+
&& bytes[i + 3] === 0x4F
|
|
231
|
+
&& bytes[i + 4] === 0x46) {
|
|
232
|
+
const start = i;
|
|
233
|
+
i += 5;
|
|
234
|
+
return { kind: 'eof_marker', offset: start, end: i };
|
|
235
|
+
}
|
|
236
|
+
while (i < N && !isEol(bytes[i])) i++;
|
|
237
|
+
if (i < N && bytes[i] === CR) i++;
|
|
238
|
+
if (i < N && bytes[i] === LF) i++;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
if (i >= N) return null;
|
|
244
|
+
|
|
245
|
+
const b = bytes[i];
|
|
246
|
+
if (b === SLASH) return readName();
|
|
247
|
+
if (b === LBRACK) { const off = i++; return { kind: 'open_arr', offset: off, end: i }; }
|
|
248
|
+
if (b === RBRACK) { const off = i++; return { kind: 'close_arr', offset: off, end: i }; }
|
|
249
|
+
if (b === LANGLE) {
|
|
250
|
+
if (i + 1 < N && bytes[i + 1] === LANGLE) {
|
|
251
|
+
const off = i; i += 2;
|
|
252
|
+
return { kind: 'open_dict', offset: off, end: i };
|
|
253
|
+
}
|
|
254
|
+
return readHexString();
|
|
255
|
+
}
|
|
256
|
+
if (b === RANGLE) {
|
|
257
|
+
if (i + 1 < N && bytes[i + 1] === RANGLE) {
|
|
258
|
+
const off = i; i += 2;
|
|
259
|
+
return { kind: 'close_dict', offset: off, end: i };
|
|
260
|
+
}
|
|
261
|
+
throw new ParseError('pdf/tokenizer/unexpected-rangle',
|
|
262
|
+
'lone > outside hex string', { context: { offset: i } });
|
|
263
|
+
}
|
|
264
|
+
if (b === LPAREN) return readLiteralString();
|
|
265
|
+
if (b === PLUS || b === MINUS || b === DOT || isDigit(b)) return readNumber();
|
|
266
|
+
return readKeyword();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function peek() {
|
|
270
|
+
if (!lookahead) lookahead = next();
|
|
271
|
+
return lookahead;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return { next, peek, pos, seek, bytes };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function lastIndexOfBytes(bytes, needle, from) {
|
|
278
|
+
const N = bytes.length, M = needle.length;
|
|
279
|
+
if (M === 0 || M > N) return -1;
|
|
280
|
+
let start = Number.isFinite(from) ? Math.min(from | 0, N - M) : N - M;
|
|
281
|
+
outer: for (let i = start; i >= 0; i--) {
|
|
282
|
+
for (let k = 0; k < M; k++) if (bytes[i + k] !== needle[k]) continue outer;
|
|
283
|
+
return i;
|
|
284
|
+
}
|
|
285
|
+
return -1;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return { tokenize, lastIndexOfBytes };
|
|
289
|
+
}
|
|
290
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Copyright (c) 2026 AwaCloud SAS
|
|
2
|
+
// Author: Matthieu Bouilloux
|
|
3
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
4
|
+
// Dual-licensed; see the NOTICE file for licensing and any additional terms.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @fileoverview Trailer typing per ISO 32000-2:2020 §7.5.5.
|
|
8
|
+
*
|
|
9
|
+
* @module pdf/syntax/trailer
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Module factory — worker-safe, self-contained.
|
|
14
|
+
*/
|
|
15
|
+
import { pdfErrors } from '../errors.js';
|
|
16
|
+
import { pdfParserObj } from './parser-obj.js';
|
|
17
|
+
|
|
18
|
+
export const pdfTrailer = {
|
|
19
|
+
name: 'pdfTrailer',
|
|
20
|
+
dependencies: ['pdfErrors', 'pdfParserObj'],
|
|
21
|
+
deps: [pdfErrors, pdfParserObj],
|
|
22
|
+
factory(errors, parserObj) {
|
|
23
|
+
const { ParseError } = errors;
|
|
24
|
+
const { isType } = parserObj;
|
|
25
|
+
|
|
26
|
+
function typeTrailer(dict) {
|
|
27
|
+
if (!isType(dict, 'dict')) {
|
|
28
|
+
throw new ParseError('pdf/trailer/not-dict',
|
|
29
|
+
'trailer is not a dictionary',
|
|
30
|
+
{ context: { type: dict && dict.type } });
|
|
31
|
+
}
|
|
32
|
+
const e = dict.entries;
|
|
33
|
+
|
|
34
|
+
const size = e.Size;
|
|
35
|
+
if (!size || size.type !== 'int' || size.value < 0) {
|
|
36
|
+
throw new ParseError('pdf/trailer/missing-size',
|
|
37
|
+
'trailer is missing required /Size entry',
|
|
38
|
+
{ context: { hasSize: !!size } });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const root = e.Root;
|
|
42
|
+
if (!root || root.type !== 'ref') {
|
|
43
|
+
throw new ParseError('pdf/trailer/missing-root',
|
|
44
|
+
'trailer is missing required /Root indirect reference',
|
|
45
|
+
{ context: { hasRoot: !!root, type: root && root.type } });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const out = {
|
|
49
|
+
size: size.value,
|
|
50
|
+
root: { num: root.num, gen: root.gen },
|
|
51
|
+
raw: dict
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (e.Info && e.Info.type === 'ref') {
|
|
55
|
+
out.info = { num: e.Info.num, gen: e.Info.gen };
|
|
56
|
+
}
|
|
57
|
+
if (e.Prev && e.Prev.type === 'int') {
|
|
58
|
+
out.prev = e.Prev.value;
|
|
59
|
+
}
|
|
60
|
+
if (e.Encrypt) {
|
|
61
|
+
out.encrypt = e.Encrypt.type === 'ref'
|
|
62
|
+
? { num: e.Encrypt.num, gen: e.Encrypt.gen }
|
|
63
|
+
: e.Encrypt;
|
|
64
|
+
}
|
|
65
|
+
if (e.ID && e.ID.type === 'array' && e.ID.items.length === 2
|
|
66
|
+
&& e.ID.items[0].type === 'string'
|
|
67
|
+
&& e.ID.items[1].type === 'string') {
|
|
68
|
+
out.id = [e.ID.items[0].value, e.ID.items[1].value];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { typeTrailer };
|
|
75
|
+
}
|
|
76
|
+
};
|