@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,212 @@
|
|
|
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 Annotation orchestrator per ISO 32000-2:2020 §12.5.
|
|
8
|
+
*
|
|
9
|
+
* Dispatches an annotation dict (`/Type /Annot`) to a subtype-specific
|
|
10
|
+
* typer based on `/Subtype`. Also exports `typeBaseAnnot` which captures
|
|
11
|
+
* the entries common to every annotation (§12.5.2 Table 166).
|
|
12
|
+
*
|
|
13
|
+
* Common entries:
|
|
14
|
+
* /Type /Subtype /Rect /Contents /P /NM /M /F /AP /AS /Border /C
|
|
15
|
+
* /StructParent /OC /AF /CA /BS /BE
|
|
16
|
+
*
|
|
17
|
+
* Unknown subtypes return `{ kind, raw, _extras }` — preserve-unknowns.
|
|
18
|
+
*
|
|
19
|
+
* @module pdf/annot/annot
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Module factory.
|
|
24
|
+
*/
|
|
25
|
+
import { pdfErrors } from '../errors.js';
|
|
26
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
27
|
+
|
|
28
|
+
export const pdfAnnot = {
|
|
29
|
+
name: 'pdfAnnot',
|
|
30
|
+
dependencies: ['pdfErrors', 'pdfParser'],
|
|
31
|
+
deps: [pdfErrors, pdfParser],
|
|
32
|
+
factory(errors, parser) {
|
|
33
|
+
const { ParseError } = errors;
|
|
34
|
+
const { isType } = parser;
|
|
35
|
+
|
|
36
|
+
const BASE_KEYS = new Set([
|
|
37
|
+
'Type', 'Subtype', 'Rect', 'Contents', 'P', 'NM', 'M', 'F', 'AP',
|
|
38
|
+
'AS', 'Border', 'C', 'StructParent', 'OC', 'AF', 'CA', 'BS', 'BE'
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
function typeBaseAnnot(dict) {
|
|
42
|
+
if (!isType(dict, 'dict')) {
|
|
43
|
+
throw new ParseError('pdf/annot/not-dict',
|
|
44
|
+
'annotation must be a dictionary',
|
|
45
|
+
{ context: { type: dict && dict.type } });
|
|
46
|
+
}
|
|
47
|
+
const e = dict.entries;
|
|
48
|
+
if (e.Type && (e.Type.type !== 'name' || e.Type.value !== 'Annot')) {
|
|
49
|
+
throw new ParseError('pdf/annot/bad-type',
|
|
50
|
+
'/Type must be /Annot when present',
|
|
51
|
+
{ context: { actual: e.Type.value } });
|
|
52
|
+
}
|
|
53
|
+
const out = {
|
|
54
|
+
subtype: isType(e.Subtype, 'name') ? e.Subtype.value : null,
|
|
55
|
+
rect: toRect(e.Rect),
|
|
56
|
+
contents: isType(e.Contents, 'string') ? e.Contents.value : null,
|
|
57
|
+
p: e.P && e.P.type === 'ref'
|
|
58
|
+
? { num: e.P.num, gen: e.P.gen } : null,
|
|
59
|
+
nm: isType(e.NM, 'string') ? e.NM.value : null,
|
|
60
|
+
m: isType(e.M, 'string') ? e.M.value : null,
|
|
61
|
+
f: toInt(e.F, 0),
|
|
62
|
+
ap: isType(e.AP, 'dict') ? e.AP : null,
|
|
63
|
+
as: isType(e.AS, 'name') ? e.AS.value : null,
|
|
64
|
+
border: toBorder(e.Border),
|
|
65
|
+
c: toNumArray(e.C),
|
|
66
|
+
structParent: toInt(e.StructParent, null),
|
|
67
|
+
oc: e.OC || null,
|
|
68
|
+
af: e.AF || null,
|
|
69
|
+
ca: toNum(e.CA, null),
|
|
70
|
+
bs: isType(e.BS, 'dict') ? e.BS : null,
|
|
71
|
+
be: isType(e.BE, 'dict') ? e.BE : null,
|
|
72
|
+
raw: dict,
|
|
73
|
+
_extras: {}
|
|
74
|
+
};
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function captureExtras(target, dict, known) {
|
|
79
|
+
const e = dict.entries;
|
|
80
|
+
for (const k of Object.keys(e)) {
|
|
81
|
+
if (BASE_KEYS.has(k)) continue;
|
|
82
|
+
if (known && known.has(k)) continue;
|
|
83
|
+
target._extras[k] = e[k];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function typeAnnot(dict, typers) {
|
|
88
|
+
if (!isType(dict, 'dict')) {
|
|
89
|
+
throw new ParseError('pdf/annot/not-dict',
|
|
90
|
+
'annotation must be a dictionary',
|
|
91
|
+
{ context: { type: dict && dict.type } });
|
|
92
|
+
}
|
|
93
|
+
const sub = dict.entries.Subtype;
|
|
94
|
+
if (!isType(sub, 'name')) {
|
|
95
|
+
throw new ParseError('pdf/annot/missing-subtype',
|
|
96
|
+
'/Subtype is required and must be a name',
|
|
97
|
+
{ context: { kind: sub && sub.type } });
|
|
98
|
+
}
|
|
99
|
+
const name = sub.value;
|
|
100
|
+
const t = typers || {};
|
|
101
|
+
switch (name) {
|
|
102
|
+
case 'Text': return tag('Text', call(t.Text, dict));
|
|
103
|
+
case 'Link': return tag('Link', call(t.Link, dict));
|
|
104
|
+
case 'FreeText': return tag('FreeText', call(t.FreeText, dict));
|
|
105
|
+
case 'Line': return tag('Line', call(t.Shape, dict, 'Line'));
|
|
106
|
+
case 'Square': return tag('Square', call(t.Shape, dict, 'Square'));
|
|
107
|
+
case 'Circle': return tag('Circle', call(t.Shape, dict, 'Circle'));
|
|
108
|
+
case 'Polygon': return tag('Polygon', call(t.Shape, dict, 'Polygon'));
|
|
109
|
+
case 'PolyLine': return tag('PolyLine', call(t.Shape, dict, 'PolyLine'));
|
|
110
|
+
case 'Highlight': return tag('Highlight', call(t.Markup, dict, 'Highlight'));
|
|
111
|
+
case 'Underline': return tag('Underline', call(t.Markup, dict, 'Underline'));
|
|
112
|
+
case 'Squiggly': return tag('Squiggly', call(t.Markup, dict, 'Squiggly'));
|
|
113
|
+
case 'StrikeOut': return tag('StrikeOut', call(t.Markup, dict, 'StrikeOut'));
|
|
114
|
+
case 'Caret': return tag('Caret', call(t.Markup, dict, 'Caret'));
|
|
115
|
+
case 'Stamp': return tag('Stamp', call(t.Stamp, dict));
|
|
116
|
+
case 'Ink': return tag('Ink', call(t.Ink, dict));
|
|
117
|
+
case 'Popup': return tag('Popup', call(t.Popup, dict));
|
|
118
|
+
case 'FileAttachment': return tag('FileAttachment', call(t.FileAttachment, dict));
|
|
119
|
+
case 'Widget': return tag('Widget', call(t.Widget, dict));
|
|
120
|
+
case 'Redact': return tag('Redact', call(t.Redact, dict));
|
|
121
|
+
case 'Projection': return tag('Projection', call(t.Projection, dict));
|
|
122
|
+
case 'Sound':
|
|
123
|
+
case 'Movie':
|
|
124
|
+
case 'Screen':
|
|
125
|
+
case 'PrinterMark':
|
|
126
|
+
case 'TrapNet':
|
|
127
|
+
case 'Watermark':
|
|
128
|
+
case '3D':
|
|
129
|
+
case 'RichMedia':
|
|
130
|
+
return tag(name, fallback(dict));
|
|
131
|
+
default:
|
|
132
|
+
return { kind: name, raw: dict, _extras: collectAll(dict) };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function call(fn, dict, arg) {
|
|
137
|
+
if (typeof fn !== 'function') {
|
|
138
|
+
// Fallback if a subtype-specific typer isn't wired.
|
|
139
|
+
return fallback(dict);
|
|
140
|
+
}
|
|
141
|
+
return arg !== undefined ? fn(dict, arg) : fn(dict);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function tag(kind, rec) { rec.kind = kind; return rec; }
|
|
145
|
+
|
|
146
|
+
function fallback(dict) {
|
|
147
|
+
const base = typeBaseAnnot(dict);
|
|
148
|
+
captureExtras(base, dict, null);
|
|
149
|
+
return base;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function collectAll(dict) {
|
|
153
|
+
const out = {};
|
|
154
|
+
const e = dict.entries;
|
|
155
|
+
for (const k of Object.keys(e)) out[k] = e[k];
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function toRect(v) {
|
|
160
|
+
if (!v || v.type !== 'array' || v.items.length !== 4) return null;
|
|
161
|
+
const r = new Array(4);
|
|
162
|
+
for (let i = 0; i < 4; i++) {
|
|
163
|
+
const it = v.items[i];
|
|
164
|
+
if (!it || (it.type !== 'int' && it.type !== 'real')) return null;
|
|
165
|
+
r[i] = it.value;
|
|
166
|
+
}
|
|
167
|
+
return r;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function toBorder(v) {
|
|
171
|
+
if (!v || v.type !== 'array') return null;
|
|
172
|
+
const out = [];
|
|
173
|
+
for (const it of v.items) {
|
|
174
|
+
if (!it) return null;
|
|
175
|
+
if (it.type === 'int' || it.type === 'real') out.push(it.value);
|
|
176
|
+
else if (it.type === 'array') out.push(toNumArray(it));
|
|
177
|
+
else return null;
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function toNumArray(v) {
|
|
183
|
+
if (!v || v.type !== 'array') return null;
|
|
184
|
+
const out = [];
|
|
185
|
+
for (const it of v.items) {
|
|
186
|
+
if (!it) return null;
|
|
187
|
+
if (it.type !== 'int' && it.type !== 'real') return null;
|
|
188
|
+
out.push(it.value);
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function toInt(v, dflt) {
|
|
194
|
+
if (!v) return dflt;
|
|
195
|
+
if (v.type !== 'int' && v.type !== 'real') return dflt;
|
|
196
|
+
return v.value | 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function toNum(v, dflt) {
|
|
200
|
+
if (!v) return dflt;
|
|
201
|
+
if (v.type !== 'int' && v.type !== 'real') return dflt;
|
|
202
|
+
return v.value;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
BASE_KEYS,
|
|
207
|
+
typeBaseAnnot,
|
|
208
|
+
captureExtras,
|
|
209
|
+
typeAnnot
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
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 FileAttachment annotation per ISO 32000-2:2020 §12.5.6.15.
|
|
8
|
+
*
|
|
9
|
+
* Subtype-specific entries:
|
|
10
|
+
* /FS dict|ref — file-specification (required)
|
|
11
|
+
* /Name name — icon (PushPin, Graph, Paperclip, Tag)
|
|
12
|
+
*
|
|
13
|
+
* @module pdf/annot/fileAttach
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Module factory.
|
|
18
|
+
*/
|
|
19
|
+
import { pdfErrors } from '../errors.js';
|
|
20
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
21
|
+
import { pdfAnnot } from './annot.js';
|
|
22
|
+
|
|
23
|
+
export const pdfFileAttachAnnot = {
|
|
24
|
+
name: 'pdfFileAttachAnnot',
|
|
25
|
+
dependencies: ['pdfErrors', 'pdfParser', 'pdfAnnot'],
|
|
26
|
+
deps: [pdfErrors, pdfParser, pdfAnnot],
|
|
27
|
+
factory(errors, parser, annot) {
|
|
28
|
+
const { ParseError } = errors;
|
|
29
|
+
const { isType } = parser;
|
|
30
|
+
const { typeBaseAnnot, captureExtras } = annot;
|
|
31
|
+
|
|
32
|
+
const KNOWN = new Set(['FS', 'Name']);
|
|
33
|
+
|
|
34
|
+
function typeFileAttachAnnot(dict) {
|
|
35
|
+
const base = typeBaseAnnot(dict);
|
|
36
|
+
if (base.subtype && base.subtype !== 'FileAttachment') {
|
|
37
|
+
throw new ParseError('pdf/annot/fileattach/bad-subtype',
|
|
38
|
+
'/Subtype must be /FileAttachment',
|
|
39
|
+
{ context: { actual: base.subtype } });
|
|
40
|
+
}
|
|
41
|
+
const e = dict.entries;
|
|
42
|
+
if (e.FS && e.FS.type !== 'dict' && e.FS.type !== 'ref') {
|
|
43
|
+
throw new ParseError('pdf/annot/fileattach/bad-fs',
|
|
44
|
+
'/FS must be a dictionary or indirect reference',
|
|
45
|
+
{ context: { kind: e.FS.type } });
|
|
46
|
+
}
|
|
47
|
+
base.fs = e.FS || null;
|
|
48
|
+
base.iconName = isType(e.Name, 'name') ? e.Name.value : null;
|
|
49
|
+
captureExtras(base, dict, KNOWN);
|
|
50
|
+
return base;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { typeFileAttachAnnot };
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
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 FreeText annotation per ISO 32000-2:2020 §12.5.6.6.
|
|
8
|
+
*
|
|
9
|
+
* Subtype-specific entries:
|
|
10
|
+
* /DA string — default appearance (required)
|
|
11
|
+
* /Q int — quadding (0 left, 1 center, 2 right)
|
|
12
|
+
* /RC string — rich-text contents (XHTML)
|
|
13
|
+
* /DS string — default style (CSS-like)
|
|
14
|
+
* /CL array — callout-line coordinates (4 or 6 numbers)
|
|
15
|
+
* /IT name — intent (FreeText, FreeTextCallout, FreeTextTypeWriter)
|
|
16
|
+
* /RD array — rectangle differences
|
|
17
|
+
* /LE name — line ending (callout)
|
|
18
|
+
*
|
|
19
|
+
* @module pdf/annot/freeText
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Module factory.
|
|
24
|
+
*/
|
|
25
|
+
import { pdfErrors } from '../errors.js';
|
|
26
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
27
|
+
import { pdfAnnot } from './annot.js';
|
|
28
|
+
|
|
29
|
+
export const pdfFreeTextAnnot = {
|
|
30
|
+
name: 'pdfFreeTextAnnot',
|
|
31
|
+
dependencies: ['pdfErrors', 'pdfParser', 'pdfAnnot'],
|
|
32
|
+
deps: [pdfErrors, pdfParser, pdfAnnot],
|
|
33
|
+
factory(errors, parser, annot) {
|
|
34
|
+
const { ParseError } = errors;
|
|
35
|
+
const { isType } = parser;
|
|
36
|
+
const { typeBaseAnnot, captureExtras } = annot;
|
|
37
|
+
|
|
38
|
+
const KNOWN = new Set(['DA', 'Q', 'RC', 'DS', 'CL', 'IT', 'RD', 'LE']);
|
|
39
|
+
|
|
40
|
+
function typeFreeTextAnnot(dict) {
|
|
41
|
+
const base = typeBaseAnnot(dict);
|
|
42
|
+
if (base.subtype && base.subtype !== 'FreeText') {
|
|
43
|
+
throw new ParseError('pdf/annot/freetext/bad-subtype',
|
|
44
|
+
'/Subtype must be /FreeText',
|
|
45
|
+
{ context: { actual: base.subtype } });
|
|
46
|
+
}
|
|
47
|
+
const e = dict.entries;
|
|
48
|
+
base.da = isType(e.DA, 'string') ? e.DA.value : null;
|
|
49
|
+
base.q = (e.Q && (e.Q.type === 'int' || e.Q.type === 'real'))
|
|
50
|
+
? (e.Q.value | 0) : 0;
|
|
51
|
+
base.rc = isType(e.RC, 'string') ? e.RC.value : null;
|
|
52
|
+
base.ds = isType(e.DS, 'string') ? e.DS.value : null;
|
|
53
|
+
base.cl = toNumArray(e.CL, 'pdf/annot/freetext/bad-cl');
|
|
54
|
+
base.it = isType(e.IT, 'name') ? e.IT.value : null;
|
|
55
|
+
base.rd = toNumArray(e.RD, 'pdf/annot/freetext/bad-rd');
|
|
56
|
+
base.le = isType(e.LE, 'name') ? e.LE.value : null;
|
|
57
|
+
captureExtras(base, dict, KNOWN);
|
|
58
|
+
return base;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function toNumArray(v, errCode) {
|
|
62
|
+
if (!v) return null;
|
|
63
|
+
if (v.type !== 'array') {
|
|
64
|
+
throw new ParseError(errCode,
|
|
65
|
+
'expected an array of numbers',
|
|
66
|
+
{ context: { kind: v.type } });
|
|
67
|
+
}
|
|
68
|
+
const out = [];
|
|
69
|
+
for (const it of v.items) {
|
|
70
|
+
if (!it || (it.type !== 'int' && it.type !== 'real')) {
|
|
71
|
+
throw new ParseError(errCode,
|
|
72
|
+
'array entries must be numbers',
|
|
73
|
+
{ context: { kind: it && it.type } });
|
|
74
|
+
}
|
|
75
|
+
out.push(it.value);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { typeFreeTextAnnot };
|
|
81
|
+
}
|
|
82
|
+
};
|
package/src/annot/ink.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
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 Ink annotation per ISO 32000-2:2020 §12.5.6.13.
|
|
8
|
+
*
|
|
9
|
+
* Subtype-specific entries:
|
|
10
|
+
* /InkList array of arrays of numbers — each inner array is a stroke
|
|
11
|
+
* as x1 y1 x2 y2 …
|
|
12
|
+
* /BS border style (on base)
|
|
13
|
+
*
|
|
14
|
+
* @module pdf/annot/ink
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Module factory.
|
|
19
|
+
*/
|
|
20
|
+
import { pdfErrors } from '../errors.js';
|
|
21
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
22
|
+
import { pdfAnnot } from './annot.js';
|
|
23
|
+
|
|
24
|
+
export const pdfInkAnnot = {
|
|
25
|
+
name: 'pdfInkAnnot',
|
|
26
|
+
dependencies: ['pdfErrors', 'pdfParser', 'pdfAnnot'],
|
|
27
|
+
deps: [pdfErrors, pdfParser, pdfAnnot],
|
|
28
|
+
factory(errors, parser, annot) {
|
|
29
|
+
const { ParseError } = errors;
|
|
30
|
+
const { isType: _isType } = parser;
|
|
31
|
+
const { typeBaseAnnot, captureExtras } = annot;
|
|
32
|
+
|
|
33
|
+
const KNOWN = new Set(['InkList']);
|
|
34
|
+
|
|
35
|
+
function typeInkAnnot(dict) {
|
|
36
|
+
const base = typeBaseAnnot(dict);
|
|
37
|
+
if (base.subtype && base.subtype !== 'Ink') {
|
|
38
|
+
throw new ParseError('pdf/annot/ink/bad-subtype',
|
|
39
|
+
'/Subtype must be /Ink',
|
|
40
|
+
{ context: { actual: base.subtype } });
|
|
41
|
+
}
|
|
42
|
+
base.inkList = toInkList(dict.entries.InkList);
|
|
43
|
+
captureExtras(base, dict, KNOWN);
|
|
44
|
+
return base;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function toInkList(v) {
|
|
48
|
+
if (!v) return [];
|
|
49
|
+
if (v.type !== 'array') {
|
|
50
|
+
throw new ParseError('pdf/annot/ink/bad-inklist',
|
|
51
|
+
'/InkList must be an array of arrays',
|
|
52
|
+
{ context: { kind: v.type } });
|
|
53
|
+
}
|
|
54
|
+
const out = [];
|
|
55
|
+
for (const stroke of v.items) {
|
|
56
|
+
if (!stroke || stroke.type !== 'array') {
|
|
57
|
+
throw new ParseError('pdf/annot/ink/bad-stroke',
|
|
58
|
+
'/InkList entries must be arrays of numbers',
|
|
59
|
+
{ context: { kind: stroke && stroke.type } });
|
|
60
|
+
}
|
|
61
|
+
const nums = [];
|
|
62
|
+
for (const it of stroke.items) {
|
|
63
|
+
if (!it || (it.type !== 'int' && it.type !== 'real')) {
|
|
64
|
+
throw new ParseError('pdf/annot/ink/bad-coord',
|
|
65
|
+
'/InkList stroke entries must be numbers',
|
|
66
|
+
{ context: { kind: it && it.type } });
|
|
67
|
+
}
|
|
68
|
+
nums.push(it.value);
|
|
69
|
+
}
|
|
70
|
+
out.push(nums);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { typeInkAnnot };
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
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 Link annotation per ISO 32000-2:2020 §12.5.6.5.
|
|
8
|
+
*
|
|
9
|
+
* Subtype-specific entries:
|
|
10
|
+
* /Dest array|name|string — destination (mutually exclusive with /A)
|
|
11
|
+
* /A dict — action to perform
|
|
12
|
+
* /H name — highlight mode (N, I, O, P)
|
|
13
|
+
* /PA dict — URI action for WWW links
|
|
14
|
+
* /QuadPoints array of numbers — link-region quadrilaterals
|
|
15
|
+
* /BS dict — border style (already on base, but
|
|
16
|
+
* captured explicitly for clarity)
|
|
17
|
+
*
|
|
18
|
+
* @module pdf/annot/link
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Module factory.
|
|
23
|
+
*/
|
|
24
|
+
import { pdfErrors } from '../errors.js';
|
|
25
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
26
|
+
import { pdfAnnot } from './annot.js';
|
|
27
|
+
|
|
28
|
+
export const pdfLinkAnnot = {
|
|
29
|
+
name: 'pdfLinkAnnot',
|
|
30
|
+
dependencies: ['pdfErrors', 'pdfParser', 'pdfAnnot'],
|
|
31
|
+
deps: [pdfErrors, pdfParser, pdfAnnot],
|
|
32
|
+
factory(errors, parser, annot) {
|
|
33
|
+
const { ParseError } = errors;
|
|
34
|
+
const { isType } = parser;
|
|
35
|
+
const { typeBaseAnnot, captureExtras } = annot;
|
|
36
|
+
|
|
37
|
+
const KNOWN = new Set(['Dest', 'A', 'H', 'PA', 'QuadPoints']);
|
|
38
|
+
|
|
39
|
+
function typeLinkAnnot(dict) {
|
|
40
|
+
const base = typeBaseAnnot(dict);
|
|
41
|
+
if (base.subtype && base.subtype !== 'Link') {
|
|
42
|
+
throw new ParseError('pdf/annot/link/bad-subtype',
|
|
43
|
+
'/Subtype must be /Link',
|
|
44
|
+
{ context: { actual: base.subtype } });
|
|
45
|
+
}
|
|
46
|
+
const e = dict.entries;
|
|
47
|
+
base.dest = e.Dest || null;
|
|
48
|
+
base.action = isType(e.A, 'dict') ? e.A : null;
|
|
49
|
+
base.h = isType(e.H, 'name') ? e.H.value : null;
|
|
50
|
+
base.pa = isType(e.PA, 'dict') ? e.PA : null;
|
|
51
|
+
base.quadPoints = toNumArray(e.QuadPoints);
|
|
52
|
+
captureExtras(base, dict, KNOWN);
|
|
53
|
+
return base;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function toNumArray(v) {
|
|
57
|
+
if (!v) return null;
|
|
58
|
+
if (v.type !== 'array') {
|
|
59
|
+
throw new ParseError('pdf/annot/link/bad-quadpoints',
|
|
60
|
+
'/QuadPoints must be an array of numbers',
|
|
61
|
+
{ context: { kind: v.type } });
|
|
62
|
+
}
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const it of v.items) {
|
|
65
|
+
if (!it || (it.type !== 'int' && it.type !== 'real')) {
|
|
66
|
+
throw new ParseError('pdf/annot/link/bad-quadpoints',
|
|
67
|
+
'/QuadPoints entries must be numbers',
|
|
68
|
+
{ context: { kind: it && it.type } });
|
|
69
|
+
}
|
|
70
|
+
out.push(it.value);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { typeLinkAnnot };
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
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 Text-markup annotations per ISO 32000-2:2020 §12.5.6.10.
|
|
8
|
+
*
|
|
9
|
+
* Subtypes handled: Highlight, Underline, Squiggly, StrikeOut, Caret.
|
|
10
|
+
*
|
|
11
|
+
* Common entry:
|
|
12
|
+
* /QuadPoints array of 8n numbers — quadrilaterals covering markup
|
|
13
|
+
*
|
|
14
|
+
* Caret-specific (§12.5.6.11) optional entries:
|
|
15
|
+
* /RD rectangle differences
|
|
16
|
+
* /Sy name — symbol displayed (P or None)
|
|
17
|
+
*
|
|
18
|
+
* @module pdf/annot/markup
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Module factory.
|
|
23
|
+
*/
|
|
24
|
+
import { pdfErrors } from '../errors.js';
|
|
25
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
26
|
+
import { pdfAnnot } from './annot.js';
|
|
27
|
+
|
|
28
|
+
export const pdfMarkupAnnot = {
|
|
29
|
+
name: 'pdfMarkupAnnot',
|
|
30
|
+
dependencies: ['pdfErrors', 'pdfParser', 'pdfAnnot'],
|
|
31
|
+
deps: [pdfErrors, pdfParser, pdfAnnot],
|
|
32
|
+
factory(errors, parser, annot) {
|
|
33
|
+
const { ParseError } = errors;
|
|
34
|
+
const { isType } = parser;
|
|
35
|
+
const { typeBaseAnnot, captureExtras } = annot;
|
|
36
|
+
|
|
37
|
+
const KNOWN = new Set(['QuadPoints', 'RD', 'Sy', 'IT']);
|
|
38
|
+
const VALID = new Set(['Highlight', 'Underline', 'Squiggly', 'StrikeOut', 'Caret']);
|
|
39
|
+
|
|
40
|
+
function typeMarkupAnnot(dict, expected) {
|
|
41
|
+
if (!VALID.has(expected)) {
|
|
42
|
+
throw new ParseError('pdf/annot/markup/bad-expected',
|
|
43
|
+
'unsupported markup subtype',
|
|
44
|
+
{ context: { expected } });
|
|
45
|
+
}
|
|
46
|
+
const base = typeBaseAnnot(dict);
|
|
47
|
+
if (base.subtype && base.subtype !== expected) {
|
|
48
|
+
throw new ParseError('pdf/annot/markup/bad-subtype',
|
|
49
|
+
'/Subtype mismatch for markup typer',
|
|
50
|
+
{ context: { expected, actual: base.subtype } });
|
|
51
|
+
}
|
|
52
|
+
const e = dict.entries;
|
|
53
|
+
base.quadPoints = toNumArray(e.QuadPoints,
|
|
54
|
+
'pdf/annot/markup/bad-quadpoints');
|
|
55
|
+
if (expected === 'Caret') {
|
|
56
|
+
base.rd = toNumArray(e.RD, 'pdf/annot/markup/bad-rd');
|
|
57
|
+
base.sy = isType(e.Sy, 'name') ? e.Sy.value : null;
|
|
58
|
+
} else if (e.QuadPoints) {
|
|
59
|
+
if (base.quadPoints && base.quadPoints.length % 8 !== 0) {
|
|
60
|
+
throw new ParseError('pdf/annot/markup/bad-quadpoints',
|
|
61
|
+
'/QuadPoints length must be a multiple of 8',
|
|
62
|
+
{ context: { length: base.quadPoints.length } });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
base.it = isType(e.IT, 'name') ? e.IT.value : null;
|
|
66
|
+
captureExtras(base, dict, KNOWN);
|
|
67
|
+
return base;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function toNumArray(v, errCode) {
|
|
71
|
+
if (!v) return null;
|
|
72
|
+
if (v.type !== 'array') {
|
|
73
|
+
throw new ParseError(errCode,
|
|
74
|
+
'expected an array of numbers',
|
|
75
|
+
{ context: { kind: v.type } });
|
|
76
|
+
}
|
|
77
|
+
const out = [];
|
|
78
|
+
for (const it of v.items) {
|
|
79
|
+
if (!it || (it.type !== 'int' && it.type !== 'real')) {
|
|
80
|
+
throw new ParseError(errCode,
|
|
81
|
+
'array entries must be numbers',
|
|
82
|
+
{ context: { kind: it && it.type } });
|
|
83
|
+
}
|
|
84
|
+
out.push(it.value);
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { typeMarkupAnnot };
|
|
90
|
+
}
|
|
91
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
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 Popup annotation per ISO 32000-2:2020 §12.5.6.14.
|
|
8
|
+
*
|
|
9
|
+
* Popups are subordinate displays for markup annotations.
|
|
10
|
+
*
|
|
11
|
+
* Subtype-specific entries:
|
|
12
|
+
* /Parent ref — annotation this popup is associated with
|
|
13
|
+
* /Open boolean — initial open state
|
|
14
|
+
*
|
|
15
|
+
* @module pdf/annot/popup
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Module factory.
|
|
20
|
+
*/
|
|
21
|
+
import { pdfErrors } from '../errors.js';
|
|
22
|
+
import { pdfParser } from '../syntax/parser.js';
|
|
23
|
+
import { pdfAnnot } from './annot.js';
|
|
24
|
+
|
|
25
|
+
export const pdfPopupAnnot = {
|
|
26
|
+
name: 'pdfPopupAnnot',
|
|
27
|
+
dependencies: ['pdfErrors', 'pdfParser', 'pdfAnnot'],
|
|
28
|
+
deps: [pdfErrors, pdfParser, pdfAnnot],
|
|
29
|
+
factory(errors, parser, annot) {
|
|
30
|
+
const { ParseError } = errors;
|
|
31
|
+
const { isType } = parser;
|
|
32
|
+
const { typeBaseAnnot, captureExtras } = annot;
|
|
33
|
+
|
|
34
|
+
const KNOWN = new Set(['Parent', 'Open']);
|
|
35
|
+
|
|
36
|
+
function typePopupAnnot(dict) {
|
|
37
|
+
const base = typeBaseAnnot(dict);
|
|
38
|
+
if (base.subtype && base.subtype !== 'Popup') {
|
|
39
|
+
throw new ParseError('pdf/annot/popup/bad-subtype',
|
|
40
|
+
'/Subtype must be /Popup',
|
|
41
|
+
{ context: { actual: base.subtype } });
|
|
42
|
+
}
|
|
43
|
+
const e = dict.entries;
|
|
44
|
+
base.parent = e.Parent && e.Parent.type === 'ref'
|
|
45
|
+
? { num: e.Parent.num, gen: e.Parent.gen } : null;
|
|
46
|
+
base.open = isType(e.Open, 'bool') ? !!e.Open.value : false;
|
|
47
|
+
captureExtras(base, dict, KNOWN);
|
|
48
|
+
return base;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { typePopupAnnot };
|
|
52
|
+
}
|
|
53
|
+
};
|