@broadpaper/forme 0.2.1 → 0.3.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 +12 -9
- package/dist/{chunk-2PT5WRKQ.js → chunk-2C3MAW2U.js} +365 -76
- package/dist/chunk-2C3MAW2U.js.map +1 -0
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.js +5 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +69 -4
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-2PT5WRKQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Browserless PDF generation for BroadPaper, via the Forme WebAssembly engine. The recommended backend.
|
|
8
8
|
|
|
9
|
-
Part of [**BroadPaper**](https://www.broadpaper.com) — an embeddable visual report designer and PDF renderer.
|
|
9
|
+
Part of [**BroadPaper**](https://www.broadpaper.com/developers/) — an embeddable visual report designer and PDF renderer.
|
|
10
10
|
Your users design the report in your application; BroadPaper owns the layout, the branding,
|
|
11
11
|
the pagination and the output.
|
|
12
12
|
|
|
@@ -21,26 +21,29 @@ engine and nothing else to install. That is what makes BroadPaper deployable in
|
|
|
21
21
|
that has no browser in it, which is most of the containers a report renderer belongs in.
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
|
-
import {
|
|
24
|
+
import { createRegistry } from "@broadpaper/blocks";
|
|
25
|
+
import { renderPdfPaginated } from "@broadpaper/forme";
|
|
25
26
|
|
|
26
|
-
const { pdf, pages } = await
|
|
27
|
+
const { pdf, pages } = await renderPdfPaginated({ template, data, registry: createRegistry() });
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
Text is selectable, fonts are embedded, and output is byte-for-byte deterministic for
|
|
30
|
+
Text is selectable, the fonts you supply are embedded, and output is byte-for-byte deterministic for
|
|
30
31
|
identical input — so a document regenerated a year later is the same file, and a diff of two
|
|
31
32
|
renders shows what actually changed.
|
|
32
33
|
|
|
33
34
|
## Licensing
|
|
34
35
|
|
|
35
36
|
BroadPaper is **free to evaluate, with no trial clock and nothing behind a feature gate**.
|
|
36
|
-
Install it, use every feature
|
|
37
|
+
Install it, use every feature and evaluate it against your real data for as long as you like.
|
|
38
|
+
Production use needs a licence.
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
licence removes it
|
|
40
|
-
machine binding, no telemetry, and no call home at
|
|
40
|
+
In the software, the only difference without a licence is that generated PDFs carry an
|
|
41
|
+
evaluation mark, and a licence removes it. Contractually, the licence is also what permits
|
|
42
|
+
production use. No seats, no activation, no machine binding, no telemetry, and no call home at
|
|
43
|
+
any point.
|
|
41
44
|
|
|
42
45
|
- [How licensing works](https://www.broadpaper.com/docs/guide/licensing/)
|
|
43
|
-
- [Pricing](https://www.broadpaper.com/pricing/)
|
|
46
|
+
- [Pricing](https://www.broadpaper.com/developers/pricing/)
|
|
44
47
|
- [Licence agreement](https://www.broadpaper.com/legal/terms/)
|
|
45
48
|
|
|
46
49
|
## Documentation
|
|
@@ -892,7 +892,7 @@ var FormeMeasurer = class {
|
|
|
892
892
|
defaultPage: { size: "A4", margin: { top: 54, right: 54, bottom: 54, left: 54 }, wrap: true },
|
|
893
893
|
defaultStyle: {}
|
|
894
894
|
};
|
|
895
|
-
if (this.opts.fonts?.length) doc.fonts = this.opts.fonts;
|
|
895
|
+
if (this.opts.fonts?.length) doc.fonts = this.opts.fonts.map((f) => ({ ...f }));
|
|
896
896
|
const result = await this.opts.render(doc);
|
|
897
897
|
const pages = result.layout?.pages ?? [];
|
|
898
898
|
const toPx = 96 / 72;
|
|
@@ -947,6 +947,7 @@ var FormeEditorMeasurer = class {
|
|
|
947
947
|
}
|
|
948
948
|
}
|
|
949
949
|
if (!pending.length) return out;
|
|
950
|
+
const fonts = await this.config.fonts;
|
|
950
951
|
const measurer = new FormeMeasurer({
|
|
951
952
|
registry: opts.registry,
|
|
952
953
|
theme: opts.theme,
|
|
@@ -955,7 +956,7 @@ var FormeEditorMeasurer = class {
|
|
|
955
956
|
mode: opts.mode,
|
|
956
957
|
pageWidth: width,
|
|
957
958
|
margins: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
958
|
-
fonts
|
|
959
|
+
fonts,
|
|
959
960
|
render: this.config.render
|
|
960
961
|
});
|
|
961
962
|
const measured = await measurer.measure(pending, width);
|
|
@@ -983,47 +984,261 @@ import {
|
|
|
983
984
|
} from "@broadpaper/core";
|
|
984
985
|
import { mapStyle as mapStyle4 } from "@formepdf/shared";
|
|
985
986
|
import { producerString, resolveLicenseStatus, watermarkFor } from "@broadpaper/license";
|
|
987
|
+
|
|
988
|
+
// src/credit.ts
|
|
989
|
+
function creditNode(spec, width, ctx) {
|
|
990
|
+
const size = spec.fontSize;
|
|
991
|
+
const children = [];
|
|
992
|
+
if (spec.mark) {
|
|
993
|
+
children.push({
|
|
994
|
+
tag: "img",
|
|
995
|
+
props: { src: spec.mark, alt: "", style: { width: `${size}px`, height: `${size}px`, opacity: String(spec.opacity) } },
|
|
996
|
+
children: []
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
children.push({
|
|
1000
|
+
tag: "span",
|
|
1001
|
+
props: { style: { "font-size": `${size}px`, color: spec.color, opacity: String(spec.opacity), "line-height": "1.2" } },
|
|
1002
|
+
children: [spec.text]
|
|
1003
|
+
});
|
|
1004
|
+
const line = {
|
|
1005
|
+
tag: "div",
|
|
1006
|
+
props: {
|
|
1007
|
+
style: {
|
|
1008
|
+
display: "flex",
|
|
1009
|
+
"align-items": "center",
|
|
1010
|
+
gap: `${Math.max(2, Math.round(size * 0.5))}px`,
|
|
1011
|
+
width: `${width}px`,
|
|
1012
|
+
"justify-content": "center"
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
children
|
|
1016
|
+
};
|
|
1017
|
+
return translate(line, ctx)?.node ?? null;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// src/api.ts
|
|
986
1021
|
import { BroadPaper, configure, resolveLicenseStatus as resolveLicenseStatus2, verifyLicense, parseLicense } from "@broadpaper/license";
|
|
987
1022
|
import { computePageGeometry as pageGeometry, paginate, resolveValue } from "@broadpaper/core";
|
|
988
1023
|
|
|
1024
|
+
// src/long-tables.ts
|
|
1025
|
+
var EPS = 0.5;
|
|
1026
|
+
function round3(n) {
|
|
1027
|
+
return Math.round(n * 100) / 100;
|
|
1028
|
+
}
|
|
1029
|
+
function isRow(c) {
|
|
1030
|
+
return typeof c !== "string" && c.tag === "tr";
|
|
1031
|
+
}
|
|
1032
|
+
function bodyRowCount(table) {
|
|
1033
|
+
let n = 0;
|
|
1034
|
+
for (const c of table.children) {
|
|
1035
|
+
if (typeof c === "string") continue;
|
|
1036
|
+
if (c.tag === "tbody") n += c.children.filter(isRow).length;
|
|
1037
|
+
else if (c.tag === "tr") n++;
|
|
1038
|
+
}
|
|
1039
|
+
return n;
|
|
1040
|
+
}
|
|
1041
|
+
function longTables(tree, limit) {
|
|
1042
|
+
const out = [];
|
|
1043
|
+
const visit = (v, key) => {
|
|
1044
|
+
const own = typeof v.props["data-key"] === "string" ? v.props["data-key"] : key;
|
|
1045
|
+
if (v.tag === "table") {
|
|
1046
|
+
const rows = bodyRowCount(v);
|
|
1047
|
+
if (rows > limit && own) out.push({ table: v, key: own, bodyRows: rows });
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
for (const c of v.children) if (typeof c !== "string") visit(c, own);
|
|
1051
|
+
};
|
|
1052
|
+
visit(tree, void 0);
|
|
1053
|
+
return out;
|
|
1054
|
+
}
|
|
1055
|
+
function sliceTables(tree, tables, start, count) {
|
|
1056
|
+
const targets = new Set(tables.map((t) => t.table));
|
|
1057
|
+
const visit = (v) => {
|
|
1058
|
+
if (targets.has(v)) return sliceRows(v, start, count);
|
|
1059
|
+
let changed = false;
|
|
1060
|
+
const children = v.children.map((c) => {
|
|
1061
|
+
if (typeof c === "string") return c;
|
|
1062
|
+
const next = visit(c);
|
|
1063
|
+
if (next !== c) changed = true;
|
|
1064
|
+
return next;
|
|
1065
|
+
});
|
|
1066
|
+
return changed ? { ...v, children } : v;
|
|
1067
|
+
};
|
|
1068
|
+
return visit(tree);
|
|
1069
|
+
}
|
|
1070
|
+
function sliceRows(table, start, count) {
|
|
1071
|
+
let index = 0;
|
|
1072
|
+
const keep = () => {
|
|
1073
|
+
const i = index++;
|
|
1074
|
+
return i >= start && i < start + count;
|
|
1075
|
+
};
|
|
1076
|
+
const children = [];
|
|
1077
|
+
for (const c of table.children) {
|
|
1078
|
+
if (typeof c === "string") children.push(c);
|
|
1079
|
+
else if (c.tag === "tbody") children.push({ ...c, children: c.children.filter((r) => !isRow(r) || keep()) });
|
|
1080
|
+
else if (c.tag === "tr") {
|
|
1081
|
+
if (keep()) children.push(c);
|
|
1082
|
+
} else children.push(c);
|
|
1083
|
+
}
|
|
1084
|
+
return { ...table, children };
|
|
1085
|
+
}
|
|
1086
|
+
function sameRows(a, b) {
|
|
1087
|
+
return !!a && a.length === b.length && a[0]?.top === b[0]?.top && a[a.length - 1]?.top === b[b.length - 1]?.top;
|
|
1088
|
+
}
|
|
1089
|
+
function stitchTables(first, tables, batches) {
|
|
1090
|
+
const g = {
|
|
1091
|
+
...first,
|
|
1092
|
+
nodes: Object.fromEntries(Object.entries(first.nodes).map(([k, n]) => [k, { ...n }]))
|
|
1093
|
+
};
|
|
1094
|
+
const bottomOf = (rows) => Math.max(...rows.map((r) => r.top + r.height));
|
|
1095
|
+
const order = tables.map((t) => ({ t, rows: first.nodes[t.key]?.rows })).filter((x) => !!x.rows && x.rows.some((r) => r.kind === "body")).sort((a, b) => bottomOf(b.rows) - bottomOf(a.rows));
|
|
1096
|
+
for (const { t } of order) {
|
|
1097
|
+
const main = g.nodes[t.key].rows;
|
|
1098
|
+
const body = main.filter((r) => r.kind === "body");
|
|
1099
|
+
const last = body[body.length - 1];
|
|
1100
|
+
const bodyEnd = last.top + last.height;
|
|
1101
|
+
const gap = body.length > 1 ? Math.max(0, body[1].top - (body[0].top + body[0].height)) : 0;
|
|
1102
|
+
const extra = [];
|
|
1103
|
+
let cursor = bodyEnd + gap;
|
|
1104
|
+
for (const batch of batches) {
|
|
1105
|
+
const rows = batch.nodes[t.key]?.rows?.filter((r) => r.kind === "body") ?? [];
|
|
1106
|
+
if (!rows.length) continue;
|
|
1107
|
+
const base = rows[0].top;
|
|
1108
|
+
for (const r of rows) extra.push({ top: round3(cursor + r.top - base), height: r.height, kind: "body" });
|
|
1109
|
+
const end = rows[rows.length - 1];
|
|
1110
|
+
cursor += end.top + end.height - base + gap;
|
|
1111
|
+
}
|
|
1112
|
+
if (!extra.length) continue;
|
|
1113
|
+
const tail = extra[extra.length - 1];
|
|
1114
|
+
const delta = round3(tail.top + tail.height - bodyEnd);
|
|
1115
|
+
const merged = [
|
|
1116
|
+
...main.filter((r) => r.kind === "header"),
|
|
1117
|
+
...body,
|
|
1118
|
+
...extra,
|
|
1119
|
+
...main.filter((r) => r.kind === "footer").map((r) => ({ ...r, top: round3(r.top + delta) }))
|
|
1120
|
+
];
|
|
1121
|
+
for (const n of Object.values(g.nodes)) {
|
|
1122
|
+
const holdsThisTable = sameRows(n.rows, main);
|
|
1123
|
+
if (n.top >= bodyEnd - EPS) {
|
|
1124
|
+
n.top = round3(n.top + delta);
|
|
1125
|
+
if (n.lines) n.lines = n.lines.map((y) => round3(y + delta));
|
|
1126
|
+
if (n.rows && !holdsThisTable) n.rows = n.rows.map((r) => ({ ...r, top: round3(r.top + delta) }));
|
|
1127
|
+
} else if (n.top + n.height > bodyEnd - EPS) {
|
|
1128
|
+
n.height = round3(n.height + delta);
|
|
1129
|
+
}
|
|
1130
|
+
if (holdsThisTable) n.rows = merged;
|
|
1131
|
+
}
|
|
1132
|
+
g.height = round3(g.height + delta);
|
|
1133
|
+
}
|
|
1134
|
+
return g;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
989
1137
|
// src/paged.ts
|
|
990
1138
|
import {
|
|
991
|
-
renderNode as renderNode2
|
|
992
|
-
walkVNodes as walkVNodes2
|
|
1139
|
+
renderNode as renderNode2
|
|
993
1140
|
} from "@broadpaper/core";
|
|
994
1141
|
import { mapStyle as mapStyle3 } from "@formepdf/shared";
|
|
995
1142
|
|
|
996
1143
|
// src/clip.ts
|
|
997
1144
|
import { styleString as styleString2 } from "@broadpaper/core";
|
|
998
|
-
var
|
|
1145
|
+
var EPS2 = 0.01;
|
|
999
1146
|
function cropToWindow(root, geom, from, to) {
|
|
1000
1147
|
const w = { geom, from, to, dropped: 0 };
|
|
1001
|
-
const node = crop(root, w, void 0);
|
|
1148
|
+
const node = crop(root, w, void 0, true);
|
|
1002
1149
|
return { node, offset: w.dropped };
|
|
1003
1150
|
}
|
|
1004
1151
|
function outside(w, top, height) {
|
|
1005
|
-
return top + height <= w.from +
|
|
1152
|
+
return top + height <= w.from + EPS2 || top >= w.to - EPS2;
|
|
1006
1153
|
}
|
|
1007
|
-
function crop(v, w, rows) {
|
|
1008
|
-
const
|
|
1009
|
-
const g = key ? w.geom.nodes[key] : void 0;
|
|
1154
|
+
function crop(v, w, rows, stacked) {
|
|
1155
|
+
const g = geometryOf(v, w.geom);
|
|
1010
1156
|
if (g) {
|
|
1011
1157
|
if (outside(w, g.top, g.height)) return blank(v, g.height);
|
|
1012
1158
|
if (g.rows) rows = g.rows;
|
|
1013
1159
|
}
|
|
1014
1160
|
if (v.tag === "table" && rows) return cropTable(v, w, rows);
|
|
1015
|
-
|
|
1016
|
-
const
|
|
1161
|
+
const childrenStacked = stacked && !sideBySide(v.children, w.geom);
|
|
1162
|
+
const removed = childrenStacked ? removeAbove(v.children, w) : 0;
|
|
1163
|
+
let changed = removed > 0;
|
|
1164
|
+
const children = v.children.slice(removed).map((c) => {
|
|
1017
1165
|
if (typeof c === "string") return c;
|
|
1018
|
-
const next = crop(c, w, rows);
|
|
1166
|
+
const next = crop(c, w, rows, childrenStacked);
|
|
1019
1167
|
if (next !== c) changed = true;
|
|
1020
1168
|
return next;
|
|
1021
1169
|
});
|
|
1022
1170
|
return changed ? { ...v, children } : v;
|
|
1023
1171
|
}
|
|
1172
|
+
function geometryOf(v, geom) {
|
|
1173
|
+
const key = typeof v.props["data-key"] === "string" ? v.props["data-key"] : void 0;
|
|
1174
|
+
return key ? geom.nodes[key] : void 0;
|
|
1175
|
+
}
|
|
1176
|
+
function extent(v, geom) {
|
|
1177
|
+
const g = geometryOf(v, geom);
|
|
1178
|
+
if (g) return g.height > EPS2 ? [g.top, g.top + g.height] : null;
|
|
1179
|
+
let span = null;
|
|
1180
|
+
for (const c of v.children) {
|
|
1181
|
+
if (typeof c === "string") continue;
|
|
1182
|
+
const e = extent(c, geom);
|
|
1183
|
+
if (e) span = span ? [Math.min(span[0], e[0]), Math.max(span[1], e[1])] : e;
|
|
1184
|
+
}
|
|
1185
|
+
return span;
|
|
1186
|
+
}
|
|
1187
|
+
function sideBySide(children, geom) {
|
|
1188
|
+
const spans = children.filter((c) => typeof c !== "string").map((c) => extent(c, geom)).filter((e) => e !== null).sort((a, b) => a[0] - b[0]);
|
|
1189
|
+
let bottom = Number.NEGATIVE_INFINITY;
|
|
1190
|
+
for (const [top, end] of spans) {
|
|
1191
|
+
if (top < bottom - EPS2) return true;
|
|
1192
|
+
bottom = Math.max(bottom, end);
|
|
1193
|
+
}
|
|
1194
|
+
return false;
|
|
1195
|
+
}
|
|
1196
|
+
function removeAbove(children, w) {
|
|
1197
|
+
let start = null;
|
|
1198
|
+
let end = 0;
|
|
1199
|
+
let count = 0;
|
|
1200
|
+
for (let i = 0; i < children.length; i++) {
|
|
1201
|
+
const c = children[i];
|
|
1202
|
+
if (typeof c === "string") {
|
|
1203
|
+
if (c.trim()) return 0;
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
const g = geometryOf(c, w.geom);
|
|
1207
|
+
if (!g) return 0;
|
|
1208
|
+
if (g.top + g.height > w.from + EPS2) {
|
|
1209
|
+
if (start === null) return 0;
|
|
1210
|
+
w.dropped += g.top - margin(c, "top") - start;
|
|
1211
|
+
return i;
|
|
1212
|
+
}
|
|
1213
|
+
start ??= g.top - margin(c, "top");
|
|
1214
|
+
end = g.top + g.height + margin(c, "bottom");
|
|
1215
|
+
count = i + 1;
|
|
1216
|
+
}
|
|
1217
|
+
if (start === null) return 0;
|
|
1218
|
+
w.dropped += end - start;
|
|
1219
|
+
return count;
|
|
1220
|
+
}
|
|
1221
|
+
function margin(v, side) {
|
|
1222
|
+
let value = 0;
|
|
1223
|
+
for (const declaration of styleString2(v.props.style).split(";")) {
|
|
1224
|
+
const m = /^\s*margin(?:-(top|bottom|left|right))?\s*:\s*(.+?)\s*$/i.exec(declaration);
|
|
1225
|
+
if (!m) continue;
|
|
1226
|
+
if (m[1]) {
|
|
1227
|
+
if (m[1].toLowerCase() === side) value = pixels(m[2]);
|
|
1228
|
+
} else {
|
|
1229
|
+
const parts = m[2].split(/\s+/);
|
|
1230
|
+
value = pixels(side === "top" ? parts[0] : parts.length >= 3 ? parts[2] : parts[0]);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return value;
|
|
1234
|
+
}
|
|
1235
|
+
function pixels(length2) {
|
|
1236
|
+
const m = /^(-?\d*\.?\d+)(px)?$/i.exec(length2.trim());
|
|
1237
|
+
return m && (m[2] || Number(m[1]) === 0) ? Number(m[1]) : 0;
|
|
1238
|
+
}
|
|
1024
1239
|
function blank(v, height) {
|
|
1025
1240
|
const kept = styleString2(v.props.style).split(";").map((d) => d.trim()).filter((d) => d && !/^height\s*:/i.test(d));
|
|
1026
|
-
kept.push(`height:${
|
|
1241
|
+
kept.push(`height:${round4(height)}px`);
|
|
1027
1242
|
return { ...v, props: { ...v.props, style: kept.join(";") }, children: [] };
|
|
1028
1243
|
}
|
|
1029
1244
|
function cropTable(table, w, rows) {
|
|
@@ -1050,7 +1265,7 @@ function cropTable(table, w, rows) {
|
|
|
1050
1265
|
};
|
|
1051
1266
|
return rewrite(table);
|
|
1052
1267
|
}
|
|
1053
|
-
function
|
|
1268
|
+
function round4(n) {
|
|
1054
1269
|
return Math.round(n * 100) / 100;
|
|
1055
1270
|
}
|
|
1056
1271
|
|
|
@@ -1067,15 +1282,17 @@ function box(left, top, width, height, children, clip = true, bleed = 0) {
|
|
|
1067
1282
|
return { kind: { type: "View" }, style, children };
|
|
1068
1283
|
}
|
|
1069
1284
|
function headerOnly(v) {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1285
|
+
if (v.tag === "table") {
|
|
1286
|
+
return { ...v, children: v.children.filter((c) => typeof c !== "string" && (c.tag === "colgroup" || c.tag === "thead")) };
|
|
1287
|
+
}
|
|
1288
|
+
let changed = false;
|
|
1289
|
+
const children = v.children.map((c) => {
|
|
1290
|
+
if (typeof c === "string") return c;
|
|
1291
|
+
const next = headerOnly(c);
|
|
1292
|
+
if (next !== c) changed = true;
|
|
1293
|
+
return next;
|
|
1077
1294
|
});
|
|
1078
|
-
return
|
|
1295
|
+
return changed ? { ...v, children } : v;
|
|
1079
1296
|
}
|
|
1080
1297
|
function sectionsByKey(doc) {
|
|
1081
1298
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -1097,10 +1314,16 @@ function emitPages(input) {
|
|
|
1097
1314
|
const bleed = page.margins.right;
|
|
1098
1315
|
const bodyDependsOnPage = isPageDependent(resolved.body);
|
|
1099
1316
|
const cache = /* @__PURE__ */ new Map();
|
|
1317
|
+
const rendered = /* @__PURE__ */ new Map();
|
|
1318
|
+
const treeOf = (section, opts) => {
|
|
1319
|
+
if (bodyDependsOnPage) return renderNode2(section, opts, width);
|
|
1320
|
+
if (!rendered.has(section.key)) rendered.set(section.key, renderNode2(section, opts, width));
|
|
1321
|
+
return rendered.get(section.key);
|
|
1322
|
+
};
|
|
1100
1323
|
const translateSection = (section, opts, from, to) => {
|
|
1101
1324
|
const cacheKey = `${section.key}|${from}|${to}`;
|
|
1102
1325
|
if (!bodyDependsOnPage && cache.has(cacheKey)) return cache.get(cacheKey);
|
|
1103
|
-
const v =
|
|
1326
|
+
const v = treeOf(section, opts);
|
|
1104
1327
|
if (!v) return null;
|
|
1105
1328
|
const geom = geometry[section.key];
|
|
1106
1329
|
const cropped = geom ? cropToWindow(v, geom, from, to) : { node: v, offset: 0 };
|
|
@@ -1110,6 +1333,16 @@ function emitPages(input) {
|
|
|
1110
1333
|
if (!bodyDependsOnPage) cache.set(cacheKey, out);
|
|
1111
1334
|
return out;
|
|
1112
1335
|
};
|
|
1336
|
+
const headers = /* @__PURE__ */ new Map();
|
|
1337
|
+
const repeatedHeader = (tableKey, table, opts, headerWidth) => {
|
|
1338
|
+
const key = `${tableKey}|${headerWidth}`;
|
|
1339
|
+
if (!bodyDependsOnPage && headers.has(key)) return headers.get(key);
|
|
1340
|
+
const v = renderNode2(table, opts, headerWidth);
|
|
1341
|
+
const t = v ? translate(headerOnly(v), ctx) : null;
|
|
1342
|
+
const node = t ? t.node : null;
|
|
1343
|
+
if (!bodyDependsOnPage) headers.set(key, node);
|
|
1344
|
+
return node;
|
|
1345
|
+
};
|
|
1113
1346
|
const region = (sections, opts) => {
|
|
1114
1347
|
const out = [];
|
|
1115
1348
|
for (const s of sections) {
|
|
@@ -1123,7 +1356,7 @@ function emitPages(input) {
|
|
|
1123
1356
|
return paged.pages.map((p) => {
|
|
1124
1357
|
const opts = { ...renderOpts, page: { number: p.number, total: paged.totalPages } };
|
|
1125
1358
|
const children = [];
|
|
1126
|
-
|
|
1359
|
+
for (const mark of input.watermarks ?? []) if (mark && (mark.placement ?? "center") === "center") children.push(watermarkNode(mark));
|
|
1127
1360
|
const headerSections = p.first && resolved.header.firstPage ? resolved.header.firstPage : resolved.header.default;
|
|
1128
1361
|
if (headerSections.length && page.headerHeight > 0) {
|
|
1129
1362
|
children.push(box(page.margins.left, page.headerTop, width, page.headerHeight, region(headerSections, opts)));
|
|
@@ -1137,10 +1370,8 @@ function emitPages(input) {
|
|
|
1137
1370
|
for (const r of f.repeatHeaders) {
|
|
1138
1371
|
const table = byKey.get(r.tableKey);
|
|
1139
1372
|
if (!table) continue;
|
|
1140
|
-
const
|
|
1141
|
-
if (
|
|
1142
|
-
const t = translate(headerOnly(v), ctx);
|
|
1143
|
-
if (t) inner.push(box(r.left, 0, r.width, r.height, [t.node]));
|
|
1373
|
+
const header = repeatedHeader(r.tableKey, table, opts, r.width);
|
|
1374
|
+
if (header) inner.push(box(r.left, 0, r.width, r.height, [header]));
|
|
1144
1375
|
}
|
|
1145
1376
|
const content = translateSection(section, opts, f.from, f.to);
|
|
1146
1377
|
if (content) {
|
|
@@ -1158,6 +1389,14 @@ function emitPages(input) {
|
|
|
1158
1389
|
if (footerSections.length && page.footerHeight > 0) {
|
|
1159
1390
|
children.push(box(page.margins.left, page.footerTop, width, page.footerHeight, region(footerSections, opts)));
|
|
1160
1391
|
}
|
|
1392
|
+
for (const mark of input.watermarks ?? []) {
|
|
1393
|
+
if (!mark || (mark.placement ?? "center") !== "bottom") continue;
|
|
1394
|
+
const node = creditNode(mark, width, ctx);
|
|
1395
|
+
if (!node) continue;
|
|
1396
|
+
const h = mark.fontSize * 1.6;
|
|
1397
|
+
const top = Math.max(page.height - h, page.height - page.margins.bottom + Math.max(0, (page.margins.bottom - h) / 2));
|
|
1398
|
+
children.push(box(page.margins.left, top, width, h, [node]));
|
|
1399
|
+
}
|
|
1161
1400
|
return {
|
|
1162
1401
|
kind: {
|
|
1163
1402
|
type: "Page",
|
|
@@ -1220,12 +1459,15 @@ async function buildFormeDocument(opts) {
|
|
|
1220
1459
|
return out;
|
|
1221
1460
|
};
|
|
1222
1461
|
const children = [];
|
|
1223
|
-
const
|
|
1224
|
-
if (mark) children.push(watermarkNode(mark));
|
|
1462
|
+
const marks = [watermarkFor(license), opts.watermark].filter((m) => Boolean(m));
|
|
1463
|
+
for (const mark of marks) if ((mark.placement ?? "center") === "center") children.push(watermarkNode(mark));
|
|
1225
1464
|
const header = translate2(resolved.header.default);
|
|
1226
1465
|
if (header.length) children.push({ kind: { type: "Fixed", position: "Header" }, style: {}, children: header });
|
|
1227
1466
|
const footer = translate2(resolved.footer.default);
|
|
1228
|
-
|
|
1467
|
+
const credits = marks.filter((m) => (m.placement ?? "center") === "bottom").map((m) => creditNode(m, geom.contentWidth, ctx)).filter((n) => Boolean(n));
|
|
1468
|
+
if (footer.length || credits.length) {
|
|
1469
|
+
children.push({ kind: { type: "Fixed", position: "Footer" }, style: {}, children: [...footer, ...credits] });
|
|
1470
|
+
}
|
|
1229
1471
|
children.push(...translate2(resolved.body));
|
|
1230
1472
|
const page = {
|
|
1231
1473
|
kind: {
|
|
@@ -1282,6 +1524,20 @@ async function renderPdf(opts) {
|
|
|
1282
1524
|
}
|
|
1283
1525
|
async function renderPdfPaginated(opts) {
|
|
1284
1526
|
const started = Date.now();
|
|
1527
|
+
const layout = await layoutPaginated(opts);
|
|
1528
|
+
const drawn = await layout.draw();
|
|
1529
|
+
return {
|
|
1530
|
+
pdf: drawn.pdf,
|
|
1531
|
+
pages: drawn.pages,
|
|
1532
|
+
layout: drawn.layout,
|
|
1533
|
+
paged: layout.paged,
|
|
1534
|
+
geometry: layout.geometry,
|
|
1535
|
+
warnings: drawn.warnings,
|
|
1536
|
+
durationMs: Date.now() - started
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
var MEASURE_BATCH_ROWS = 1e3;
|
|
1540
|
+
async function layoutPaginated(opts) {
|
|
1285
1541
|
const license = await resolveLicenseStatus(opts.license !== void 0 ? { license: opts.license } : {});
|
|
1286
1542
|
const { template } = migrateTemplate(opts.template);
|
|
1287
1543
|
const theme = mergeTheme(opts.theme);
|
|
@@ -1316,17 +1572,15 @@ async function renderPdfPaginated(opts) {
|
|
|
1316
1572
|
};
|
|
1317
1573
|
const ctx = { warnings: /* @__PURE__ */ new Set(), suppressPageBreaks: true };
|
|
1318
1574
|
const render = opts.renderer ?? defaultRenderer;
|
|
1319
|
-
const
|
|
1320
|
-
if (!
|
|
1575
|
+
const layoutSections = async (list) => {
|
|
1576
|
+
if (!list.length) return {};
|
|
1321
1577
|
const measured = [];
|
|
1322
|
-
for (const section of
|
|
1323
|
-
const
|
|
1324
|
-
if (!v) continue;
|
|
1325
|
-
const t = translate(v, ctx);
|
|
1578
|
+
for (const { section, tree } of list) {
|
|
1579
|
+
const t = translate(tree, ctx);
|
|
1326
1580
|
if (t) measured.push({ section, node: t.node, ann: t.ann });
|
|
1327
1581
|
}
|
|
1328
1582
|
if (!measured.length) return {};
|
|
1329
|
-
const
|
|
1583
|
+
const doc = {
|
|
1330
1584
|
children: [
|
|
1331
1585
|
{
|
|
1332
1586
|
kind: {
|
|
@@ -1345,53 +1599,86 @@ async function renderPdfPaginated(opts) {
|
|
|
1345
1599
|
defaultPage: { size: "A4", margin: { top: 54, right: 54, bottom: 54, left: 54 }, wrap: true },
|
|
1346
1600
|
defaultStyle: baseStyle(theme)
|
|
1347
1601
|
};
|
|
1348
|
-
if (opts.fonts?.length)
|
|
1349
|
-
const
|
|
1350
|
-
const
|
|
1602
|
+
if (opts.fonts?.length) doc.fonts = fontEntries(opts.fonts);
|
|
1603
|
+
const result = await render(doc);
|
|
1604
|
+
const pages = result.layout?.pages ?? [];
|
|
1351
1605
|
const scale2 = 1 / PX_TO_PT_LOCAL;
|
|
1352
|
-
const scaled =
|
|
1606
|
+
const scaled = pages[0]?.elements.map((e) => scaleElement(e, scale2)) ?? [];
|
|
1353
1607
|
return geometryFromLayout(measured, scaled, geom.contentWidth);
|
|
1354
1608
|
};
|
|
1609
|
+
const batchRows = Math.max(1, opts.measureBatchRows ?? MEASURE_BATCH_ROWS);
|
|
1610
|
+
const measure = async (sections) => {
|
|
1611
|
+
const parts = [];
|
|
1612
|
+
for (const section of sections) {
|
|
1613
|
+
const tree = renderNode3(section, renderOpts, geom.contentWidth);
|
|
1614
|
+
if (tree) parts.push({ section, tree, long: longTables(tree, batchRows) });
|
|
1615
|
+
}
|
|
1616
|
+
const out = await layoutSections(parts.map((p) => ({ section: p.section, tree: p.long.length ? sliceTables(p.tree, p.long, 0, batchRows) : p.tree })));
|
|
1617
|
+
for (const p of parts) {
|
|
1618
|
+
const first = out[p.section.key];
|
|
1619
|
+
if (!p.long.length || !first) continue;
|
|
1620
|
+
const most = Math.max(...p.long.map((t) => t.bodyRows));
|
|
1621
|
+
const batches = [];
|
|
1622
|
+
for (let start = batchRows; start < most; start += batchRows) {
|
|
1623
|
+
batches.push({ section: { ...p.section, key: `${p.section.key}#rows-${start}` }, tree: sliceTables(p.tree, p.long, start, batchRows) });
|
|
1624
|
+
}
|
|
1625
|
+
const measured = [];
|
|
1626
|
+
for (let i = 0; i < batches.length; i += BATCHES_PER_PASS) {
|
|
1627
|
+
const chunk = batches.slice(i, i + BATCHES_PER_PASS);
|
|
1628
|
+
const got = await layoutSections(chunk);
|
|
1629
|
+
for (const b of chunk) {
|
|
1630
|
+
const g = got[b.section.key];
|
|
1631
|
+
if (g) measured.push(g);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
out[p.section.key] = stitchTables(first, p.long, measured);
|
|
1635
|
+
}
|
|
1636
|
+
return out;
|
|
1637
|
+
};
|
|
1355
1638
|
const geometry = await measure(resolved.body);
|
|
1356
1639
|
const regionGeometry = await measure([...resolved.header.default, ...resolved.header.firstPage ?? [], ...resolved.footer.default, ...resolved.footer.firstPage ?? []]);
|
|
1357
1640
|
const paged = paginate({ document: resolved, geometry, page: geom, minPages: 1, regionGeometry });
|
|
1358
|
-
const
|
|
1359
|
-
resolved,
|
|
1360
|
-
paged,
|
|
1361
|
-
page: geom,
|
|
1362
|
-
renderOpts,
|
|
1363
|
-
ctx,
|
|
1364
|
-
geometry,
|
|
1365
|
-
background: template.page.background ? resolveValue(template.page.background, theme, "#ffffff") : void 0,
|
|
1366
|
-
watermark: watermarkFor(license)
|
|
1367
|
-
});
|
|
1368
|
-
const doc = {
|
|
1369
|
-
children: pages,
|
|
1370
|
-
metadata: { title: template.name, creator: producerString(license), ...opts.metadata ?? {} },
|
|
1371
|
-
defaultPage: { size: "A4", margin: { top: 54, right: 54, bottom: 54, left: 54 }, wrap: true },
|
|
1372
|
-
defaultStyle: baseStyle(theme)
|
|
1373
|
-
};
|
|
1374
|
-
if (opts.fonts?.length) doc.fonts = fontEntries(opts.fonts);
|
|
1375
|
-
if (opts.tagged) doc.tagged = true;
|
|
1376
|
-
if (opts.pdfUa) doc.pdfUa = true;
|
|
1377
|
-
if (opts.pdfA) doc.pdfa = opts.pdfA;
|
|
1378
|
-
const result = await render(doc);
|
|
1379
|
-
const layout = result.layout ?? null;
|
|
1641
|
+
const background = template.page.background ? resolveValue(template.page.background, theme, "#ffffff") : void 0;
|
|
1380
1642
|
return {
|
|
1381
|
-
pdf: result.pdf,
|
|
1382
|
-
pages: layout?.pages.length ?? paged.totalPages,
|
|
1383
|
-
layout,
|
|
1384
1643
|
paged,
|
|
1385
1644
|
geometry,
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1645
|
+
async draw(range) {
|
|
1646
|
+
const from = Math.max(0, range?.from ?? 0);
|
|
1647
|
+
const to = Math.min(paged.pages.length, range?.to ?? paged.pages.length);
|
|
1648
|
+
const pages = emitPages({
|
|
1649
|
+
resolved,
|
|
1650
|
+
// `totalPages` stays the whole document's, so a later file still counts
|
|
1651
|
+
// its pages as part of the whole.
|
|
1652
|
+
paged: { ...paged, pages: paged.pages.slice(from, to) },
|
|
1653
|
+
page: geom,
|
|
1654
|
+
renderOpts,
|
|
1655
|
+
ctx,
|
|
1656
|
+
geometry,
|
|
1657
|
+
background,
|
|
1658
|
+
watermarks: [watermarkFor(license), opts.watermark]
|
|
1659
|
+
});
|
|
1660
|
+
const doc = {
|
|
1661
|
+
children: pages,
|
|
1662
|
+
metadata: { title: template.name, creator: producerString(license), ...opts.metadata ?? {} },
|
|
1663
|
+
defaultPage: { size: "A4", margin: { top: 54, right: 54, bottom: 54, left: 54 }, wrap: true },
|
|
1664
|
+
defaultStyle: baseStyle(theme)
|
|
1665
|
+
};
|
|
1666
|
+
if (opts.fonts?.length) doc.fonts = fontEntries(opts.fonts);
|
|
1667
|
+
if (opts.tagged) doc.tagged = true;
|
|
1668
|
+
if (opts.pdfUa) doc.pdfUa = true;
|
|
1669
|
+
if (opts.pdfA) doc.pdfa = opts.pdfA;
|
|
1670
|
+
const result = await render(doc);
|
|
1671
|
+
const layout = result.layout ?? null;
|
|
1672
|
+
return {
|
|
1673
|
+
pdf: result.pdf,
|
|
1674
|
+
pages: layout?.pages.length ?? to - from,
|
|
1675
|
+
layout,
|
|
1676
|
+
warnings: [...ctx.warnings, ...resolved.warnings.map((w) => w.message), ...paged.warnings.map((w) => w.message), ...result.warnings ?? []]
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1393
1679
|
};
|
|
1394
1680
|
}
|
|
1681
|
+
var BATCHES_PER_PASS = 2;
|
|
1395
1682
|
var PX_TO_PT_LOCAL = 72 / 96;
|
|
1396
1683
|
function scaleElement(el, scale2) {
|
|
1397
1684
|
return {
|
|
@@ -1429,10 +1716,12 @@ export {
|
|
|
1429
1716
|
setEngineLoader,
|
|
1430
1717
|
renderPdf,
|
|
1431
1718
|
renderPdfPaginated,
|
|
1719
|
+
MEASURE_BATCH_ROWS,
|
|
1720
|
+
layoutPaginated,
|
|
1432
1721
|
BroadPaper,
|
|
1433
1722
|
configure,
|
|
1434
1723
|
resolveLicenseStatus2 as resolveLicenseStatus,
|
|
1435
1724
|
verifyLicense,
|
|
1436
1725
|
parseLicense
|
|
1437
1726
|
};
|
|
1438
|
-
//# sourceMappingURL=chunk-
|
|
1727
|
+
//# sourceMappingURL=chunk-2C3MAW2U.js.map
|