@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,236 @@
|
|
|
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 RFC 3161 / PAdES B-T timestamp token parser.
|
|
8
|
+
*
|
|
9
|
+
* A document-level timestamp token (PAdES B-T, ISO/TS 32001 §5.4) is a
|
|
10
|
+
* RFC 3161 `TimeStampToken`, i.e. a CMS `SignedData` whose
|
|
11
|
+
* `encapContentInfo` carries `id-ct-TSTInfo`
|
|
12
|
+
* (`1.2.840.113549.1.9.16.1.4`). Inside `TSTInfo` (RFC 3161 §2.4.2)
|
|
13
|
+
* we recover the policy, `messageImprint`, serial number, generation
|
|
14
|
+
* time and optional nonce / TSA identity.
|
|
15
|
+
*
|
|
16
|
+
* @module pdf/sig/timestamp
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Module factory.
|
|
21
|
+
*/
|
|
22
|
+
import { pdfErrors } from '../errors.js';
|
|
23
|
+
import { pdfSigOids } from './oids.js';
|
|
24
|
+
import { asn1 } from '@awacloud/fw/crypto/utils/asn1.js';
|
|
25
|
+
import { rsa } from '@awacloud/fw/crypto/pkc/rsa.js';
|
|
26
|
+
import { ecc } from '@awacloud/fw/crypto/pkc/ecc.js';
|
|
27
|
+
import { ed25519 } from '@awacloud/fw/crypto/pkc/ed25519.js';
|
|
28
|
+
import { sha256 } from '@awacloud/fw/crypto/hash/sha256.js';
|
|
29
|
+
import { sha384 } from '@awacloud/fw/crypto/hash/sha384.js';
|
|
30
|
+
import { sha512 } from '@awacloud/fw/crypto/hash/sha512.js';
|
|
31
|
+
|
|
32
|
+
export const pdfTimestamp = {
|
|
33
|
+
name: 'pdfTimestamp',
|
|
34
|
+
dependencies: ['pdfErrors', 'pdfSigOids',
|
|
35
|
+
'asn1', 'rsa', 'ecc', 'ed25519',
|
|
36
|
+
'sha256', 'sha384', 'sha512'],
|
|
37
|
+
deps: [pdfErrors, pdfSigOids, asn1, rsa, ecc, ed25519, sha256, sha384, sha512],
|
|
38
|
+
factory(errors, sigOids, asn1, rsa, ecc, ed25519,
|
|
39
|
+
sha256, sha384, sha512) {
|
|
40
|
+
const { EncryptionError, ParseError } = errors;
|
|
41
|
+
const {
|
|
42
|
+
DIGEST_OIDS,
|
|
43
|
+
SIG_DISPATCH_OIDS: SIG_OIDS,
|
|
44
|
+
OID_TST_INFO,
|
|
45
|
+
OID_AA_TIMESTAMP
|
|
46
|
+
} = sigOids;
|
|
47
|
+
const bundle = { asn1, rsa, ecc, ed25519, sha256, sha384, sha512 };
|
|
48
|
+
|
|
49
|
+
function _parseGeneralizedTime(bytes) {
|
|
50
|
+
if (!bytes || bytes.length < 14) return null;
|
|
51
|
+
const s = String.fromCharCode.apply(null, Array.from(bytes));
|
|
52
|
+
const m = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(s);
|
|
53
|
+
if (!m) return null;
|
|
54
|
+
const d = new Date(Date.UTC(
|
|
55
|
+
+m[1], +m[2] - 1, +m[3], +m[4], +m[5], +m[6]));
|
|
56
|
+
return isNaN(d.getTime()) ? null : d;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function _sliceTLV(buf) {
|
|
60
|
+
if (!buf || buf.length < 2) return null;
|
|
61
|
+
let lenLen = 1;
|
|
62
|
+
let len = buf[1];
|
|
63
|
+
if (len & 0x80) {
|
|
64
|
+
const n = len & 0x7F;
|
|
65
|
+
len = 0;
|
|
66
|
+
for (let i = 0; i < n; i++) len = (len << 8) | buf[2 + i];
|
|
67
|
+
lenLen = 1 + n;
|
|
68
|
+
}
|
|
69
|
+
return buf.subarray(0, 1 + lenLen + len);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseTimestampToken(blob, asn1Mod) {
|
|
73
|
+
const a1 = asn1Mod || asn1;
|
|
74
|
+
if (!(blob instanceof Uint8Array)) {
|
|
75
|
+
throw new ParseError('pdf/ts/bad-input',
|
|
76
|
+
'parseTimestampToken expects a Uint8Array');
|
|
77
|
+
}
|
|
78
|
+
if (!a1 || typeof a1.parseOne !== 'function') {
|
|
79
|
+
throw new EncryptionError('pdf/ts/missing-fw',
|
|
80
|
+
'parseTimestampToken requires the fw asn1 module');
|
|
81
|
+
}
|
|
82
|
+
const ci = a1.parseOne(blob, 0);
|
|
83
|
+
if (!ci) {
|
|
84
|
+
throw new ParseError('pdf/ts/malformed',
|
|
85
|
+
'ContentInfo parse failed');
|
|
86
|
+
}
|
|
87
|
+
const ciChildren = a1.parseChildren(ci.value);
|
|
88
|
+
if (!ciChildren || ciChildren.length < 2) {
|
|
89
|
+
throw new ParseError('pdf/ts/malformed',
|
|
90
|
+
'ContentInfo missing signedData');
|
|
91
|
+
}
|
|
92
|
+
const sdNode = a1.parseOne(ciChildren[1].value, 0);
|
|
93
|
+
if (!sdNode) {
|
|
94
|
+
throw new ParseError('pdf/ts/malformed',
|
|
95
|
+
'SignedData parse failed');
|
|
96
|
+
}
|
|
97
|
+
const sd = a1.parseChildren(sdNode.value);
|
|
98
|
+
if (!sd) {
|
|
99
|
+
throw new ParseError('pdf/ts/malformed',
|
|
100
|
+
'SignedData children parse failed');
|
|
101
|
+
}
|
|
102
|
+
const eci = sd[2] && a1.parseChildren(sd[2].value);
|
|
103
|
+
if (!eci || eci.length < 2) {
|
|
104
|
+
throw new ParseError('pdf/ts/no-econtent',
|
|
105
|
+
'TSTInfo encapContentInfo missing');
|
|
106
|
+
}
|
|
107
|
+
const eciTypeOid = a1.readOid(eci[0]);
|
|
108
|
+
if (eciTypeOid !== OID_TST_INFO) {
|
|
109
|
+
throw new ParseError('pdf/ts/wrong-econtent',
|
|
110
|
+
'eContentType is not id-ct-TSTInfo',
|
|
111
|
+
{ context: { oid: eciTypeOid } });
|
|
112
|
+
}
|
|
113
|
+
const inner = a1.parseOne(eci[1].value, 0);
|
|
114
|
+
if (!inner) {
|
|
115
|
+
throw new ParseError('pdf/ts/no-tstinfo',
|
|
116
|
+
'TSTInfo OCTET STRING missing');
|
|
117
|
+
}
|
|
118
|
+
const tst = a1.parseOne(inner.value, 0);
|
|
119
|
+
if (!tst) {
|
|
120
|
+
throw new ParseError('pdf/ts/bad-tstinfo',
|
|
121
|
+
'TSTInfo parse failed');
|
|
122
|
+
}
|
|
123
|
+
const tstChildren = a1.parseChildren(tst.value);
|
|
124
|
+
if (!tstChildren || tstChildren.length < 4) {
|
|
125
|
+
throw new ParseError('pdf/ts/bad-tstinfo',
|
|
126
|
+
'TSTInfo too short');
|
|
127
|
+
}
|
|
128
|
+
const out = {
|
|
129
|
+
version: tstChildren[0].value[0] || 1,
|
|
130
|
+
policy: a1.readOid(tstChildren[1]),
|
|
131
|
+
serialNumber: tstChildren[3].value,
|
|
132
|
+
genTime: null,
|
|
133
|
+
messageImprint: null
|
|
134
|
+
};
|
|
135
|
+
const mi = a1.parseChildren(tstChildren[2].value);
|
|
136
|
+
if (mi && mi.length >= 2) {
|
|
137
|
+
const algChildren = a1.parseChildren(mi[0].value);
|
|
138
|
+
const algOid = algChildren && algChildren[0]
|
|
139
|
+
? a1.readOid(algChildren[0]) : null;
|
|
140
|
+
out.messageImprint = {
|
|
141
|
+
hashAlg: DIGEST_OIDS[algOid] || algOid,
|
|
142
|
+
hashedMessage: mi[1].value
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (tstChildren[4]) {
|
|
146
|
+
out.genTime = _parseGeneralizedTime(tstChildren[4].value);
|
|
147
|
+
}
|
|
148
|
+
for (let i = 5; i < tstChildren.length; i++) {
|
|
149
|
+
const n = tstChildren[i];
|
|
150
|
+
if (n.tag === 0x02) {
|
|
151
|
+
out.nonce = n.value;
|
|
152
|
+
} else if (n.tag === 0xA0) {
|
|
153
|
+
out.tsa = n.value;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function verifyTimestamp(tokenBlob, fwBundle) {
|
|
160
|
+
const fb = fwBundle || bundle;
|
|
161
|
+
if (!fb || !fb.asn1) {
|
|
162
|
+
throw new EncryptionError('pdf/ts/missing-fw',
|
|
163
|
+
'verifyTimestamp requires fwBundle.asn1');
|
|
164
|
+
}
|
|
165
|
+
const errs = [];
|
|
166
|
+
let parsed;
|
|
167
|
+
try {
|
|
168
|
+
parsed = parseTimestampToken(tokenBlob, fb.asn1);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
errs.push({ code: e.code || 'pdf/ts/parse',
|
|
171
|
+
message: e.message,
|
|
172
|
+
cause: e });
|
|
173
|
+
return { valid: false, errors: errs, tstInfo: null,
|
|
174
|
+
hashAlg: null, signatureAlg: null };
|
|
175
|
+
}
|
|
176
|
+
const ci = fb.asn1.parseOne(tokenBlob, 0);
|
|
177
|
+
const ciKids = ci ? fb.asn1.parseChildren(ci.value) : null;
|
|
178
|
+
const sdNode = ciKids && ciKids[1]
|
|
179
|
+
? fb.asn1.parseOne(ciKids[1].value, 0) : null;
|
|
180
|
+
const sd = sdNode ? fb.asn1.parseChildren(sdNode.value) : null;
|
|
181
|
+
let signatureAlg = null;
|
|
182
|
+
if (sd) {
|
|
183
|
+
let siSet = null;
|
|
184
|
+
for (let i = sd.length - 1; i >= 0; i--) {
|
|
185
|
+
if (sd[i].tag === 0x31) { siSet = sd[i]; break; }
|
|
186
|
+
}
|
|
187
|
+
if (siSet) {
|
|
188
|
+
const signers = fb.asn1.parseChildren(siSet.value);
|
|
189
|
+
const si = signers && signers[0]
|
|
190
|
+
? fb.asn1.parseChildren(signers[0].value) : null;
|
|
191
|
+
if (si) {
|
|
192
|
+
for (let i = 0; i < si.length - 1; i++) {
|
|
193
|
+
if (si[i].tag === 0x30 && si[i + 1].tag === 0x04) {
|
|
194
|
+
const algChildren =
|
|
195
|
+
fb.asn1.parseChildren(si[i].value);
|
|
196
|
+
if (algChildren && algChildren[0]) {
|
|
197
|
+
const oid = fb.asn1.readOid(algChildren[0]);
|
|
198
|
+
signatureAlg = SIG_OIDS[oid] || null;
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
valid: errs.length === 0,
|
|
208
|
+
errors: errs, tstInfo: parsed,
|
|
209
|
+
hashAlg: parsed.messageImprint && parsed.messageImprint.hashAlg,
|
|
210
|
+
signatureAlg
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function extractTimestampFromUnsignedAttrs(signedAttrs, asn1Mod) {
|
|
215
|
+
const a1 = asn1Mod || asn1;
|
|
216
|
+
if (!Array.isArray(signedAttrs)) return null;
|
|
217
|
+
for (let i = 0; i < signedAttrs.length; i++) {
|
|
218
|
+
const attr = a1.parseChildren(signedAttrs[i].value);
|
|
219
|
+
if (!attr || attr.length < 2) continue;
|
|
220
|
+
const oid = a1.readOid(attr[0]);
|
|
221
|
+
if (oid !== OID_AA_TIMESTAMP) continue;
|
|
222
|
+
const set = a1.parseChildren(attr[1].value);
|
|
223
|
+
if (!set || !set.length) continue;
|
|
224
|
+
return _sliceTLV(attr[1].value);
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
parseTimestampToken,
|
|
231
|
+
verifyTimestamp,
|
|
232
|
+
extractTimestampFromUnsignedAttrs,
|
|
233
|
+
OID_TST_INFO, OID_AA_TIMESTAMP
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
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 Cross-reference stream parser per ISO 32000-2:2020 §7.5.8.
|
|
8
|
+
*
|
|
9
|
+
* @module pdf/syntax/crossRefStream
|
|
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 pdfCrossRefStream = {
|
|
19
|
+
name: 'pdfCrossRefStream',
|
|
20
|
+
dependencies: ['pdfErrors', 'pdfParserObj'],
|
|
21
|
+
deps: [pdfErrors, pdfParserObj],
|
|
22
|
+
factory(errors, parserObj) {
|
|
23
|
+
const { ParseError } = errors;
|
|
24
|
+
const { isType } = parserObj;
|
|
25
|
+
|
|
26
|
+
function readBE(bytes, off, len) {
|
|
27
|
+
let v = 0;
|
|
28
|
+
for (let i = 0; i < len; i++) v = (v * 256) + bytes[off + i];
|
|
29
|
+
return v;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readInt(entry, label) {
|
|
33
|
+
if (!entry || entry.type !== 'int') {
|
|
34
|
+
throw new ParseError('pdf/xrefstm/missing-int',
|
|
35
|
+
`XRef stream dict missing required /${label}`,
|
|
36
|
+
{ context: { label } });
|
|
37
|
+
}
|
|
38
|
+
return entry.value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseCrossRefStream(decoded, dict) {
|
|
42
|
+
if (!(decoded instanceof Uint8Array)) {
|
|
43
|
+
throw new ParseError('pdf/xrefstm/bad-input',
|
|
44
|
+
'parseCrossRefStream expects Uint8Array');
|
|
45
|
+
}
|
|
46
|
+
if (!isType(dict, 'dict')) {
|
|
47
|
+
throw new ParseError('pdf/xrefstm/bad-dict',
|
|
48
|
+
'XRef stream dict must be a typed dict');
|
|
49
|
+
}
|
|
50
|
+
const e = dict.entries;
|
|
51
|
+
if (!e.Type || e.Type.type !== 'name' || e.Type.value !== 'XRef') {
|
|
52
|
+
throw new ParseError('pdf/xrefstm/wrong-type',
|
|
53
|
+
'/Type must be /XRef',
|
|
54
|
+
{ context: { actual: e.Type && e.Type.value } });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const W = e.W;
|
|
58
|
+
if (!W || W.type !== 'array' || W.items.length !== 3) {
|
|
59
|
+
throw new ParseError('pdf/xrefstm/bad-W',
|
|
60
|
+
'/W must be a 3-element array', { context: { items: W && W.items && W.items.length } });
|
|
61
|
+
}
|
|
62
|
+
const w = W.items.map(it => {
|
|
63
|
+
if (it.type !== 'int' || it.value < 0) {
|
|
64
|
+
throw new ParseError('pdf/xrefstm/bad-W-entry',
|
|
65
|
+
'/W entries must be non-negative ints');
|
|
66
|
+
}
|
|
67
|
+
return it.value;
|
|
68
|
+
});
|
|
69
|
+
const recordSize = w[0] + w[1] + w[2];
|
|
70
|
+
if (recordSize <= 0) {
|
|
71
|
+
throw new ParseError('pdf/xrefstm/bad-W-zero',
|
|
72
|
+
'/W must yield at least one non-zero field');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let index;
|
|
76
|
+
if (e.Index) {
|
|
77
|
+
if (e.Index.type !== 'array' || (e.Index.items.length & 1) !== 0) {
|
|
78
|
+
throw new ParseError('pdf/xrefstm/bad-Index',
|
|
79
|
+
'/Index must be an array of (first, count) pairs');
|
|
80
|
+
}
|
|
81
|
+
index = [];
|
|
82
|
+
for (let i = 0; i < e.Index.items.length; i += 2) {
|
|
83
|
+
const f = e.Index.items[i], c = e.Index.items[i + 1];
|
|
84
|
+
if (f.type !== 'int' || c.type !== 'int') {
|
|
85
|
+
throw new ParseError('pdf/xrefstm/bad-Index-entry',
|
|
86
|
+
'/Index entries must be ints');
|
|
87
|
+
}
|
|
88
|
+
index.push([f.value, c.value]);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
const size = readInt(e.Size, 'Size');
|
|
92
|
+
index = [[0, size]];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const entries = {};
|
|
96
|
+
let pos = 0;
|
|
97
|
+
for (const [first, count] of index) {
|
|
98
|
+
for (let k = 0; k < count; k++) {
|
|
99
|
+
if (pos + recordSize > decoded.length) {
|
|
100
|
+
throw new ParseError('pdf/xrefstm/truncated',
|
|
101
|
+
'truncated xref stream payload',
|
|
102
|
+
{ context: { pos, recordSize, length: decoded.length } });
|
|
103
|
+
}
|
|
104
|
+
const t = w[0] > 0 ? readBE(decoded, pos, w[0]) : 1;
|
|
105
|
+
const f2 = w[1] > 0 ? readBE(decoded, pos + w[0], w[1]) : 0;
|
|
106
|
+
const f3 = w[2] > 0 ? readBE(decoded, pos + w[0] + w[1], w[2]) : 0;
|
|
107
|
+
pos += recordSize;
|
|
108
|
+
const num = first + k;
|
|
109
|
+
if (t === 0) {
|
|
110
|
+
entries[num] = { type: 0, offset: f2, gen: f3, free: true };
|
|
111
|
+
} else if (t === 1) {
|
|
112
|
+
entries[num] = { type: 1, offset: f2, gen: f3, free: false };
|
|
113
|
+
} else if (t === 2) {
|
|
114
|
+
entries[num] = {
|
|
115
|
+
type: 2, objStm: f2, index: f3,
|
|
116
|
+
free: false, offset: 0, gen: 0
|
|
117
|
+
};
|
|
118
|
+
} else {
|
|
119
|
+
entries[num] = { type: t, offset: 0, gen: 0, free: true };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
entries,
|
|
126
|
+
size: e.Size && e.Size.type === 'int' ? e.Size.value : Object.keys(entries).length,
|
|
127
|
+
trailer: dict
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { parseCrossRefStream };
|
|
132
|
+
}
|
|
133
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
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 ASCII85Decode filter per ISO 32000-2:2020 §7.4.3.
|
|
8
|
+
*
|
|
9
|
+
* 5 ASCII characters in the range `!`..`u` decode to 4 binary bytes
|
|
10
|
+
* encoding a 32-bit big-endian integer (base-85). The special
|
|
11
|
+
* character `z` represents 4 zero bytes. The stream is terminated by
|
|
12
|
+
* `~>`. The final group may be 2..4 characters, decoding to 1..3
|
|
13
|
+
* trailing bytes.
|
|
14
|
+
*
|
|
15
|
+
* @module pdf/syntax/filters/ascii85
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Module factory — worker-safe, self-contained.
|
|
20
|
+
*/
|
|
21
|
+
import { pdfErrors } from '../../errors.js';
|
|
22
|
+
|
|
23
|
+
export const pdfAscii85 = {
|
|
24
|
+
name: 'pdfAscii85',
|
|
25
|
+
dependencies: ['pdfErrors'],
|
|
26
|
+
deps: [pdfErrors],
|
|
27
|
+
factory(errors) {
|
|
28
|
+
const { ParseError } = errors;
|
|
29
|
+
const TILDE = 0x7E, GT = 0x3E, Z = 0x7A, BANG = 0x21, U = 0x75;
|
|
30
|
+
const SP = 0x20, HT = 0x09, LF = 0x0A, CR = 0x0D, FF = 0x0C, NUL = 0x00;
|
|
31
|
+
function isWs(b) {
|
|
32
|
+
return b === SP || b === HT || b === LF || b === CR || b === FF || b === NUL;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function decode(bytes) {
|
|
36
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
37
|
+
throw new ParseError('pdf/ascii85/bad-input',
|
|
38
|
+
'ASCII85Decode expects Uint8Array');
|
|
39
|
+
}
|
|
40
|
+
const out = [];
|
|
41
|
+
let group = 0, count = 0;
|
|
42
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
43
|
+
const b = bytes[i];
|
|
44
|
+
if (isWs(b)) continue;
|
|
45
|
+
if (b === TILDE) {
|
|
46
|
+
if (i + 1 < bytes.length && bytes[i + 1] === GT) break;
|
|
47
|
+
throw new ParseError('pdf/ascii85/bad-eod',
|
|
48
|
+
'~ not followed by >', { context: { offset: i } });
|
|
49
|
+
}
|
|
50
|
+
if (b === Z) {
|
|
51
|
+
if (count !== 0) {
|
|
52
|
+
throw new ParseError('pdf/ascii85/bad-z',
|
|
53
|
+
'z must appear at group boundary', { context: { offset: i } });
|
|
54
|
+
}
|
|
55
|
+
out.push(0, 0, 0, 0);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (b < BANG || b > U) {
|
|
59
|
+
throw new ParseError('pdf/ascii85/bad-digit',
|
|
60
|
+
'character outside ! .. u',
|
|
61
|
+
{ context: { offset: i, byte: b } });
|
|
62
|
+
}
|
|
63
|
+
group = group * 85 + (b - BANG);
|
|
64
|
+
count++;
|
|
65
|
+
if (count === 5) {
|
|
66
|
+
out.push((group >>> 24) & 0xFF, (group >>> 16) & 0xFF,
|
|
67
|
+
(group >>> 8) & 0xFF, group & 0xFF);
|
|
68
|
+
group = 0; count = 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (count > 0) {
|
|
72
|
+
for (let k = count; k < 5; k++) group = group * 85 + 84;
|
|
73
|
+
const decoded = [
|
|
74
|
+
(group >>> 24) & 0xFF, (group >>> 16) & 0xFF,
|
|
75
|
+
(group >>> 8) & 0xFF, group & 0xFF
|
|
76
|
+
];
|
|
77
|
+
for (let k = 0; k < count - 1; k++) out.push(decoded[k]);
|
|
78
|
+
}
|
|
79
|
+
return Uint8Array.from(out);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function encode(bytes) {
|
|
83
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
84
|
+
throw new ParseError('pdf/ascii85/bad-input',
|
|
85
|
+
'ASCII85Encode expects Uint8Array');
|
|
86
|
+
}
|
|
87
|
+
const chars = [];
|
|
88
|
+
let i = 0;
|
|
89
|
+
while (i + 4 <= bytes.length) {
|
|
90
|
+
const v = (bytes[i] * 0x1000000) + (bytes[i + 1] << 16)
|
|
91
|
+
+ (bytes[i + 2] << 8) + bytes[i + 3];
|
|
92
|
+
if (v === 0) chars.push(Z);
|
|
93
|
+
else {
|
|
94
|
+
const g = [0, 0, 0, 0, 0];
|
|
95
|
+
let n = v;
|
|
96
|
+
for (let k = 4; k >= 0; k--) {
|
|
97
|
+
g[k] = (n % 85) + BANG; n = Math.floor(n / 85);
|
|
98
|
+
}
|
|
99
|
+
chars.push(g[0], g[1], g[2], g[3], g[4]);
|
|
100
|
+
}
|
|
101
|
+
i += 4;
|
|
102
|
+
}
|
|
103
|
+
const rem = bytes.length - i;
|
|
104
|
+
if (rem > 0) {
|
|
105
|
+
const padded = new Uint8Array(4);
|
|
106
|
+
padded.set(bytes.subarray(i));
|
|
107
|
+
const v = (padded[0] * 0x1000000) + (padded[1] << 16)
|
|
108
|
+
+ (padded[2] << 8) + padded[3];
|
|
109
|
+
const g = [0, 0, 0, 0, 0];
|
|
110
|
+
let n = v;
|
|
111
|
+
for (let k = 4; k >= 0; k--) {
|
|
112
|
+
g[k] = (n % 85) + BANG; n = Math.floor(n / 85);
|
|
113
|
+
}
|
|
114
|
+
for (let k = 0; k < rem + 1; k++) chars.push(g[k]);
|
|
115
|
+
}
|
|
116
|
+
chars.push(TILDE, GT);
|
|
117
|
+
return Uint8Array.from(chars);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { decode, encode };
|
|
121
|
+
}
|
|
122
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
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 ASCIIHexDecode filter per ISO 32000-2:2020 §7.4.2.
|
|
8
|
+
*
|
|
9
|
+
* Each pair of ASCII hex digits decodes to one byte. Whitespace is
|
|
10
|
+
* ignored. The terminator `>` ends the stream ; an odd number of hex
|
|
11
|
+
* digits is treated as if a trailing `0` were present.
|
|
12
|
+
*
|
|
13
|
+
* @module pdf/syntax/filters/asciiHex
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Module factory — worker-safe, self-contained.
|
|
18
|
+
*/
|
|
19
|
+
import { pdfErrors } from '../../errors.js';
|
|
20
|
+
|
|
21
|
+
export const pdfAsciiHex = {
|
|
22
|
+
name: 'pdfAsciiHex',
|
|
23
|
+
dependencies: ['pdfErrors'],
|
|
24
|
+
deps: [pdfErrors],
|
|
25
|
+
factory(errors) {
|
|
26
|
+
const { ParseError } = errors;
|
|
27
|
+
const HEX_LO = [
|
|
28
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
29
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
30
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
31
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
|
|
32
|
+
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
33
|
+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
34
|
+
-1, 10, 11, 12, 13, 14, 15, -1
|
|
35
|
+
];
|
|
36
|
+
const SP = 0x20, HT = 0x09, LF = 0x0A, CR = 0x0D, FF = 0x0C, NUL = 0x00;
|
|
37
|
+
function isWs(b) {
|
|
38
|
+
return b === SP || b === HT || b === LF || b === CR || b === FF || b === NUL;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function decode(bytes) {
|
|
42
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
43
|
+
throw new ParseError('pdf/asciiHex/bad-input',
|
|
44
|
+
'ASCIIHexDecode expects Uint8Array');
|
|
45
|
+
}
|
|
46
|
+
const out = [];
|
|
47
|
+
let pending = -1;
|
|
48
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
49
|
+
const b = bytes[i];
|
|
50
|
+
if (b === 0x3E) break;
|
|
51
|
+
if (isWs(b)) continue;
|
|
52
|
+
const v = b < HEX_LO.length ? HEX_LO[b] : -1;
|
|
53
|
+
if (v < 0) {
|
|
54
|
+
throw new ParseError('pdf/asciiHex/bad-digit',
|
|
55
|
+
'invalid hex digit',
|
|
56
|
+
{ context: { offset: i, byte: b } });
|
|
57
|
+
}
|
|
58
|
+
if (pending < 0) pending = v;
|
|
59
|
+
else { out.push((pending << 4) | v); pending = -1; }
|
|
60
|
+
}
|
|
61
|
+
if (pending >= 0) out.push(pending << 4);
|
|
62
|
+
return Uint8Array.from(out);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function encode(bytes) {
|
|
66
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
67
|
+
throw new ParseError('pdf/asciiHex/bad-input',
|
|
68
|
+
'ASCIIHexEncode expects Uint8Array');
|
|
69
|
+
}
|
|
70
|
+
const H = '0123456789ABCDEF';
|
|
71
|
+
let s = '';
|
|
72
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
73
|
+
if (i > 0 && (i & 31) === 0) s += '\n';
|
|
74
|
+
const b = bytes[i];
|
|
75
|
+
s += H[b >> 4] + H[b & 0xF];
|
|
76
|
+
}
|
|
77
|
+
s += '>';
|
|
78
|
+
return new TextEncoder().encode(s);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { decode, encode };
|
|
82
|
+
}
|
|
83
|
+
};
|