@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,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form field appearance stream generation.
|
|
3
|
+
*
|
|
4
|
+
* Generates visual appearance streams for PDF form fields so that field
|
|
5
|
+
* values are visible in all PDF viewers — even those that do not honor
|
|
6
|
+
* the `/NeedAppearances` flag.
|
|
7
|
+
*
|
|
8
|
+
* @see PDF Reference 1.7, §12.5.5 — Appearance Streams
|
|
9
|
+
* @see PDF Reference 1.7, §12.7.4 — Field Appearance
|
|
10
|
+
*/
|
|
11
|
+
/** Options for generating a text field appearance stream. */
|
|
12
|
+
export interface TextFieldAppearanceOptions {
|
|
13
|
+
/** The text value to display. */
|
|
14
|
+
value: string;
|
|
15
|
+
/** Widget annotation rectangle [x1, y1, x2, y2]. */
|
|
16
|
+
rect: number[];
|
|
17
|
+
/** Font size in points. 0 or omitted = auto-size to fit the field height. */
|
|
18
|
+
fontSize?: number;
|
|
19
|
+
/** Font resource name to reference (e.g. "Helv"). */
|
|
20
|
+
fontName?: string;
|
|
21
|
+
/** Text alignment within the field. */
|
|
22
|
+
alignment?: "left" | "center" | "right";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Generate an appearance stream for a text form field.
|
|
26
|
+
*
|
|
27
|
+
* Builds a minimal content stream that clips to the widget rect and
|
|
28
|
+
* draws the field value using the specified (or default) font.
|
|
29
|
+
*
|
|
30
|
+
* @returns The raw stream bytes and a resources dictionary string.
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateTextFieldAppearance(options: TextFieldAppearanceOptions): {
|
|
33
|
+
stream: Uint8Array;
|
|
34
|
+
resources: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Generate appearance streams for a checkbox form field.
|
|
38
|
+
*
|
|
39
|
+
* Returns two streams:
|
|
40
|
+
* - `streamOn`: draws a checkmark (✓-like shape) inside the rect
|
|
41
|
+
* - `streamOff`: empty appearance (blank field)
|
|
42
|
+
*
|
|
43
|
+
* @param checked - Whether to generate the checked or unchecked variant
|
|
44
|
+
* (both are always returned; `checked` is ignored — both
|
|
45
|
+
* on and off streams are produced).
|
|
46
|
+
* @param rect - Widget annotation rectangle [x1, y1, x2, y2].
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateCheckboxAppearance(_checked: boolean, rect: number[]): {
|
|
49
|
+
streamOn: Uint8Array;
|
|
50
|
+
streamOff: Uint8Array;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Build the BBox array string for a form XObject appearance stream.
|
|
54
|
+
* The bounding box is in the widget's coordinate space: [0 0 width height].
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildAppearanceBBox(rect: number[]): string;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form field appearance stream generation.
|
|
3
|
+
*
|
|
4
|
+
* Generates visual appearance streams for PDF form fields so that field
|
|
5
|
+
* values are visible in all PDF viewers — even those that do not honor
|
|
6
|
+
* the `/NeedAppearances` flag.
|
|
7
|
+
*
|
|
8
|
+
* @see PDF Reference 1.7, §12.5.5 — Appearance Streams
|
|
9
|
+
* @see PDF Reference 1.7, §12.7.4 — Field Appearance
|
|
10
|
+
*/
|
|
11
|
+
import { PdfContentStream } from "../core/pdf-stream.js";
|
|
12
|
+
import { pdfNumber } from "../core/pdf-object.js";
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Text Field Appearance
|
|
15
|
+
// =============================================================================
|
|
16
|
+
/** Default padding inside the field rectangle (in points). */
|
|
17
|
+
const FIELD_PADDING = 2;
|
|
18
|
+
/**
|
|
19
|
+
* Generate an appearance stream for a text form field.
|
|
20
|
+
*
|
|
21
|
+
* Builds a minimal content stream that clips to the widget rect and
|
|
22
|
+
* draws the field value using the specified (or default) font.
|
|
23
|
+
*
|
|
24
|
+
* @returns The raw stream bytes and a resources dictionary string.
|
|
25
|
+
*/
|
|
26
|
+
export function generateTextFieldAppearance(options) {
|
|
27
|
+
const { value, rect, alignment = "left" } = options;
|
|
28
|
+
const fontName = options.fontName ?? "Helv";
|
|
29
|
+
// Derive bounding box dimensions from the widget rect
|
|
30
|
+
const x1 = rect[0] ?? 0;
|
|
31
|
+
const y1 = rect[1] ?? 0;
|
|
32
|
+
const x2 = rect[2] ?? x1;
|
|
33
|
+
const y2 = rect[3] ?? y1;
|
|
34
|
+
const width = Math.abs(x2 - x1);
|
|
35
|
+
const height = Math.abs(y2 - y1);
|
|
36
|
+
// Auto-size font: use ~70% of the field height, clamped to a reasonable range
|
|
37
|
+
let fontSize = options.fontSize ?? 0;
|
|
38
|
+
if (fontSize <= 0) {
|
|
39
|
+
fontSize = Math.max(4, Math.min(height * 0.7, 20));
|
|
40
|
+
}
|
|
41
|
+
// Approximate text width using 0.5 * fontSize per character (conservative estimate
|
|
42
|
+
// for Helvetica). This is intentionally simple — the appearance is a best-effort
|
|
43
|
+
// rendering; the authoritative value is in /V.
|
|
44
|
+
const approxCharWidth = fontSize * 0.5;
|
|
45
|
+
const textWidth = value.length * approxCharWidth;
|
|
46
|
+
// Compute horizontal offset based on alignment
|
|
47
|
+
let tx;
|
|
48
|
+
const usableWidth = width - 2 * FIELD_PADDING;
|
|
49
|
+
switch (alignment) {
|
|
50
|
+
case "center":
|
|
51
|
+
tx = FIELD_PADDING + Math.max(0, (usableWidth - textWidth) / 2);
|
|
52
|
+
break;
|
|
53
|
+
case "right":
|
|
54
|
+
tx = FIELD_PADDING + Math.max(0, usableWidth - textWidth);
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
tx = FIELD_PADDING;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
// Vertical centering: place baseline so the text is roughly centered
|
|
61
|
+
// Approximate ascent ≈ 0.75 * fontSize, descent ≈ -0.25 * fontSize
|
|
62
|
+
const ascent = fontSize * 0.75;
|
|
63
|
+
const descent = fontSize * 0.25;
|
|
64
|
+
const textHeight = ascent + descent;
|
|
65
|
+
const ty = (height - textHeight) / 2 + descent;
|
|
66
|
+
// Build the content stream
|
|
67
|
+
const cs = new PdfContentStream();
|
|
68
|
+
// Clip to the bounding box (form XObject coordinate space: 0,0 to width,height)
|
|
69
|
+
cs.rect(0, 0, width, height).clip().endPath();
|
|
70
|
+
// Draw the text
|
|
71
|
+
cs.beginText().setFont(fontName, fontSize).moveText(tx, ty).showText(value).endText();
|
|
72
|
+
const stream = cs.toUint8Array();
|
|
73
|
+
// Build a minimal resources dict referencing a standard Helvetica font.
|
|
74
|
+
// The font is declared inline as a Type1 font so the appearance stream
|
|
75
|
+
// is self-contained (no dependency on the page's resources).
|
|
76
|
+
const resources = `<< /Font << /${fontName} << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> >> >>`;
|
|
77
|
+
return { stream, resources };
|
|
78
|
+
}
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// Checkbox Appearance
|
|
81
|
+
// =============================================================================
|
|
82
|
+
/**
|
|
83
|
+
* Generate appearance streams for a checkbox form field.
|
|
84
|
+
*
|
|
85
|
+
* Returns two streams:
|
|
86
|
+
* - `streamOn`: draws a checkmark (✓-like shape) inside the rect
|
|
87
|
+
* - `streamOff`: empty appearance (blank field)
|
|
88
|
+
*
|
|
89
|
+
* @param checked - Whether to generate the checked or unchecked variant
|
|
90
|
+
* (both are always returned; `checked` is ignored — both
|
|
91
|
+
* on and off streams are produced).
|
|
92
|
+
* @param rect - Widget annotation rectangle [x1, y1, x2, y2].
|
|
93
|
+
*/
|
|
94
|
+
export function generateCheckboxAppearance(_checked, rect) {
|
|
95
|
+
const x1 = rect[0] ?? 0;
|
|
96
|
+
const y1 = rect[1] ?? 0;
|
|
97
|
+
const x2 = rect[2] ?? x1;
|
|
98
|
+
const y2 = rect[3] ?? y1;
|
|
99
|
+
const width = Math.abs(x2 - x1);
|
|
100
|
+
const height = Math.abs(y2 - y1);
|
|
101
|
+
// --- "On" appearance: draw an X ---
|
|
102
|
+
const onCs = new PdfContentStream();
|
|
103
|
+
const inset = Math.min(width, height) * 0.15;
|
|
104
|
+
onCs
|
|
105
|
+
.save()
|
|
106
|
+
.setStrokeColor({ r: 0, g: 0, b: 0 })
|
|
107
|
+
.setLineWidth(Math.max(1, Math.min(width, height) * 0.1))
|
|
108
|
+
// First stroke of the X: bottom-left to top-right
|
|
109
|
+
.moveTo(inset, inset)
|
|
110
|
+
.lineTo(width - inset, height - inset)
|
|
111
|
+
.stroke()
|
|
112
|
+
// Second stroke of the X: top-left to bottom-right
|
|
113
|
+
.moveTo(inset, height - inset)
|
|
114
|
+
.lineTo(width - inset, inset)
|
|
115
|
+
.stroke()
|
|
116
|
+
.restore();
|
|
117
|
+
// --- "Off" appearance: empty ---
|
|
118
|
+
const offCs = new PdfContentStream();
|
|
119
|
+
// Intentionally empty — nothing to draw for unchecked state
|
|
120
|
+
return {
|
|
121
|
+
streamOn: onCs.toUint8Array(),
|
|
122
|
+
streamOff: offCs.toUint8Array()
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Helpers
|
|
127
|
+
// =============================================================================
|
|
128
|
+
/**
|
|
129
|
+
* Build the BBox array string for a form XObject appearance stream.
|
|
130
|
+
* The bounding box is in the widget's coordinate space: [0 0 width height].
|
|
131
|
+
*/
|
|
132
|
+
export function buildAppearanceBBox(rect) {
|
|
133
|
+
const x1 = rect[0] ?? 0;
|
|
134
|
+
const y1 = rect[1] ?? 0;
|
|
135
|
+
const x2 = rect[2] ?? x1;
|
|
136
|
+
const y2 = rect[3] ?? y1;
|
|
137
|
+
const width = Math.abs(x2 - x1);
|
|
138
|
+
const height = Math.abs(y2 - y1);
|
|
139
|
+
return `[0 0 ${pdfNumber(width)} ${pdfNumber(height)}]`;
|
|
140
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared image utilities for PDF generation.
|
|
3
|
+
*
|
|
4
|
+
* Centralises JPEG/PNG dimension parsing and PDF XObject writing so that
|
|
5
|
+
* both the builder (`document-builder.ts`) and the exporter (`pdf-exporter.ts`)
|
|
6
|
+
* share a single implementation.
|
|
7
|
+
*/
|
|
8
|
+
import type { PdfWriter } from "../core/pdf-writer.js";
|
|
9
|
+
/**
|
|
10
|
+
* Parse image dimensions from raw bytes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseImageDimensions(data: Uint8Array, format: "jpeg" | "png"): {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Read width/height from a PNG IHDR chunk (bytes 16-23).
|
|
18
|
+
*/
|
|
19
|
+
export declare function parsePngDimensions(data: Uint8Array): {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Read width/height from JPEG SOF marker.
|
|
25
|
+
*
|
|
26
|
+
* Correctly excludes non-SOF markers in the 0xC0-0xCF range:
|
|
27
|
+
* - 0xC4 = DHT (Define Huffman Table)
|
|
28
|
+
* - 0xC8 = JPG (reserved)
|
|
29
|
+
* - 0xCC = DAC (Define Arithmetic Coding)
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseJpegDimensions(data: Uint8Array): {
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Write an image XObject (JPEG or PNG) to the writer.
|
|
37
|
+
* Returns the allocated object number.
|
|
38
|
+
*/
|
|
39
|
+
export declare function writeImageXObject(writer: PdfWriter, data: Uint8Array, format: "jpeg" | "png"): number;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared image utilities for PDF generation.
|
|
3
|
+
*
|
|
4
|
+
* Centralises JPEG/PNG dimension parsing and PDF XObject writing so that
|
|
5
|
+
* both the builder (`document-builder.ts`) and the exporter (`pdf-exporter.ts`)
|
|
6
|
+
* share a single implementation.
|
|
7
|
+
*/
|
|
8
|
+
import { PdfDict, pdfRef, pdfNumber } from "../core/pdf-object.js";
|
|
9
|
+
import { decodePng } from "../render/png-decoder.js";
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Image Dimension Parsing
|
|
12
|
+
// =============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Parse image dimensions from raw bytes.
|
|
15
|
+
*/
|
|
16
|
+
export function parseImageDimensions(data, format) {
|
|
17
|
+
if (format === "png") {
|
|
18
|
+
return parsePngDimensions(data);
|
|
19
|
+
}
|
|
20
|
+
return parseJpegDimensions(data);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read width/height from a PNG IHDR chunk (bytes 16-23).
|
|
24
|
+
*/
|
|
25
|
+
export function parsePngDimensions(data) {
|
|
26
|
+
// PNG header: 8 byte signature, then IHDR chunk: 4 byte length, 4 byte type, 4 byte width, 4 byte height
|
|
27
|
+
if (data.length >= 24 &&
|
|
28
|
+
data[12] === 0x49 &&
|
|
29
|
+
data[13] === 0x48 &&
|
|
30
|
+
data[14] === 0x44 &&
|
|
31
|
+
data[15] === 0x52) {
|
|
32
|
+
const width = (data[16] << 24) | (data[17] << 16) | (data[18] << 8) | data[19];
|
|
33
|
+
const height = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23];
|
|
34
|
+
return { width, height };
|
|
35
|
+
}
|
|
36
|
+
return { width: 1, height: 1 };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read width/height from JPEG SOF marker.
|
|
40
|
+
*
|
|
41
|
+
* Correctly excludes non-SOF markers in the 0xC0-0xCF range:
|
|
42
|
+
* - 0xC4 = DHT (Define Huffman Table)
|
|
43
|
+
* - 0xC8 = JPG (reserved)
|
|
44
|
+
* - 0xCC = DAC (Define Arithmetic Coding)
|
|
45
|
+
*/
|
|
46
|
+
export function parseJpegDimensions(data) {
|
|
47
|
+
let offset = 2; // skip SOI marker
|
|
48
|
+
while (offset < data.length - 1) {
|
|
49
|
+
// Skip padding 0xFF bytes
|
|
50
|
+
while (offset < data.length && data[offset] === 0xff && data[offset + 1] === 0xff) {
|
|
51
|
+
offset++;
|
|
52
|
+
}
|
|
53
|
+
if (offset >= data.length - 1 || data[offset] !== 0xff) {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
const marker = data[offset + 1];
|
|
57
|
+
const isSof = marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;
|
|
58
|
+
if (isSof && offset + 8 < data.length) {
|
|
59
|
+
return {
|
|
60
|
+
width: (data[offset + 7] << 8) | data[offset + 8],
|
|
61
|
+
height: (data[offset + 5] << 8) | data[offset + 6]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (offset + 3 >= data.length) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
const segLen = (data[offset + 2] << 8) | data[offset + 3];
|
|
68
|
+
offset += 2 + segLen;
|
|
69
|
+
}
|
|
70
|
+
return { width: 1, height: 1 };
|
|
71
|
+
}
|
|
72
|
+
// =============================================================================
|
|
73
|
+
// PDF Image XObject Writing
|
|
74
|
+
// =============================================================================
|
|
75
|
+
/**
|
|
76
|
+
* Write an image XObject (JPEG or PNG) to the writer.
|
|
77
|
+
* Returns the allocated object number.
|
|
78
|
+
*/
|
|
79
|
+
export function writeImageXObject(writer, data, format) {
|
|
80
|
+
if (format === "png") {
|
|
81
|
+
return writePngImageXObject(writer, data);
|
|
82
|
+
}
|
|
83
|
+
return writeJpegImageXObject(writer, data);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Write a JPEG image using DCTDecode (raw JPEG data embedded directly).
|
|
87
|
+
*/
|
|
88
|
+
function writeJpegImageXObject(writer, data) {
|
|
89
|
+
const objNum = writer.allocObject();
|
|
90
|
+
const dims = parseJpegDimensions(data);
|
|
91
|
+
const dict = new PdfDict()
|
|
92
|
+
.set("Type", "/XObject")
|
|
93
|
+
.set("Subtype", "/Image")
|
|
94
|
+
.set("Width", pdfNumber(dims.width))
|
|
95
|
+
.set("Height", pdfNumber(dims.height))
|
|
96
|
+
.set("ColorSpace", "/DeviceRGB")
|
|
97
|
+
.set("BitsPerComponent", "8")
|
|
98
|
+
.set("Filter", "/DCTDecode");
|
|
99
|
+
writer.addStreamObject(objNum, dict, data);
|
|
100
|
+
return objNum;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Write a PNG image: decode to raw RGB, create SMask for alpha if needed.
|
|
104
|
+
*/
|
|
105
|
+
function writePngImageXObject(writer, data) {
|
|
106
|
+
const png = decodePng(data);
|
|
107
|
+
const objNum = writer.allocObject();
|
|
108
|
+
const dict = new PdfDict()
|
|
109
|
+
.set("Type", "/XObject")
|
|
110
|
+
.set("Subtype", "/Image")
|
|
111
|
+
.set("Width", pdfNumber(png.width))
|
|
112
|
+
.set("Height", pdfNumber(png.height))
|
|
113
|
+
.set("ColorSpace", "/DeviceRGB")
|
|
114
|
+
.set("BitsPerComponent", pdfNumber(png.bitsPerComponent));
|
|
115
|
+
if (png.alpha) {
|
|
116
|
+
const smaskObjNum = writer.allocObject();
|
|
117
|
+
const smaskDict = new PdfDict()
|
|
118
|
+
.set("Type", "/XObject")
|
|
119
|
+
.set("Subtype", "/Image")
|
|
120
|
+
.set("Width", pdfNumber(png.width))
|
|
121
|
+
.set("Height", pdfNumber(png.height))
|
|
122
|
+
.set("ColorSpace", "/DeviceGray")
|
|
123
|
+
.set("BitsPerComponent", "8");
|
|
124
|
+
writer.addStreamObject(smaskObjNum, smaskDict, png.alpha);
|
|
125
|
+
dict.set("SMask", pdfRef(smaskObjNum));
|
|
126
|
+
}
|
|
127
|
+
writer.addStreamObject(objNum, dict, png.pixels);
|
|
128
|
+
return objNum;
|
|
129
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF editor — modify existing PDF documents.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - Adding new pages with free-form content
|
|
6
|
+
* - Adding text/shapes/images to existing pages (overlay)
|
|
7
|
+
* - Filling form fields (AcroForm)
|
|
8
|
+
* - Copying pages from other PDFs (merge)
|
|
9
|
+
* - Preserving page properties (Rotate, CropBox, etc.) and metadata
|
|
10
|
+
*
|
|
11
|
+
* Note: save() rebuilds the PDF from scratch rather than using incremental
|
|
12
|
+
* updates. This is simpler and more reliable but means object numbers change
|
|
13
|
+
* and existing digital signatures will be invalidated.
|
|
14
|
+
*
|
|
15
|
+
* @example Edit an existing PDF:
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { PdfEditor } from "@cj-tech-master/excelts/pdf";
|
|
18
|
+
*
|
|
19
|
+
* const editor = PdfEditor.load(existingPdfBytes);
|
|
20
|
+
* editor.getPage(0).drawText("APPROVED", { x: 200, y: 400, fontSize: 48, color: { r: 0, g: 0.5, b: 0 } });
|
|
21
|
+
* editor.setFormField("name", "John Doe");
|
|
22
|
+
* const result = await editor.save();
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import type { PdfContentStream } from "../core/pdf-stream.js";
|
|
26
|
+
import type { PdfFormField } from "../reader/form-extractor.js";
|
|
27
|
+
import { PdfPageBuilder } from "./document-builder.js";
|
|
28
|
+
import type { DrawTextOptions, DrawRectOptions, DrawCircleOptions, DrawEllipseOptions, DrawLineOptions, DrawImageOptions, DrawPathOptions, PathOp, PageOptions, AnnotationOptions, FormFieldOptions, PdfSignatureOptions } from "./document-builder.js";
|
|
29
|
+
/** Options for loading a PDF for editing. */
|
|
30
|
+
export interface LoadOptions {
|
|
31
|
+
/** Password for encrypted PDFs. */
|
|
32
|
+
password?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Proxy for an existing page that allows overlaying new content.
|
|
36
|
+
* New content is drawn on top of existing content via a separate content stream.
|
|
37
|
+
*/
|
|
38
|
+
export declare class PdfEditorPage {
|
|
39
|
+
/** Page width in points. */
|
|
40
|
+
get width(): number;
|
|
41
|
+
/** Page height in points. */
|
|
42
|
+
get height(): number;
|
|
43
|
+
/**
|
|
44
|
+
* Measure the width of a text string in points.
|
|
45
|
+
*/
|
|
46
|
+
measureText(text: string, options?: {
|
|
47
|
+
fontSize?: number;
|
|
48
|
+
fontFamily?: string;
|
|
49
|
+
bold?: boolean;
|
|
50
|
+
italic?: boolean;
|
|
51
|
+
}): number;
|
|
52
|
+
/**
|
|
53
|
+
* Draw text on this existing page (overlaid on top).
|
|
54
|
+
*/
|
|
55
|
+
drawText(text: string, options: DrawTextOptions): this;
|
|
56
|
+
/**
|
|
57
|
+
* Draw a rectangle on this existing page.
|
|
58
|
+
*/
|
|
59
|
+
drawRect(options: DrawRectOptions): this;
|
|
60
|
+
/**
|
|
61
|
+
* Draw a circle on this existing page.
|
|
62
|
+
*/
|
|
63
|
+
drawCircle(options: DrawCircleOptions): this;
|
|
64
|
+
/**
|
|
65
|
+
* Draw an ellipse on this existing page.
|
|
66
|
+
*/
|
|
67
|
+
drawEllipse(options: DrawEllipseOptions): this;
|
|
68
|
+
/**
|
|
69
|
+
* Draw a line on this existing page.
|
|
70
|
+
*/
|
|
71
|
+
drawLine(options: DrawLineOptions): this;
|
|
72
|
+
/**
|
|
73
|
+
* Draw an image on this existing page.
|
|
74
|
+
*/
|
|
75
|
+
drawImage(options: DrawImageOptions): this;
|
|
76
|
+
/**
|
|
77
|
+
* Get the raw overlay content stream.
|
|
78
|
+
*/
|
|
79
|
+
getContentStream(): PdfContentStream;
|
|
80
|
+
/**
|
|
81
|
+
* Add an annotation to this existing page (Highlight, Text, FreeText, Stamp, etc.).
|
|
82
|
+
*/
|
|
83
|
+
addAnnotation(options: AnnotationOptions): this;
|
|
84
|
+
/**
|
|
85
|
+
* Add a form field to this existing page.
|
|
86
|
+
*/
|
|
87
|
+
addFormField(options: FormFieldOptions): this;
|
|
88
|
+
/**
|
|
89
|
+
* Draw an SVG path on this existing page.
|
|
90
|
+
*/
|
|
91
|
+
drawSvgPath(d: string, options?: DrawPathOptions): this;
|
|
92
|
+
/**
|
|
93
|
+
* Draw a complex path from a list of path operations.
|
|
94
|
+
*/
|
|
95
|
+
drawPath(ops: PathOp[], options?: DrawPathOptions): this;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Editor for modifying existing PDF documents.
|
|
99
|
+
*
|
|
100
|
+
* Load an existing PDF, overlay content on existing pages, fill form fields,
|
|
101
|
+
* add new pages, copy pages from other documents, and save.
|
|
102
|
+
*/
|
|
103
|
+
export declare class PdfEditor {
|
|
104
|
+
private _doc;
|
|
105
|
+
private _password;
|
|
106
|
+
private _pages;
|
|
107
|
+
private _newPages;
|
|
108
|
+
private _fontManager;
|
|
109
|
+
private _formFieldUpdates;
|
|
110
|
+
private _copiedPages;
|
|
111
|
+
private _signaturePlaceholder;
|
|
112
|
+
private constructor();
|
|
113
|
+
/**
|
|
114
|
+
* Load a PDF for editing.
|
|
115
|
+
*
|
|
116
|
+
* @param data - Raw PDF file bytes
|
|
117
|
+
* @param options - Load options (e.g., password)
|
|
118
|
+
* @returns A PdfEditor instance
|
|
119
|
+
*/
|
|
120
|
+
static load(data: Uint8Array, options?: LoadOptions): PdfEditor;
|
|
121
|
+
/** Number of existing pages. */
|
|
122
|
+
get pageCount(): number;
|
|
123
|
+
/**
|
|
124
|
+
* Get an existing page for editing (overlaying content).
|
|
125
|
+
*
|
|
126
|
+
* @param index - 0-based page index
|
|
127
|
+
*/
|
|
128
|
+
getPage(index: number): PdfEditorPage;
|
|
129
|
+
/**
|
|
130
|
+
* Add a new blank page to the end of the document.
|
|
131
|
+
*/
|
|
132
|
+
addPage(options?: PageOptions): PdfPageBuilder;
|
|
133
|
+
/**
|
|
134
|
+
* Remove a page from the document.
|
|
135
|
+
*
|
|
136
|
+
* @param index - 0-based page index (of original pages only)
|
|
137
|
+
*/
|
|
138
|
+
removePage(index: number): this;
|
|
139
|
+
/**
|
|
140
|
+
* Set the rotation of an existing page.
|
|
141
|
+
*
|
|
142
|
+
* @param index - 0-based page index (of original pages only)
|
|
143
|
+
* @param degrees - Rotation in degrees (must be 0, 90, 180, or 270)
|
|
144
|
+
*/
|
|
145
|
+
rotatePage(index: number, degrees: number): this;
|
|
146
|
+
/**
|
|
147
|
+
* Split the document: save each page (or a subset) as a separate PDF.
|
|
148
|
+
*
|
|
149
|
+
* @param pageIndices - 0-based page indices to extract. Omit to split all pages.
|
|
150
|
+
* @returns Array of Uint8Array, one per requested page.
|
|
151
|
+
*/
|
|
152
|
+
splitPages(pageIndices?: number[]): Promise<Uint8Array[]>;
|
|
153
|
+
/**
|
|
154
|
+
* Embed a TrueType font for Unicode/CJK support.
|
|
155
|
+
*/
|
|
156
|
+
embedFont(fontBytes: Uint8Array): this;
|
|
157
|
+
/**
|
|
158
|
+
* Set the value of a form field.
|
|
159
|
+
* The field is identified by its fully qualified name (e.g., "form.address.city").
|
|
160
|
+
*
|
|
161
|
+
* @param fieldName - Fully qualified field name
|
|
162
|
+
* @param value - New value to set
|
|
163
|
+
*/
|
|
164
|
+
setFormField(fieldName: string, value: string): this;
|
|
165
|
+
/**
|
|
166
|
+
* Set multiple form field values at once.
|
|
167
|
+
*
|
|
168
|
+
* @param fields - Object mapping field names to values
|
|
169
|
+
*/
|
|
170
|
+
setFormFields(fields: Record<string, string>): this;
|
|
171
|
+
/**
|
|
172
|
+
* Get current form fields (before any modifications).
|
|
173
|
+
*/
|
|
174
|
+
getFormFields(): PdfFormField[];
|
|
175
|
+
/**
|
|
176
|
+
* Copy pages from another PDF document into this document.
|
|
177
|
+
*
|
|
178
|
+
* @param sourcePdf - Raw bytes of the source PDF
|
|
179
|
+
* @param pageIndices - 0-based page indices to copy. Omit to copy all pages.
|
|
180
|
+
* @param options - Load options for the source PDF (e.g., password)
|
|
181
|
+
*/
|
|
182
|
+
copyPagesFrom(sourcePdf: Uint8Array, pageIndices?: number[], options?: LoadOptions): this;
|
|
183
|
+
/**
|
|
184
|
+
* Save the modified PDF.
|
|
185
|
+
*
|
|
186
|
+
* Rebuilds the PDF from scratch — content streams, resources, and page
|
|
187
|
+
* properties are deep-cloned into a new document. Original metadata and
|
|
188
|
+
* XMP streams are preserved. Digital signatures will be invalidated.
|
|
189
|
+
*
|
|
190
|
+
* @returns The modified PDF as Uint8Array
|
|
191
|
+
*/
|
|
192
|
+
save(): Promise<Uint8Array>;
|
|
193
|
+
/**
|
|
194
|
+
* Save the modified PDF using incremental update.
|
|
195
|
+
*
|
|
196
|
+
* Appends new/modified objects after the original PDF bytes, preserving the
|
|
197
|
+
* original data byte-for-byte. This is ideal for overlays and form field
|
|
198
|
+
* updates on existing pages — it preserves digital signatures on unmodified
|
|
199
|
+
* content and produces smaller output.
|
|
200
|
+
*
|
|
201
|
+
* Falls back to {@link save} (full rebuild) if structural changes are
|
|
202
|
+
* present (new pages, copied pages, or removed pages).
|
|
203
|
+
*
|
|
204
|
+
* @returns The modified PDF as Uint8Array
|
|
205
|
+
*/
|
|
206
|
+
saveIncremental(): Promise<Uint8Array>;
|
|
207
|
+
/** Page-level keys to preserve when rebuilding page dicts. */
|
|
208
|
+
private static readonly _PAGE_PRESERVE_KEYS;
|
|
209
|
+
/**
|
|
210
|
+
* Sign this PDF with a digital signature.
|
|
211
|
+
*
|
|
212
|
+
* Performs a full save with an embedded PKCS#7 signature placeholder,
|
|
213
|
+
* then fills in the real CMS SignedData.
|
|
214
|
+
*
|
|
215
|
+
* @param options - Certificate, private key, and optional signer metadata
|
|
216
|
+
* @returns The signed PDF as Uint8Array
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```typescript
|
|
220
|
+
* const editor = PdfEditor.load(pdfBytes);
|
|
221
|
+
* const signed = await editor.sign({
|
|
222
|
+
* certificate: certDerBytes,
|
|
223
|
+
* privateKey: pkcs8DerBytes,
|
|
224
|
+
* name: "Jane Doe",
|
|
225
|
+
* reason: "Approval"
|
|
226
|
+
* });
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
sign(options: PdfSignatureOptions): Promise<Uint8Array>;
|
|
230
|
+
}
|