@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,88 @@
|
|
|
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 Opt-in: strict sandbox linter for active-content PDF
|
|
8
|
+
* features.
|
|
9
|
+
*
|
|
10
|
+
* @module pdf/extra/pdf-sandbox
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Module factory.
|
|
15
|
+
*/
|
|
16
|
+
import { pdfErrors } from '../errors.js';
|
|
17
|
+
|
|
18
|
+
export const pdfSandbox = {
|
|
19
|
+
name: 'pdfSandbox',
|
|
20
|
+
dependencies: ['pdfErrors'],
|
|
21
|
+
deps: [pdfErrors],
|
|
22
|
+
factory(errors) {
|
|
23
|
+
const { ContractError } = errors;
|
|
24
|
+
|
|
25
|
+
const ACTIVE_KINDS = {
|
|
26
|
+
Launch: { severity: 'error',
|
|
27
|
+
message: '/Launch action present — external execution '
|
|
28
|
+
+ 'requested by document' },
|
|
29
|
+
JavaScript: { severity: 'error',
|
|
30
|
+
message: '/JavaScript action present — script '
|
|
31
|
+
+ 'execution requested by document' },
|
|
32
|
+
ImportData: { severity: 'error',
|
|
33
|
+
message: '/ImportData action present — file-system '
|
|
34
|
+
+ 'read requested by document' },
|
|
35
|
+
SubmitForm: { severity: 'warning',
|
|
36
|
+
message: '/SubmitForm action present — network '
|
|
37
|
+
+ 'submission requested by document' },
|
|
38
|
+
Rendition: { severity: 'warning',
|
|
39
|
+
message: '/Rendition action carries /JS payload' },
|
|
40
|
+
URI: { severity: 'warning',
|
|
41
|
+
message: '/URI action present — external navigation '
|
|
42
|
+
+ 'requested by document' }
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function lintAction(rec) {
|
|
46
|
+
if (!rec || typeof rec !== 'object') return null;
|
|
47
|
+
if (rec.kind === 'Rendition' && !rec.sandboxed) return null;
|
|
48
|
+
const profile = ACTIVE_KINDS[rec.kind];
|
|
49
|
+
if (!profile) return null;
|
|
50
|
+
return {
|
|
51
|
+
kind: rec.kind,
|
|
52
|
+
code: 'pdf/sandbox/active-' + rec.kind.toLowerCase(),
|
|
53
|
+
severity: profile.severity,
|
|
54
|
+
message: profile.message,
|
|
55
|
+
context: { sandboxed: rec.sandboxed === true }
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function lintActions(records) {
|
|
60
|
+
if (!records || typeof records[Symbol.iterator] !== 'function') {
|
|
61
|
+
throw new ContractError('pdf/sandbox/bad-input',
|
|
62
|
+
'lintActions expects an iterable of action records');
|
|
63
|
+
}
|
|
64
|
+
const issues = [];
|
|
65
|
+
let hasErrors = false;
|
|
66
|
+
for (const rec of records) {
|
|
67
|
+
const issue = lintAction(rec);
|
|
68
|
+
if (issue) {
|
|
69
|
+
issues.push(issue);
|
|
70
|
+
if (issue.severity === 'error') hasErrors = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
sandboxed: true,
|
|
75
|
+
issues,
|
|
76
|
+
hasActiveContent: issues.length > 0,
|
|
77
|
+
hasErrors
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
lintAction,
|
|
83
|
+
lintActions,
|
|
84
|
+
ACTIVE_KINDS
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
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 Opt-in: PDF/UA validation rules.
|
|
8
|
+
*
|
|
9
|
+
* Implements a static linter for PDF/UA-1 (ISO 14289-1:2014) and the
|
|
10
|
+
* draft PDF/UA-2 (ISO 14289-2). Required catalog structure:
|
|
11
|
+
*
|
|
12
|
+
* - `/StructTreeRoot` (required, tagged document)
|
|
13
|
+
* - `/MarkInfo /Marked true` (required)
|
|
14
|
+
* - `/Lang` (required)
|
|
15
|
+
* - `/ViewerPreferences /DisplayDocTitle true` (required)
|
|
16
|
+
*
|
|
17
|
+
* Structure-element checks (sampled):
|
|
18
|
+
* - Figures must carry `/Alt` or `/ActualText`.
|
|
19
|
+
* - Tables must include `TH`/`TD`/`TR`.
|
|
20
|
+
* - The accessible-permission flag (bit 10) must be 1.
|
|
21
|
+
*
|
|
22
|
+
* @module pdf/extra/pdf-ua-tagged
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Module factory.
|
|
27
|
+
*/
|
|
28
|
+
import { pdfErrors } from '../errors.js';
|
|
29
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
30
|
+
|
|
31
|
+
export const pdfUaTagged = {
|
|
32
|
+
name: 'pdfUaTagged',
|
|
33
|
+
dependencies: ['pdfErrors', 'pdfParser'],
|
|
34
|
+
deps: [pdfErrors, pdfParser],
|
|
35
|
+
factory(errors, parser) {
|
|
36
|
+
const { ParseError } = errors;
|
|
37
|
+
const { isType } = parser;
|
|
38
|
+
|
|
39
|
+
const UA_STRUCTURE = Object.freeze({
|
|
40
|
+
required: Object.freeze(['Document', 'Part', 'Sect', 'Art', 'H', 'P', 'L', 'LI', 'Lbl', 'LBody',
|
|
41
|
+
'Table', 'TR', 'TH', 'TD', 'Figure']),
|
|
42
|
+
forbidden: Object.freeze(['Span']),
|
|
43
|
+
figure: Object.freeze(['Alt', 'ActualText']),
|
|
44
|
+
headings: Object.freeze(['H', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'])
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
function validatePdfUa(typedDoc) {
|
|
48
|
+
if (!typedDoc || typeof typedDoc !== 'object') {
|
|
49
|
+
throw new ParseError('pdf/extra/pdfua/bad-doc',
|
|
50
|
+
'validatePdfUa requires a typed document object',
|
|
51
|
+
{ context: { type: typeof typedDoc } });
|
|
52
|
+
}
|
|
53
|
+
const errors = [];
|
|
54
|
+
const warnings = [];
|
|
55
|
+
const cat = typedDoc.catalog && typedDoc.catalog.entries;
|
|
56
|
+
if (!cat) {
|
|
57
|
+
errors.push('catalog missing');
|
|
58
|
+
return { pass: false, errors, warnings };
|
|
59
|
+
}
|
|
60
|
+
if (!cat.StructTreeRoot) errors.push('catalog requires /StructTreeRoot');
|
|
61
|
+
if (!cat.Lang || !isType(cat.Lang, 'string')) {
|
|
62
|
+
errors.push('catalog requires /Lang string');
|
|
63
|
+
}
|
|
64
|
+
if (!cat.MarkInfo) {
|
|
65
|
+
errors.push('catalog requires /MarkInfo dictionary');
|
|
66
|
+
} else if (!isType(cat.MarkInfo, 'dict')) {
|
|
67
|
+
errors.push('/MarkInfo must be a dictionary');
|
|
68
|
+
} else {
|
|
69
|
+
const m = cat.MarkInfo.entries.Marked;
|
|
70
|
+
if (!isType(m, 'bool') || m.value !== true) {
|
|
71
|
+
errors.push('/MarkInfo /Marked must be true');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (cat.ViewerPreferences && isType(cat.ViewerPreferences, 'dict')) {
|
|
75
|
+
const ddt = cat.ViewerPreferences.entries.DisplayDocTitle;
|
|
76
|
+
if (!isType(ddt, 'bool') || ddt.value !== true) {
|
|
77
|
+
warnings.push('/ViewerPreferences /DisplayDocTitle should be true');
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
warnings.push('catalog should have /ViewerPreferences');
|
|
81
|
+
}
|
|
82
|
+
if (typeof typedDoc.p === 'number') {
|
|
83
|
+
if ((typedDoc.p & (1 << 9)) === 0) {
|
|
84
|
+
errors.push('permissions bit 10 (accessible) must be set');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { pass: errors.length === 0, errors, warnings };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function validateUaStructElement(node) {
|
|
91
|
+
if (!node || typeof node !== 'object') {
|
|
92
|
+
throw new ParseError('pdf/extra/pdfua/struct/bad-node',
|
|
93
|
+
'validateUaStructElement requires a node',
|
|
94
|
+
{ context: { type: typeof node } });
|
|
95
|
+
}
|
|
96
|
+
const errors = [];
|
|
97
|
+
const warnings = [];
|
|
98
|
+
if (node.s === 'Figure') {
|
|
99
|
+
if (!node.alt && !node.actualText) {
|
|
100
|
+
errors.push('Figure requires /Alt or /ActualText');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (UA_STRUCTURE.forbidden.includes(node.s)) {
|
|
104
|
+
warnings.push('avoid /' + node.s + ' as a top-level structure type');
|
|
105
|
+
}
|
|
106
|
+
return { pass: errors.length === 0, errors, warnings };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
validatePdfUa,
|
|
111
|
+
validateUaStructElement,
|
|
112
|
+
UA_STRUCTURE
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
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 Opt-in: PDF/X conformance helpers.
|
|
8
|
+
*
|
|
9
|
+
* PDF/X (ISO 15930) defines a family of prepress-targeted profiles
|
|
10
|
+
* (PDF/X-1a, X-3, X-4, X-4p, X-5g, X-5n, X-5pg, X-6) with an
|
|
11
|
+
* `/OutputIntent` whose `/S` is `GTS_PDFX`. Required structural checks:
|
|
12
|
+
*
|
|
13
|
+
* - `/TrimBox` (and optionally `/BleedBox`, `/ArtBox`) on every page.
|
|
14
|
+
* - Output intent profile (Dest profile or registry identifier).
|
|
15
|
+
* - Separation info dict (per page or document level) for X-4+.
|
|
16
|
+
*
|
|
17
|
+
* @module pdf/extra/pdf-x-prepress
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Module factory.
|
|
22
|
+
*/
|
|
23
|
+
import { pdfErrors } from '../errors.js';
|
|
24
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
25
|
+
|
|
26
|
+
export const pdfXPrepress = {
|
|
27
|
+
name: 'pdfXPrepress',
|
|
28
|
+
dependencies: ['pdfErrors', 'pdfParser'],
|
|
29
|
+
deps: [pdfErrors, pdfParser],
|
|
30
|
+
factory(errors, parser) {
|
|
31
|
+
const { ParseError } = errors;
|
|
32
|
+
const { isType } = parser;
|
|
33
|
+
|
|
34
|
+
const PDFX_PROFILES = Object.freeze({
|
|
35
|
+
'X-1a:2001': { iso: 'ISO 15930-1:2001', color: 'cmyk+spot', flavor: 'closed' },
|
|
36
|
+
'X-1a:2003': { iso: 'ISO 15930-4:2003', color: 'cmyk+spot', flavor: 'closed' },
|
|
37
|
+
'X-3:2002': { iso: 'ISO 15930-3:2002', color: 'any', flavor: 'closed' },
|
|
38
|
+
'X-3:2003': { iso: 'ISO 15930-6:2003', color: 'any', flavor: 'closed' },
|
|
39
|
+
'X-4': { iso: 'ISO 15930-7:2010', color: 'any', flavor: 'transparency' },
|
|
40
|
+
'X-4p': { iso: 'ISO 15930-7:2010', color: 'any', flavor: 'external-profile' },
|
|
41
|
+
'X-5g': { iso: 'ISO 15930-8:2010', color: 'any', flavor: 'external-graphics' },
|
|
42
|
+
'X-5n': { iso: 'ISO 15930-8:2010', color: 'n-channel', flavor: 'external-profile' },
|
|
43
|
+
'X-5pg': { iso: 'ISO 15930-8:2010', color: 'any', flavor: 'external-both' },
|
|
44
|
+
'X-6': { iso: 'ISO 15930-9:2020', color: 'any', flavor: 'pdf-2.0' }
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const COLOR_POLICY = Object.freeze({
|
|
48
|
+
None: 'no transformation',
|
|
49
|
+
Convert: 'convert to OutputIntent profile',
|
|
50
|
+
Tag: 'tag with profile only',
|
|
51
|
+
Embed: 'embed source profile'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function typePdfXOutputIntent(dict) {
|
|
55
|
+
if (!isType(dict, 'dict')) {
|
|
56
|
+
throw new ParseError('pdf/extra/pdfx/not-dict',
|
|
57
|
+
'OutputIntent must be a dictionary',
|
|
58
|
+
{ context: { type: dict && dict.type } });
|
|
59
|
+
}
|
|
60
|
+
const e = dict.entries;
|
|
61
|
+
const s = isType(e.S, 'name') ? e.S.value : null;
|
|
62
|
+
const ident = isType(e.OutputConditionIdentifier, 'string')
|
|
63
|
+
? new TextDecoder('latin1').decode(e.OutputConditionIdentifier.value)
|
|
64
|
+
: null;
|
|
65
|
+
const profile = profileFromIdentifier(ident);
|
|
66
|
+
return {
|
|
67
|
+
isPdfX: s === 'GTS_PDFX',
|
|
68
|
+
subtype: s,
|
|
69
|
+
identifier: ident,
|
|
70
|
+
profile,
|
|
71
|
+
catalog: profile ? PDFX_PROFILES[profile] : null,
|
|
72
|
+
raw: dict
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function profileFromIdentifier(s) {
|
|
77
|
+
if (!s) return null;
|
|
78
|
+
const m = s.match(/PDF\/(X-(?:1a:200[13]|3:200[23]|4p?|5(?:g|n|pg)|6))/i);
|
|
79
|
+
if (!m) return null;
|
|
80
|
+
const key = m[1].toUpperCase().replace('X-', 'X-');
|
|
81
|
+
for (const k of Object.keys(PDFX_PROFILES)) {
|
|
82
|
+
if (k.toLowerCase() === key.toLowerCase()) return k;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function lintPdfXPage(page) {
|
|
88
|
+
if (!page || !page.entries) {
|
|
89
|
+
throw new ParseError('pdf/extra/pdfx/lint/bad-page',
|
|
90
|
+
'lintPdfXPage requires a typed page dict',
|
|
91
|
+
{ context: { type: typeof page } });
|
|
92
|
+
}
|
|
93
|
+
const errors = [];
|
|
94
|
+
const warnings = [];
|
|
95
|
+
const e = page.entries;
|
|
96
|
+
if (!e.TrimBox && !e.ArtBox) {
|
|
97
|
+
errors.push('page requires /TrimBox or /ArtBox');
|
|
98
|
+
}
|
|
99
|
+
if (e.TrimBox && e.ArtBox) {
|
|
100
|
+
errors.push('page must not have both /TrimBox and /ArtBox');
|
|
101
|
+
}
|
|
102
|
+
if (!e.BleedBox) warnings.push('page has no /BleedBox');
|
|
103
|
+
return { pass: errors.length === 0, errors, warnings };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
typePdfXOutputIntent,
|
|
108
|
+
lintPdfXPage,
|
|
109
|
+
PDFX_PROFILES,
|
|
110
|
+
COLOR_POLICY
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
@@ -0,0 +1,136 @@
|
|
|
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 Opt-in: typing of the ISO/TS 32005 apply-redaction audit
|
|
8
|
+
* record (read side). The TS text is not vendored; the entries below are what
|
|
9
|
+
* this module recognises. No content removal is performed.
|
|
10
|
+
*
|
|
11
|
+
* An audit record describes:
|
|
12
|
+
* - the annotations that were applied,
|
|
13
|
+
* - the overlay rendering hints (`/RD`, `/IC`, `/RO`),
|
|
14
|
+
* - content-stream redact markers (`/Redact ... BMC ... EMC`) tracking
|
|
15
|
+
* positions for visualization / forensic review.
|
|
16
|
+
*
|
|
17
|
+
* @module pdf/extra/redaction-iso32005
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Module factory.
|
|
22
|
+
*/
|
|
23
|
+
import { pdfErrors } from '../errors.js';
|
|
24
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
25
|
+
|
|
26
|
+
export const pdfRedactionIso32005 = {
|
|
27
|
+
name: 'pdfRedactionIso32005',
|
|
28
|
+
dependencies: ['pdfErrors', 'pdfParser'],
|
|
29
|
+
deps: [pdfErrors, pdfParser],
|
|
30
|
+
factory(errors, parser) {
|
|
31
|
+
const { ParseError } = errors;
|
|
32
|
+
const { isType } = parser;
|
|
33
|
+
|
|
34
|
+
const KNOWN = new Set([
|
|
35
|
+
'Type', 'AppliedAt', 'Tool', 'Annotations', 'RD', 'RO', 'IC',
|
|
36
|
+
'MarkedRegions'
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const REDACT_MARKERS = Object.freeze({
|
|
40
|
+
begin: '/Redact BMC',
|
|
41
|
+
beginPropertyList: '/Redact BDC',
|
|
42
|
+
end: 'EMC',
|
|
43
|
+
notes: 'TS 32005 §6.3 — wraps content removed/overlaid by /RD.'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
function typeRedactionRecord(dict) {
|
|
47
|
+
if (!isType(dict, 'dict')) {
|
|
48
|
+
throw new ParseError('pdf/extra/redact32005/not-dict',
|
|
49
|
+
'redaction record must be a dictionary',
|
|
50
|
+
{ context: { type: dict && dict.type } });
|
|
51
|
+
}
|
|
52
|
+
const e = dict.entries;
|
|
53
|
+
const out = {
|
|
54
|
+
appliedAt: isType(e.AppliedAt, 'string') ? e.AppliedAt.value : null,
|
|
55
|
+
tool: isType(e.Tool, 'string') ? e.Tool.value : null,
|
|
56
|
+
annotations: toRefArray(e.Annotations),
|
|
57
|
+
rd: toRect(e.RD),
|
|
58
|
+
ro: isType(e.RO, 'ref') || isType(e.RO, 'stream') ? e.RO : null,
|
|
59
|
+
ic: toNumArray(e.IC),
|
|
60
|
+
markedRegions: toMarkedRegions(e.MarkedRegions),
|
|
61
|
+
raw: dict,
|
|
62
|
+
_extras: {}
|
|
63
|
+
};
|
|
64
|
+
if (e.Annotations && e.Annotations.type !== 'array') {
|
|
65
|
+
throw new ParseError('pdf/extra/redact32005/bad-annotations',
|
|
66
|
+
'/Annotations must be an array',
|
|
67
|
+
{ context: { type: e.Annotations.type } });
|
|
68
|
+
}
|
|
69
|
+
for (const k of Object.keys(e)) {
|
|
70
|
+
if (!KNOWN.has(k)) out._extras[k] = e[k];
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function findRedactMarkers(bytes) {
|
|
76
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
77
|
+
throw new ParseError('pdf/extra/redact32005/scan/bad-input',
|
|
78
|
+
'findRedactMarkers requires a Uint8Array',
|
|
79
|
+
{ context: { type: typeof bytes } });
|
|
80
|
+
}
|
|
81
|
+
const text = new TextDecoder('latin1').decode(bytes);
|
|
82
|
+
const regions = [];
|
|
83
|
+
const re = /\/Redact\s+(BMC|BDC)/g;
|
|
84
|
+
let m;
|
|
85
|
+
while ((m = re.exec(text)) !== null) {
|
|
86
|
+
const begin = m.index;
|
|
87
|
+
const tail = text.indexOf('EMC', re.lastIndex);
|
|
88
|
+
regions.push({
|
|
89
|
+
beginOffset: begin,
|
|
90
|
+
endOffset: tail < 0 ? null : tail + 3,
|
|
91
|
+
kind: m[1]
|
|
92
|
+
});
|
|
93
|
+
if (tail >= 0) re.lastIndex = tail + 3;
|
|
94
|
+
}
|
|
95
|
+
return { regions };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function toRect(v) {
|
|
99
|
+
if (!v || v.type !== 'array') return null;
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const it of v.items) {
|
|
102
|
+
if (!it || (it.type !== 'int' && it.type !== 'real')) return null;
|
|
103
|
+
out.push(it.value);
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function toNumArray(v) {
|
|
109
|
+
if (!v || v.type !== 'array') return null;
|
|
110
|
+
const out = [];
|
|
111
|
+
for (const it of v.items) {
|
|
112
|
+
if (!it || (it.type !== 'int' && it.type !== 'real')) return null;
|
|
113
|
+
out.push(it.value);
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function toRefArray(v) {
|
|
119
|
+
if (!v || v.type !== 'array') return null;
|
|
120
|
+
return v.items.slice();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function toMarkedRegions(v) {
|
|
124
|
+
if (!v) return null;
|
|
125
|
+
if (v.type !== 'array') return null;
|
|
126
|
+
return v.items.slice();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
typeRedactionRecord,
|
|
131
|
+
findRedactMarkers,
|
|
132
|
+
REDACT_MARKERS
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
@@ -0,0 +1,222 @@
|
|
|
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 Opt-in: typed Shading dictionaries (§8.7.4) and Function
|
|
8
|
+
* dictionaries (§7.10) per ISO 32000-2:2020.
|
|
9
|
+
*
|
|
10
|
+
* Shading types:
|
|
11
|
+
* 1 — Function-based
|
|
12
|
+
* 2 — Axial
|
|
13
|
+
* 3 — Radial
|
|
14
|
+
* 4 — Free-form Gouraud
|
|
15
|
+
* 5 — Lattice-form Gouraud
|
|
16
|
+
* 6 — Coons patch mesh
|
|
17
|
+
* 7 — Tensor-product patch mesh
|
|
18
|
+
*
|
|
19
|
+
* Function types: 0 (Sampled), 2 (Exponential), 3 (Stitching), 4 (PostScript).
|
|
20
|
+
*
|
|
21
|
+
* @module pdf/extra/shading-typed
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Module factory.
|
|
26
|
+
*/
|
|
27
|
+
import { pdfErrors } from '../errors.js';
|
|
28
|
+
import { pdfParserObj } from '../syntax/parser-obj.js';
|
|
29
|
+
|
|
30
|
+
export const pdfShadingTyped = {
|
|
31
|
+
name: 'pdfShadingTyped',
|
|
32
|
+
dependencies: ['pdfErrors', 'pdfParserObj'],
|
|
33
|
+
deps: [pdfErrors, pdfParserObj],
|
|
34
|
+
factory(errors, parserObj) {
|
|
35
|
+
const { ParseError } = errors;
|
|
36
|
+
const { isType } = parserObj;
|
|
37
|
+
|
|
38
|
+
const NUM = (v) => v && (v.type === 'int' || v.type === 'real');
|
|
39
|
+
|
|
40
|
+
function typeShading(obj_) {
|
|
41
|
+
let dict;
|
|
42
|
+
if (isType(obj_, 'dict')) dict = obj_;
|
|
43
|
+
else if (isType(obj_, 'stream')) dict = obj_.dict;
|
|
44
|
+
else {
|
|
45
|
+
throw new ParseError('pdf/extra/shading/not-dict-or-stream',
|
|
46
|
+
'Shading must be dict or stream',
|
|
47
|
+
{ context: { type: obj_ && obj_.type } });
|
|
48
|
+
}
|
|
49
|
+
const e = dict.entries;
|
|
50
|
+
if (!NUM(e.ShadingType)) {
|
|
51
|
+
throw new ParseError('pdf/extra/shading/missing-type',
|
|
52
|
+
'/ShadingType is required and must be numeric',
|
|
53
|
+
{ context: { type: e.ShadingType && e.ShadingType.type } });
|
|
54
|
+
}
|
|
55
|
+
const st = e.ShadingType.value | 0;
|
|
56
|
+
if (st < 1 || st > 7) {
|
|
57
|
+
throw new ParseError('pdf/extra/shading/bad-type',
|
|
58
|
+
'invalid /ShadingType (must be 1..7)',
|
|
59
|
+
{ context: { st } });
|
|
60
|
+
}
|
|
61
|
+
const base = {
|
|
62
|
+
shadingType: st,
|
|
63
|
+
colorSpace: e.ColorSpace || null,
|
|
64
|
+
background: isType(e.Background, 'array') ? e.Background.items : null,
|
|
65
|
+
bbox: isType(e.BBox, 'array') ? e.BBox.items.map(it => NUM(it) ? it.value : null) : null,
|
|
66
|
+
antiAlias: isType(e.AntiAlias, 'bool') ? e.AntiAlias.value : false,
|
|
67
|
+
raw: obj_,
|
|
68
|
+
_extras: {}
|
|
69
|
+
};
|
|
70
|
+
let typed;
|
|
71
|
+
switch (st) {
|
|
72
|
+
case 1: typed = readFnBased(e); break;
|
|
73
|
+
case 2: typed = readAxial(e); break;
|
|
74
|
+
case 3: typed = readRadial(e); break;
|
|
75
|
+
case 4: typed = readFreeForm(e); break;
|
|
76
|
+
case 5: typed = readLattice(e); break;
|
|
77
|
+
case 6: typed = readCoons(e); break;
|
|
78
|
+
case 7: typed = readTensor(e); break;
|
|
79
|
+
}
|
|
80
|
+
Object.assign(base, typed);
|
|
81
|
+
const known = new Set(['ShadingType', 'ColorSpace', 'Background', 'BBox', 'AntiAlias',
|
|
82
|
+
'Domain', 'Matrix', 'Function', 'Coords', 'Extend',
|
|
83
|
+
'BitsPerCoordinate', 'BitsPerComponent', 'BitsPerFlag',
|
|
84
|
+
'Decode', 'VerticesPerRow']);
|
|
85
|
+
for (const k of Object.keys(e)) if (!known.has(k)) base._extras[k] = e[k];
|
|
86
|
+
return base;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readFnBased(e) {
|
|
90
|
+
return {
|
|
91
|
+
domain: isType(e.Domain, 'array') ? toNumArr(e.Domain) : [0, 1, 0, 1],
|
|
92
|
+
matrix: isType(e.Matrix, 'array') ? toNumArr(e.Matrix) : [1, 0, 0, 1, 0, 0],
|
|
93
|
+
function: e.Function || null
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function readAxial(e) {
|
|
98
|
+
requireArrLen(e.Coords, 4, 'Axial /Coords');
|
|
99
|
+
return {
|
|
100
|
+
coords: toNumArr(e.Coords),
|
|
101
|
+
domain: isType(e.Domain, 'array') ? toNumArr(e.Domain) : [0, 1],
|
|
102
|
+
function: e.Function || null,
|
|
103
|
+
extend: extendArr(e.Extend)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readRadial(e) {
|
|
108
|
+
requireArrLen(e.Coords, 6, 'Radial /Coords');
|
|
109
|
+
return {
|
|
110
|
+
coords: toNumArr(e.Coords),
|
|
111
|
+
domain: isType(e.Domain, 'array') ? toNumArr(e.Domain) : [0, 1],
|
|
112
|
+
function: e.Function || null,
|
|
113
|
+
extend: extendArr(e.Extend)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function readFreeForm(e) { return meshCommon(e, true); }
|
|
118
|
+
function readLattice(e) {
|
|
119
|
+
const c = meshCommon(e, false);
|
|
120
|
+
if (!NUM(e.VerticesPerRow)) {
|
|
121
|
+
throw new ParseError('pdf/extra/shading/lattice-missing-vpr',
|
|
122
|
+
'Lattice shading requires /VerticesPerRow');
|
|
123
|
+
}
|
|
124
|
+
c.verticesPerRow = e.VerticesPerRow.value | 0;
|
|
125
|
+
return c;
|
|
126
|
+
}
|
|
127
|
+
function readCoons(e) { return meshCommon(e, true); }
|
|
128
|
+
function readTensor(e) { return meshCommon(e, true); }
|
|
129
|
+
|
|
130
|
+
function meshCommon(e, hasFlag) {
|
|
131
|
+
if (!NUM(e.BitsPerCoordinate) || !NUM(e.BitsPerComponent)) {
|
|
132
|
+
throw new ParseError('pdf/extra/shading/mesh-missing-bits',
|
|
133
|
+
'mesh shading requires /BitsPerCoordinate and /BitsPerComponent');
|
|
134
|
+
}
|
|
135
|
+
if (!isType(e.Decode, 'array')) {
|
|
136
|
+
throw new ParseError('pdf/extra/shading/mesh-missing-decode',
|
|
137
|
+
'mesh shading requires /Decode');
|
|
138
|
+
}
|
|
139
|
+
const out = {
|
|
140
|
+
bitsPerCoordinate: e.BitsPerCoordinate.value | 0,
|
|
141
|
+
bitsPerComponent: e.BitsPerComponent.value | 0,
|
|
142
|
+
decode: toNumArr(e.Decode),
|
|
143
|
+
function: e.Function || null
|
|
144
|
+
};
|
|
145
|
+
if (hasFlag) {
|
|
146
|
+
if (!NUM(e.BitsPerFlag)) {
|
|
147
|
+
throw new ParseError('pdf/extra/shading/mesh-missing-flag',
|
|
148
|
+
'this mesh shading requires /BitsPerFlag');
|
|
149
|
+
}
|
|
150
|
+
out.bitsPerFlag = e.BitsPerFlag.value | 0;
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function extendArr(v) {
|
|
156
|
+
if (!isType(v, 'array') || v.items.length !== 2) return [false, false];
|
|
157
|
+
return [
|
|
158
|
+
isType(v.items[0], 'bool') ? v.items[0].value : false,
|
|
159
|
+
isType(v.items[1], 'bool') ? v.items[1].value : false
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function toNumArr(v) {
|
|
164
|
+
if (!isType(v, 'array')) return null;
|
|
165
|
+
const out = [];
|
|
166
|
+
for (const it of v.items) {
|
|
167
|
+
if (!NUM(it)) return null;
|
|
168
|
+
out.push(it.value);
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function requireArrLen(v, len, what) {
|
|
174
|
+
if (!isType(v, 'array') || v.items.length !== len) {
|
|
175
|
+
throw new ParseError('pdf/extra/shading/bad-coords',
|
|
176
|
+
`${what} must be an array of ${len} numbers`,
|
|
177
|
+
{ context: { actualLen: v && v.items && v.items.length } });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function typeFunction(obj_) {
|
|
182
|
+
let dict;
|
|
183
|
+
if (isType(obj_, 'dict')) dict = obj_;
|
|
184
|
+
else if (isType(obj_, 'stream')) dict = obj_.dict;
|
|
185
|
+
else {
|
|
186
|
+
throw new ParseError('pdf/extra/function/not-dict-or-stream',
|
|
187
|
+
'Function must be dict or stream',
|
|
188
|
+
{ context: { type: obj_ && obj_.type } });
|
|
189
|
+
}
|
|
190
|
+
const e = dict.entries;
|
|
191
|
+
if (!NUM(e.FunctionType)) {
|
|
192
|
+
throw new ParseError('pdf/extra/function/missing-type',
|
|
193
|
+
'/FunctionType is required');
|
|
194
|
+
}
|
|
195
|
+
const ft = e.FunctionType.value | 0;
|
|
196
|
+
if (![0, 2, 3, 4].includes(ft)) {
|
|
197
|
+
throw new ParseError('pdf/extra/function/bad-type',
|
|
198
|
+
'/FunctionType must be 0, 2, 3, or 4',
|
|
199
|
+
{ context: { ft } });
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
functionType: ft,
|
|
203
|
+
domain: toNumArr(e.Domain),
|
|
204
|
+
range: toNumArr(e.Range),
|
|
205
|
+
c0: toNumArr(e.C0),
|
|
206
|
+
c1: toNumArr(e.C1),
|
|
207
|
+
n: NUM(e.N) ? e.N.value : null,
|
|
208
|
+
functions: isType(e.Functions, 'array') ? e.Functions.items : null,
|
|
209
|
+
bounds: toNumArr(e.Bounds),
|
|
210
|
+
encode: toNumArr(e.Encode),
|
|
211
|
+
size: toNumArr(e.Size),
|
|
212
|
+
bitsPerSample: NUM(e.BitsPerSample) ? (e.BitsPerSample.value | 0) : null,
|
|
213
|
+
order: NUM(e.Order) ? (e.Order.value | 0) : null,
|
|
214
|
+
decode: toNumArr(e.Decode),
|
|
215
|
+
raw: obj_
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return { typeShading, typeFunction };
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|