@f-o-t/pdf 0.4.1 → 0.5.1

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.
@@ -1,306 +1,10 @@
1
1
  // @bun
2
- // src/plugins/editing/parser.ts
3
- function findStartXref(pdfStr) {
4
- const idx = pdfStr.lastIndexOf("startxref");
5
- if (idx === -1)
6
- throw new Error("Cannot find startxref in PDF");
7
- const after = pdfStr.slice(idx + 9).trim().split(/[\r\n\s]/)[0];
8
- return parseInt(after, 10);
9
- }
10
- function parseTrailer(pdfStr) {
11
- const startxrefIdx = pdfStr.lastIndexOf("startxref");
12
- const trailerIdx = pdfStr.lastIndexOf("trailer");
13
- let dictStr;
14
- if (trailerIdx !== -1 && trailerIdx < startxrefIdx) {
15
- dictStr = pdfStr.slice(trailerIdx, startxrefIdx);
16
- } else {
17
- const xrefOffset = findStartXref(pdfStr);
18
- const xrefObjStr = pdfStr.slice(xrefOffset, xrefOffset + 4096);
19
- const dictStart = xrefObjStr.indexOf("<<");
20
- if (dictStart === -1) {
21
- throw new Error("Cannot find trailer or xref stream dictionary in PDF");
22
- }
23
- const dictEnd = findMatchingDictEnd(xrefObjStr, dictStart);
24
- if (dictEnd === -1) {
25
- throw new Error("Cannot find end of xref stream dictionary");
26
- }
27
- dictStr = xrefObjStr.slice(dictStart, dictEnd + 2);
28
- }
29
- const rootMatch = dictStr.match(/\/Root\s+(\d+)\s+\d+\s+R/);
30
- if (!rootMatch)
31
- throw new Error("Cannot find Root ref in trailer");
32
- const sizeMatch = dictStr.match(/\/Size\s+(\d+)/);
33
- if (!sizeMatch)
34
- throw new Error("Cannot find Size in trailer");
35
- const infoMatch = dictStr.match(/\/Info\s+(\d+)\s+\d+\s+R/);
36
- const prevMatch = dictStr.match(/\/Prev\s+(\d+)/);
37
- return {
38
- root: parseInt(rootMatch[1], 10),
39
- size: parseInt(sizeMatch[1], 10),
40
- info: infoMatch ? parseInt(infoMatch[1], 10) : null,
41
- prevXref: prevMatch ? parseInt(prevMatch[1], 10) : findStartXref(pdfStr)
42
- };
43
- }
44
- function buildObjectIndex(pdfStr) {
45
- const index = new Map;
46
- const regex = /(?:^|\s)(\d+)\s+0\s+obj/gm;
47
- let m;
48
- while ((m = regex.exec(pdfStr)) !== null) {
49
- const objNum = parseInt(m[1], 10);
50
- if (!index.has(objNum)) {
51
- index.set(objNum, m.index + m[0].length);
52
- }
53
- }
54
- return index;
55
- }
56
- function extractObjectDictContent(pdfStr, objNum, objIndex) {
57
- let searchStart;
58
- if (objIndex) {
59
- const pos = objIndex.get(objNum);
60
- if (pos === undefined) {
61
- throw new Error(`Cannot find object ${objNum} in PDF`);
62
- }
63
- searchStart = pos;
64
- } else {
65
- const objRegex = new RegExp(`(?:^|\\s)${objNum}\\s+0\\s+obj`, "m");
66
- const match = pdfStr.match(objRegex);
67
- if (!match || match.index === undefined) {
68
- throw new Error(`Cannot find object ${objNum} in PDF`);
69
- }
70
- searchStart = match.index + match[0].length;
71
- }
72
- const dictStart = pdfStr.indexOf("<<", searchStart);
73
- if (dictStart === -1 || dictStart > searchStart + 200) {
74
- throw new Error(`Cannot find dictionary start for object ${objNum}`);
75
- }
76
- const dictEnd = findMatchingDictEnd(pdfStr, dictStart);
77
- if (dictEnd === -1) {
78
- throw new Error(`Cannot find dictionary end for object ${objNum}`);
79
- }
80
- return pdfStr.slice(dictStart + 2, dictEnd);
81
- }
82
- function findPageObjectsIndexed(pdfStr, rootNum, objIndex) {
83
- const rootContent = extractObjectDictContent(pdfStr, rootNum, objIndex);
84
- const pagesMatch = rootContent.match(/\/Pages\s+(\d+)\s+\d+\s+R/);
85
- if (!pagesMatch)
86
- throw new Error("Cannot find Pages ref in Root catalog");
87
- const pagesNum = parseInt(pagesMatch[1], 10);
88
- return collectPageLeafsIndexed(pdfStr, pagesNum, new Set, objIndex);
89
- }
90
- function collectPageLeafsIndexed(pdfStr, objNum, visited, objIndex) {
91
- if (visited.has(objNum))
92
- return [];
93
- visited.add(objNum);
94
- const content = extractObjectDictContent(pdfStr, objNum, objIndex);
95
- const typeMatch = content.match(/\/Type\s+\/(\w+)/);
96
- if (typeMatch?.[1] === "Page") {
97
- return [objNum];
98
- }
99
- const kidsMatch = content.match(/\/Kids\s*\[([^\]]+)\]/);
100
- if (!kidsMatch) {
101
- return [objNum];
102
- }
103
- const refs = [];
104
- const refRegex = /(\d+)\s+\d+\s+R/g;
105
- let m;
106
- while ((m = refRegex.exec(kidsMatch[1])) !== null) {
107
- refs.push(parseInt(m[1], 10));
108
- }
109
- const pages = [];
110
- for (const ref of refs) {
111
- pages.push(...collectPageLeafsIndexed(pdfStr, ref, visited, objIndex));
112
- }
113
- return pages;
114
- }
115
- function getMediaBox(pdfStr, pageObjNum, objIndex) {
116
- const visited = new Set;
117
- let objNum = pageObjNum;
118
- while (objNum !== null && !visited.has(objNum)) {
119
- visited.add(objNum);
120
- const content = extractObjectDictContent(pdfStr, objNum, objIndex);
121
- const mediaBoxMatch = content.match(/\/MediaBox\s*\[\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s*\]/);
122
- if (mediaBoxMatch) {
123
- return [
124
- parseFloat(mediaBoxMatch[1]),
125
- parseFloat(mediaBoxMatch[2]),
126
- parseFloat(mediaBoxMatch[3]),
127
- parseFloat(mediaBoxMatch[4])
128
- ];
129
- }
130
- const parentMatch = content.match(/\/Parent\s+(\d+)\s+\d+\s+R/);
131
- objNum = parentMatch ? parseInt(parentMatch[1], 10) : null;
132
- }
133
- throw new Error(`Cannot find MediaBox for page object ${pageObjNum}`);
134
- }
135
- function parsePdfStructure(pdfStr) {
136
- const xrefOffset = findStartXref(pdfStr);
137
- const trailer = parseTrailer(pdfStr);
138
- const objIndex = buildObjectIndex(pdfStr);
139
- const rootContent = extractObjectDictContent(pdfStr, trailer.root, objIndex);
140
- const pagesMatch = rootContent.match(/\/Pages\s+(\d+)\s+\d+\s+R/);
141
- if (!pagesMatch)
142
- throw new Error("Cannot find Pages ref in Root catalog");
143
- const pagesNum = parseInt(pagesMatch[1], 10);
144
- const pageNums = findPageObjectsIndexed(pdfStr, trailer.root, objIndex);
145
- const pageDictContents = pageNums.map((pn) => extractObjectDictContent(pdfStr, pn, objIndex));
146
- return {
147
- xrefOffset,
148
- rootNum: trailer.root,
149
- infoNum: trailer.info,
150
- size: trailer.size,
151
- pagesNum,
152
- pageNums,
153
- rootDictContent: rootContent,
154
- pageDictContents,
155
- objIndex
156
- };
157
- }
158
- function findMatchingDictEnd(str, startPos) {
159
- let depth = 0;
160
- let i = startPos;
161
- while (i < str.length - 1) {
162
- if (str[i] === "(") {
163
- i++;
164
- while (i < str.length && str[i] !== ")") {
165
- if (str[i] === "\\")
166
- i++;
167
- i++;
168
- }
169
- i++;
170
- } else if (str[i] === "<" && str[i + 1] === "<") {
171
- depth++;
172
- i += 2;
173
- } else if (str[i] === ">" && str[i + 1] === ">") {
174
- depth--;
175
- if (depth === 0)
176
- return i;
177
- i += 2;
178
- } else {
179
- i++;
180
- }
181
- }
182
- return -1;
183
- }
184
- function findMatchingArrayEnd(str, startPos) {
185
- let depth = 0;
186
- let i = startPos;
187
- while (i < str.length) {
188
- if (str[i] === "(") {
189
- i++;
190
- while (i < str.length && str[i] !== ")") {
191
- if (str[i] === "\\")
192
- i++;
193
- i++;
194
- }
195
- i++;
196
- } else if (str[i] === "[") {
197
- depth++;
198
- i++;
199
- } else if (str[i] === "]") {
200
- depth--;
201
- if (depth === 0)
202
- return i;
203
- i++;
204
- } else {
205
- i++;
206
- }
207
- }
208
- return -1;
209
- }
210
- function parseResourcesDict(pageContent, pdfStr, objIndex) {
211
- const result = {};
212
- const inlineMatch = pageContent.match(/\/Resources\s*<</);
213
- if (inlineMatch) {
214
- const resIdx = pageContent.indexOf("/Resources");
215
- const resStart = pageContent.indexOf("<<", resIdx);
216
- const resEnd = findMatchingDictEnd(pageContent, resStart);
217
- if (resEnd === -1) {
218
- throw new Error("Cannot find end of Resources dictionary");
219
- }
220
- const resContent = pageContent.slice(resStart + 2, resEnd);
221
- return parseResourceEntries(resContent);
222
- }
223
- const refMatch = pageContent.match(/\/Resources\s+(\d+)\s+\d+\s+R/);
224
- if (refMatch) {
225
- const objNum = parseInt(refMatch[1], 10);
226
- const objContent = extractObjectDictContent(pdfStr, objNum, objIndex);
227
- return parseResourceEntries(objContent);
228
- }
229
- return result;
230
- }
231
- function mergeResourcesDicts(existing, additions) {
232
- const result = { ...existing };
233
- for (const [resType, addValue] of Object.entries(additions)) {
234
- if (!result[resType]) {
235
- result[resType] = addValue;
236
- continue;
237
- }
238
- const existingValue = result[resType];
239
- if (existingValue.startsWith("[")) {
240
- continue;
241
- }
242
- if (existingValue.startsWith("<<")) {
243
- result[resType] = mergeDictEntries(existingValue, addValue);
244
- } else {
245
- throw new Error(`Unexpected resource format for ${resType}: ${existingValue}`);
246
- }
247
- }
248
- return result;
249
- }
250
- function mergeDictEntries(existing, additions) {
251
- const existingEntries = extractDictEntries(existing);
252
- const additionEntries = extractDictEntries(additions);
253
- const merged = { ...existingEntries, ...additionEntries };
254
- const entries = Object.entries(merged).map(([name, ref]) => `${name} ${ref}`).join(" ");
255
- return `<< ${entries} >>`;
256
- }
257
- function extractDictEntries(dict) {
258
- const entries = {};
259
- const inner = dict.replace(/^<<\s*/, "").replace(/\s*>>$/, "");
260
- const regex = /(\/[^\s<>\[\]()\/]+)\s+(\d+\s+\d+\s+R)/g;
261
- let match;
262
- while ((match = regex.exec(inner)) !== null) {
263
- entries[match[1]] = match[2];
264
- }
265
- return entries;
266
- }
267
- function parseResourceEntries(content) {
268
- const result = {};
269
- const resourceTypes = [
270
- "/Font",
271
- "/XObject",
272
- "/ExtGState",
273
- "/ColorSpace",
274
- "/Pattern",
275
- "/Shading",
276
- "/ProcSet"
277
- ];
278
- for (const resType of resourceTypes) {
279
- const pattern = new RegExp(`${resType.replace(/\//g, "\\/")}\\s+([<\\[])`);
280
- const match = content.match(pattern);
281
- if (!match)
282
- continue;
283
- const idx = match.index;
284
- let valueStart = idx + resType.length;
285
- while (valueStart < content.length && /\s/.test(content[valueStart])) {
286
- valueStart++;
287
- }
288
- if (content[valueStart] === "<" && content[valueStart + 1] === "<") {
289
- const dictEnd = findMatchingDictEnd(content, valueStart);
290
- if (dictEnd === -1) {
291
- throw new Error(`Cannot find end of ${resType} dictionary`);
292
- }
293
- result[resType] = content.slice(valueStart, dictEnd + 2);
294
- } else if (content[valueStart] === "[") {
295
- const arrayEnd = findMatchingArrayEnd(content, valueStart);
296
- if (arrayEnd === -1) {
297
- throw new Error(`Cannot find end of ${resType} array`);
298
- }
299
- result[resType] = content.slice(valueStart, arrayEnd + 1);
300
- }
301
- }
302
- return result;
303
- }
2
+ import {
3
+ getMediaBox,
4
+ mergeResourcesDicts,
5
+ parsePdfStructure,
6
+ parseResourcesDict
7
+ } from "./index-ty7xfwkn.js";
304
8
 
305
9
  // src/plugins/editing/page.ts
306
10
  function toWinAnsi(text) {
@@ -372,6 +76,7 @@ class PdfPageImpl {
372
76
  originalDictContent;
373
77
  operators = [];
374
78
  imageRefs = new Map;
79
+ linkAnnotations = [];
375
80
  fontObjNum = 0;
376
81
  get dirty() {
377
82
  return this.operators.length > 0;
@@ -428,10 +133,30 @@ class PdfPageImpl {
428
133
  this.operators.push(`/${imgName} Do`);
429
134
  this.operators.push("Q");
430
135
  }
136
+ drawLink(text, url, options) {
137
+ this.drawText(text, options);
138
+ const fontSize = options.size ?? 12;
139
+ const estimatedWidth = text.length * fontSize * 0.5;
140
+ const { x, y } = options;
141
+ this.linkAnnotations.push({
142
+ x,
143
+ y: y - fontSize * 0.2,
144
+ width: estimatedWidth,
145
+ height: fontSize,
146
+ url
147
+ });
148
+ }
149
+ getLinkAnnotations() {
150
+ return [...this.linkAnnotations];
151
+ }
431
152
  buildContentStream() {
432
153
  const content = this.operators.join(`
433
154
  `);
434
- return new TextEncoder().encode(content);
155
+ const bytes = new Uint8Array(content.length);
156
+ for (let i = 0;i < content.length; i++) {
157
+ bytes[i] = content.charCodeAt(i);
158
+ }
159
+ return bytes;
435
160
  }
436
161
  getImageRefs() {
437
162
  return new Map(this.imageRefs);
@@ -614,6 +339,33 @@ class PdfDocumentImpl {
614
339
  streamData: prefixedData
615
340
  });
616
341
  }
342
+ const linkAnnotObjNums = new Map;
343
+ for (const page of this.pages) {
344
+ if (!page.dirty)
345
+ continue;
346
+ const links = page.getLinkAnnotations();
347
+ if (links.length === 0)
348
+ continue;
349
+ const objNums = [];
350
+ for (const link of links) {
351
+ const annotObjNum = currentNextObj++;
352
+ objNums.push(annotObjNum);
353
+ const rect = `${link.x} ${link.y} ${link.x + link.width} ${link.y + link.height}`;
354
+ objects.push({
355
+ objNum: annotObjNum,
356
+ content: [
357
+ "<< /Type /Annot",
358
+ "/Subtype /Link",
359
+ `/Rect [${rect}]`,
360
+ "/Border [0 0 0]",
361
+ `/A << /S /URI /URI ${pdfString(link.url)} >>`,
362
+ ">>"
363
+ ].join(`
364
+ `)
365
+ });
366
+ }
367
+ linkAnnotObjNums.set(page.pageObjNum, objNums);
368
+ }
617
369
  for (const page of this.pages) {
618
370
  if (!page.dirty && !this.hasImagesForPage(page))
619
371
  continue;
@@ -666,6 +418,17 @@ class PdfDocumentImpl {
666
418
  pageContent += `
667
419
  /Resources << ${resourceEntries} >>`;
668
420
  }
421
+ const annotRefs = linkAnnotObjNums.get(page.pageObjNum);
422
+ if (annotRefs && annotRefs.length > 0) {
423
+ const refsStr = annotRefs.map((n) => `${n} 0 R`).join(" ");
424
+ if (pageContent.includes("/Annots")) {
425
+ const bracketEnd = pageContent.indexOf("]", pageContent.indexOf("/Annots"));
426
+ pageContent = pageContent.slice(0, bracketEnd) + ` ${refsStr}` + pageContent.slice(bracketEnd);
427
+ } else {
428
+ pageContent += `
429
+ /Annots [${refsStr}]`;
430
+ }
431
+ }
669
432
  objects.push({
670
433
  objNum: page.pageObjNum,
671
434
  content: `<<${pageContent}
@@ -990,4 +753,4 @@ function countPdfPages(data) {
990
753
 
991
754
  export { PdfDocumentImpl, findByteRange, extractBytesToSign, embedSignature, loadPdf, countPdfPages };
992
755
 
993
- //# debugId=0B1C9458379061FF64756E2164756E21
756
+ //# debugId=2006205BBEFB17FE64756E2164756E21
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/plugins/editing/page.ts", "../src/plugins/editing/document.ts", "../src/plugins/editing/index.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * PdfPage implementation for the editing plugin\n *\n * Collects drawing operations as PDF content stream operators.\n * The accumulated operators are later serialised as a new content stream\n * object appended via incremental update.\n */\n\nimport type { PdfImage, PdfPage, TextOptions, RectOptions, ImageOptions } from \"./types.ts\";\n\nexport type LinkAnnotation = {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n\turl: string;\n};\n\n/**\n * Parse a hex colour string like \"#RRGGBB\" into normalised [r, g, b] values (0-1).\n * Returns null for invalid/missing input so callers can fall back to defaults.\n */\n/**\n * Convert a UTF-16 JS string to a WinAnsiEncoding-escaped PDF string.\n * Characters outside WinAnsi are replaced with '?'.\n */\nfunction toWinAnsi(text: string): string {\n\tlet out = \"\";\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst code = text.charCodeAt(i);\n\t\tlet byte: number;\n\n\t\tif (code < 0x80) {\n\t\t\tbyte = code;\n\t\t} else {\n\t\t\t// Map Unicode code points to WinAnsiEncoding bytes\n\t\t\tbyte = UNICODE_TO_WIN_ANSI.get(code) ?? 0x3f; // '?'\n\t\t}\n\n\t\tconst ch = String.fromCharCode(byte);\n\t\tif (ch === \"\\\\\" || ch === \"(\" || ch === \")\") {\n\t\t\tout += `\\\\${ch}`;\n\t\t} else {\n\t\t\tout += ch;\n\t\t}\n\t}\n\treturn out;\n}\n\n/**\n * Mapping from Unicode code points to WinAnsiEncoding byte values\n * for characters in the 0x80-0xFF range that differ from Unicode.\n */\nconst UNICODE_TO_WIN_ANSI = new Map<number, number>([\n\t// 0x80-0x9F range (WinAnsi-specific mappings)\n\t[0x20ac, 0x80], // €\n\t[0x201a, 0x82], // ‚\n\t[0x0192, 0x83], // ƒ\n\t[0x201e, 0x84], // „\n\t[0x2026, 0x85], // …\n\t[0x2020, 0x86], // †\n\t[0x2021, 0x87], // ‡\n\t[0x02c6, 0x88], // ˆ\n\t[0x2030, 0x89], // ‰\n\t[0x0160, 0x8a], // Š\n\t[0x2039, 0x8b], // ‹\n\t[0x0152, 0x8c], // Œ\n\t[0x017d, 0x8e], // Ž\n\t[0x2018, 0x91], // '\n\t[0x2019, 0x92], // '\n\t[0x201c, 0x93], // \"\n\t[0x201d, 0x94], // \"\n\t[0x2022, 0x95], // •\n\t[0x2013, 0x96], // –\n\t[0x2014, 0x97], // —\n\t[0x02dc, 0x98], // ˜\n\t[0x2122, 0x99], // ™\n\t[0x0161, 0x9a], // š\n\t[0x203a, 0x9b], // ›\n\t[0x0153, 0x9c], // œ\n\t[0x017e, 0x9e], // ž\n\t[0x0178, 0x9f], // Ÿ\n\t// 0xA0-0xFF: Unicode and WinAnsi are identical\n\t...Array.from({ length: 96 }, (_, i) => [0xa0 + i, 0xa0 + i] as [number, number]),\n]);\n\nfunction parseHexColor(hex: string | undefined): [number, number, number] | null {\n\tif (!hex) return null;\n\tconst m = hex.match(/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/);\n\tif (!m) return null;\n\treturn [\n\t\tparseInt(m[1]!, 16) / 255,\n\t\tparseInt(m[2]!, 16) / 255,\n\t\tparseInt(m[3]!, 16) / 255,\n\t];\n}\n\nexport class PdfPageImpl implements PdfPage {\n\treadonly width: number;\n\treadonly height: number;\n\n\t/** The existing page object number in the original PDF */\n\treadonly pageObjNum: number;\n\t/** The raw dictionary content string of the original page */\n\treadonly originalDictContent: string;\n\n\t/** Accumulated content-stream operators added by draw* methods */\n\tprivate operators: string[] = [];\n\n\t/** Images referenced by drawImage (name -> obj num) */\n\tprivate imageRefs: Map<string, number> = new Map();\n\n\t/** Link annotations recorded by drawLink */\n\tprivate linkAnnotations: LinkAnnotation[] = [];\n\n\t/** Font object number allocated by the document (set externally) */\n\tfontObjNum = 0;\n\n\t/** Whether any drawing operations have been recorded */\n\tget dirty(): boolean {\n\t\treturn this.operators.length > 0;\n\t}\n\n\tconstructor(\n\t\tpageObjNum: number,\n\t\twidth: number,\n\t\theight: number,\n\t\toriginalDictContent: string,\n\t) {\n\t\tthis.pageObjNum = pageObjNum;\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.originalDictContent = originalDictContent;\n\t}\n\n\t/**\n\t * Draw text on the page using Helvetica\n\t */\n\tdrawText(text: string, options: TextOptions): void {\n\t\tconst { x, y, size = 12, color } = options;\n\t\tconst rgb = parseHexColor(color);\n\n\t\tif (rgb) {\n\t\t\tthis.operators.push(`${rgb[0].toFixed(3)} ${rgb[1].toFixed(3)} ${rgb[2].toFixed(3)} rg`);\n\t\t}\n\n\t\t// Encode text as WinAnsiEncoding and escape special PDF string characters\n\t\tconst encoded = toWinAnsi(text);\n\n\t\tthis.operators.push(\"BT\");\n\t\tthis.operators.push(`/F1 ${size} Tf`);\n\t\tthis.operators.push(`${x} ${y} Td`);\n\t\tthis.operators.push(`(${encoded}) Tj`);\n\t\tthis.operators.push(\"ET\");\n\t}\n\n\t/**\n\t * Draw a rectangle on the page\n\t */\n\tdrawRectangle(options: RectOptions): void {\n\t\tconst { x, y, width, height, color, borderColor, borderWidth } = options;\n\n\t\tconst fillRgb = parseHexColor(color);\n\t\tconst strokeRgb = parseHexColor(borderColor);\n\n\t\tif (fillRgb) {\n\t\t\tthis.operators.push(`${fillRgb[0].toFixed(3)} ${fillRgb[1].toFixed(3)} ${fillRgb[2].toFixed(3)} rg`);\n\t\t}\n\t\tif (strokeRgb) {\n\t\t\tthis.operators.push(`${strokeRgb[0].toFixed(3)} ${strokeRgb[1].toFixed(3)} ${strokeRgb[2].toFixed(3)} RG`);\n\t\t}\n\t\tif (borderWidth !== undefined) {\n\t\t\tthis.operators.push(`${borderWidth} w`);\n\t\t}\n\n\t\tthis.operators.push(`${x} ${y} ${width} ${height} re`);\n\n\t\tif (fillRgb && strokeRgb) {\n\t\t\tthis.operators.push(\"B\"); // fill and stroke\n\t\t} else if (fillRgb) {\n\t\t\tthis.operators.push(\"f\"); // fill only\n\t\t} else if (strokeRgb) {\n\t\t\tthis.operators.push(\"S\"); // stroke only\n\t\t} else {\n\t\t\tthis.operators.push(\"f\"); // default: fill with current colour (black)\n\t\t}\n\t}\n\n\t/**\n\t * Draw an embedded image on the page\n\t */\n\tdrawImage(image: PdfImage, options: ImageOptions): void {\n\t\tconst { x, y, width, height } = options;\n\t\tconst imgName = `Im${image.objectNumber}`;\n\t\tthis.imageRefs.set(imgName, image.objectNumber);\n\n\t\tthis.operators.push(\"q\");\n\t\tthis.operators.push(`${width} 0 0 ${height} ${x} ${y} cm`);\n\t\tthis.operators.push(`/${imgName} Do`);\n\t\tthis.operators.push(\"Q\");\n\t}\n\n\t/**\n\t * Draw text as a clickable hyperlink\n\t */\n\tdrawLink(text: string, url: string, options: TextOptions): void {\n\t\t// Draw the text visually (same as drawText)\n\t\tthis.drawText(text, options);\n\n\t\t// Estimate text width: approximate Helvetica character width as ~0.5 * fontSize\n\t\tconst fontSize = options.size ?? 12;\n\t\tconst estimatedWidth = text.length * fontSize * 0.5;\n\t\tconst { x, y } = options;\n\n\t\tthis.linkAnnotations.push({\n\t\t\tx,\n\t\t\ty: y - fontSize * 0.2, // slight descent\n\t\t\twidth: estimatedWidth,\n\t\t\theight: fontSize,\n\t\t\turl,\n\t\t});\n\t}\n\n\t/**\n\t * Get link annotations recorded by drawLink calls\n\t */\n\tgetLinkAnnotations(): LinkAnnotation[] {\n\t\treturn [...this.linkAnnotations];\n\t}\n\n\t/**\n\t * Build the content stream bytes for the accumulated operators\n\t */\n\tbuildContentStream(): Uint8Array {\n\t\tconst content = this.operators.join(\"\\n\");\n\t\t// Write raw bytes — toWinAnsi already produced byte values in the\n\t\t// 0x00-0xFF range. TextEncoder would re-encode bytes >= 0x80 as\n\t\t// multi-byte UTF-8 sequences, corrupting WinAnsi characters like \"á\".\n\t\tconst bytes = new Uint8Array(content.length);\n\t\tfor (let i = 0; i < content.length; i++) {\n\t\t\tbytes[i] = content.charCodeAt(i);\n\t\t}\n\t\treturn bytes;\n\t}\n\n\t/**\n\t * Get image references used in drawing operations\n\t */\n\tgetImageRefs(): Map<string, number> {\n\t\treturn new Map(this.imageRefs);\n\t}\n}\n",
6
+ "/**\n * PdfDocument implementation for the editing plugin\n *\n * Manages loading an existing PDF, tracking modifications, and producing an\n * incremental update (appended after the original %%EOF) that adds or\n * overrides objects without rewriting the original content.\n */\n\nimport {\n\tparsePdfStructure,\n\tgetMediaBox,\n\textractObjectDictContent,\n\tparseResourcesDict,\n\tmergeResourcesDicts,\n} from \"./parser.ts\";\nimport { PdfPageImpl } from \"./page.ts\";\nimport type {\n\tPdfDocument,\n\tPdfImage,\n\tPdfPage,\n\tSignaturePlaceholderOptions,\n} from \"./types.ts\";\n\nconst BYTE_RANGE_PLACEHOLDER = \"0 0000000000 0000000000 0000000000\";\nconst DEFAULT_SIGNATURE_LENGTH = 16384;\n\nconst latin1Encoder = new TextEncoder(); // UTF-8 but we only feed ASCII/latin1-safe chars\nconst latin1Decoder = new TextDecoder(\"latin1\");\n\n// Pre-encoded byte patterns for PDF structure search (avoids per-call allocation)\nconst CONTENTS_MARKER = latin1Encoder.encode(\"/Contents <\");\nconst BYTE_RANGE_MARKER = latin1Encoder.encode(\"/ByteRange [\");\n\n/**\n * Search for the last occurrence of `pattern` inside `data`.\n * Returns the byte offset of the first byte of the match, or -1.\n */\nfunction findLastBytes(data: Uint8Array, pattern: Uint8Array): number {\n\touter: for (let i = data.length - pattern.length; i >= 0; i--) {\n\t\tfor (let j = 0; j < pattern.length; j++) {\n\t\t\tif (data[i + j] !== pattern[j]) continue outer;\n\t\t}\n\t\treturn i;\n\t}\n\treturn -1;\n}\n\n/**\n * Parse PNG IHDR to extract width, height, bit depth, and colour type.\n */\nfunction parsePngIhdr(data: Uint8Array): {\n\twidth: number;\n\theight: number;\n\tbitDepth: number;\n\tcolorType: number;\n} {\n\t// PNG signature: 8 bytes, then first chunk is IHDR\n\tconst sig = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\tfor (let i = 0; i < sig.length; i++) {\n\t\tif (data[i] !== sig[i]) throw new Error(\"Not a valid PNG file\");\n\t}\n\n\t// IHDR chunk starts at offset 8\n\t// 4 bytes length + 4 bytes \"IHDR\" + 13 bytes data\n\tconst view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\tconst chunkType = latin1Decoder.decode(data.slice(12, 16));\n\tif (chunkType !== \"IHDR\") throw new Error(\"First PNG chunk is not IHDR\");\n\n\treturn {\n\t\twidth: view.getUint32(16),\n\t\theight: view.getUint32(20),\n\t\tbitDepth: data[24]!,\n\t\tcolorType: data[25]!,\n\t};\n}\n\n/**\n * Extract and concatenate all IDAT chunk data from a PNG\n */\nfunction extractIdatData(data: Uint8Array): Uint8Array {\n\tconst view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\tconst chunks: Uint8Array[] = [];\n\tlet offset = 8; // skip PNG signature\n\n\twhile (offset < data.length) {\n\t\tconst chunkLen = view.getUint32(offset);\n\t\tconst chunkType = latin1Decoder.decode(data.slice(offset + 4, offset + 8));\n\n\t\tif (chunkType === \"IDAT\") {\n\t\t\tchunks.push(data.slice(offset + 8, offset + 8 + chunkLen));\n\t\t}\n\n\t\t// skip: length(4) + type(4) + data(chunkLen) + crc(4)\n\t\toffset += 12 + chunkLen;\n\t}\n\n\tif (chunks.length === 0) throw new Error(\"No IDAT chunks found in PNG\");\n\n\t// Concatenate all IDAT data\n\tconst totalLen = chunks.reduce((s, c) => s + c.length, 0);\n\tconst result = new Uint8Array(totalLen);\n\tlet pos = 0;\n\tfor (const chunk of chunks) {\n\t\tresult.set(chunk, pos);\n\t\tpos += chunk.length;\n\t}\n\treturn result;\n}\n\nexport class PdfDocumentImpl implements PdfDocument {\n\tprivate originalData: Uint8Array;\n\tprivate pdfStr: string; // latin1 string decoded ONCE — reused by all parser calls\n\tprivate structure: ReturnType<typeof parsePdfStructure>;\n\tprivate pages: PdfPageImpl[] = [];\n\tprivate nextObjNum: number;\n\tprivate fontObjNum: number;\n\tprivate embeddedImages: Array<{\n\t\tobjNum: number;\n\t\twidth: number;\n\t\theight: number;\n\t\tidatData: Uint8Array;\n\t\tcolorType: number;\n\t\tbitDepth: number;\n\t}> = [];\n\n\tconstructor(data: Uint8Array) {\n\t\tthis.originalData = data;\n\t\t// Decode the PDF bytes to a latin1 string exactly ONCE.\n\t\t// All parser helpers reuse this string so we never create redundant copies.\n\t\tthis.pdfStr = latin1Decoder.decode(data);\n\t\tthis.structure = parsePdfStructure(this.pdfStr);\n\n\t\t// Allocate a font object number right away (Helvetica)\n\t\tthis.nextObjNum = this.structure.size;\n\t\tthis.fontObjNum = this.nextObjNum++;\n\n\t\t// Build page objects (reuse pre-built object index for O(1) lookups)\n\t\tconst { objIndex } = this.structure;\n\t\tfor (let i = 0; i < this.structure.pageNums.length; i++) {\n\t\t\tconst pageNum = this.structure.pageNums[i]!;\n\t\t\tconst mediaBox = getMediaBox(this.pdfStr, pageNum, objIndex);\n\t\t\tconst width = mediaBox[2] - mediaBox[0];\n\t\t\tconst height = mediaBox[3] - mediaBox[1];\n\t\t\tconst dictContent = this.structure.pageDictContents[i]!;\n\t\t\tconst page = new PdfPageImpl(pageNum, width, height, dictContent);\n\t\t\tpage.fontObjNum = this.fontObjNum;\n\t\t\tthis.pages.push(page);\n\t\t}\n\t}\n\n\tget pageCount(): number {\n\t\treturn this.pages.length;\n\t}\n\n\tgetPage(index: number): PdfPage {\n\t\tif (index < 0 || index >= this.pages.length) {\n\t\t\tthrow new Error(\n\t\t\t\t`Page index ${index} out of range (0-${this.pages.length - 1})`,\n\t\t\t);\n\t\t}\n\t\treturn this.pages[index]!;\n\t}\n\n\tembedPng(data: Uint8Array): PdfImage {\n\t\tconst ihdr = parsePngIhdr(data);\n\t\tconst idatData = extractIdatData(data);\n\t\tconst objNum = this.nextObjNum++;\n\n\t\tthis.embeddedImages.push({\n\t\t\tobjNum,\n\t\t\twidth: ihdr.width,\n\t\t\theight: ihdr.height,\n\t\t\tidatData,\n\t\t\tcolorType: ihdr.colorType,\n\t\t\tbitDepth: ihdr.bitDepth,\n\t\t});\n\n\t\treturn {\n\t\t\tobjectNumber: objNum,\n\t\t\twidth: ihdr.width,\n\t\t\theight: ihdr.height,\n\t\t};\n\t}\n\n\t/**\n\t * Save the modified PDF using an incremental update\n\t */\n\tsave(): Uint8Array {\n\t\treturn this.buildIncrementalUpdate(false).pdf;\n\t}\n\n\t/**\n\t * Save with a signature placeholder for digital signing\n\t */\n\tsaveWithPlaceholder(options: SignaturePlaceholderOptions): {\n\t\tpdf: Uint8Array;\n\t\tbyteRange: [number, number, number, number];\n\t} {\n\t\treturn this.buildIncrementalUpdate(true, options);\n\t}\n\n\tprivate buildIncrementalUpdate(\n\t\twithSignature: boolean,\n\t\tsigOptions?: SignaturePlaceholderOptions,\n\t): { pdf: Uint8Array; byteRange: [number, number, number, number] } {\n\t\tconst objects: Array<{ objNum: number; content: string; streamData?: Uint8Array }> = [];\n\t\tlet currentNextObj = this.nextObjNum;\n\n\t\t// --- 1. Font object (Helvetica, always emitted if any page is dirty) ---\n\t\tconst anyDirty = this.pages.some((p) => p.dirty);\n\t\tif (anyDirty || this.embeddedImages.length > 0) {\n\t\t\tobjects.push({\n\t\t\t\tobjNum: this.fontObjNum,\n\t\t\t\tcontent: \"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>\",\n\t\t\t});\n\t\t}\n\n\t\t// --- 2. Embedded image XObjects ---\n\t\tfor (const img of this.embeddedImages) {\n\t\t\tconst colorSpace = img.colorType === 2 ? \"/DeviceRGB\" : img.colorType === 0 ? \"/DeviceGray\" : \"/DeviceRGB\";\n\t\t\tconst colors = img.colorType === 2 ? 3 : 1;\n\t\t\tconst bpc = img.bitDepth;\n\n\t\t\t// PNG IDAT data uses per-row filter bytes. Tell the PDF reader\n\t\t\t// via DecodeParms with Predictor 15 (PNG optimum prediction).\n\t\t\tobjects.push({\n\t\t\t\tobjNum: img.objNum,\n\t\t\t\tcontent: [\n\t\t\t\t\t\"<< /Type /XObject\",\n\t\t\t\t\t\"/Subtype /Image\",\n\t\t\t\t\t`/Width ${img.width}`,\n\t\t\t\t\t`/Height ${img.height}`,\n\t\t\t\t\t`/ColorSpace ${colorSpace}`,\n\t\t\t\t\t`/BitsPerComponent ${bpc}`,\n\t\t\t\t\t\"/Filter /FlateDecode\",\n\t\t\t\t\t`/DecodeParms << /Predictor 15 /Colors ${colors} /BitsPerComponent ${bpc} /Columns ${img.width} >>`,\n\t\t\t\t\t`/Length ${img.idatData.length}`,\n\t\t\t\t\t\">>\",\n\t\t\t\t].join(\"\\n\"),\n\t\t\t\tstreamData: img.idatData,\n\t\t\t});\n\t\t}\n\n\t\t// --- 3. Wrapper save-state streams + content streams for dirty pages ---\n\t\t// The original page content may modify the CTM (e.g. Y-axis flip for\n\t\t// top-left origin). We wrap the original content in q/Q so our new\n\t\t// drawing operators run with the default PDF coordinate system.\n\t\tconst wrapperStreamMap = new Map<number, number>(); // pageObjNum -> wrapperStreamObjNum\n\t\tconst contentStreamMap = new Map<number, number>(); // pageObjNum -> contentStreamObjNum\n\t\tfor (const page of this.pages) {\n\t\t\tif (!page.dirty) continue;\n\n\t\t\t// \"save state\" stream — placed BEFORE original content\n\t\t\tconst wrapperObjNum = currentNextObj++;\n\t\t\twrapperStreamMap.set(page.pageObjNum, wrapperObjNum);\n\t\t\tconst wrapperData = latin1Encoder.encode(\"q\");\n\t\t\tobjects.push({\n\t\t\t\tobjNum: wrapperObjNum,\n\t\t\t\tcontent: `<< /Length ${wrapperData.length} >>`,\n\t\t\t\tstreamData: wrapperData,\n\t\t\t});\n\n\t\t\t// Actual content stream — prefixed with Q to restore state\n\t\t\tconst contentObjNum = currentNextObj++;\n\t\t\tcontentStreamMap.set(page.pageObjNum, contentObjNum);\n\t\t\tconst pageStreamData = page.buildContentStream();\n\t\t\tconst prefixedData = new Uint8Array(2 + pageStreamData.length);\n\t\t\tprefixedData[0] = 0x51; // 'Q'\n\t\t\tprefixedData[1] = 0x0a; // '\\n'\n\t\t\tprefixedData.set(pageStreamData, 2);\n\t\t\tobjects.push({\n\t\t\t\tobjNum: contentObjNum,\n\t\t\t\tcontent: `<< /Length ${prefixedData.length} >>`,\n\t\t\t\tstreamData: prefixedData,\n\t\t\t});\n\t\t}\n\n\t\t// --- 3b. Link annotation objects for dirty pages ---\n\t\tconst linkAnnotObjNums = new Map<number, number[]>(); // pageObjNum -> annotation obj nums\n\t\tfor (const page of this.pages) {\n\t\t\tif (!page.dirty) continue;\n\t\t\tconst links = (page as PdfPageImpl).getLinkAnnotations();\n\t\t\tif (links.length === 0) continue;\n\t\t\tconst objNums: number[] = [];\n\t\t\tfor (const link of links) {\n\t\t\t\tconst annotObjNum = currentNextObj++;\n\t\t\t\tobjNums.push(annotObjNum);\n\t\t\t\tconst rect = `${link.x} ${link.y} ${link.x + link.width} ${link.y + link.height}`;\n\t\t\t\tobjects.push({\n\t\t\t\t\tobjNum: annotObjNum,\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\"<< /Type /Annot\",\n\t\t\t\t\t\t\"/Subtype /Link\",\n\t\t\t\t\t\t`/Rect [${rect}]`,\n\t\t\t\t\t\t\"/Border [0 0 0]\",\n\t\t\t\t\t\t`/A << /S /URI /URI ${pdfString(link.url)} >>`,\n\t\t\t\t\t\t\">>\",\n\t\t\t\t\t].join(\"\\n\"),\n\t\t\t\t});\n\t\t\t}\n\t\t\tlinkAnnotObjNums.set(page.pageObjNum, objNums);\n\t\t}\n\n\t\t// --- 4. Updated page dictionaries (add new content stream + font/image resources) ---\n\t\tfor (const page of this.pages) {\n\t\t\tif (!page.dirty && !this.hasImagesForPage(page)) continue;\n\n\t\t\tlet pageContent = page.originalDictContent;\n\n\t\t\t// Add or replace Contents reference if page is dirty\n\t\t\tif (page.dirty) {\n\t\t\t\tconst contentObjNum = contentStreamMap.get(page.pageObjNum)!;\n\t\t\t\tconst wrapperObjNum = wrapperStreamMap.get(page.pageObjNum)!;\n\n\t\t\t\tif (pageContent.match(/\\/Contents\\s/)) {\n\t\t\t\t\t// Replace existing Contents with an array: [wrapper, original, new]\n\t\t\t\t\t// The wrapper stream saves graphics state (q) before original content,\n\t\t\t\t\t// and the new stream restores it (Q) before our drawing operators.\n\t\t\t\t\tpageContent = pageContent.replace(\n\t\t\t\t\t\t/\\/Contents\\s+(\\d+\\s+\\d+\\s+R)/,\n\t\t\t\t\t\t`/Contents [${wrapperObjNum} 0 R $1 ${contentObjNum} 0 R]`,\n\t\t\t\t\t);\n\t\t\t\t\t// Also handle existing Contents arrays\n\t\t\t\t\tpageContent = pageContent.replace(\n\t\t\t\t\t\t/\\/Contents\\s*\\[([^\\]]+)\\]/,\n\t\t\t\t\t\t(match, inner) => {\n\t\t\t\t\t\t\t// If we already added our ref above, skip\n\t\t\t\t\t\t\tif (inner.includes(`${contentObjNum} 0 R`)) return match;\n\t\t\t\t\t\t\treturn `/Contents [${wrapperObjNum} 0 R ${inner.trim()} ${contentObjNum} 0 R]`;\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tpageContent += `\\n/Contents ${contentObjNum} 0 R`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Build signature's new resources\n\t\t\tconst newResourceParts: string[] = [];\n\t\t\tnewResourceParts.push(`/Font << /F1 ${this.fontObjNum} 0 R >>`);\n\n\t\t\t// Image resources\n\t\t\tconst imageRefs = page.dirty ? (page as PdfPageImpl).getImageRefs() : new Map<string, number>();\n\t\t\tif (imageRefs.size > 0) {\n\t\t\t\tconst xobjEntries = Array.from(imageRefs.entries())\n\t\t\t\t\t.map(([name, objNum]) => `/${name} ${objNum} 0 R`)\n\t\t\t\t\t.join(\" \");\n\t\t\t\tnewResourceParts.push(`/XObject << ${xobjEntries} >>`);\n\t\t\t}\n\n\t\t\tconst newResources: Record<string, string> = {};\n\t\t\tfor (const part of newResourceParts) {\n\t\t\t\tconst [resType, ...rest] = part.split(/\\s+/);\n\t\t\t\tif (resType) {\n\t\t\t\t\tnewResources[resType] = rest.join(\" \");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Parse existing Resources from page\n\t\t\tconst existingResources = parseResourcesDict(pageContent, this.pdfStr, this.structure.objIndex);\n\n\t\t\t// Merge existing with new\n\t\t\tconst mergedResources = mergeResourcesDicts(existingResources, newResources);\n\n\t\t\t// Build merged Resources dictionary string\n\t\t\tconst resourceEntries = Object.entries(mergedResources)\n\t\t\t\t.map(([name, value]) => `${name} ${value}`)\n\t\t\t\t.join(\"\\n\");\n\n\t\t\t// Update page content with merged Resources\n\t\t\tif (pageContent.match(/\\/Resources\\s*<</)) {\n\t\t\t\t// Replace existing inline Resources\n\t\t\t\tconst resIdx = pageContent.indexOf(\"/Resources\");\n\t\t\t\tconst resStart = pageContent.indexOf(\"<<\", resIdx);\n\t\t\t\tif (resStart !== -1) {\n\t\t\t\t\tconst resEnd = findMatchingDictEndInContent(pageContent, resStart);\n\t\t\t\t\tif (resEnd !== -1) {\n\t\t\t\t\t\tpageContent =\n\t\t\t\t\t\t\tpageContent.slice(0, resStart) +\n\t\t\t\t\t\t\t`<< ${resourceEntries} >>` +\n\t\t\t\t\t\t\tpageContent.slice(resEnd + 2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (pageContent.match(/\\/Resources\\s+\\d+\\s+\\d+\\s+R/)) {\n\t\t\t\t// Replace indirect reference with merged inline dictionary\n\t\t\t\tpageContent = pageContent.replace(\n\t\t\t\t\t/\\/Resources\\s+\\d+\\s+\\d+\\s+R/,\n\t\t\t\t\t`/Resources << ${resourceEntries} >>`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// No resources at all — add them\n\t\t\t\tpageContent += `\\n/Resources << ${resourceEntries} >>`;\n\t\t\t}\n\n\t\t\t// Add link annotations to /Annots\n\t\t\tconst annotRefs = linkAnnotObjNums.get(page.pageObjNum);\n\t\t\tif (annotRefs && annotRefs.length > 0) {\n\t\t\t\tconst refsStr = annotRefs.map((n) => `${n} 0 R`).join(\" \");\n\t\t\t\tif (pageContent.includes(\"/Annots\")) {\n\t\t\t\t\tconst bracketEnd = pageContent.indexOf(\"]\", pageContent.indexOf(\"/Annots\"));\n\t\t\t\t\tpageContent =\n\t\t\t\t\t\tpageContent.slice(0, bracketEnd) +\n\t\t\t\t\t\t` ${refsStr}` +\n\t\t\t\t\t\tpageContent.slice(bracketEnd);\n\t\t\t\t} else {\n\t\t\t\t\tpageContent += `\\n/Annots [${refsStr}]`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tobjects.push({\n\t\t\t\tobjNum: page.pageObjNum,\n\t\t\t\tcontent: `<<${pageContent}\\n>>`,\n\t\t\t});\n\t\t}\n\n\t\t// --- 5. Signature placeholder objects (if requested) ---\n\t\tlet sigObjNum = 0;\n\t\tlet widgetObjNum = 0;\n\t\tlet acroFormObjNum = 0;\n\n\t\tif (withSignature && sigOptions) {\n\t\t\tsigObjNum = currentNextObj++;\n\t\t\twidgetObjNum = currentNextObj++;\n\t\t\tacroFormObjNum = currentNextObj++;\n\n\t\t\tconst signatureLength = sigOptions.signatureLength ?? DEFAULT_SIGNATURE_LENGTH;\n\t\t\tconst reason = sigOptions.reason ?? \"Digitally signed\";\n\t\t\tconst name = sigOptions.name ?? \"Digital Signature\";\n\t\t\tconst location = sigOptions.location ?? \"\";\n\t\t\tconst contactInfo = sigOptions.contactInfo ?? \"\";\n\t\t\tconst signingTime = formatPdfDate(new Date());\n\t\t\tconst contentsPlaceholder = \"0\".repeat(signatureLength * 2);\n\n\t\t\tconst sigParts = [\n\t\t\t\t\"<< /Type /Sig\",\n\t\t\t\t\"/Filter /Adobe.PPKLite\",\n\t\t\t\t\"/SubFilter /adbe.pkcs7.detached\",\n\t\t\t\t`/ByteRange [${BYTE_RANGE_PLACEHOLDER}]`,\n\t\t\t\t`/Contents <${contentsPlaceholder}>`,\n\t\t\t\t`/Reason ${pdfString(reason)}`,\n\t\t\t\t`/M ${pdfString(signingTime)}`,\n\t\t\t\t`/Name ${pdfString(name)}`,\n\t\t\t];\n\t\t\tif (location) sigParts.push(`/Location ${pdfString(location)}`);\n\t\t\tif (contactInfo) sigParts.push(`/ContactInfo ${pdfString(contactInfo)}`);\n\t\t\tsigParts.push(\">>\");\n\n\t\t\tobjects.push({ objNum: sigObjNum, content: sigParts.join(\"\\n\") });\n\n\t\t\t// Resolve which page hosts the signature widget annotation.\n\t\t\t// Defaults to page 0; callers pass `appearancePage` to match the\n\t\t\t// visual appearance location so PDF readers navigate correctly.\n\t\t\tconst sigPageIdx = Math.min(\n\t\t\t\tMath.max(sigOptions.appearancePage ?? 0, 0),\n\t\t\t\tthis.pages.length - 1,\n\t\t\t);\n\t\t\tconst sigPageNum = this.structure.pageNums[sigPageIdx]!;\n\n\t\t\t// Widget annotation\n\t\t\tobjects.push({\n\t\t\t\tobjNum: widgetObjNum,\n\t\t\t\tcontent: [\n\t\t\t\t\t\"<< /Type /Annot\",\n\t\t\t\t\t\"/Subtype /Widget\",\n\t\t\t\t\t\"/FT /Sig\",\n\t\t\t\t\t\"/Rect [0 0 0 0]\",\n\t\t\t\t\t`/V ${sigObjNum} 0 R`,\n\t\t\t\t\t`/T ${pdfString(\"Signature1\")}`,\n\t\t\t\t\t\"/F 4\",\n\t\t\t\t\t`/P ${sigPageNum} 0 R`,\n\t\t\t\t\t\">>\",\n\t\t\t\t].join(\"\\n\"),\n\t\t\t});\n\n\t\t\t// AcroForm\n\t\t\tobjects.push({\n\t\t\t\tobjNum: acroFormObjNum,\n\t\t\t\tcontent: [\n\t\t\t\t\t\"<< /Type /AcroForm\",\n\t\t\t\t\t\"/SigFlags 3\",\n\t\t\t\t\t`/Fields [${widgetObjNum} 0 R]`,\n\t\t\t\t\t\">>\",\n\t\t\t\t].join(\"\\n\"),\n\t\t\t});\n\n\t\t\t// Updated Root catalog with AcroForm\n\t\t\tlet rootContent = this.structure.rootDictContent;\n\t\t\trootContent = rootContent.replace(/\\/AcroForm\\s+\\d+\\s+\\d+\\s+R/g, \"\");\n\t\t\trootContent = rootContent.replace(/\\/Perms\\s*<<[^>]*>>/g, \"\");\n\t\t\trootContent = rootContent.replace(/\\/Perms\\s+\\d+\\s+\\d+\\s+R/g, \"\");\n\n\t\t\tobjects.push({\n\t\t\t\tobjNum: this.structure.rootNum,\n\t\t\t\tcontent: `<<${rootContent}\\n/AcroForm ${acroFormObjNum} 0 R\\n>>`,\n\t\t\t});\n\n\t\t\t// Updated sigPage with Annots\n\t\t\tconst sigPage = this.pages[sigPageIdx]!;\n\t\t\tlet pageContent: string;\n\n\t\t\t// Check if we already have this page in objects (from dirty page update)\n\t\t\tconst existingPageObj = objects.find((o) => o.objNum === sigPage.pageObjNum);\n\t\t\tif (existingPageObj) {\n\t\t\t\t// Extract content from existing updated page (strip outer << >>)\n\t\t\t\tpageContent = existingPageObj.content.slice(2, existingPageObj.content.length - 2);\n\t\t\t} else {\n\t\t\t\tpageContent = sigPage.originalDictContent;\n\t\t\t}\n\n\t\t\tif (pageContent.includes(\"/Annots\")) {\n\t\t\t\tconst bracketEnd = pageContent.indexOf(\"]\", pageContent.indexOf(\"/Annots\"));\n\t\t\t\tpageContent =\n\t\t\t\t\tpageContent.slice(0, bracketEnd) +\n\t\t\t\t\t` ${widgetObjNum} 0 R` +\n\t\t\t\t\tpageContent.slice(bracketEnd);\n\t\t\t} else {\n\t\t\t\tpageContent += `\\n/Annots [${widgetObjNum} 0 R]`;\n\t\t\t}\n\n\t\t\t// Update or add the page object\n\t\t\tif (existingPageObj) {\n\t\t\t\texistingPageObj.content = `<<${pageContent}\\n>>`;\n\t\t\t} else {\n\t\t\t\tobjects.push({\n\t\t\t\t\tobjNum: sigPage.pageObjNum,\n\t\t\t\t\tcontent: `<<${pageContent}\\n>>`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// --- Serialise incremental update ---\n\t\tconst newSize = currentNextObj;\n\t\tconst appendParts: Uint8Array[] = [];\n\t\tconst objectOffsets: Array<{ objNum: number; offset: number }> = [];\n\n\t\t// Compute starting byte offset (after original data)\n\t\tlet currentOffset = this.originalData.length;\n\n\t\tfor (const obj of objects) {\n\t\t\t// Separator newline\n\t\t\tconst sep = latin1Encoder.encode(\"\\n\");\n\t\t\tappendParts.push(sep);\n\t\t\tcurrentOffset += sep.length;\n\n\t\t\tobjectOffsets.push({ objNum: obj.objNum, offset: currentOffset });\n\n\t\t\tif (obj.streamData) {\n\t\t\t\t// Object with stream\n\t\t\t\tconst header = latin1Encoder.encode(`${obj.objNum} 0 obj\\n${obj.content}\\nstream\\n`);\n\t\t\t\tappendParts.push(header);\n\t\t\t\tcurrentOffset += header.length;\n\n\t\t\t\tappendParts.push(obj.streamData);\n\t\t\t\tcurrentOffset += obj.streamData.length;\n\n\t\t\t\tconst footer = latin1Encoder.encode(\"\\nendstream\\nendobj\\n\");\n\t\t\t\tappendParts.push(footer);\n\t\t\t\tcurrentOffset += footer.length;\n\t\t\t} else {\n\t\t\t\tconst objBytes = latin1Encoder.encode(\n\t\t\t\t\t`${obj.objNum} 0 obj\\n${obj.content}\\nendobj\\n`,\n\t\t\t\t);\n\t\t\t\tappendParts.push(objBytes);\n\t\t\t\tcurrentOffset += objBytes.length;\n\t\t\t}\n\t\t}\n\n\t\t// Xref table\n\t\tconst xrefOffset = currentOffset;\n\t\tconst xrefStr = buildXrefTable(objectOffsets);\n\t\tconst xrefBytes = latin1Encoder.encode(xrefStr);\n\t\tappendParts.push(xrefBytes);\n\t\tcurrentOffset += xrefBytes.length;\n\n\t\t// Trailer\n\t\tconst trailerLines = [\"<<\", `/Size ${newSize}`, `/Root ${this.structure.rootNum} 0 R`];\n\t\tif (this.structure.infoNum !== null) {\n\t\t\ttrailerLines.push(`/Info ${this.structure.infoNum} 0 R`);\n\t\t}\n\t\ttrailerLines.push(`/Prev ${this.structure.xrefOffset}`, \">>\");\n\n\t\tconst trailerStr =\n\t\t\t`trailer\\n${trailerLines.join(\"\\n\")}\\nstartxref\\n${xrefOffset}\\n%%EOF`;\n\t\tconst trailerBytes = latin1Encoder.encode(trailerStr);\n\t\tappendParts.push(trailerBytes);\n\n\t\t// Combine original + append parts\n\t\tconst totalAppendLength = appendParts.reduce((s, p) => s + p.length, 0);\n\t\tconst result = new Uint8Array(this.originalData.length + totalAppendLength);\n\t\tresult.set(this.originalData, 0);\n\t\tlet pos = this.originalData.length;\n\t\tfor (const part of appendParts) {\n\t\t\tresult.set(part, pos);\n\t\t\tpos += part.length;\n\t\t}\n\n\t\t// --- Calculate byte range for signature ---\n\t\tlet byteRange: [number, number, number, number] = [0, 0, 0, 0];\n\n\t\tif (withSignature) {\n\t\t\tconst br = updateByteRangeInPlace(result);\n\t\t\treturn { pdf: result, byteRange: br };\n\t\t}\n\n\t\treturn { pdf: result, byteRange };\n\t}\n\n\tprivate hasImagesForPage(page: PdfPageImpl): boolean {\n\t\treturn page.dirty && page.getImageRefs().size > 0;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction buildXrefTable(\n\tentries: Array<{ objNum: number; offset: number }>,\n): string {\n\tconst sorted = [...entries].sort((a, b) => a.objNum - b.objNum);\n\n\tconst subsections: Array<{ start: number; offsets: number[] }> = [];\n\tfor (const entry of sorted) {\n\t\tconst last = subsections[subsections.length - 1];\n\t\tif (last && entry.objNum === last.start + last.offsets.length) {\n\t\t\tlast.offsets.push(entry.offset);\n\t\t} else {\n\t\t\tsubsections.push({ start: entry.objNum, offsets: [entry.offset] });\n\t\t}\n\t}\n\n\tlet result = \"xref\\n\";\n\tfor (const sub of subsections) {\n\t\tresult += `${sub.start} ${sub.offsets.length}\\n`;\n\t\tfor (const offset of sub.offsets) {\n\t\t\tresult += `${String(offset).padStart(10, \"0\")} 00000 n \\n`;\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction formatPdfDate(date: Date): string {\n\tconst y = date.getUTCFullYear();\n\tconst m = String(date.getUTCMonth() + 1).padStart(2, \"0\");\n\tconst d = String(date.getUTCDate()).padStart(2, \"0\");\n\tconst h = String(date.getUTCHours()).padStart(2, \"0\");\n\tconst min = String(date.getUTCMinutes()).padStart(2, \"0\");\n\tconst s = String(date.getUTCSeconds()).padStart(2, \"0\");\n\treturn `D:${y}${m}${d}${h}${min}${s}Z`;\n}\n\nfunction pdfString(str: string): string {\n\tconst escaped = str\n\t\t.replace(/\\\\/g, \"\\\\\\\\\")\n\t\t.replace(/\\(/g, \"\\\\(\")\n\t\t.replace(/\\)/g, \"\\\\)\");\n\treturn `(${escaped})`;\n}\n\n/**\n * Find and overwrite the ByteRange placeholder in an assembled PDF buffer.\n * Modifies `pdf` in-place. Returns the final byte range values.\n */\nfunction updateByteRangeInPlace(pdf: Uint8Array): [number, number, number, number] {\n\t// Locate /Contents < ... >\n\tconst contentsIdx = findLastBytes(pdf, CONTENTS_MARKER);\n\tif (contentsIdx === -1) throw new Error(\"Cannot find Contents in signature\");\n\tconst contentsStart = contentsIdx + CONTENTS_MARKER.length;\n\tlet contentsEnd = contentsStart;\n\twhile (contentsEnd < pdf.length && pdf[contentsEnd] !== 0x3e) contentsEnd++;\n\tif (contentsEnd >= pdf.length) throw new Error(\"Cannot find end of Contents hex\");\n\n\tconst br: [number, number, number, number] = [\n\t\t0,\n\t\tcontentsStart - 1,\n\t\tcontentsEnd + 1,\n\t\tpdf.length - (contentsEnd + 1),\n\t];\n\n\t// Locate /ByteRange [ ... ]\n\tconst brIdx = findLastBytes(pdf, BYTE_RANGE_MARKER);\n\tif (brIdx === -1) throw new Error(\"Cannot find ByteRange in PDF\");\n\tconst brStart = brIdx + BYTE_RANGE_MARKER.length;\n\tlet brEnd = brStart;\n\twhile (brEnd < pdf.length && pdf[brEnd] !== 0x5d) brEnd++;\n\tif (brEnd >= pdf.length) throw new Error(\"Cannot find end of ByteRange\");\n\n\tconst placeholderLen = brEnd - brStart;\n\tconst brValueStr = `${br[0]} ${br[1]} ${br[2]} ${br[3]}`.padEnd(placeholderLen, \" \");\n\tpdf.set(latin1Encoder.encode(brValueStr), brStart);\n\n\treturn br;\n}\n\n/**\n * Find matching >> for a << in a content string\n */\nfunction findMatchingDictEndInContent(str: string, startPos: number): number {\n\tlet depth = 0;\n\tlet i = startPos;\n\n\twhile (i < str.length - 1) {\n\t\tif (str[i] === \"(\") {\n\t\t\ti++;\n\t\t\twhile (i < str.length && str[i] !== \")\") {\n\t\t\t\tif (str[i] === \"\\\\\") i++;\n\t\t\t\ti++;\n\t\t\t}\n\t\t\ti++;\n\t\t} else if (str[i] === \"<\" && str[i + 1] === \"<\") {\n\t\t\tdepth++;\n\t\t\ti += 2;\n\t\t} else if (str[i] === \">\" && str[i + 1] === \">\") {\n\t\t\tdepth--;\n\t\t\tif (depth === 0) return i;\n\t\t\ti += 2;\n\t\t} else {\n\t\t\ti++;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n// ---------------------------------------------------------------------------\n// Signature utility exports (for use by the e-signature library)\n// ---------------------------------------------------------------------------\n\n/**\n * Find the ByteRange and Contents placeholder in a signed PDF\n */\nexport function findByteRange(pdfData: Uint8Array): {\n\tbyteRange: [number, number, number, number];\n\tcontentsStart: number;\n\tcontentsEnd: number;\n\tplaceholderLength: number;\n} {\n\tconst contentsIdx = findLastBytes(pdfData, CONTENTS_MARKER);\n\tif (contentsIdx === -1) throw new Error(\"Could not find Contents in PDF\");\n\n\tconst contentsStart = contentsIdx + CONTENTS_MARKER.length;\n\tlet contentsEnd = contentsStart;\n\twhile (contentsEnd < pdfData.length && pdfData[contentsEnd] !== 0x3e) contentsEnd++;\n\tif (contentsEnd >= pdfData.length) throw new Error(\"Could not find end of Contents field\");\n\n\tconst placeholderLength = contentsEnd - contentsStart;\n\tconst byteRange: [number, number, number, number] = [\n\t\t0,\n\t\tcontentsStart - 1,\n\t\tcontentsEnd + 1,\n\t\tpdfData.length - (contentsEnd + 1),\n\t];\n\n\treturn { byteRange, contentsStart, contentsEnd, placeholderLength };\n}\n\n/**\n * Extract the bytes that need to be signed according to the ByteRange\n */\nexport function extractBytesToSign(\n\tpdfData: Uint8Array,\n\tbyteRange: [number, number, number, number],\n): Uint8Array {\n\tconst [offset1, length1, offset2, length2] = byteRange;\n\n\tif (offset1 < 0 || length1 <= 0 || offset2 <= 0 || length2 <= 0) {\n\t\tthrow new Error(`Invalid ByteRange values: [${byteRange.join(\", \")}]`);\n\t}\n\n\tif (offset1 + length1 > pdfData.length || offset2 + length2 > pdfData.length) {\n\t\tthrow new Error(\"ByteRange exceeds PDF data size\");\n\t}\n\n\t// Use subarray (zero-copy views) to avoid two intermediate allocations before\n\t// writing into the final combined buffer.\n\tconst chunk1 = pdfData.subarray(offset1, offset1 + length1);\n\tconst chunk2 = pdfData.subarray(offset2, offset2 + length2);\n\n\tconst result = new Uint8Array(chunk1.length + chunk2.length);\n\tresult.set(chunk1, 0);\n\tresult.set(chunk2, chunk1.length);\n\treturn result;\n}\n\n/**\n * Embed a signature (as raw bytes) into the Contents placeholder.\n *\n * **Mutates `pdfData` in-place** and returns the same buffer. Callers must not\n * retain a reference to `pdfData` and expect it to remain unchanged after this call.\n */\nexport function embedSignature(\n\tpdfData: Uint8Array,\n\tsignature: Uint8Array,\n): Uint8Array {\n\tconst { contentsStart, placeholderLength } = findByteRange(pdfData);\n\n\t// Convert signature to hex\n\tconst hexChars: string[] = [];\n\tfor (let i = 0; i < signature.length; i++) {\n\t\thexChars.push(signature[i]!.toString(16).padStart(2, \"0\"));\n\t}\n\tconst signatureHex = hexChars.join(\"\");\n\n\tif (signatureHex.length > placeholderLength) {\n\t\tthrow new Error(\n\t\t\t`Signature too large: ${signatureHex.length} hex chars, placeholder is ${placeholderLength}`,\n\t\t);\n\t}\n\n\tconst paddedHex = signatureHex.padEnd(placeholderLength, \"0\");\n\tconst hexBytes = new TextEncoder().encode(paddedHex);\n\n\t// Patch the placeholder in-place — pdfData is a freshly-created buffer from\n\t// saveWithPlaceholder() and not shared with any other consumer at this point,\n\t// so mutating it avoids allocating a full PDF-sized copy.\n\tpdfData.set(hexBytes, contentsStart);\n\treturn pdfData;\n}\n",
7
+ "/**\n * PDF Editing Plugin\n *\n * Load existing PDFs, modify them (draw text, rectangles, images),\n * and save with incremental updates. Also supports creating signature\n * placeholders for digital signing workflows.\n */\n\nexport type {\n\tPdfDocument,\n\tPdfPage,\n\tPdfImage,\n\tTextOptions,\n\tRectOptions,\n\tImageOptions,\n\tSignaturePlaceholderOptions,\n} from \"./types.ts\";\n\nexport { PdfDocumentImpl } from \"./document.ts\";\n\nexport {\n\tfindByteRange,\n\textractBytesToSign,\n\tembedSignature,\n} from \"./document.ts\";\n\nimport { PdfDocumentImpl } from \"./document.ts\";\nimport { parsePdfStructure } from \"./parser.ts\";\nimport type { PdfDocument } from \"./types.ts\";\n\n/**\n * Load an existing PDF for editing\n *\n * @param data - The PDF file contents as a Uint8Array\n * @returns A PdfDocument that can be modified and saved\n */\nexport function loadPdf(data: Uint8Array): PdfDocument {\n\treturn new PdfDocumentImpl(data);\n}\n\n/**\n * Count the number of pages in a PDF without fully loading it.\n *\n * @param data - The PDF file contents as a Uint8Array\n * @returns The number of pages in the PDF\n */\nexport function countPdfPages(data: Uint8Array): number {\n\tconst pdfStr = new TextDecoder(\"latin1\").decode(data);\n\tconst structure = parsePdfStructure(pdfStr);\n\treturn structure.pageNums.length;\n}\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "2006205BBEFB17FE64756E2164756E21",
11
+ "names": []
12
+ }
package/dist/index.js CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  PDFReader,
43
43
  PDFSignatureError,
44
44
  TokenType
45
- } from "./index-3qa5wvjk.js";
45
+ } from "./index-eedrjd25.js";
46
46
  import {
47
47
  createArray,
48
48
  createDictionary,
@@ -57,7 +57,8 @@ import {
57
57
  } from "./index-4jtcmpfh.js";
58
58
  import {
59
59
  countPdfPages
60
- } from "./index-w5nfn63z.js";
60
+ } from "./index-xbm3820d.js";
61
+ import"./index-ty7xfwkn.js";
61
62
  export {
62
63
  serializeValue,
63
64
  serializeStream,
@@ -112,4 +113,4 @@ export {
112
113
  CMYKColorSchema
113
114
  };
114
115
 
115
- //# debugId=A4670F83538C77BE64756E2164756E21
116
+ //# debugId=15860D4A61FEDB1764756E2164756E21
package/dist/index.js.map CHANGED
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "A4670F83538C77BE64756E2164756E21",
7
+ "debugId": "15860D4A61FEDB1764756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../../src/plugins/editing/document.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,KAAK,EACX,WAAW,EACX,QAAQ,EACR,OAAO,EACP,2BAA2B,EAC3B,MAAM,YAAY,CAAC;AAwFpB,qBAAa,eAAgB,YAAW,WAAW;IAClD,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAOd;gBAEI,IAAI,EAAE,UAAU;IAyB5B,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAS/B,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ;IAqBpC;;OAEG;IACH,IAAI,IAAI,UAAU;IAIlB;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,2BAA2B,GAAG;QAC1D,GAAG,EAAE,UAAU,CAAC;QAChB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;KAC5C;IAID,OAAO,CAAC,sBAAsB;IA4W9B,OAAO,CAAC,gBAAgB;CAGxB;AAsHD;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG;IACnD,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC1B,CAkBA;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GACzC,UAAU,CAoBZ;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,UAAU,GACnB,UAAU,CAwBZ"}
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../../src/plugins/editing/document.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,KAAK,EACX,WAAW,EACX,QAAQ,EACR,OAAO,EACP,2BAA2B,EAC3B,MAAM,YAAY,CAAC;AAwFpB,qBAAa,eAAgB,YAAW,WAAW;IAClD,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAOd;gBAEI,IAAI,EAAE,UAAU;IAyB5B,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAS/B,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ;IAqBpC;;OAEG;IACH,IAAI,IAAI,UAAU;IAIlB;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,2BAA2B,GAAG;QAC1D,GAAG,EAAE,UAAU,CAAC;QAChB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;KAC5C;IAID,OAAO,CAAC,sBAAsB;IAqZ9B,OAAO,CAAC,gBAAgB;CAGxB;AAsHD;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG;IACnD,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC1B,CAkBA;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GACzC,UAAU,CAoBZ;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,UAAU,GACnB,UAAU,CAwBZ"}
@@ -6,7 +6,8 @@ import {
6
6
  extractBytesToSign,
7
7
  findByteRange,
8
8
  loadPdf
9
- } from "../../index-w5nfn63z.js";
9
+ } from "../../index-xbm3820d.js";
10
+ import"../../index-ty7xfwkn.js";
10
11
  export {
11
12
  loadPdf,
12
13
  findByteRange,
@@ -16,4 +17,4 @@ export {
16
17
  PdfDocumentImpl
17
18
  };
18
19
 
19
- //# debugId=80C123609B6825FA64756E2164756E21
20
+ //# debugId=D124E97E033C57BE64756E2164756E21
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "80C123609B6825FA64756E2164756E21",
7
+ "debugId": "D124E97E033C57BE64756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -6,6 +6,13 @@
6
6
  * object appended via incremental update.
7
7
  */
8
8
  import type { PdfImage, PdfPage, TextOptions, RectOptions, ImageOptions } from "./types.ts";
9
+ export type LinkAnnotation = {
10
+ x: number;
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ url: string;
15
+ };
9
16
  export declare class PdfPageImpl implements PdfPage {
10
17
  readonly width: number;
11
18
  readonly height: number;
@@ -17,6 +24,8 @@ export declare class PdfPageImpl implements PdfPage {
17
24
  private operators;
18
25
  /** Images referenced by drawImage (name -> obj num) */
19
26
  private imageRefs;
27
+ /** Link annotations recorded by drawLink */
28
+ private linkAnnotations;
20
29
  /** Font object number allocated by the document (set externally) */
21
30
  fontObjNum: number;
22
31
  /** Whether any drawing operations have been recorded */
@@ -34,6 +43,14 @@ export declare class PdfPageImpl implements PdfPage {
34
43
  * Draw an embedded image on the page
35
44
  */
36
45
  drawImage(image: PdfImage, options: ImageOptions): void;
46
+ /**
47
+ * Draw text as a clickable hyperlink
48
+ */
49
+ drawLink(text: string, url: string, options: TextOptions): void;
50
+ /**
51
+ * Get link annotations recorded by drawLink calls
52
+ */
53
+ getLinkAnnotations(): LinkAnnotation[];
37
54
  /**
38
55
  * Build the content stream bytes for the accumulated operators
39
56
  */