@cj-tech-master/excelts 9.1.0 → 9.2.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/README.md +16 -1
- package/dist/browser/modules/archive/compression/crc32.js +1 -1
- package/dist/browser/modules/archive/crypto/aes.d.ts +0 -8
- package/dist/browser/modules/archive/crypto/aes.js +1 -20
- package/dist/browser/modules/archive/crypto/index.d.ts +2 -1
- package/dist/browser/modules/archive/crypto/index.js +3 -1
- package/dist/browser/modules/csv/parse/row-processor.d.ts +1 -1
- package/dist/browser/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/browser/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/browser/modules/excel/utils/encryptor.browser.d.ts +4 -5
- package/dist/browser/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/browser/modules/excel/utils/encryptor.d.ts +1 -1
- package/dist/browser/modules/excel/utils/encryptor.js +4 -7
- package/dist/browser/modules/pdf/builder/document-builder.d.ts +517 -0
- package/dist/browser/modules/pdf/builder/document-builder.js +1493 -0
- package/dist/browser/modules/pdf/builder/form-appearance.d.ts +56 -0
- package/dist/browser/modules/pdf/builder/form-appearance.js +140 -0
- package/dist/browser/modules/pdf/builder/image-utils.d.ts +39 -0
- package/dist/browser/modules/pdf/builder/image-utils.js +129 -0
- package/dist/browser/modules/pdf/builder/pdf-editor.d.ts +230 -0
- package/dist/browser/modules/pdf/builder/pdf-editor.js +1574 -0
- package/dist/browser/modules/pdf/builder/resource-merger.d.ts +41 -0
- package/dist/browser/modules/pdf/builder/resource-merger.js +258 -0
- package/dist/browser/modules/pdf/core/digital-signature.d.ts +109 -0
- package/dist/browser/modules/pdf/core/digital-signature.js +659 -0
- package/dist/browser/modules/pdf/core/encryption.js +8 -7
- package/dist/browser/modules/pdf/core/pdf-object.d.ts +11 -0
- package/dist/browser/modules/pdf/core/pdf-object.js +38 -0
- package/dist/browser/modules/pdf/core/pdf-stream.d.ts +32 -0
- package/dist/browser/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/browser/modules/pdf/core/pdf-writer.d.ts +55 -1
- package/dist/browser/modules/pdf/core/pdf-writer.js +271 -6
- package/dist/browser/modules/pdf/core/pdfa.d.ts +62 -0
- package/dist/browser/modules/pdf/core/pdfa.js +261 -0
- package/dist/browser/modules/pdf/index.d.ts +11 -0
- package/dist/browser/modules/pdf/index.js +9 -0
- package/dist/browser/modules/pdf/reader/bookmark-extractor.d.ts +35 -0
- package/dist/browser/modules/pdf/reader/bookmark-extractor.js +324 -0
- package/dist/browser/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/browser/modules/pdf/reader/pdf-reader.d.ts +17 -0
- package/dist/browser/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/browser/modules/pdf/reader/table-extractor.d.ts +69 -0
- package/dist/browser/modules/pdf/reader/table-extractor.js +365 -0
- package/dist/browser/modules/pdf/render/layout-engine.d.ts +21 -1
- package/dist/browser/modules/pdf/render/layout-engine.js +112 -5
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +2 -9
- package/dist/browser/modules/pdf/render/page-renderer.js +62 -103
- package/dist/browser/modules/pdf/render/pdf-exporter.js +2 -61
- package/dist/browser/modules/pdf/render/style-converter.d.ts +4 -0
- package/dist/browser/modules/pdf/render/style-converter.js +1 -1
- package/dist/browser/modules/pdf/types.d.ts +14 -1
- package/dist/browser/modules/stream/browser/readable.js +8 -2
- package/dist/browser/utils/crypto.browser.d.ts +64 -0
- package/dist/browser/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +91 -101
- package/dist/browser/utils/crypto.d.ts +97 -0
- package/dist/browser/utils/crypto.js +209 -0
- package/dist/cjs/modules/archive/compression/crc32.js +1 -1
- package/dist/cjs/modules/archive/crypto/aes.js +2 -23
- package/dist/cjs/modules/archive/crypto/index.js +3 -1
- package/dist/cjs/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/cjs/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/cjs/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/cjs/modules/excel/utils/encryptor.js +4 -10
- package/dist/cjs/modules/pdf/builder/document-builder.js +1532 -0
- package/dist/cjs/modules/pdf/builder/form-appearance.js +145 -0
- package/dist/cjs/modules/pdf/builder/image-utils.js +135 -0
- package/dist/cjs/modules/pdf/builder/pdf-editor.js +1612 -0
- package/dist/cjs/modules/pdf/builder/resource-merger.js +263 -0
- package/dist/cjs/modules/pdf/core/digital-signature.js +667 -0
- package/dist/cjs/modules/pdf/core/encryption.js +8 -7
- package/dist/cjs/modules/pdf/core/pdf-object.js +38 -0
- package/dist/cjs/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/cjs/modules/pdf/core/pdf-writer.js +272 -6
- package/dist/cjs/modules/pdf/core/pdfa.js +266 -0
- package/dist/cjs/modules/pdf/index.js +19 -1
- package/dist/cjs/modules/pdf/reader/bookmark-extractor.js +327 -0
- package/dist/cjs/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/cjs/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/cjs/modules/pdf/reader/table-extractor.js +368 -0
- package/dist/cjs/modules/pdf/render/layout-engine.js +113 -4
- package/dist/cjs/modules/pdf/render/page-renderer.js +63 -105
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +3 -62
- package/dist/cjs/modules/pdf/render/style-converter.js +1 -0
- package/dist/cjs/modules/stream/browser/readable.js +8 -2
- package/dist/cjs/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +95 -102
- package/dist/cjs/utils/crypto.js +228 -0
- package/dist/esm/modules/archive/compression/crc32.js +1 -1
- package/dist/esm/modules/archive/crypto/aes.js +1 -20
- package/dist/esm/modules/archive/crypto/index.js +3 -1
- package/dist/esm/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/esm/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/esm/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/esm/modules/excel/utils/encryptor.js +4 -7
- package/dist/esm/modules/pdf/builder/document-builder.js +1493 -0
- package/dist/esm/modules/pdf/builder/form-appearance.js +140 -0
- package/dist/esm/modules/pdf/builder/image-utils.js +129 -0
- package/dist/esm/modules/pdf/builder/pdf-editor.js +1574 -0
- package/dist/esm/modules/pdf/builder/resource-merger.js +258 -0
- package/dist/esm/modules/pdf/core/digital-signature.js +659 -0
- package/dist/esm/modules/pdf/core/encryption.js +8 -7
- package/dist/esm/modules/pdf/core/pdf-object.js +38 -0
- package/dist/esm/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/esm/modules/pdf/core/pdf-writer.js +271 -6
- package/dist/esm/modules/pdf/core/pdfa.js +261 -0
- package/dist/esm/modules/pdf/index.js +9 -0
- package/dist/esm/modules/pdf/reader/bookmark-extractor.js +324 -0
- package/dist/esm/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/esm/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/esm/modules/pdf/reader/table-extractor.js +365 -0
- package/dist/esm/modules/pdf/render/layout-engine.js +112 -5
- package/dist/esm/modules/pdf/render/page-renderer.js +62 -103
- package/dist/esm/modules/pdf/render/pdf-exporter.js +2 -61
- package/dist/esm/modules/pdf/render/style-converter.js +1 -1
- package/dist/esm/modules/stream/browser/readable.js +8 -2
- package/dist/esm/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +91 -101
- package/dist/esm/utils/crypto.js +209 -0
- package/dist/iife/excelts.iife.js +1248 -1074
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +53 -54
- package/dist/types/modules/archive/crypto/aes.d.ts +0 -8
- package/dist/types/modules/archive/crypto/index.d.ts +2 -1
- package/dist/types/modules/csv/parse/row-processor.d.ts +1 -1
- package/dist/types/modules/excel/utils/encryptor.browser.d.ts +4 -5
- package/dist/types/modules/excel/utils/encryptor.d.ts +1 -1
- package/dist/types/modules/pdf/builder/document-builder.d.ts +517 -0
- package/dist/types/modules/pdf/builder/form-appearance.d.ts +56 -0
- package/dist/types/modules/pdf/builder/image-utils.d.ts +39 -0
- package/dist/types/modules/pdf/builder/pdf-editor.d.ts +230 -0
- package/dist/types/modules/pdf/builder/resource-merger.d.ts +41 -0
- package/dist/types/modules/pdf/core/digital-signature.d.ts +109 -0
- package/dist/types/modules/pdf/core/pdf-object.d.ts +11 -0
- package/dist/types/modules/pdf/core/pdf-stream.d.ts +32 -0
- package/dist/types/modules/pdf/core/pdf-writer.d.ts +55 -1
- package/dist/types/modules/pdf/core/pdfa.d.ts +62 -0
- package/dist/types/modules/pdf/index.d.ts +11 -0
- package/dist/types/modules/pdf/reader/bookmark-extractor.d.ts +35 -0
- package/dist/types/modules/pdf/reader/pdf-reader.d.ts +17 -0
- package/dist/types/modules/pdf/reader/table-extractor.d.ts +69 -0
- package/dist/types/modules/pdf/render/layout-engine.d.ts +21 -1
- package/dist/types/modules/pdf/render/page-renderer.d.ts +2 -9
- package/dist/types/modules/pdf/render/style-converter.d.ts +4 -0
- package/dist/types/modules/pdf/types.d.ts +14 -1
- package/dist/types/utils/crypto.browser.d.ts +64 -0
- package/dist/types/utils/crypto.d.ts +97 -0
- package/package.json +110 -111
- package/dist/browser/modules/pdf/core/crypto.d.ts +0 -65
- package/dist/types/modules/pdf/core/crypto.d.ts +0 -65
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF resource dictionary merger — structured merging at the parsed object level.
|
|
3
|
+
*
|
|
4
|
+
* Instead of manipulating serialized PDF dict strings directly, this module
|
|
5
|
+
* parses resource dicts into a structured `PdfResourceDict` (nested Maps),
|
|
6
|
+
* merges them, and serializes back to PDF dict strings.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Structured representation of a PDF resource dictionary.
|
|
10
|
+
*
|
|
11
|
+
* Outer key: category name (e.g. `Font`, `XObject`, `ExtGState`, `ColorSpace`,
|
|
12
|
+
* `Pattern`, `Shading`, `Properties`).
|
|
13
|
+
* Inner key: resource name (e.g. `F1`, `Im1`, `GS0`).
|
|
14
|
+
* Value: serialized ref or value string (e.g. `3 0 R`, `/DeviceRGB`).
|
|
15
|
+
*
|
|
16
|
+
* Categories whose value is not a sub-dict (e.g. `/ProcSet [/PDF /Text]`)
|
|
17
|
+
* are stored with an empty inner key `""` mapping to the raw value string.
|
|
18
|
+
*/
|
|
19
|
+
export type PdfResourceDict = Map<string, Map<string, string>>;
|
|
20
|
+
/**
|
|
21
|
+
* Merge two parsed resource dicts. For sub-dict categories, entries are
|
|
22
|
+
* combined; overlay entries win on name collision. For non-sub-dict categories,
|
|
23
|
+
* the overlay value replaces the original.
|
|
24
|
+
*/
|
|
25
|
+
export declare function mergeResourceDicts(original: PdfResourceDict, overlay: PdfResourceDict): PdfResourceDict;
|
|
26
|
+
/**
|
|
27
|
+
* Parse a serialized PDF resource dict string into a structured `PdfResourceDict`.
|
|
28
|
+
*
|
|
29
|
+
* Handles top-level entries like:
|
|
30
|
+
* ```
|
|
31
|
+
* << /Font << /F1 3 0 R /F2 5 0 R >> /XObject << /Im1 7 0 R >> /ProcSet [/PDF /Text] >>
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Sub-dict categories (`Font`, `XObject`, etc.) are parsed into inner name→value maps.
|
|
35
|
+
* Other categories are stored with a single entry keyed by `""`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseResourceDict(dictStr: string): PdfResourceDict;
|
|
38
|
+
/**
|
|
39
|
+
* Serialize a structured `PdfResourceDict` back to a PDF dict string.
|
|
40
|
+
*/
|
|
41
|
+
export declare function serializeResourceDict(dict: PdfResourceDict): string;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF resource dictionary merger — structured merging at the parsed object level.
|
|
3
|
+
*
|
|
4
|
+
* Instead of manipulating serialized PDF dict strings directly, this module
|
|
5
|
+
* parses resource dicts into a structured `PdfResourceDict` (nested Maps),
|
|
6
|
+
* merges them, and serializes back to PDF dict strings.
|
|
7
|
+
*/
|
|
8
|
+
// Known resource categories that contain sub-dicts
|
|
9
|
+
const SUB_DICT_CATEGORIES = new Set([
|
|
10
|
+
"Font",
|
|
11
|
+
"XObject",
|
|
12
|
+
"ExtGState",
|
|
13
|
+
"ColorSpace",
|
|
14
|
+
"Pattern",
|
|
15
|
+
"Shading",
|
|
16
|
+
"Properties"
|
|
17
|
+
]);
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Merge
|
|
20
|
+
// =============================================================================
|
|
21
|
+
/**
|
|
22
|
+
* Merge two parsed resource dicts. For sub-dict categories, entries are
|
|
23
|
+
* combined; overlay entries win on name collision. For non-sub-dict categories,
|
|
24
|
+
* the overlay value replaces the original.
|
|
25
|
+
*/
|
|
26
|
+
export function mergeResourceDicts(original, overlay) {
|
|
27
|
+
const merged = new Map();
|
|
28
|
+
// Copy all original entries (deep-copy inner maps)
|
|
29
|
+
for (const [category, innerMap] of original) {
|
|
30
|
+
merged.set(category, new Map(innerMap));
|
|
31
|
+
}
|
|
32
|
+
// Merge overlay entries
|
|
33
|
+
for (const [category, overlayInner] of overlay) {
|
|
34
|
+
const existing = merged.get(category);
|
|
35
|
+
if (existing && SUB_DICT_CATEGORIES.has(category)) {
|
|
36
|
+
// Both have this sub-dict category — merge inner entries (overlay wins)
|
|
37
|
+
for (const [name, value] of overlayInner) {
|
|
38
|
+
existing.set(name, value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (existing && !SUB_DICT_CATEGORIES.has(category)) {
|
|
42
|
+
// Non-sub-dict category — overlay replaces
|
|
43
|
+
merged.set(category, new Map(overlayInner));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Category only in overlay — copy it
|
|
47
|
+
merged.set(category, new Map(overlayInner));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return merged;
|
|
51
|
+
}
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// Parse
|
|
54
|
+
// =============================================================================
|
|
55
|
+
/**
|
|
56
|
+
* Parse a serialized PDF resource dict string into a structured `PdfResourceDict`.
|
|
57
|
+
*
|
|
58
|
+
* Handles top-level entries like:
|
|
59
|
+
* ```
|
|
60
|
+
* << /Font << /F1 3 0 R /F2 5 0 R >> /XObject << /Im1 7 0 R >> /ProcSet [/PDF /Text] >>
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* Sub-dict categories (`Font`, `XObject`, etc.) are parsed into inner name→value maps.
|
|
64
|
+
* Other categories are stored with a single entry keyed by `""`.
|
|
65
|
+
*/
|
|
66
|
+
export function parseResourceDict(dictStr) {
|
|
67
|
+
const result = new Map();
|
|
68
|
+
const trimmed = dictStr.trim();
|
|
69
|
+
if (!trimmed.startsWith("<<") || !trimmed.endsWith(">>")) {
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
// Strip the outer << >>
|
|
73
|
+
const inner = trimmed.slice(2, -2).trim();
|
|
74
|
+
if (!inner) {
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
// Parse top-level entries
|
|
78
|
+
const entries = parseDictEntries(inner);
|
|
79
|
+
for (const [key, value] of entries) {
|
|
80
|
+
const valueTrimmed = value.trim();
|
|
81
|
+
if (SUB_DICT_CATEGORIES.has(key) && valueTrimmed.startsWith("<<")) {
|
|
82
|
+
// Parse the sub-dict into name→value pairs
|
|
83
|
+
const subInner = valueTrimmed.slice(2, -2).trim();
|
|
84
|
+
const subEntries = parseDictEntries(subInner);
|
|
85
|
+
const innerMap = new Map();
|
|
86
|
+
for (const [subKey, subValue] of subEntries) {
|
|
87
|
+
innerMap.set(subKey, subValue.trim());
|
|
88
|
+
}
|
|
89
|
+
result.set(key, innerMap);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Non-sub-dict category — store with empty key
|
|
93
|
+
const innerMap = new Map();
|
|
94
|
+
innerMap.set("", valueTrimmed);
|
|
95
|
+
result.set(key, innerMap);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// Serialize
|
|
102
|
+
// =============================================================================
|
|
103
|
+
/**
|
|
104
|
+
* Serialize a structured `PdfResourceDict` back to a PDF dict string.
|
|
105
|
+
*/
|
|
106
|
+
export function serializeResourceDict(dict) {
|
|
107
|
+
if (dict.size === 0) {
|
|
108
|
+
return "<< >>";
|
|
109
|
+
}
|
|
110
|
+
const parts = ["<<"];
|
|
111
|
+
for (const [category, innerMap] of dict) {
|
|
112
|
+
if (SUB_DICT_CATEGORIES.has(category) && !(innerMap.size === 1 && innerMap.has(""))) {
|
|
113
|
+
// Sub-dict category — serialize as << /Name value ... >>
|
|
114
|
+
const subParts = ["<<"];
|
|
115
|
+
for (const [name, value] of innerMap) {
|
|
116
|
+
subParts.push(`/${name} ${value}`);
|
|
117
|
+
}
|
|
118
|
+
subParts.push(">>");
|
|
119
|
+
parts.push(`/${category} ${subParts.join(" ")}`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Non-sub-dict or single-value category
|
|
123
|
+
const value = innerMap.get("") ?? "";
|
|
124
|
+
if (value) {
|
|
125
|
+
parts.push(`/${category} ${value}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
parts.push(">>");
|
|
130
|
+
return parts.join(" ");
|
|
131
|
+
}
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// Internal: Dict Entry Parser
|
|
134
|
+
// =============================================================================
|
|
135
|
+
/**
|
|
136
|
+
* Parse the inner content of a `<< ... >>` dict (with outer delimiters already
|
|
137
|
+
* stripped) into key→value pairs. Handles nested `<< >>` and `[ ]` by depth
|
|
138
|
+
* counting.
|
|
139
|
+
*/
|
|
140
|
+
function parseDictEntries(inner) {
|
|
141
|
+
const entries = new Map();
|
|
142
|
+
let i = 0;
|
|
143
|
+
while (i < inner.length) {
|
|
144
|
+
// Skip whitespace
|
|
145
|
+
while (i < inner.length && isWhitespace(inner[i])) {
|
|
146
|
+
i++;
|
|
147
|
+
}
|
|
148
|
+
if (i >= inner.length) {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
// Expect a name: /KeyName
|
|
152
|
+
if (inner[i] !== "/") {
|
|
153
|
+
break; // malformed — bail
|
|
154
|
+
}
|
|
155
|
+
i++; // skip '/'
|
|
156
|
+
const nameStart = i;
|
|
157
|
+
while (i < inner.length && !isDelimiterOrWhitespace(inner[i])) {
|
|
158
|
+
i++;
|
|
159
|
+
}
|
|
160
|
+
const key = inner.slice(nameStart, i);
|
|
161
|
+
// Skip whitespace
|
|
162
|
+
while (i < inner.length && isWhitespace(inner[i])) {
|
|
163
|
+
i++;
|
|
164
|
+
}
|
|
165
|
+
// Read the value
|
|
166
|
+
const valueStart = i;
|
|
167
|
+
if (i < inner.length && inner[i] === "<" && i + 1 < inner.length && inner[i + 1] === "<") {
|
|
168
|
+
// Sub-dict: count nested << >>
|
|
169
|
+
i = skipNestedDict(inner, i);
|
|
170
|
+
}
|
|
171
|
+
else if (i < inner.length && inner[i] === "[") {
|
|
172
|
+
// Array: find matching ]
|
|
173
|
+
i = skipNestedArray(inner, i);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// Token(s) — read until next top-level '/' or end
|
|
177
|
+
i = skipTokenValue(inner, i, valueStart);
|
|
178
|
+
}
|
|
179
|
+
const value = inner.slice(valueStart, i).trim();
|
|
180
|
+
if (key && value) {
|
|
181
|
+
entries.set(key, value);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return entries;
|
|
185
|
+
}
|
|
186
|
+
function isWhitespace(ch) {
|
|
187
|
+
return ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f";
|
|
188
|
+
}
|
|
189
|
+
function isDelimiterOrWhitespace(ch) {
|
|
190
|
+
return (isWhitespace(ch) ||
|
|
191
|
+
ch === "/" ||
|
|
192
|
+
ch === "<" ||
|
|
193
|
+
ch === ">" ||
|
|
194
|
+
ch === "[" ||
|
|
195
|
+
ch === "]" ||
|
|
196
|
+
ch === "(" ||
|
|
197
|
+
ch === ")");
|
|
198
|
+
}
|
|
199
|
+
/** Skip over a `<< ... >>` block, handling nesting. Returns position after `>>`. */
|
|
200
|
+
function skipNestedDict(str, start) {
|
|
201
|
+
let i = start;
|
|
202
|
+
let depth = 0;
|
|
203
|
+
while (i < str.length) {
|
|
204
|
+
if (str[i] === "<" && i + 1 < str.length && str[i + 1] === "<") {
|
|
205
|
+
depth++;
|
|
206
|
+
i += 2;
|
|
207
|
+
}
|
|
208
|
+
else if (str[i] === ">" && i + 1 < str.length && str[i + 1] === ">") {
|
|
209
|
+
depth--;
|
|
210
|
+
i += 2;
|
|
211
|
+
if (depth === 0) {
|
|
212
|
+
return i;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
i++;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return i;
|
|
220
|
+
}
|
|
221
|
+
/** Skip over a `[ ... ]` block, handling nesting. Returns position after `]`. */
|
|
222
|
+
function skipNestedArray(str, start) {
|
|
223
|
+
let i = start;
|
|
224
|
+
let depth = 0;
|
|
225
|
+
while (i < str.length) {
|
|
226
|
+
if (str[i] === "[") {
|
|
227
|
+
depth++;
|
|
228
|
+
}
|
|
229
|
+
else if (str[i] === "]") {
|
|
230
|
+
depth--;
|
|
231
|
+
if (depth === 0) {
|
|
232
|
+
i++;
|
|
233
|
+
return i;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
i++;
|
|
237
|
+
}
|
|
238
|
+
return i;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Skip a token value (ref, name, number, etc.) that is not a dict or array.
|
|
242
|
+
* Stops at the next top-level `/` preceded by whitespace, or at a `<<` pair.
|
|
243
|
+
*/
|
|
244
|
+
function skipTokenValue(str, i, valueStart) {
|
|
245
|
+
while (i < str.length) {
|
|
246
|
+
if (str[i] === "/" && i > valueStart) {
|
|
247
|
+
// Check if this '/' starts a new key (preceded by whitespace)
|
|
248
|
+
if (isWhitespace(str[i - 1])) {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (str[i] === "<" && i + 1 < str.length && str[i + 1] === "<") {
|
|
253
|
+
break; // shouldn't happen at this level, but be safe
|
|
254
|
+
}
|
|
255
|
+
i++;
|
|
256
|
+
}
|
|
257
|
+
return i;
|
|
258
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF digital signature — verification and creation.
|
|
3
|
+
*
|
|
4
|
+
* Implements:
|
|
5
|
+
* - ASN.1 DER decode/encode (shared codec)
|
|
6
|
+
* - PKCS#7 / CMS SignedData parse and build
|
|
7
|
+
* - X.509 certificate public key extraction
|
|
8
|
+
* - PDF /ByteRange extraction and hash computation
|
|
9
|
+
* - Signature verification (RSA PKCS#1 v1.5 + SHA-256)
|
|
10
|
+
* - Signature creation (with ByteRange placeholder/backfill)
|
|
11
|
+
*
|
|
12
|
+
* Uses platform-native RSA via `@utils/crypto` (node:crypto on Node,
|
|
13
|
+
* Web Crypto API in browsers).
|
|
14
|
+
*
|
|
15
|
+
* @see RFC 5652 — CMS (Cryptographic Message Syntax)
|
|
16
|
+
* @see ITU-T X.690 — ASN.1 DER encoding rules
|
|
17
|
+
* @see ISO 32000-2:2020 §12.8 — Digital Signatures in PDF
|
|
18
|
+
*/
|
|
19
|
+
/** Parsed ASN.1 node. */
|
|
20
|
+
export interface Asn1Node {
|
|
21
|
+
tag: number;
|
|
22
|
+
/** Raw bytes of the value (for primitive types). */
|
|
23
|
+
bytes: Uint8Array;
|
|
24
|
+
/** Child nodes (for constructed types). */
|
|
25
|
+
children: Asn1Node[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse ASN.1 DER data from the root.
|
|
29
|
+
*/
|
|
30
|
+
export declare function asn1Parse(data: Uint8Array): Asn1Node;
|
|
31
|
+
/** Parsed CMS SignedData info for verification. */
|
|
32
|
+
export interface CmsSignedData {
|
|
33
|
+
/** The signer's certificate (DER). */
|
|
34
|
+
certificate: Uint8Array;
|
|
35
|
+
/** The signature value. */
|
|
36
|
+
signature: Uint8Array;
|
|
37
|
+
/** The digest algorithm OID. */
|
|
38
|
+
digestAlgorithmOid: string;
|
|
39
|
+
/** The signed attributes (DER-encoded SET for hash computation). */
|
|
40
|
+
signedAttrsRaw: Uint8Array;
|
|
41
|
+
/** The message digest from signed attributes. */
|
|
42
|
+
messageDigest: Uint8Array;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse a PKCS#7 / CMS SignedData structure from DER bytes.
|
|
46
|
+
* Extracts the first signer's info for verification.
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseCmsSignedData(derBytes: Uint8Array): CmsSignedData;
|
|
49
|
+
/** Options for building a CMS SignedData for PDF signing. */
|
|
50
|
+
export interface SignOptions {
|
|
51
|
+
/** DER-encoded X.509 certificate. */
|
|
52
|
+
certificate: Uint8Array;
|
|
53
|
+
/** DER-encoded PKCS#8 private key. */
|
|
54
|
+
privateKey: Uint8Array;
|
|
55
|
+
/** The data to sign (the PDF byte ranges). */
|
|
56
|
+
data: Uint8Array;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build a CMS SignedData (PKCS#7) structure for a PDF signature.
|
|
60
|
+
*
|
|
61
|
+
* Uses SHA-256 for digest and RSA PKCS#1 v1.5 for signing.
|
|
62
|
+
* The signature is created over signed attributes that include
|
|
63
|
+
* the content-type, message-digest, and signing-time.
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildCmsSignedData(options: SignOptions): Promise<Uint8Array>;
|
|
66
|
+
/** Result of verifying a PDF signature. */
|
|
67
|
+
export interface SignatureVerificationResult {
|
|
68
|
+
/** Whether the signature is cryptographically valid. */
|
|
69
|
+
valid: boolean;
|
|
70
|
+
/** Whether the signed byte ranges cover the entire file (no unsigned gaps). */
|
|
71
|
+
coversWholeFile: boolean;
|
|
72
|
+
/** Digest algorithm used. */
|
|
73
|
+
digestAlgorithm: string;
|
|
74
|
+
/** Reason for failure, if any. */
|
|
75
|
+
reason?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Verify a digital signature in a PDF document.
|
|
79
|
+
*
|
|
80
|
+
* @param pdfData - The complete PDF file bytes
|
|
81
|
+
* @param signatureHex - The hex-encoded PKCS#7 signature from the /Contents field
|
|
82
|
+
* @param byteRange - The /ByteRange array [offset1, length1, offset2, length2]
|
|
83
|
+
*/
|
|
84
|
+
export declare function verifyPdfSignature(pdfData: Uint8Array, signatureHex: string, byteRange: [number, number, number, number]): Promise<SignatureVerificationResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Create a PDF signature dictionary string with a placeholder /Contents.
|
|
87
|
+
* Returns the dict string and the placeholder that will be replaced.
|
|
88
|
+
*
|
|
89
|
+
* @param signerName - Optional signer name for /Name field
|
|
90
|
+
* @param reason - Optional reason for /Reason field
|
|
91
|
+
*/
|
|
92
|
+
export declare function buildSignatureDictPlaceholder(options?: {
|
|
93
|
+
name?: string;
|
|
94
|
+
reason?: string;
|
|
95
|
+
location?: string;
|
|
96
|
+
contactInfo?: string;
|
|
97
|
+
}): {
|
|
98
|
+
dictString: string;
|
|
99
|
+
placeholder: string;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Patch a PDF with a real signature after the /ByteRange placeholder has been written.
|
|
103
|
+
*
|
|
104
|
+
* @param pdfBytes - The PDF bytes with placeholder /Contents and /ByteRange
|
|
105
|
+
* @param certificate - DER-encoded X.509 certificate
|
|
106
|
+
* @param privateKey - DER-encoded PKCS#8 private key
|
|
107
|
+
* @returns The signed PDF bytes
|
|
108
|
+
*/
|
|
109
|
+
export declare function signPdf(pdfBytes: Uint8Array, certificate: Uint8Array, privateKey: Uint8Array): Promise<Uint8Array>;
|