@lpdfio/lpdf 0.0.3
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/LICENSE +131 -0
- package/README.md +60 -0
- package/dist/_shared.d.ts +28 -0
- package/dist/_shared.js +5 -0
- package/dist/browser.js +468 -0
- package/dist/canvas.d.ts +131 -0
- package/dist/canvas.js +181 -0
- package/dist/engine.d.ts +70 -0
- package/dist/engine.js +191 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +34 -0
- package/dist/kit-to-xml.d.ts +30 -0
- package/dist/kit-to-xml.js +37 -0
- package/dist/kit.d.ts +109 -0
- package/dist/kit.js +78 -0
- package/dist/layout.d.ts +260 -0
- package/dist/layout.js +131 -0
- package/dist/wasm/lpdf-web.js +460 -0
- package/dist/wasm/lpdf.d.ts +95 -0
- package/dist/wasm/lpdf.js +368 -0
- package/dist/wasm/lpdf_bg.wasm +0 -0
- package/package.json +45 -0
package/dist/canvas.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LpdfCanvas = exports.CanvasTransform = void 0;
|
|
4
|
+
// ── CanvasTransform ───────────────────────────────────────────────────────────
|
|
5
|
+
class CanvasTransform {
|
|
6
|
+
constructor(matrix) {
|
|
7
|
+
this.matrix = matrix;
|
|
8
|
+
}
|
|
9
|
+
/** Rotate clockwise by `degrees` around the origin, or around (cx, cy). */
|
|
10
|
+
static rotate(degrees, cx = 0, cy = 0) {
|
|
11
|
+
const rad = (degrees * Math.PI) / 180;
|
|
12
|
+
const cos = Math.cos(rad);
|
|
13
|
+
const sin = Math.sin(rad);
|
|
14
|
+
const e = cx - cx * cos + cy * sin;
|
|
15
|
+
const f = cy - cx * sin - cy * cos;
|
|
16
|
+
return new CanvasTransform([cos, sin, -sin, cos, e, f]);
|
|
17
|
+
}
|
|
18
|
+
/** Scale by sx (and optionally sy; defaults to sx for uniform scale). */
|
|
19
|
+
static scale(sx, sy) {
|
|
20
|
+
return new CanvasTransform([sx, 0, 0, sy ?? sx, 0, 0]);
|
|
21
|
+
}
|
|
22
|
+
/** Translate by (tx, ty). */
|
|
23
|
+
static translate(tx, ty) {
|
|
24
|
+
return new CanvasTransform([1, 0, 0, 1, tx, ty]);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Combine: apply `other` first, then `this`.
|
|
28
|
+
* Equivalent to matrix multiplication: this × other.
|
|
29
|
+
*/
|
|
30
|
+
then(other) {
|
|
31
|
+
const [a1, b1, c1, d1, e1, f1] = this.matrix;
|
|
32
|
+
const [a2, b2, c2, d2, e2, f2] = other.matrix;
|
|
33
|
+
return new CanvasTransform([
|
|
34
|
+
a1 * a2 + c1 * b2,
|
|
35
|
+
b1 * a2 + d1 * b2,
|
|
36
|
+
a1 * c2 + c1 * d2,
|
|
37
|
+
b1 * c2 + d1 * d2,
|
|
38
|
+
a1 * e2 + c1 * f2 + e1,
|
|
39
|
+
b1 * e2 + d1 * f2 + f1,
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
/** Serialise to the `"matrix(a,b,c,d,e,f)"` string form that Rust `jattr()` reads. */
|
|
43
|
+
toString() {
|
|
44
|
+
return `matrix(${this.matrix.join(',')})`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.CanvasTransform = CanvasTransform;
|
|
48
|
+
// ── LpdfCanvas factory ────────────────────────────────────────────────────────
|
|
49
|
+
function rect(x, y, w, h, style) {
|
|
50
|
+
const attrs = {
|
|
51
|
+
x: String(x), y: String(y), w: String(w), h: String(h),
|
|
52
|
+
};
|
|
53
|
+
if (style?.fill !== undefined)
|
|
54
|
+
attrs['fill'] = style.fill;
|
|
55
|
+
if (style?.stroke !== undefined)
|
|
56
|
+
attrs['stroke'] = style.stroke;
|
|
57
|
+
if (style?.strokeWidth !== undefined)
|
|
58
|
+
attrs['stroke-width'] = String(style.strokeWidth);
|
|
59
|
+
if (style?.strokeDash !== undefined)
|
|
60
|
+
attrs['stroke-dash'] = style.strokeDash.join(' ');
|
|
61
|
+
if (style?.borderRadius !== undefined)
|
|
62
|
+
attrs['radius'] = String(style.borderRadius);
|
|
63
|
+
return { type: 'canvas-rect', attrs };
|
|
64
|
+
}
|
|
65
|
+
function line(x1, y1, x2, y2, style) {
|
|
66
|
+
const attrs = {
|
|
67
|
+
x1: String(x1), y1: String(y1), x2: String(x2), y2: String(y2),
|
|
68
|
+
};
|
|
69
|
+
if (style?.stroke !== undefined)
|
|
70
|
+
attrs['stroke'] = style.stroke;
|
|
71
|
+
if (style?.strokeWidth !== undefined)
|
|
72
|
+
attrs['stroke-width'] = String(style.strokeWidth);
|
|
73
|
+
if (style?.strokeDash !== undefined)
|
|
74
|
+
attrs['stroke-dash'] = style.strokeDash.join(' ');
|
|
75
|
+
if (style?.lineCap !== undefined)
|
|
76
|
+
attrs['line-cap'] = style.lineCap;
|
|
77
|
+
if (style?.lineJoin !== undefined)
|
|
78
|
+
attrs['line-join'] = style.lineJoin;
|
|
79
|
+
return { type: 'canvas-line', attrs };
|
|
80
|
+
}
|
|
81
|
+
function ellipse(cx, cy, rx, ry, style) {
|
|
82
|
+
const attrs = {
|
|
83
|
+
cx: String(cx), cy: String(cy), rx: String(rx), ry: String(ry),
|
|
84
|
+
};
|
|
85
|
+
if (style?.fill !== undefined)
|
|
86
|
+
attrs['fill'] = style.fill;
|
|
87
|
+
if (style?.stroke !== undefined)
|
|
88
|
+
attrs['stroke'] = style.stroke;
|
|
89
|
+
if (style?.strokeWidth !== undefined)
|
|
90
|
+
attrs['stroke-width'] = String(style.strokeWidth);
|
|
91
|
+
if (style?.strokeDash !== undefined)
|
|
92
|
+
attrs['stroke-dash'] = style.strokeDash.join(' ');
|
|
93
|
+
return { type: 'canvas-ellipse', attrs };
|
|
94
|
+
}
|
|
95
|
+
function circle(cx, cy, r, style) {
|
|
96
|
+
const attrs = {
|
|
97
|
+
cx: String(cx), cy: String(cy), r: String(r),
|
|
98
|
+
};
|
|
99
|
+
if (style?.fill !== undefined)
|
|
100
|
+
attrs['fill'] = style.fill;
|
|
101
|
+
if (style?.stroke !== undefined)
|
|
102
|
+
attrs['stroke'] = style.stroke;
|
|
103
|
+
if (style?.strokeWidth !== undefined)
|
|
104
|
+
attrs['stroke-width'] = String(style.strokeWidth);
|
|
105
|
+
if (style?.strokeDash !== undefined)
|
|
106
|
+
attrs['stroke-dash'] = style.strokeDash.join(' ');
|
|
107
|
+
return { type: 'canvas-circle', attrs };
|
|
108
|
+
}
|
|
109
|
+
function path(d, style) {
|
|
110
|
+
const attrs = { d };
|
|
111
|
+
if (style?.fill !== undefined)
|
|
112
|
+
attrs['fill'] = style.fill;
|
|
113
|
+
if (style?.stroke !== undefined)
|
|
114
|
+
attrs['stroke'] = style.stroke;
|
|
115
|
+
if (style?.strokeWidth !== undefined)
|
|
116
|
+
attrs['stroke-width'] = String(style.strokeWidth);
|
|
117
|
+
if (style?.strokeDash !== undefined)
|
|
118
|
+
attrs['stroke-dash'] = style.strokeDash.join(' ');
|
|
119
|
+
if (style?.fillRuleEvenodd !== undefined)
|
|
120
|
+
attrs['fill-rule'] = style.fillRuleEvenodd ? 'evenodd' : 'nonzero';
|
|
121
|
+
if (style?.lineCap !== undefined)
|
|
122
|
+
attrs['line-cap'] = style.lineCap;
|
|
123
|
+
if (style?.lineJoin !== undefined)
|
|
124
|
+
attrs['line-join'] = style.lineJoin;
|
|
125
|
+
return { type: 'canvas-path', attrs };
|
|
126
|
+
}
|
|
127
|
+
function canvasText(x, y, content, style, runs) {
|
|
128
|
+
const attrs = { x: String(x), y: String(y) };
|
|
129
|
+
if (style?.font !== undefined)
|
|
130
|
+
attrs['font'] = style.font;
|
|
131
|
+
if (style?.size !== undefined)
|
|
132
|
+
attrs['font-size'] = String(style.size);
|
|
133
|
+
if (style?.color !== undefined)
|
|
134
|
+
attrs['color'] = style.color;
|
|
135
|
+
if (style?.align !== undefined)
|
|
136
|
+
attrs['align'] = style.align;
|
|
137
|
+
if (style?.lineHeight !== undefined)
|
|
138
|
+
attrs['line-height'] = String(style.lineHeight);
|
|
139
|
+
if (style?.width !== undefined)
|
|
140
|
+
attrs['w'] = String(style.width);
|
|
141
|
+
const node = { type: 'canvas-text', text: content, attrs };
|
|
142
|
+
if (runs && runs.length > 0) {
|
|
143
|
+
node.runs = runs.map(r => {
|
|
144
|
+
const runAttrs = {};
|
|
145
|
+
if (r.font !== undefined)
|
|
146
|
+
runAttrs['font'] = r.font;
|
|
147
|
+
if (r.size !== undefined)
|
|
148
|
+
runAttrs['font-size'] = String(r.size);
|
|
149
|
+
if (r.color !== undefined)
|
|
150
|
+
runAttrs['color'] = r.color;
|
|
151
|
+
return { text: r.text, attrs: runAttrs };
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return node;
|
|
155
|
+
}
|
|
156
|
+
function img(x, y, w, h, name) {
|
|
157
|
+
return {
|
|
158
|
+
type: 'canvas-img',
|
|
159
|
+
attrs: { x: String(x), y: String(y), w: String(w), h: String(h), name },
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function layer(nodes, options) {
|
|
163
|
+
const attrs = {};
|
|
164
|
+
if (options?.page !== undefined)
|
|
165
|
+
attrs['page'] = options.page;
|
|
166
|
+
if (options?.opacity !== undefined)
|
|
167
|
+
attrs['opacity'] = String(options.opacity);
|
|
168
|
+
if (options?.transform !== undefined)
|
|
169
|
+
attrs['transform'] = options.transform.toString();
|
|
170
|
+
return { type: 'canvas-layer', attrs, nodes };
|
|
171
|
+
}
|
|
172
|
+
exports.LpdfCanvas = Object.freeze({
|
|
173
|
+
rect,
|
|
174
|
+
line,
|
|
175
|
+
ellipse,
|
|
176
|
+
circle,
|
|
177
|
+
path,
|
|
178
|
+
text: canvasText,
|
|
179
|
+
img,
|
|
180
|
+
layer,
|
|
181
|
+
});
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { RenderOptions } from './_shared';
|
|
2
|
+
import type { LpdfDocument } from './kit';
|
|
3
|
+
/** Thrown when the lpdf engine returns a layout or parse error. */
|
|
4
|
+
export declare class LpdfRenderError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/** PDF permission flags for RC4-128 encryption. All flags default to `true` (allowed). */
|
|
8
|
+
export interface EncryptPermissions {
|
|
9
|
+
print?: boolean;
|
|
10
|
+
modify?: boolean;
|
|
11
|
+
copy?: boolean;
|
|
12
|
+
annotate?: boolean;
|
|
13
|
+
fill_forms?: boolean;
|
|
14
|
+
accessibility?: boolean;
|
|
15
|
+
assemble?: boolean;
|
|
16
|
+
print_hq?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** RC4-128 encryption options passed to {@link LpdfEngine.setEncryption}. */
|
|
19
|
+
export interface EncryptOptions {
|
|
20
|
+
/** Open password shown to readers. Empty string = no open password required. */
|
|
21
|
+
userPassword: string;
|
|
22
|
+
/** Owner (permissions) password. Required; must be non-empty. */
|
|
23
|
+
ownerPassword: string;
|
|
24
|
+
/** Permission flags applied to the document. Omitted flags default to `true`. */
|
|
25
|
+
permissions?: EncryptPermissions;
|
|
26
|
+
}
|
|
27
|
+
export declare class LpdfEngine {
|
|
28
|
+
private readonly _licenseKey;
|
|
29
|
+
private readonly _opts;
|
|
30
|
+
private readonly _fonts;
|
|
31
|
+
private readonly _images;
|
|
32
|
+
private _disposed;
|
|
33
|
+
private _encrypt;
|
|
34
|
+
constructor(licenseKey: string, options?: RenderOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Register raw TTF/OTF bytes for a custom font name used in `<font src="…">`.
|
|
37
|
+
* Call before `renderPdf`. Returns `this` for chaining.
|
|
38
|
+
*/
|
|
39
|
+
loadFont(name: string, bytes: Uint8Array): this;
|
|
40
|
+
/**
|
|
41
|
+
* Register raw image bytes (PNG or JPEG) for an image name used in `<img name="…">`.
|
|
42
|
+
* Call before `renderPdf`. Returns `this` for chaining.
|
|
43
|
+
*/
|
|
44
|
+
loadImage(name: string, bytes: Uint8Array): this;
|
|
45
|
+
/**
|
|
46
|
+
* Configure RC4-128 encryption for all subsequent `renderPdf` calls.
|
|
47
|
+
* Returns `this` for chaining.
|
|
48
|
+
*/
|
|
49
|
+
setEncryption(options: EncryptOptions): this;
|
|
50
|
+
/**
|
|
51
|
+
* Remove any previously configured encryption.
|
|
52
|
+
* Returns `this` for chaining.
|
|
53
|
+
*/
|
|
54
|
+
clearEncryption(): this;
|
|
55
|
+
/**
|
|
56
|
+
* Release held resources. Idempotent. Subsequent `renderPdf` / `loadFont`
|
|
57
|
+
* calls after disposal will throw.
|
|
58
|
+
*/
|
|
59
|
+
dispose(): void;
|
|
60
|
+
[Symbol.dispose](): void;
|
|
61
|
+
private _throwIfDisposed;
|
|
62
|
+
/**
|
|
63
|
+
* Render an lpdf XML string to PDF bytes (Node.js).
|
|
64
|
+
*/
|
|
65
|
+
renderPdf(input: string, callOptions?: RenderOptions): Promise<Uint8Array>;
|
|
66
|
+
/**
|
|
67
|
+
* Render an `LpdfDocument` tree (built with `LpdfKit`) to PDF bytes (Node.js).
|
|
68
|
+
*/
|
|
69
|
+
renderPdf(input: LpdfDocument, callOptions?: RenderOptions): Promise<Uint8Array>;
|
|
70
|
+
}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LpdfEngine = exports.LpdfRenderError = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
// require() path is relative to the compiled output at dist/engine.js.
|
|
6
|
+
// In the published package, wasm-pack artifacts live in dist/wasm/.
|
|
7
|
+
// In the monorepo, `npm run build` copies them there from <root>/dist/node/.
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
9
|
+
const wasmModule = require('./wasm/lpdf.js');
|
|
10
|
+
const WasmEngine = wasmModule.LpdfEngine;
|
|
11
|
+
/** Thrown when the lpdf engine returns a layout or parse error. */
|
|
12
|
+
class LpdfRenderError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'LpdfRenderError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.LpdfRenderError = LpdfRenderError;
|
|
19
|
+
class LpdfEngine {
|
|
20
|
+
constructor(licenseKey, options = {}) {
|
|
21
|
+
this._fonts = new Map();
|
|
22
|
+
this._images = new Map();
|
|
23
|
+
this._disposed = false;
|
|
24
|
+
this._encrypt = null;
|
|
25
|
+
this._licenseKey = licenseKey;
|
|
26
|
+
this._opts = options;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Register raw TTF/OTF bytes for a custom font name used in `<font src="…">`.
|
|
30
|
+
* Call before `renderPdf`. Returns `this` for chaining.
|
|
31
|
+
*/
|
|
32
|
+
loadFont(name, bytes) {
|
|
33
|
+
this._throwIfDisposed();
|
|
34
|
+
this._fonts.set(name, bytes);
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Register raw image bytes (PNG or JPEG) for an image name used in `<img name="…">`.
|
|
39
|
+
* Call before `renderPdf`. Returns `this` for chaining.
|
|
40
|
+
*/
|
|
41
|
+
loadImage(name, bytes) {
|
|
42
|
+
this._throwIfDisposed();
|
|
43
|
+
this._images.set(name, bytes);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Configure RC4-128 encryption for all subsequent `renderPdf` calls.
|
|
48
|
+
* Returns `this` for chaining.
|
|
49
|
+
*/
|
|
50
|
+
setEncryption(options) {
|
|
51
|
+
this._throwIfDisposed();
|
|
52
|
+
this._encrypt = options;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Remove any previously configured encryption.
|
|
57
|
+
* Returns `this` for chaining.
|
|
58
|
+
*/
|
|
59
|
+
clearEncryption() {
|
|
60
|
+
this._throwIfDisposed();
|
|
61
|
+
this._encrypt = null;
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Release held resources. Idempotent. Subsequent `renderPdf` / `loadFont`
|
|
66
|
+
* calls after disposal will throw.
|
|
67
|
+
*/
|
|
68
|
+
dispose() {
|
|
69
|
+
this._disposed = true;
|
|
70
|
+
}
|
|
71
|
+
[Symbol.dispose]() { this.dispose(); }
|
|
72
|
+
_throwIfDisposed() {
|
|
73
|
+
if (this._disposed)
|
|
74
|
+
throw new Error('LpdfEngine has been disposed.');
|
|
75
|
+
}
|
|
76
|
+
async renderPdf(input, callOptions = {}) {
|
|
77
|
+
this._throwIfDisposed();
|
|
78
|
+
// Merge fonts: instance-level loadFont() calls take precedence over the
|
|
79
|
+
// deprecated fontBytes option, which is kept for one-version compat.
|
|
80
|
+
const allFonts = new Map(this._fonts);
|
|
81
|
+
const extraBytes = { ...this._opts.fontBytes, ...callOptions.fontBytes };
|
|
82
|
+
for (const [name, bytes] of Object.entries(extraBytes)) {
|
|
83
|
+
if (!allFonts.has(name))
|
|
84
|
+
allFonts.set(name, bytes);
|
|
85
|
+
}
|
|
86
|
+
const engine = new WasmEngine(this._licenseKey);
|
|
87
|
+
const createdOn = callOptions.createdOn ?? this._opts.createdOn;
|
|
88
|
+
if (createdOn) {
|
|
89
|
+
engine.set_created_on(createdOn);
|
|
90
|
+
}
|
|
91
|
+
let pdf;
|
|
92
|
+
try {
|
|
93
|
+
if (typeof input === 'string') {
|
|
94
|
+
// XML path — auto-load fonts and images declared via src="…".
|
|
95
|
+
const xml = input;
|
|
96
|
+
for (const [key, src] of extractAssetSrcs(xml, 'font')) {
|
|
97
|
+
if (!allFonts.has(key)) {
|
|
98
|
+
try {
|
|
99
|
+
allFonts.set(key, (0, node_fs_1.readFileSync)(src));
|
|
100
|
+
}
|
|
101
|
+
catch { /* not found; Rust falls back to Helvetica */ }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (const [name, bytes] of allFonts) {
|
|
105
|
+
engine.load_font(name, bytes);
|
|
106
|
+
}
|
|
107
|
+
for (const [name, bytes] of this._images) {
|
|
108
|
+
engine.load_image(name, bytes);
|
|
109
|
+
}
|
|
110
|
+
// Auto-load images declared via <image src="…"> that weren't pre-loaded.
|
|
111
|
+
for (const [key, src] of extractAssetSrcs(xml, 'image')) {
|
|
112
|
+
if (!this._images.has(key)) {
|
|
113
|
+
try {
|
|
114
|
+
engine.load_image(key, (0, node_fs_1.readFileSync)(src));
|
|
115
|
+
}
|
|
116
|
+
catch { /* skip unresolvable image */ }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (this._encrypt) {
|
|
120
|
+
const permsJson = JSON.stringify(this._encrypt.permissions ?? {});
|
|
121
|
+
engine.set_encryption(this._encrypt.userPassword, this._encrypt.ownerPassword, permsJson);
|
|
122
|
+
}
|
|
123
|
+
const dataJson = callOptions.data != null ? JSON.stringify(callOptions.data) : null;
|
|
124
|
+
pdf = engine.render_pdf(xml, dataJson);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// JSON (Kit tree) path — pass JSON directly to render_tree_pdf.
|
|
128
|
+
const json = JSON.stringify(input);
|
|
129
|
+
for (const [key, src] of extractFontSrcsFromJson(json)) {
|
|
130
|
+
if (!allFonts.has(key)) {
|
|
131
|
+
try {
|
|
132
|
+
allFonts.set(key, (0, node_fs_1.readFileSync)(src));
|
|
133
|
+
}
|
|
134
|
+
catch { /* not found; Rust falls back to Helvetica */ }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
for (const [name, bytes] of allFonts) {
|
|
138
|
+
engine.load_font(name, bytes);
|
|
139
|
+
}
|
|
140
|
+
for (const [name, bytes] of this._images) {
|
|
141
|
+
engine.load_image(name, bytes);
|
|
142
|
+
}
|
|
143
|
+
if (this._encrypt) {
|
|
144
|
+
const permsJson = JSON.stringify(this._encrypt.permissions ?? {});
|
|
145
|
+
engine.set_encryption(this._encrypt.userPassword, this._encrypt.ownerPassword, permsJson);
|
|
146
|
+
}
|
|
147
|
+
pdf = engine.render_tree_pdf(json);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
engine.free();
|
|
152
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
153
|
+
throw new LpdfRenderError(msg);
|
|
154
|
+
}
|
|
155
|
+
engine.free();
|
|
156
|
+
return pdf;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exports.LpdfEngine = LpdfEngine;
|
|
160
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
161
|
+
/** Extract `ref??name → src` pairs from `<font>` or `<image>` tags in XML. */
|
|
162
|
+
function extractAssetSrcs(xml, tag) {
|
|
163
|
+
const result = new Map();
|
|
164
|
+
const re = tag === 'font' ? /<font\s[^>]*>/g : /<image\s[^>]*>/g;
|
|
165
|
+
for (const match of xml.matchAll(re)) {
|
|
166
|
+
const t = match[0];
|
|
167
|
+
const name = /\bname="([^"]*)"/.exec(t)?.[1];
|
|
168
|
+
const ref = /\bref="([^"]*)"/.exec(t)?.[1];
|
|
169
|
+
const src = /\bsrc="([^"]*)"/.exec(t)?.[1];
|
|
170
|
+
const key = ref ?? name;
|
|
171
|
+
if (key && src)
|
|
172
|
+
result.set(key, src);
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
/** Extract `ref??name → src` pairs from `attrs.tokens.fonts[name].src` in a kit JSON string. */
|
|
177
|
+
function extractFontSrcsFromJson(json) {
|
|
178
|
+
const result = new Map();
|
|
179
|
+
try {
|
|
180
|
+
const doc = JSON.parse(json);
|
|
181
|
+
const fonts = doc?.attrs?.tokens?.fonts ?? {};
|
|
182
|
+
for (const [name, def] of Object.entries(fonts)) {
|
|
183
|
+
if (def.src) {
|
|
184
|
+
const key = def.ref ?? name;
|
|
185
|
+
result.set(key, def.src);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch { /* ignore */ }
|
|
190
|
+
return result;
|
|
191
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export * from './engine';
|
|
2
|
+
export * from './kit';
|
|
3
|
+
export * from './layout';
|
|
4
|
+
export * from './canvas';
|
|
5
|
+
export * from './_shared';
|
|
6
|
+
export { kitToXml } from './kit-to-xml';
|
|
7
|
+
import { LpdfEngine } from './engine';
|
|
8
|
+
export declare const Lpdf: Readonly<{
|
|
9
|
+
Engine: typeof LpdfEngine;
|
|
10
|
+
Kit: Readonly<{
|
|
11
|
+
layout: (nodes?: import("./layout").LpdfNode[]) => import("./kit").LpdfLayoutBlock;
|
|
12
|
+
canvas: (layers?: import("./canvas").LpdfCanvasLayerNode[]) => import("./kit").LpdfCanvasBlock;
|
|
13
|
+
section: (input?: import("./kit").SectionInput) => import("./kit").LpdfSectionNode;
|
|
14
|
+
document: (input?: import("./kit").DocumentInput) => import("./kit").LpdfDocument;
|
|
15
|
+
}>;
|
|
16
|
+
Layout: Readonly<{
|
|
17
|
+
stack: (nodes?: import("./layout").LpdfNode[], options?: import("./layout").StackOptions) => import("./layout").LpdfContainerNode;
|
|
18
|
+
flank: (nodes?: import("./layout").LpdfNode[], options?: import("./layout").FlankOptions) => import("./layout").LpdfContainerNode;
|
|
19
|
+
split: (nodes?: import("./layout").LpdfNode[], options?: import("./layout").SplitOptions) => import("./layout").LpdfContainerNode;
|
|
20
|
+
cluster: (nodes?: import("./layout").LpdfNode[], options?: import("./layout").ClusterOptions) => import("./layout").LpdfContainerNode;
|
|
21
|
+
grid: (nodes?: import("./layout").LpdfNode[], options?: import("./layout").GridOptions) => import("./layout").LpdfContainerNode;
|
|
22
|
+
frame: (nodes?: import("./layout").LpdfNode[], options?: import("./layout").FrameOptions) => import("./layout").LpdfContainerNode;
|
|
23
|
+
link: (nodes?: import("./layout").LpdfNode[], options?: import("./layout").LinkOptions) => import("./layout").LpdfContainerNode;
|
|
24
|
+
text: (nodes?: (string | import("./layout").LpdfSpanNode)[], options?: import("./layout").TextOptions) => import("./layout").LpdfTextNode;
|
|
25
|
+
span: (nodes?: string[], options?: import("./layout").SpanOptions) => import("./layout").LpdfSpanNode;
|
|
26
|
+
divider: (options?: import("./layout").DividerOptions) => import("./layout").LpdfDividerNode;
|
|
27
|
+
img: (options: import("./layout").ImgOptions) => import("./layout").LpdfImgNode;
|
|
28
|
+
barcode: (options: import("./layout").BarcodeOptions) => import("./layout").LpdfBarcodeNode;
|
|
29
|
+
table: (options: import("./layout").TableOptions, nodes?: (import("./layout").LpdfTheadNode | import("./layout").LpdfTrNode)[]) => import("./layout").LpdfTableNode;
|
|
30
|
+
thead: (nodes?: import("./layout").LpdfTdNode[], options?: import("./layout").TheadOptions) => import("./layout").LpdfTheadNode;
|
|
31
|
+
tr: (nodes?: import("./layout").LpdfTdNode[], options?: import("./layout").TrOptions) => import("./layout").LpdfTrNode;
|
|
32
|
+
td: (nodes?: import("./layout").LpdfNode[], options?: import("./layout").TdOptions) => import("./layout").LpdfTdNode;
|
|
33
|
+
region: (pin: string, nodes?: import("./layout").LpdfNode[], options?: import("./layout").RegionOptions) => import("./layout").LpdfRegionNode;
|
|
34
|
+
}>;
|
|
35
|
+
Canvas: Readonly<{
|
|
36
|
+
rect: (x: number, y: number, w: number, h: number, style?: import("./canvas").CanvasRectStyle) => import("./canvas").LpdfCanvasRectNode;
|
|
37
|
+
line: (x1: number, y1: number, x2: number, y2: number, style?: import("./canvas").CanvasLineStyle) => import("./canvas").LpdfCanvasLineNode;
|
|
38
|
+
ellipse: (cx: number, cy: number, rx: number, ry: number, style?: import("./canvas").CanvasEllipseStyle) => import("./canvas").LpdfCanvasEllipseNode;
|
|
39
|
+
circle: (cx: number, cy: number, r: number, style?: import("./canvas").CanvasEllipseStyle) => import("./canvas").LpdfCanvasCircleNode;
|
|
40
|
+
path: (d: string, style?: import("./canvas").CanvasPathStyle) => import("./canvas").LpdfCanvasPathNode;
|
|
41
|
+
text: (x: number, y: number, content: string, style?: import("./canvas").CanvasTextStyle, runs?: import("./canvas").CanvasRun[]) => import("./canvas").LpdfCanvasTextNode;
|
|
42
|
+
img: (x: number, y: number, w: number, h: number, name: string) => import("./canvas").LpdfCanvasImgNode;
|
|
43
|
+
layer: (nodes: import("./canvas").LpdfCanvasPrimitiveNode[], options?: import("./canvas").CanvasLayerOptions) => import("./canvas").LpdfCanvasLayerNode;
|
|
44
|
+
}>;
|
|
45
|
+
}>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Lpdf = exports.kitToXml = void 0;
|
|
18
|
+
__exportStar(require("./engine"), exports);
|
|
19
|
+
__exportStar(require("./kit"), exports);
|
|
20
|
+
__exportStar(require("./layout"), exports);
|
|
21
|
+
__exportStar(require("./canvas"), exports);
|
|
22
|
+
__exportStar(require("./_shared"), exports);
|
|
23
|
+
var kit_to_xml_1 = require("./kit-to-xml");
|
|
24
|
+
Object.defineProperty(exports, "kitToXml", { enumerable: true, get: function () { return kit_to_xml_1.kitToXml; } });
|
|
25
|
+
const engine_1 = require("./engine");
|
|
26
|
+
const kit_1 = require("./kit");
|
|
27
|
+
const layout_1 = require("./layout");
|
|
28
|
+
const canvas_1 = require("./canvas");
|
|
29
|
+
exports.Lpdf = Object.freeze({
|
|
30
|
+
Engine: engine_1.LpdfEngine,
|
|
31
|
+
Kit: kit_1.LpdfKit,
|
|
32
|
+
Layout: layout_1.LpdfLayout,
|
|
33
|
+
Canvas: canvas_1.LpdfCanvas,
|
|
34
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kitToXml — serialise an LpdfDocument tree (produced by LpdfKit) back to an
|
|
3
|
+
* lpdf XML string that can be passed directly to `LpdfEngine.renderPdf()` or
|
|
4
|
+
* saved to disk.
|
|
5
|
+
*
|
|
6
|
+
* The serialisation is performed by the Rust core (exposed as a WASM export)
|
|
7
|
+
* so every adapter produces identical XML and any schema change only requires
|
|
8
|
+
* a single Rust update.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { LpdfKit, kitToXml } from './dist/index.js';
|
|
13
|
+
*
|
|
14
|
+
* const doc = LpdfKit.document({ nodes: [...], options: { ... } });
|
|
15
|
+
* const xml = kitToXml(doc);
|
|
16
|
+
* console.log(xml);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { LpdfDocument } from './kit';
|
|
20
|
+
/**
|
|
21
|
+
* Convert an `LpdfDocument` tree (built with `LpdfKit`) into an lpdf XML
|
|
22
|
+
* string.
|
|
23
|
+
*
|
|
24
|
+
* The returned string can be passed directly to `LpdfEngine.renderPdf()` or
|
|
25
|
+
* written to a `.xml` file.
|
|
26
|
+
*
|
|
27
|
+
* @param doc - The document tree returned by `LpdfKit.document(...)`.
|
|
28
|
+
* @returns A well-formed XML string with an `<?xml ...?>` declaration.
|
|
29
|
+
*/
|
|
30
|
+
export declare function kitToXml(doc: LpdfDocument): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* kitToXml — serialise an LpdfDocument tree (produced by LpdfKit) back to an
|
|
4
|
+
* lpdf XML string that can be passed directly to `LpdfEngine.renderPdf()` or
|
|
5
|
+
* saved to disk.
|
|
6
|
+
*
|
|
7
|
+
* The serialisation is performed by the Rust core (exposed as a WASM export)
|
|
8
|
+
* so every adapter produces identical XML and any schema change only requires
|
|
9
|
+
* a single Rust update.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { LpdfKit, kitToXml } from './dist/index.js';
|
|
14
|
+
*
|
|
15
|
+
* const doc = LpdfKit.document({ nodes: [...], options: { ... } });
|
|
16
|
+
* const xml = kitToXml(doc);
|
|
17
|
+
* console.log(xml);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.kitToXml = kitToXml;
|
|
22
|
+
// Load the standalone kit_to_xml export from the compiled WASM module.
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
24
|
+
const { kit_to_xml: wasmKitToXml } = require('./wasm/lpdf.js');
|
|
25
|
+
/**
|
|
26
|
+
* Convert an `LpdfDocument` tree (built with `LpdfKit`) into an lpdf XML
|
|
27
|
+
* string.
|
|
28
|
+
*
|
|
29
|
+
* The returned string can be passed directly to `LpdfEngine.renderPdf()` or
|
|
30
|
+
* written to a `.xml` file.
|
|
31
|
+
*
|
|
32
|
+
* @param doc - The document tree returned by `LpdfKit.document(...)`.
|
|
33
|
+
* @returns A well-formed XML string with an `<?xml ...?>` declaration.
|
|
34
|
+
*/
|
|
35
|
+
function kitToXml(doc) {
|
|
36
|
+
return wasmKitToXml(JSON.stringify(doc));
|
|
37
|
+
}
|