@jsonpdf/renderer 0.1.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/anchors.d.ts +13 -0
- package/dist/anchors.d.ts.map +1 -0
- package/dist/anchors.js +37 -0
- package/dist/anchors.js.map +1 -0
- package/dist/band-expander.d.ts +25 -0
- package/dist/band-expander.d.ts.map +1 -0
- package/dist/band-expander.js +193 -0
- package/dist/band-expander.js.map +1 -0
- package/dist/bookmarks.d.ts +22 -0
- package/dist/bookmarks.d.ts.map +1 -0
- package/dist/bookmarks.js +78 -0
- package/dist/bookmarks.js.map +1 -0
- package/dist/columns.d.ts +18 -0
- package/dist/columns.d.ts.map +1 -0
- package/dist/columns.js +31 -0
- package/dist/columns.js.map +1 -0
- package/dist/context.d.ts +8 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/coordinate.d.ts +14 -0
- package/dist/coordinate.d.ts.map +1 -0
- package/dist/coordinate.js +16 -0
- package/dist/coordinate.js.map +1 -0
- package/dist/data.d.ts +18 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +56 -0
- package/dist/data.js.map +1 -0
- package/dist/expression.d.ts +13 -0
- package/dist/expression.d.ts.map +1 -0
- package/dist/expression.js +90 -0
- package/dist/expression.js.map +1 -0
- package/dist/font-loader.d.ts +8 -0
- package/dist/font-loader.d.ts.map +1 -0
- package/dist/font-loader.js +29 -0
- package/dist/font-loader.js.map +1 -0
- package/dist/fonts.d.ts +19 -0
- package/dist/fonts.d.ts.map +1 -0
- package/dist/fonts.js +124 -0
- package/dist/fonts.js.map +1 -0
- package/dist/footnotes.d.ts +34 -0
- package/dist/footnotes.d.ts.map +1 -0
- package/dist/footnotes.js +103 -0
- package/dist/footnotes.js.map +1 -0
- package/dist/gradient.d.ts +19 -0
- package/dist/gradient.d.ts.map +1 -0
- package/dist/gradient.js +176 -0
- package/dist/gradient.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +54 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +680 -0
- package/dist/layout.js.map +1 -0
- package/dist/renderer.d.ts +19 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +570 -0
- package/dist/renderer.js.map +1 -0
- package/dist/style-resolver.d.ts +15 -0
- package/dist/style-resolver.d.ts.map +1 -0
- package/dist/style-resolver.js +39 -0
- package/dist/style-resolver.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate.js","sourceRoot":"","sources":["../src/coordinate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,SAAiB,EACjB,SAAiB,EACjB,UAAkB,EAClB,SAAiB,EACjB,UAAkB;IAElB,OAAO;QACL,CAAC,EAAE,UAAU,GAAG,SAAS;QACzB,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS;KACtC,CAAC;AACJ,CAAC"}
|
package/dist/data.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { JSONSchema } from '@jsonpdf/core';
|
|
2
|
+
/** Validate user data against a template's dataSchema. Throws on failure. */
|
|
3
|
+
export declare function validateData(data: Record<string, unknown>, schema: JSONSchema): void;
|
|
4
|
+
/**
|
|
5
|
+
* Return a copy of `data` with `default` values from the schema filled in
|
|
6
|
+
* for any missing properties. Uses AJV's `useDefaults` option.
|
|
7
|
+
*/
|
|
8
|
+
export declare function applySchemaDefaults(data: Record<string, unknown>, schema: JSONSchema): Record<string, unknown>;
|
|
9
|
+
/** Resolve a dot-separated path against a data object. */
|
|
10
|
+
export declare function resolveDotPath(data: Record<string, unknown>, path: string): unknown;
|
|
11
|
+
export interface ItemContext {
|
|
12
|
+
item: unknown;
|
|
13
|
+
itemName: string;
|
|
14
|
+
index: number;
|
|
15
|
+
}
|
|
16
|
+
/** Build a Liquid scope from data, page info, and optional item context. */
|
|
17
|
+
export declare function buildScope(data: Record<string, unknown>, pageNumber: number, totalPages: number, itemContext?: ItemContext): Record<string, unknown>;
|
|
18
|
+
//# sourceMappingURL=data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAWpF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,UAAU,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAUnF;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,WAAW,GACxB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWzB"}
|
package/dist/data.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { validateWithSchema, applySchemaDefaults as coreApplyDefaults } from '@jsonpdf/core';
|
|
2
|
+
/** Validate user data against a template's dataSchema. Throws on failure. */
|
|
3
|
+
export function validateData(data, schema) {
|
|
4
|
+
// Skip validation if schema is empty or just { type: 'object' }
|
|
5
|
+
const keys = Object.keys(schema);
|
|
6
|
+
if (keys.length === 0)
|
|
7
|
+
return;
|
|
8
|
+
if (keys.length === 1 && schema['type'] === 'object')
|
|
9
|
+
return;
|
|
10
|
+
const result = validateWithSchema(schema, data);
|
|
11
|
+
if (!result.valid) {
|
|
12
|
+
const messages = result.errors.map((e) => `${e.path}: ${e.message}`).join('; ');
|
|
13
|
+
throw new Error(`Data validation failed: ${messages}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Return a copy of `data` with `default` values from the schema filled in
|
|
18
|
+
* for any missing properties. Uses AJV's `useDefaults` option.
|
|
19
|
+
*/
|
|
20
|
+
export function applySchemaDefaults(data, schema) {
|
|
21
|
+
const keys = Object.keys(schema);
|
|
22
|
+
if (keys.length === 0)
|
|
23
|
+
return data;
|
|
24
|
+
if (keys.length === 1 && schema['type'] === 'object')
|
|
25
|
+
return data;
|
|
26
|
+
return coreApplyDefaults(schema, data);
|
|
27
|
+
}
|
|
28
|
+
/** Resolve a dot-separated path against a data object. */
|
|
29
|
+
export function resolveDotPath(data, path) {
|
|
30
|
+
if (!path)
|
|
31
|
+
return undefined;
|
|
32
|
+
const segments = path.split('.');
|
|
33
|
+
let current = data;
|
|
34
|
+
for (const segment of segments) {
|
|
35
|
+
if (current === null || current === undefined)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (typeof current !== 'object')
|
|
38
|
+
return undefined;
|
|
39
|
+
current = current[segment];
|
|
40
|
+
}
|
|
41
|
+
return current;
|
|
42
|
+
}
|
|
43
|
+
/** Build a Liquid scope from data, page info, and optional item context. */
|
|
44
|
+
export function buildScope(data, pageNumber, totalPages, itemContext) {
|
|
45
|
+
const scope = {
|
|
46
|
+
...data,
|
|
47
|
+
_pageNumber: pageNumber,
|
|
48
|
+
_totalPages: totalPages,
|
|
49
|
+
};
|
|
50
|
+
if (itemContext) {
|
|
51
|
+
scope[itemContext.itemName] = itemContext.item;
|
|
52
|
+
scope['_index'] = itemContext.index;
|
|
53
|
+
}
|
|
54
|
+
return scope;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=data.js.map
|
package/dist/data.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE7F,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,IAA6B,EAAE,MAAkB;IAC5E,gEAAgE;IAChE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC9B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,QAAQ;QAAE,OAAO;IAE7D,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChF,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAA6B,EAC7B,MAAkB;IAElB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAElE,OAAO,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,cAAc,CAAC,IAA6B,EAAE,IAAY;IACxE,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,OAAO,GAAY,IAAI,CAAC;IAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAChE,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAClD,OAAO,GAAI,OAAmC,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAQD,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CACxB,IAA6B,EAC7B,UAAkB,EAClB,UAAkB,EAClB,WAAyB;IAEzB,MAAM,KAAK,GAA4B;QACrC,GAAG,IAAI;QACP,WAAW,EAAE,UAAU;QACvB,WAAW,EAAE,UAAU;KACxB,CAAC;IACF,IAAI,WAAW,EAAE,CAAC;QAChB,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC;QAC/C,KAAK,CAAC,QAAQ,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ExpressionEngine {
|
|
2
|
+
/** Resolve Liquid expressions in a string. */
|
|
3
|
+
resolve(template: string, scope: Record<string, unknown>): Promise<string>;
|
|
4
|
+
/** Recursively resolve all string values in a props object. */
|
|
5
|
+
resolveProps(props: Record<string, unknown>, scope: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
6
|
+
/** Evaluate a Liquid expression as a boolean. */
|
|
7
|
+
evaluate(expression: string, scope: Record<string, unknown>): Promise<boolean>;
|
|
8
|
+
/** Register a custom Liquid filter. */
|
|
9
|
+
registerFilter(name: string, fn: (value: unknown, ...args: unknown[]) => unknown): void;
|
|
10
|
+
}
|
|
11
|
+
/** Create a new expression engine with custom filters. */
|
|
12
|
+
export declare function createExpressionEngine(): ExpressionEngine;
|
|
13
|
+
//# sourceMappingURL=expression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression.d.ts","sourceRoot":"","sources":["../src/expression.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3E,+DAA+D;IAC/D,YAAY,CACV,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpC,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,uCAAuC;IACvC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CACzF;AAED,0DAA0D;AAC1D,wBAAgB,sBAAsB,IAAI,gBAAgB,CAkGzD"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Liquid } from 'liquidjs';
|
|
2
|
+
/** Create a new expression engine with custom filters. */
|
|
3
|
+
export function createExpressionEngine() {
|
|
4
|
+
const liquid = new Liquid({
|
|
5
|
+
strictVariables: false,
|
|
6
|
+
strictFilters: false,
|
|
7
|
+
});
|
|
8
|
+
// Cache parsed Liquid templates to avoid re-parsing identical expression strings
|
|
9
|
+
const parseCache = new Map();
|
|
10
|
+
function getParsed(templateStr) {
|
|
11
|
+
let parsed = parseCache.get(templateStr);
|
|
12
|
+
if (!parsed) {
|
|
13
|
+
parsed = liquid.parse(templateStr);
|
|
14
|
+
parseCache.set(templateStr, parsed);
|
|
15
|
+
}
|
|
16
|
+
return parsed;
|
|
17
|
+
}
|
|
18
|
+
liquid.registerFilter('money', (value, currency) => {
|
|
19
|
+
const num = Number(value);
|
|
20
|
+
if (isNaN(num))
|
|
21
|
+
return String(value);
|
|
22
|
+
return new Intl.NumberFormat('en-US', {
|
|
23
|
+
style: 'currency',
|
|
24
|
+
currency: typeof currency === 'string' ? currency : 'USD',
|
|
25
|
+
}).format(num);
|
|
26
|
+
});
|
|
27
|
+
liquid.registerFilter('pad', (value, width, char) => {
|
|
28
|
+
return String(value).padStart(Number(width) || 0, typeof char === 'string' ? char : ' ');
|
|
29
|
+
});
|
|
30
|
+
async function resolve(template, scope) {
|
|
31
|
+
const parsed = getParsed(template);
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
33
|
+
const result = await liquid.render(parsed, scope);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
// Matches a pure Liquid expression: exactly {{ expr }} with nothing else
|
|
37
|
+
const PURE_EXPR_RE = /^\{\{\s*(.+?)\s*\}\}$/;
|
|
38
|
+
async function resolveValue(value, scope) {
|
|
39
|
+
if (typeof value === 'string') {
|
|
40
|
+
// For pure expressions like "{{ departmentSummary }}", return the raw
|
|
41
|
+
// scope value when it resolves to a non-primitive (array/object).
|
|
42
|
+
// This allows properties like chart.dataSource to receive actual arrays
|
|
43
|
+
// instead of their stringified representation.
|
|
44
|
+
const match = value.match(PURE_EXPR_RE);
|
|
45
|
+
if (match) {
|
|
46
|
+
try {
|
|
47
|
+
const raw = await liquid.evalValue(match[1], scope);
|
|
48
|
+
if (raw !== null && raw !== undefined && typeof raw === 'object') {
|
|
49
|
+
return raw;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// evalValue can't handle all Liquid syntax (e.g. filters with |).
|
|
54
|
+
// Fall through to normal string resolution.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return resolve(value, scope);
|
|
58
|
+
}
|
|
59
|
+
if (Array.isArray(value)) {
|
|
60
|
+
return Promise.all(value.map((item) => resolveValue(item, scope)));
|
|
61
|
+
}
|
|
62
|
+
if (value !== null && typeof value === 'object') {
|
|
63
|
+
const result = {};
|
|
64
|
+
for (const [k, v] of Object.entries(value)) {
|
|
65
|
+
result[k] = await resolveValue(v, scope);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
async function resolveProps(props, scope) {
|
|
72
|
+
return (await resolveValue(props, scope));
|
|
73
|
+
}
|
|
74
|
+
async function evaluate(expression, scope) {
|
|
75
|
+
// Safety note: `expression` is interpolated directly into a Liquid template.
|
|
76
|
+
// This is safe because conditions come from template definitions (developer-authored),
|
|
77
|
+
// not from end-user data. If template authoring is ever opened to untrusted users,
|
|
78
|
+
// this would need to be sanitized against template injection.
|
|
79
|
+
const wrapped = `{% if ${expression} %}true{% endif %}`;
|
|
80
|
+
const parsed = getParsed(wrapped);
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
82
|
+
const result = await liquid.render(parsed, scope);
|
|
83
|
+
return result.trim() === 'true';
|
|
84
|
+
}
|
|
85
|
+
function registerFilter(name, fn) {
|
|
86
|
+
liquid.registerFilter(name, fn);
|
|
87
|
+
}
|
|
88
|
+
return { resolve, resolveProps, evaluate, registerFilter };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression.js","sourceRoot":"","sources":["../src/expression.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load font bytes from a source path or URL.
|
|
3
|
+
*
|
|
4
|
+
* - HTTP/HTTPS URLs are fetched via fetch() with a 30s timeout
|
|
5
|
+
* - file:// URLs and local paths are handled by the platform-specific readFileBytes
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadFontBytes(src: string): Promise<Uint8Array>;
|
|
8
|
+
//# sourceMappingURL=font-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"font-loader.d.ts","sourceRoot":"","sources":["../src/font-loader.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkBpE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { readFileBytes } from '@jsonpdf/plugins';
|
|
2
|
+
/** Default fetch timeout in milliseconds (30 seconds). */
|
|
3
|
+
const FETCH_TIMEOUT_MS = 30_000;
|
|
4
|
+
/**
|
|
5
|
+
* Load font bytes from a source path or URL.
|
|
6
|
+
*
|
|
7
|
+
* - HTTP/HTTPS URLs are fetched via fetch() with a 30s timeout
|
|
8
|
+
* - file:// URLs and local paths are handled by the platform-specific readFileBytes
|
|
9
|
+
*/
|
|
10
|
+
export async function loadFontBytes(src) {
|
|
11
|
+
if (src.startsWith('http://') || src.startsWith('https://')) {
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const timeoutId = setTimeout(() => {
|
|
14
|
+
controller.abort();
|
|
15
|
+
}, FETCH_TIMEOUT_MS);
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch(src, { signal: controller.signal });
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(`Failed to fetch font: ${String(response.status)} ${response.statusText}`);
|
|
20
|
+
}
|
|
21
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
clearTimeout(timeoutId);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return readFileBytes(src);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=font-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"font-loader.js","sourceRoot":"","sources":["../src/font-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,0DAA0D;AAC1D,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;YACjE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YAC7F,CAAC;YACD,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QACtD,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
|
package/dist/fonts.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PDFDocument } from 'pdf-lib';
|
|
2
|
+
import type { FontMap } from '@jsonpdf/plugins';
|
|
3
|
+
import type { Template, FontDeclaration } from '@jsonpdf/core';
|
|
4
|
+
export interface FontSpec {
|
|
5
|
+
family: string;
|
|
6
|
+
weight: 'normal' | 'bold';
|
|
7
|
+
style: 'normal' | 'italic';
|
|
8
|
+
}
|
|
9
|
+
/** Map a numeric font weight to 'normal' or 'bold'. */
|
|
10
|
+
export declare function mapWeight(weight?: number): 'normal' | 'bold';
|
|
11
|
+
/**
|
|
12
|
+
* Embed all required fonts. Each font spec must have a matching
|
|
13
|
+
* FontDeclaration with base64-encoded data. Fontkit must be registered
|
|
14
|
+
* on the doc before calling this function.
|
|
15
|
+
*/
|
|
16
|
+
export declare function embedFonts(doc: PDFDocument, specs: FontSpec[], fontDeclarations: FontDeclaration[]): Promise<FontMap>;
|
|
17
|
+
/** Collect all unique font specs referenced in a template's styles. */
|
|
18
|
+
export declare function collectFontSpecs(template: Template): FontSpec[];
|
|
19
|
+
//# sourceMappingURL=fonts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,KAAK,EAAE,QAAQ,EAAoB,eAAe,EAAW,MAAM,eAAe,CAAC;AAG1F,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAC5B;AAED,uDAAuD;AACvD,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAG5D;AAuBD;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,QAAQ,EAAE,EACjB,gBAAgB,EAAE,eAAe,EAAE,GAClC,OAAO,CAAC,OAAO,CAAC,CAiClB;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAyE/D"}
|
package/dist/fonts.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { fontKey } from '@jsonpdf/plugins';
|
|
2
|
+
import { resolveElementStyle } from './style-resolver.js';
|
|
3
|
+
/** Map a numeric font weight to 'normal' or 'bold'. */
|
|
4
|
+
export function mapWeight(weight) {
|
|
5
|
+
if (weight === undefined)
|
|
6
|
+
return 'normal';
|
|
7
|
+
return weight > 500 ? 'bold' : 'normal';
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Find a FontDeclaration that matches a given font spec.
|
|
11
|
+
* Tries exact match (family + weight + style) first, then falls back
|
|
12
|
+
* to any declaration with the same family name.
|
|
13
|
+
*/
|
|
14
|
+
function findDeclaration(spec, declarations) {
|
|
15
|
+
const familyLower = spec.family.toLowerCase();
|
|
16
|
+
let familyFallback;
|
|
17
|
+
for (const d of declarations) {
|
|
18
|
+
if (d.family.toLowerCase() !== familyLower)
|
|
19
|
+
continue;
|
|
20
|
+
const weightMatch = mapWeight(d.weight) === spec.weight;
|
|
21
|
+
const styleMatch = (d.style ?? 'normal') === spec.style;
|
|
22
|
+
if (weightMatch && styleMatch)
|
|
23
|
+
return d;
|
|
24
|
+
familyFallback ??= d;
|
|
25
|
+
}
|
|
26
|
+
return familyFallback;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Embed all required fonts. Each font spec must have a matching
|
|
30
|
+
* FontDeclaration with base64-encoded data. Fontkit must be registered
|
|
31
|
+
* on the doc before calling this function.
|
|
32
|
+
*/
|
|
33
|
+
export async function embedFonts(doc, specs, fontDeclarations) {
|
|
34
|
+
const fonts = new Map();
|
|
35
|
+
const uniqueKeys = new Set();
|
|
36
|
+
for (const spec of specs) {
|
|
37
|
+
uniqueKeys.add(fontKey(spec.family, spec.weight, spec.style));
|
|
38
|
+
}
|
|
39
|
+
for (const key of uniqueKeys) {
|
|
40
|
+
if (fonts.has(key))
|
|
41
|
+
continue;
|
|
42
|
+
const [family, weight, style] = key.split(':');
|
|
43
|
+
const spec = { family, weight, style };
|
|
44
|
+
const decl = findDeclaration(spec, fontDeclarations);
|
|
45
|
+
if (!decl) {
|
|
46
|
+
throw new Error(`No font declaration found for "${key}". Declare the font in template.fonts.`);
|
|
47
|
+
}
|
|
48
|
+
const binary = atob(decl.data);
|
|
49
|
+
const bytes = new Uint8Array(binary.length);
|
|
50
|
+
for (let i = 0; i < binary.length; i++) {
|
|
51
|
+
bytes[i] = binary.charCodeAt(i);
|
|
52
|
+
}
|
|
53
|
+
const embedded = await doc.embedFont(bytes, { subset: true });
|
|
54
|
+
fonts.set(key, embedded);
|
|
55
|
+
}
|
|
56
|
+
return fonts;
|
|
57
|
+
}
|
|
58
|
+
/** Collect all unique font specs referenced in a template's styles. */
|
|
59
|
+
export function collectFontSpecs(template) {
|
|
60
|
+
const specs = new Map();
|
|
61
|
+
const defaultFamily = template.defaultStyle.fontFamily;
|
|
62
|
+
function addFromStyle(style) {
|
|
63
|
+
const family = style.fontFamily ?? defaultFamily;
|
|
64
|
+
const weight = style.fontWeight ?? 'normal';
|
|
65
|
+
const fontStyle = style.fontStyle ?? 'normal';
|
|
66
|
+
const key = fontKey(family, weight, fontStyle);
|
|
67
|
+
if (!specs.has(key)) {
|
|
68
|
+
specs.set(key, { family, weight, style: fontStyle });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Always include the default font so the font map is never empty
|
|
72
|
+
addFromStyle(template.defaultStyle);
|
|
73
|
+
// Named styles
|
|
74
|
+
for (const style of Object.values(template.styles)) {
|
|
75
|
+
addFromStyle(style);
|
|
76
|
+
}
|
|
77
|
+
// Walk elements recursively, computing the RESOLVED style for each
|
|
78
|
+
// so that cross-source font combinations (e.g. fontFamily from override +
|
|
79
|
+
// fontWeight from named style) are properly collected.
|
|
80
|
+
function walkElement(element) {
|
|
81
|
+
// Resolved style captures the full merge of defaults + named + overrides
|
|
82
|
+
addFromStyle(resolveElementStyle(element, template.styles, template.defaultStyle));
|
|
83
|
+
// Conditional style variations
|
|
84
|
+
if (element.conditionalStyles) {
|
|
85
|
+
for (const cs of element.conditionalStyles) {
|
|
86
|
+
const effective = {
|
|
87
|
+
...element,
|
|
88
|
+
style: cs.style ?? element.style,
|
|
89
|
+
styleOverrides: { ...(element.styleOverrides ?? {}), ...(cs.styleOverrides ?? {}) },
|
|
90
|
+
};
|
|
91
|
+
addFromStyle(resolveElementStyle(effective, template.styles, template.defaultStyle));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Rich text StyledRun variations
|
|
95
|
+
const content = element.properties.content;
|
|
96
|
+
if (Array.isArray(content)) {
|
|
97
|
+
for (const run of content) {
|
|
98
|
+
if (run.styleOverrides || run.style) {
|
|
99
|
+
const effective = {
|
|
100
|
+
...element,
|
|
101
|
+
style: run.style ?? element.style,
|
|
102
|
+
styleOverrides: { ...(element.styleOverrides ?? {}), ...(run.styleOverrides ?? {}) },
|
|
103
|
+
};
|
|
104
|
+
addFromStyle(resolveElementStyle(effective, template.styles, template.defaultStyle));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Recurse into nested elements (containers, frames)
|
|
109
|
+
if (element.elements) {
|
|
110
|
+
for (const child of element.elements) {
|
|
111
|
+
walkElement(child);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const section of template.sections) {
|
|
116
|
+
for (const band of section.bands) {
|
|
117
|
+
for (const element of band.elements) {
|
|
118
|
+
walkElement(element);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return [...specs.values()];
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { PDFPage } from 'pdf-lib';
|
|
2
|
+
import type { RichContent, Style } from '@jsonpdf/core';
|
|
3
|
+
import type { FontMap } from '@jsonpdf/plugins';
|
|
4
|
+
/** A single footnote entry collected during rendering. */
|
|
5
|
+
export interface FootnoteEntry {
|
|
6
|
+
/** 1-based footnote number. */
|
|
7
|
+
number: number;
|
|
8
|
+
/** The footnote content (string or StyledRun[]). */
|
|
9
|
+
content: RichContent;
|
|
10
|
+
}
|
|
11
|
+
/** Accumulated footnotes for a single page. */
|
|
12
|
+
export interface PageFootnotes {
|
|
13
|
+
entries: FootnoteEntry[];
|
|
14
|
+
/** Total height consumed by footnotes + separator line. */
|
|
15
|
+
totalHeight: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Measure the total height of a set of footnotes.
|
|
19
|
+
* Includes separator line height + gap + content lines.
|
|
20
|
+
*/
|
|
21
|
+
export declare function measureFootnoteHeight(entries: FootnoteEntry[], baseStyle: Style, fonts: FontMap, width: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* Render footnotes at the bottom of a page.
|
|
24
|
+
*
|
|
25
|
+
* @param page - The pdf-lib page
|
|
26
|
+
* @param entries - Footnote entries to render
|
|
27
|
+
* @param x - Left edge of the footnote area (left margin)
|
|
28
|
+
* @param y - Top of the footnote area in pdf-lib coordinates
|
|
29
|
+
* @param width - Available width for footnotes
|
|
30
|
+
* @param baseStyle - Base style for footnote text
|
|
31
|
+
* @param fonts - Font map
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderFootnotes(page: PDFPage, entries: FootnoteEntry[], x: number, y: number, width: number, baseStyle: Style, fonts: FontMap): void;
|
|
34
|
+
//# sourceMappingURL=footnotes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footnotes.d.ts","sourceRoot":"","sources":["../src/footnotes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAGhD,0DAA0D;AAC1D,MAAM,WAAW,aAAa;IAC5B,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,aAAa,EAAE,EACxB,SAAS,EAAE,KAAK,EAChB,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,GACZ,MAAM,CAsBR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,aAAa,EAAE,EACxB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,KAAK,EAChB,KAAK,EAAE,OAAO,GACb,IAAI,CA8DN"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { rgb } from 'pdf-lib';
|
|
2
|
+
import { parseColor } from '@jsonpdf/core';
|
|
3
|
+
import { getFont, getLineHeight, wrapText } from '@jsonpdf/plugins';
|
|
4
|
+
const SEPARATOR_WIDTH_FRACTION = 1 / 3;
|
|
5
|
+
const SEPARATOR_THICKNESS = 0.5;
|
|
6
|
+
const SEPARATOR_GAP = 4; // gap above and below separator
|
|
7
|
+
const FOOTNOTE_FONT_SIZE_RATIO = 0.8; // footnote text is 80% of base font size
|
|
8
|
+
/**
|
|
9
|
+
* Measure the total height of a set of footnotes.
|
|
10
|
+
* Includes separator line height + gap + content lines.
|
|
11
|
+
*/
|
|
12
|
+
export function measureFootnoteHeight(entries, baseStyle, fonts, width) {
|
|
13
|
+
if (entries.length === 0)
|
|
14
|
+
return 0;
|
|
15
|
+
const fontSize = (baseStyle.fontSize ?? 12) * FOOTNOTE_FONT_SIZE_RATIO;
|
|
16
|
+
const superFontSize = fontSize * 0.65;
|
|
17
|
+
const style = { ...baseStyle, fontSize };
|
|
18
|
+
const font = getFont(fonts, style);
|
|
19
|
+
const lh = getLineHeight(style);
|
|
20
|
+
// Separator line + gaps
|
|
21
|
+
let height = SEPARATOR_GAP + SEPARATOR_THICKNESS + SEPARATOR_GAP;
|
|
22
|
+
// Each footnote wrapped to available width
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
const text = getFootnoteText(entry);
|
|
25
|
+
const numWidth = font.widthOfTextAtSize(String(entry.number), superFontSize);
|
|
26
|
+
const indent = numWidth + 3;
|
|
27
|
+
const wrapped = wrapText(text, font, fontSize, width - indent, lh);
|
|
28
|
+
height += wrapped.lines.length * lh;
|
|
29
|
+
}
|
|
30
|
+
return height;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Render footnotes at the bottom of a page.
|
|
34
|
+
*
|
|
35
|
+
* @param page - The pdf-lib page
|
|
36
|
+
* @param entries - Footnote entries to render
|
|
37
|
+
* @param x - Left edge of the footnote area (left margin)
|
|
38
|
+
* @param y - Top of the footnote area in pdf-lib coordinates
|
|
39
|
+
* @param width - Available width for footnotes
|
|
40
|
+
* @param baseStyle - Base style for footnote text
|
|
41
|
+
* @param fonts - Font map
|
|
42
|
+
*/
|
|
43
|
+
export function renderFootnotes(page, entries, x, y, width, baseStyle, fonts) {
|
|
44
|
+
if (entries.length === 0)
|
|
45
|
+
return;
|
|
46
|
+
const fontSize = (baseStyle.fontSize ?? 12) * FOOTNOTE_FONT_SIZE_RATIO;
|
|
47
|
+
const style = { ...baseStyle, fontSize };
|
|
48
|
+
const font = getFont(fonts, style);
|
|
49
|
+
const lh = getLineHeight(style);
|
|
50
|
+
const colorStr = style.color ?? '#000000';
|
|
51
|
+
const color = parseColor(colorStr);
|
|
52
|
+
const superFontSize = fontSize * 0.65;
|
|
53
|
+
let cursorY = y;
|
|
54
|
+
// Draw separator line (1/3 of page width)
|
|
55
|
+
cursorY -= SEPARATOR_GAP;
|
|
56
|
+
const lineWidth = width * SEPARATOR_WIDTH_FRACTION;
|
|
57
|
+
page.drawLine({
|
|
58
|
+
start: { x, y: cursorY },
|
|
59
|
+
end: { x: x + lineWidth, y: cursorY },
|
|
60
|
+
thickness: SEPARATOR_THICKNESS,
|
|
61
|
+
color: rgb(color.r, color.g, color.b),
|
|
62
|
+
});
|
|
63
|
+
cursorY -= SEPARATOR_THICKNESS + SEPARATOR_GAP;
|
|
64
|
+
// Render each footnote
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const text = getFootnoteText(entry);
|
|
67
|
+
const ascent = font.heightAtSize(fontSize, { descender: false });
|
|
68
|
+
// Draw superscript number
|
|
69
|
+
const superAscent = font.heightAtSize(superFontSize, { descender: false });
|
|
70
|
+
const superRaise = ascent * 0.35;
|
|
71
|
+
const numText = String(entry.number);
|
|
72
|
+
const numWidth = font.widthOfTextAtSize(numText, superFontSize);
|
|
73
|
+
const drawY = cursorY - ascent;
|
|
74
|
+
page.drawText(numText, {
|
|
75
|
+
x,
|
|
76
|
+
y: drawY + superRaise + (ascent - superAscent),
|
|
77
|
+
font,
|
|
78
|
+
size: superFontSize,
|
|
79
|
+
color: rgb(color.r, color.g, color.b),
|
|
80
|
+
});
|
|
81
|
+
// Draw footnote content text with wrapping
|
|
82
|
+
const indent = numWidth + 3;
|
|
83
|
+
const wrapped = wrapText(text, font, fontSize, width - indent, lh);
|
|
84
|
+
for (const line of wrapped.lines) {
|
|
85
|
+
const lineDrawY = cursorY - ascent;
|
|
86
|
+
page.drawText(line, {
|
|
87
|
+
x: x + indent,
|
|
88
|
+
y: lineDrawY,
|
|
89
|
+
font,
|
|
90
|
+
size: fontSize,
|
|
91
|
+
color: rgb(color.r, color.g, color.b),
|
|
92
|
+
});
|
|
93
|
+
cursorY -= lh;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Extract plain text from footnote content (string or StyledRun[]). */
|
|
98
|
+
function getFootnoteText(entry) {
|
|
99
|
+
if (typeof entry.content === 'string')
|
|
100
|
+
return entry.content;
|
|
101
|
+
return entry.content.map((run) => run.text).join('');
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=footnotes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footnotes.js","sourceRoot":"","sources":["../src/footnotes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PDFPage, PDFDocument } from 'pdf-lib';
|
|
2
|
+
import type { Gradient } from '@jsonpdf/core';
|
|
3
|
+
/**
|
|
4
|
+
* Draw a gradient-filled rectangle on a PDF page.
|
|
5
|
+
*
|
|
6
|
+
* Uses PDF shading patterns (Type 2 axial for linear, Type 3 radial for radial).
|
|
7
|
+
* The gradient is clipped to the specified rectangle.
|
|
8
|
+
*
|
|
9
|
+
* @param page - The pdf-lib page
|
|
10
|
+
* @param doc - The pdf-lib document
|
|
11
|
+
* @param x - Left edge in pdf-lib coordinates
|
|
12
|
+
* @param y - Bottom edge in pdf-lib coordinates (NOT top)
|
|
13
|
+
* @param width - Rectangle width
|
|
14
|
+
* @param height - Rectangle height
|
|
15
|
+
* @param gradient - The gradient definition
|
|
16
|
+
* @param opacity - Optional opacity (0-1)
|
|
17
|
+
*/
|
|
18
|
+
export declare function drawGradientRect(page: PDFPage, doc: PDFDocument, x: number, y: number, width: number, height: number, gradient: Gradient, opacity?: number): void;
|
|
19
|
+
//# sourceMappingURL=gradient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gradient.d.ts","sourceRoot":"","sources":["../src/gradient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGpD,OAAO,KAAK,EAAE,QAAQ,EAAkC,MAAM,eAAe,CAAC;AAE9E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,WAAW,EAChB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAiFN"}
|