@broadpaper/forme 0.2.1 → 0.4.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-QA4APTWM.js} +532 -89
- package/dist/chunk-QA4APTWM.js.map +1 -0
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.js +15 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +152 -12
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-2PT5WRKQ.js.map +0 -1
|
@@ -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;
|
|
@@ -902,6 +902,146 @@ var FormeMeasurer = class {
|
|
|
902
902
|
}
|
|
903
903
|
};
|
|
904
904
|
|
|
905
|
+
// src/fonts.ts
|
|
906
|
+
var localReader = null;
|
|
907
|
+
function setFontFileReader(reader) {
|
|
908
|
+
localReader = reader;
|
|
909
|
+
}
|
|
910
|
+
var MAX_FONT_BYTES = 32 * 1024 * 1024;
|
|
911
|
+
var FETCH_TIMEOUT_MS = 15e3;
|
|
912
|
+
var MAX_CACHED = 64;
|
|
913
|
+
var cache = /* @__PURE__ */ new Map();
|
|
914
|
+
async function resolveFonts(input) {
|
|
915
|
+
const fonts = [];
|
|
916
|
+
const warnings = [];
|
|
917
|
+
const seen = /* @__PURE__ */ new Set();
|
|
918
|
+
const add = (font) => {
|
|
919
|
+
const weight = font.weight ?? 400;
|
|
920
|
+
const style = font.style ?? "normal";
|
|
921
|
+
const key = `${font.family.trim().toLowerCase()}|${weight}|${style}`;
|
|
922
|
+
if (seen.has(key)) return;
|
|
923
|
+
seen.add(key);
|
|
924
|
+
fonts.push({ family: font.family, src: font.src, weight, style });
|
|
925
|
+
};
|
|
926
|
+
for (const font of input.fonts ?? []) add(font);
|
|
927
|
+
const faces = await Promise.all((input.theme?.fonts ?? []).map((face) => themeFace(face, input.sources ?? {})));
|
|
928
|
+
for (const face of faces) {
|
|
929
|
+
warnings.push(...face.warnings);
|
|
930
|
+
if (face.font) add(face.font);
|
|
931
|
+
}
|
|
932
|
+
return { fonts, warnings };
|
|
933
|
+
}
|
|
934
|
+
async function themeFace(face, policy) {
|
|
935
|
+
const { weight, range } = weightOf(face.weight);
|
|
936
|
+
const style = face.style === "italic" ? "italic" : "normal";
|
|
937
|
+
const name = `"${face.family}" (${weight}${style === "italic" ? " italic" : ""})`;
|
|
938
|
+
const sources = (Array.isArray(face.src) ? face.src : [face.src]).filter((s) => typeof s === "string" && s.trim() !== "");
|
|
939
|
+
const reasons = [];
|
|
940
|
+
for (const src of sources) {
|
|
941
|
+
const loaded = await load(src.trim(), policy);
|
|
942
|
+
if ("reason" in loaded) {
|
|
943
|
+
reasons.push(loaded.reason);
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
const warnings = range ? [`The font "${face.family}" declares the weights ${String(face.weight)} from one file; the PDF engine is given it at ${weight} only. Declare a file per weight for the others to be embedded.`] : [];
|
|
947
|
+
return { font: { family: face.family, src: loaded.src, weight, style }, warnings };
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
warnings: [
|
|
951
|
+
`The font ${name} has no TrueType or OpenType source the PDF engine can read, so the PDF draws that text in a standard face and breaks its lines differently from the screen. Add a .ttf or .otf to its src${reasons.length ? ` (${reasons.join("; ")})` : ""}.`
|
|
952
|
+
]
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
function weightOf(value) {
|
|
956
|
+
if (typeof value === "number" && Number.isFinite(value)) return { weight: Math.round(value), range: false };
|
|
957
|
+
const text = String(value ?? "").trim().toLowerCase();
|
|
958
|
+
if (text === "bold") return { weight: 700, range: false };
|
|
959
|
+
const numbers = text.split(/\s+/).map(Number).filter((n) => Number.isFinite(n) && n > 0);
|
|
960
|
+
if (!numbers.length) return { weight: 400, range: false };
|
|
961
|
+
return { weight: Math.round(numbers[0]), range: numbers.length > 1 && numbers[1] !== numbers[0] };
|
|
962
|
+
}
|
|
963
|
+
async function load(src, policy) {
|
|
964
|
+
if (/^data:(font|application)\/(x-)?woff2?[;,]/i.test(src) || /\.woff2?([?#]|$)/i.test(src.startsWith("data:") ? "" : src)) {
|
|
965
|
+
return { reason: `${describe(src)} is WOFF, which only a browser reads` };
|
|
966
|
+
}
|
|
967
|
+
if (src.startsWith("data:")) return fromDataUri(src);
|
|
968
|
+
const remote = /^https?:\/\//i.test(src);
|
|
969
|
+
if (remote) {
|
|
970
|
+
let host;
|
|
971
|
+
try {
|
|
972
|
+
host = new URL(src).hostname;
|
|
973
|
+
} catch {
|
|
974
|
+
return { reason: `${describe(src)} is not a valid address` };
|
|
975
|
+
}
|
|
976
|
+
const allowed = policy.allowedHosts;
|
|
977
|
+
if (allowed && !allowed.includes("*") && !allowed.includes(host)) return { reason: `${host} is not an allowed host` };
|
|
978
|
+
} else if (policy.local === false) {
|
|
979
|
+
return { reason: `${describe(src)} is neither a data: URI nor a web address, and files are not read here` };
|
|
980
|
+
} else if (!localReader) {
|
|
981
|
+
return { src };
|
|
982
|
+
}
|
|
983
|
+
const key = src;
|
|
984
|
+
const hit = cache.get(key);
|
|
985
|
+
if (hit) return hit;
|
|
986
|
+
const pending = (async () => {
|
|
987
|
+
const bytes = remote ? await download(src, Boolean(policy.allowedHosts && !policy.allowedHosts.includes("*"))) : await localReader(src);
|
|
988
|
+
if (bytes.byteLength > MAX_FONT_BYTES) return { reason: `${describe(src)} is larger than ${MAX_FONT_BYTES / 1024 / 1024} MB` };
|
|
989
|
+
const problem = notEmbeddable(bytes, src);
|
|
990
|
+
return problem ? { reason: problem } : { src: `data:font/ttf;base64,${toBase64(bytes)}` };
|
|
991
|
+
})().catch((e) => {
|
|
992
|
+
cache.delete(key);
|
|
993
|
+
return { reason: `${describe(src)} could not be read (${e.message || String(e)})` };
|
|
994
|
+
});
|
|
995
|
+
cache.set(key, pending);
|
|
996
|
+
while (cache.size > MAX_CACHED) cache.delete(cache.keys().next().value);
|
|
997
|
+
return pending;
|
|
998
|
+
}
|
|
999
|
+
async function download(src, restricted) {
|
|
1000
|
+
const signal = typeof AbortSignal !== "undefined" && "timeout" in AbortSignal ? AbortSignal.timeout(FETCH_TIMEOUT_MS) : void 0;
|
|
1001
|
+
const response = await fetch(src, { redirect: restricted ? "error" : "follow", ...signal ? { signal } : {} });
|
|
1002
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1003
|
+
const length2 = Number(response.headers.get("content-length"));
|
|
1004
|
+
if (length2 > MAX_FONT_BYTES) throw new Error(`larger than ${MAX_FONT_BYTES / 1024 / 1024} MB`);
|
|
1005
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
1006
|
+
}
|
|
1007
|
+
function fromDataUri(src) {
|
|
1008
|
+
const match = /^data:([^,]*),(.*)$/s.exec(src);
|
|
1009
|
+
if (!match) return { reason: "a data: URI with no data" };
|
|
1010
|
+
const base64 = /;base64$/i.test(match[1]);
|
|
1011
|
+
let head;
|
|
1012
|
+
try {
|
|
1013
|
+
head = base64 ? fromBase64(match[2].slice(0, 8)) : Uint8Array.from(decodeURIComponent(match[2].slice(0, 64)), (c) => c.charCodeAt(0) & 255);
|
|
1014
|
+
} catch {
|
|
1015
|
+
return { reason: "a data: URI that does not decode" };
|
|
1016
|
+
}
|
|
1017
|
+
const problem = notEmbeddable(head, "a data: URI");
|
|
1018
|
+
if (problem) return { reason: problem };
|
|
1019
|
+
if (base64) return { src };
|
|
1020
|
+
const bytes = Uint8Array.from(decodeURIComponent(match[2]), (c) => c.charCodeAt(0) & 255);
|
|
1021
|
+
return { src: `data:font/ttf;base64,${toBase64(bytes)}` };
|
|
1022
|
+
}
|
|
1023
|
+
function notEmbeddable(bytes, src) {
|
|
1024
|
+
if (bytes.length < 4) return `${describe(src)} is not a font file`;
|
|
1025
|
+
const tag = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3]);
|
|
1026
|
+
if (bytes[0] === 0 && bytes[1] === 1 && bytes[2] === 0 && bytes[3] === 0 || tag === "OTTO" || tag === "true") return null;
|
|
1027
|
+
if (tag === "wOFF" || tag === "wOF2") return `${describe(src)} is WOFF, which only a browser reads`;
|
|
1028
|
+
if (tag === "ttcf") return `${describe(src)} is a font collection; give the single face instead`;
|
|
1029
|
+
return `${describe(src)} is not a TrueType or OpenType file`;
|
|
1030
|
+
}
|
|
1031
|
+
function describe(src) {
|
|
1032
|
+
return src.startsWith("data:") ? "a data: URI" : src.length > 80 ? `${src.slice(0, 77)}\u2026` : src;
|
|
1033
|
+
}
|
|
1034
|
+
function toBase64(bytes) {
|
|
1035
|
+
let binary = "";
|
|
1036
|
+
for (let i = 0; i < bytes.length; i += 32768) binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
1037
|
+
return btoa(binary);
|
|
1038
|
+
}
|
|
1039
|
+
function fromBase64(text) {
|
|
1040
|
+
const clean = text.replace(/[^A-Za-z0-9+/]/g, "");
|
|
1041
|
+
const binary = atob(clean.slice(0, clean.length - clean.length % 4));
|
|
1042
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
1043
|
+
}
|
|
1044
|
+
|
|
905
1045
|
// src/editor.ts
|
|
906
1046
|
function hash(s) {
|
|
907
1047
|
let h = 2166136261;
|
|
@@ -928,8 +1068,8 @@ var FormeEditorMeasurer = class {
|
|
|
928
1068
|
destroy() {
|
|
929
1069
|
this.cache.clear();
|
|
930
1070
|
}
|
|
931
|
-
key(section, width, mode) {
|
|
932
|
-
return hash(JSON.stringify(section)) + ":" + Math.round(width * 100) + ":" + mode;
|
|
1071
|
+
key(section, width, mode, faces) {
|
|
1072
|
+
return hash(JSON.stringify(section)) + ":" + Math.round(width * 100) + ":" + mode + ":" + faces;
|
|
933
1073
|
}
|
|
934
1074
|
async measure(sections, width) {
|
|
935
1075
|
const opts = this.opts;
|
|
@@ -937,8 +1077,9 @@ var FormeEditorMeasurer = class {
|
|
|
937
1077
|
const out = {};
|
|
938
1078
|
const pending = [];
|
|
939
1079
|
const keys = /* @__PURE__ */ new Map();
|
|
1080
|
+
const faces = facesKey(opts.theme.fonts);
|
|
940
1081
|
for (const section of sections) {
|
|
941
|
-
const k = this.key(section, width, opts.mode);
|
|
1082
|
+
const k = this.key(section, width, opts.mode, faces);
|
|
942
1083
|
const hit = this.cache.get(k);
|
|
943
1084
|
if (hit) out[section.key] = hit;
|
|
944
1085
|
else {
|
|
@@ -947,6 +1088,9 @@ var FormeEditorMeasurer = class {
|
|
|
947
1088
|
}
|
|
948
1089
|
}
|
|
949
1090
|
if (!pending.length) return out;
|
|
1091
|
+
const given = await this.config.fonts;
|
|
1092
|
+
const { fonts, warnings } = await resolveFonts({ fonts: given, theme: opts.theme });
|
|
1093
|
+
for (const w of warnings) this.warn(w);
|
|
950
1094
|
const measurer = new FormeMeasurer({
|
|
951
1095
|
registry: opts.registry,
|
|
952
1096
|
theme: opts.theme,
|
|
@@ -955,7 +1099,7 @@ var FormeEditorMeasurer = class {
|
|
|
955
1099
|
mode: opts.mode,
|
|
956
1100
|
pageWidth: width,
|
|
957
1101
|
margins: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
958
|
-
fonts
|
|
1102
|
+
fonts,
|
|
959
1103
|
render: this.config.render
|
|
960
1104
|
});
|
|
961
1105
|
const measured = await measurer.measure(pending, width);
|
|
@@ -964,14 +1108,21 @@ var FormeEditorMeasurer = class {
|
|
|
964
1108
|
const k = keys.get(sectionKey);
|
|
965
1109
|
if (k) this.cache.set(k, geometry);
|
|
966
1110
|
}
|
|
967
|
-
for (const w of measurer.warnings)
|
|
968
|
-
if (this.seen.has(w)) continue;
|
|
969
|
-
this.seen.add(w);
|
|
970
|
-
this.config.onWarning?.(w);
|
|
971
|
-
}
|
|
1111
|
+
for (const w of measurer.warnings) this.warn(w);
|
|
972
1112
|
return out;
|
|
973
1113
|
}
|
|
1114
|
+
warn(message) {
|
|
1115
|
+
if (this.seen.has(message)) return;
|
|
1116
|
+
this.seen.add(message);
|
|
1117
|
+
this.config.onWarning?.(message);
|
|
1118
|
+
}
|
|
974
1119
|
};
|
|
1120
|
+
function facesKey(faces) {
|
|
1121
|
+
if (!faces?.length) return "";
|
|
1122
|
+
return hash(
|
|
1123
|
+
faces.map((f) => [f.family, f.weight, f.style, ...(Array.isArray(f.src) ? f.src : [f.src]).map((s) => `${s.length}:${s.slice(0, 64)}${s.slice(-64)}`)].join("|")).join(";")
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
975
1126
|
|
|
976
1127
|
// src/api.ts
|
|
977
1128
|
import {
|
|
@@ -983,47 +1134,261 @@ import {
|
|
|
983
1134
|
} from "@broadpaper/core";
|
|
984
1135
|
import { mapStyle as mapStyle4 } from "@formepdf/shared";
|
|
985
1136
|
import { producerString, resolveLicenseStatus, watermarkFor } from "@broadpaper/license";
|
|
1137
|
+
|
|
1138
|
+
// src/credit.ts
|
|
1139
|
+
function creditNode(spec, width, ctx) {
|
|
1140
|
+
const size = spec.fontSize;
|
|
1141
|
+
const children = [];
|
|
1142
|
+
if (spec.mark) {
|
|
1143
|
+
children.push({
|
|
1144
|
+
tag: "img",
|
|
1145
|
+
props: { src: spec.mark, alt: "", style: { width: `${size}px`, height: `${size}px`, opacity: String(spec.opacity) } },
|
|
1146
|
+
children: []
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
children.push({
|
|
1150
|
+
tag: "span",
|
|
1151
|
+
props: { style: { "font-size": `${size}px`, color: spec.color, opacity: String(spec.opacity), "line-height": "1.2" } },
|
|
1152
|
+
children: [spec.text]
|
|
1153
|
+
});
|
|
1154
|
+
const line = {
|
|
1155
|
+
tag: "div",
|
|
1156
|
+
props: {
|
|
1157
|
+
style: {
|
|
1158
|
+
display: "flex",
|
|
1159
|
+
"align-items": "center",
|
|
1160
|
+
gap: `${Math.max(2, Math.round(size * 0.5))}px`,
|
|
1161
|
+
width: `${width}px`,
|
|
1162
|
+
"justify-content": "center"
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
1165
|
+
children
|
|
1166
|
+
};
|
|
1167
|
+
return translate(line, ctx)?.node ?? null;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// src/api.ts
|
|
986
1171
|
import { BroadPaper, configure, resolveLicenseStatus as resolveLicenseStatus2, verifyLicense, parseLicense } from "@broadpaper/license";
|
|
987
1172
|
import { computePageGeometry as pageGeometry, paginate, resolveValue } from "@broadpaper/core";
|
|
988
1173
|
|
|
1174
|
+
// src/long-tables.ts
|
|
1175
|
+
var EPS = 0.5;
|
|
1176
|
+
function round3(n) {
|
|
1177
|
+
return Math.round(n * 100) / 100;
|
|
1178
|
+
}
|
|
1179
|
+
function isRow(c) {
|
|
1180
|
+
return typeof c !== "string" && c.tag === "tr";
|
|
1181
|
+
}
|
|
1182
|
+
function bodyRowCount(table) {
|
|
1183
|
+
let n = 0;
|
|
1184
|
+
for (const c of table.children) {
|
|
1185
|
+
if (typeof c === "string") continue;
|
|
1186
|
+
if (c.tag === "tbody") n += c.children.filter(isRow).length;
|
|
1187
|
+
else if (c.tag === "tr") n++;
|
|
1188
|
+
}
|
|
1189
|
+
return n;
|
|
1190
|
+
}
|
|
1191
|
+
function longTables(tree, limit) {
|
|
1192
|
+
const out = [];
|
|
1193
|
+
const visit = (v, key) => {
|
|
1194
|
+
const own = typeof v.props["data-key"] === "string" ? v.props["data-key"] : key;
|
|
1195
|
+
if (v.tag === "table") {
|
|
1196
|
+
const rows = bodyRowCount(v);
|
|
1197
|
+
if (rows > limit && own) out.push({ table: v, key: own, bodyRows: rows });
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
for (const c of v.children) if (typeof c !== "string") visit(c, own);
|
|
1201
|
+
};
|
|
1202
|
+
visit(tree, void 0);
|
|
1203
|
+
return out;
|
|
1204
|
+
}
|
|
1205
|
+
function sliceTables(tree, tables, start, count) {
|
|
1206
|
+
const targets = new Set(tables.map((t) => t.table));
|
|
1207
|
+
const visit = (v) => {
|
|
1208
|
+
if (targets.has(v)) return sliceRows(v, start, count);
|
|
1209
|
+
let changed = false;
|
|
1210
|
+
const children = v.children.map((c) => {
|
|
1211
|
+
if (typeof c === "string") return c;
|
|
1212
|
+
const next = visit(c);
|
|
1213
|
+
if (next !== c) changed = true;
|
|
1214
|
+
return next;
|
|
1215
|
+
});
|
|
1216
|
+
return changed ? { ...v, children } : v;
|
|
1217
|
+
};
|
|
1218
|
+
return visit(tree);
|
|
1219
|
+
}
|
|
1220
|
+
function sliceRows(table, start, count) {
|
|
1221
|
+
let index = 0;
|
|
1222
|
+
const keep = () => {
|
|
1223
|
+
const i = index++;
|
|
1224
|
+
return i >= start && i < start + count;
|
|
1225
|
+
};
|
|
1226
|
+
const children = [];
|
|
1227
|
+
for (const c of table.children) {
|
|
1228
|
+
if (typeof c === "string") children.push(c);
|
|
1229
|
+
else if (c.tag === "tbody") children.push({ ...c, children: c.children.filter((r) => !isRow(r) || keep()) });
|
|
1230
|
+
else if (c.tag === "tr") {
|
|
1231
|
+
if (keep()) children.push(c);
|
|
1232
|
+
} else children.push(c);
|
|
1233
|
+
}
|
|
1234
|
+
return { ...table, children };
|
|
1235
|
+
}
|
|
1236
|
+
function sameRows(a, b) {
|
|
1237
|
+
return !!a && a.length === b.length && a[0]?.top === b[0]?.top && a[a.length - 1]?.top === b[b.length - 1]?.top;
|
|
1238
|
+
}
|
|
1239
|
+
function stitchTables(first, tables, batches) {
|
|
1240
|
+
const g = {
|
|
1241
|
+
...first,
|
|
1242
|
+
nodes: Object.fromEntries(Object.entries(first.nodes).map(([k, n]) => [k, { ...n }]))
|
|
1243
|
+
};
|
|
1244
|
+
const bottomOf = (rows) => Math.max(...rows.map((r) => r.top + r.height));
|
|
1245
|
+
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));
|
|
1246
|
+
for (const { t } of order) {
|
|
1247
|
+
const main = g.nodes[t.key].rows;
|
|
1248
|
+
const body = main.filter((r) => r.kind === "body");
|
|
1249
|
+
const last = body[body.length - 1];
|
|
1250
|
+
const bodyEnd = last.top + last.height;
|
|
1251
|
+
const gap = body.length > 1 ? Math.max(0, body[1].top - (body[0].top + body[0].height)) : 0;
|
|
1252
|
+
const extra = [];
|
|
1253
|
+
let cursor = bodyEnd + gap;
|
|
1254
|
+
for (const batch of batches) {
|
|
1255
|
+
const rows = batch.nodes[t.key]?.rows?.filter((r) => r.kind === "body") ?? [];
|
|
1256
|
+
if (!rows.length) continue;
|
|
1257
|
+
const base = rows[0].top;
|
|
1258
|
+
for (const r of rows) extra.push({ top: round3(cursor + r.top - base), height: r.height, kind: "body" });
|
|
1259
|
+
const end = rows[rows.length - 1];
|
|
1260
|
+
cursor += end.top + end.height - base + gap;
|
|
1261
|
+
}
|
|
1262
|
+
if (!extra.length) continue;
|
|
1263
|
+
const tail = extra[extra.length - 1];
|
|
1264
|
+
const delta = round3(tail.top + tail.height - bodyEnd);
|
|
1265
|
+
const merged = [
|
|
1266
|
+
...main.filter((r) => r.kind === "header"),
|
|
1267
|
+
...body,
|
|
1268
|
+
...extra,
|
|
1269
|
+
...main.filter((r) => r.kind === "footer").map((r) => ({ ...r, top: round3(r.top + delta) }))
|
|
1270
|
+
];
|
|
1271
|
+
for (const n of Object.values(g.nodes)) {
|
|
1272
|
+
const holdsThisTable = sameRows(n.rows, main);
|
|
1273
|
+
if (n.top >= bodyEnd - EPS) {
|
|
1274
|
+
n.top = round3(n.top + delta);
|
|
1275
|
+
if (n.lines) n.lines = n.lines.map((y) => round3(y + delta));
|
|
1276
|
+
if (n.rows && !holdsThisTable) n.rows = n.rows.map((r) => ({ ...r, top: round3(r.top + delta) }));
|
|
1277
|
+
} else if (n.top + n.height > bodyEnd - EPS) {
|
|
1278
|
+
n.height = round3(n.height + delta);
|
|
1279
|
+
}
|
|
1280
|
+
if (holdsThisTable) n.rows = merged;
|
|
1281
|
+
}
|
|
1282
|
+
g.height = round3(g.height + delta);
|
|
1283
|
+
}
|
|
1284
|
+
return g;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
989
1287
|
// src/paged.ts
|
|
990
1288
|
import {
|
|
991
|
-
renderNode as renderNode2
|
|
992
|
-
walkVNodes as walkVNodes2
|
|
1289
|
+
renderNode as renderNode2
|
|
993
1290
|
} from "@broadpaper/core";
|
|
994
1291
|
import { mapStyle as mapStyle3 } from "@formepdf/shared";
|
|
995
1292
|
|
|
996
1293
|
// src/clip.ts
|
|
997
1294
|
import { styleString as styleString2 } from "@broadpaper/core";
|
|
998
|
-
var
|
|
1295
|
+
var EPS2 = 0.01;
|
|
999
1296
|
function cropToWindow(root, geom, from, to) {
|
|
1000
1297
|
const w = { geom, from, to, dropped: 0 };
|
|
1001
|
-
const node = crop(root, w, void 0);
|
|
1298
|
+
const node = crop(root, w, void 0, true);
|
|
1002
1299
|
return { node, offset: w.dropped };
|
|
1003
1300
|
}
|
|
1004
1301
|
function outside(w, top, height) {
|
|
1005
|
-
return top + height <= w.from +
|
|
1302
|
+
return top + height <= w.from + EPS2 || top >= w.to - EPS2;
|
|
1006
1303
|
}
|
|
1007
|
-
function crop(v, w, rows) {
|
|
1008
|
-
const
|
|
1009
|
-
const g = key ? w.geom.nodes[key] : void 0;
|
|
1304
|
+
function crop(v, w, rows, stacked) {
|
|
1305
|
+
const g = geometryOf(v, w.geom);
|
|
1010
1306
|
if (g) {
|
|
1011
1307
|
if (outside(w, g.top, g.height)) return blank(v, g.height);
|
|
1012
1308
|
if (g.rows) rows = g.rows;
|
|
1013
1309
|
}
|
|
1014
1310
|
if (v.tag === "table" && rows) return cropTable(v, w, rows);
|
|
1015
|
-
|
|
1016
|
-
const
|
|
1311
|
+
const childrenStacked = stacked && !sideBySide(v.children, w.geom);
|
|
1312
|
+
const removed = childrenStacked ? removeAbove(v.children, w) : 0;
|
|
1313
|
+
let changed = removed > 0;
|
|
1314
|
+
const children = v.children.slice(removed).map((c) => {
|
|
1017
1315
|
if (typeof c === "string") return c;
|
|
1018
|
-
const next = crop(c, w, rows);
|
|
1316
|
+
const next = crop(c, w, rows, childrenStacked);
|
|
1019
1317
|
if (next !== c) changed = true;
|
|
1020
1318
|
return next;
|
|
1021
1319
|
});
|
|
1022
1320
|
return changed ? { ...v, children } : v;
|
|
1023
1321
|
}
|
|
1322
|
+
function geometryOf(v, geom) {
|
|
1323
|
+
const key = typeof v.props["data-key"] === "string" ? v.props["data-key"] : void 0;
|
|
1324
|
+
return key ? geom.nodes[key] : void 0;
|
|
1325
|
+
}
|
|
1326
|
+
function extent(v, geom) {
|
|
1327
|
+
const g = geometryOf(v, geom);
|
|
1328
|
+
if (g) return g.height > EPS2 ? [g.top, g.top + g.height] : null;
|
|
1329
|
+
let span = null;
|
|
1330
|
+
for (const c of v.children) {
|
|
1331
|
+
if (typeof c === "string") continue;
|
|
1332
|
+
const e = extent(c, geom);
|
|
1333
|
+
if (e) span = span ? [Math.min(span[0], e[0]), Math.max(span[1], e[1])] : e;
|
|
1334
|
+
}
|
|
1335
|
+
return span;
|
|
1336
|
+
}
|
|
1337
|
+
function sideBySide(children, geom) {
|
|
1338
|
+
const spans = children.filter((c) => typeof c !== "string").map((c) => extent(c, geom)).filter((e) => e !== null).sort((a, b) => a[0] - b[0]);
|
|
1339
|
+
let bottom = Number.NEGATIVE_INFINITY;
|
|
1340
|
+
for (const [top, end] of spans) {
|
|
1341
|
+
if (top < bottom - EPS2) return true;
|
|
1342
|
+
bottom = Math.max(bottom, end);
|
|
1343
|
+
}
|
|
1344
|
+
return false;
|
|
1345
|
+
}
|
|
1346
|
+
function removeAbove(children, w) {
|
|
1347
|
+
let start = null;
|
|
1348
|
+
let end = 0;
|
|
1349
|
+
let count = 0;
|
|
1350
|
+
for (let i = 0; i < children.length; i++) {
|
|
1351
|
+
const c = children[i];
|
|
1352
|
+
if (typeof c === "string") {
|
|
1353
|
+
if (c.trim()) return 0;
|
|
1354
|
+
continue;
|
|
1355
|
+
}
|
|
1356
|
+
const g = geometryOf(c, w.geom);
|
|
1357
|
+
if (!g) return 0;
|
|
1358
|
+
if (g.top + g.height > w.from + EPS2) {
|
|
1359
|
+
if (start === null) return 0;
|
|
1360
|
+
w.dropped += g.top - margin(c, "top") - start;
|
|
1361
|
+
return i;
|
|
1362
|
+
}
|
|
1363
|
+
start ??= g.top - margin(c, "top");
|
|
1364
|
+
end = g.top + g.height + margin(c, "bottom");
|
|
1365
|
+
count = i + 1;
|
|
1366
|
+
}
|
|
1367
|
+
if (start === null) return 0;
|
|
1368
|
+
w.dropped += end - start;
|
|
1369
|
+
return count;
|
|
1370
|
+
}
|
|
1371
|
+
function margin(v, side) {
|
|
1372
|
+
let value = 0;
|
|
1373
|
+
for (const declaration of styleString2(v.props.style).split(";")) {
|
|
1374
|
+
const m = /^\s*margin(?:-(top|bottom|left|right))?\s*:\s*(.+?)\s*$/i.exec(declaration);
|
|
1375
|
+
if (!m) continue;
|
|
1376
|
+
if (m[1]) {
|
|
1377
|
+
if (m[1].toLowerCase() === side) value = pixels(m[2]);
|
|
1378
|
+
} else {
|
|
1379
|
+
const parts = m[2].split(/\s+/);
|
|
1380
|
+
value = pixels(side === "top" ? parts[0] : parts.length >= 3 ? parts[2] : parts[0]);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
return value;
|
|
1384
|
+
}
|
|
1385
|
+
function pixels(length2) {
|
|
1386
|
+
const m = /^(-?\d*\.?\d+)(px)?$/i.exec(length2.trim());
|
|
1387
|
+
return m && (m[2] || Number(m[1]) === 0) ? Number(m[1]) : 0;
|
|
1388
|
+
}
|
|
1024
1389
|
function blank(v, height) {
|
|
1025
1390
|
const kept = styleString2(v.props.style).split(";").map((d) => d.trim()).filter((d) => d && !/^height\s*:/i.test(d));
|
|
1026
|
-
kept.push(`height:${
|
|
1391
|
+
kept.push(`height:${round4(height)}px`);
|
|
1027
1392
|
return { ...v, props: { ...v.props, style: kept.join(";") }, children: [] };
|
|
1028
1393
|
}
|
|
1029
1394
|
function cropTable(table, w, rows) {
|
|
@@ -1050,7 +1415,7 @@ function cropTable(table, w, rows) {
|
|
|
1050
1415
|
};
|
|
1051
1416
|
return rewrite(table);
|
|
1052
1417
|
}
|
|
1053
|
-
function
|
|
1418
|
+
function round4(n) {
|
|
1054
1419
|
return Math.round(n * 100) / 100;
|
|
1055
1420
|
}
|
|
1056
1421
|
|
|
@@ -1067,15 +1432,17 @@ function box(left, top, width, height, children, clip = true, bleed = 0) {
|
|
|
1067
1432
|
return { kind: { type: "View" }, style, children };
|
|
1068
1433
|
}
|
|
1069
1434
|
function headerOnly(v) {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1435
|
+
if (v.tag === "table") {
|
|
1436
|
+
return { ...v, children: v.children.filter((c) => typeof c !== "string" && (c.tag === "colgroup" || c.tag === "thead")) };
|
|
1437
|
+
}
|
|
1438
|
+
let changed = false;
|
|
1439
|
+
const children = v.children.map((c) => {
|
|
1440
|
+
if (typeof c === "string") return c;
|
|
1441
|
+
const next = headerOnly(c);
|
|
1442
|
+
if (next !== c) changed = true;
|
|
1443
|
+
return next;
|
|
1077
1444
|
});
|
|
1078
|
-
return
|
|
1445
|
+
return changed ? { ...v, children } : v;
|
|
1079
1446
|
}
|
|
1080
1447
|
function sectionsByKey(doc) {
|
|
1081
1448
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -1096,20 +1463,36 @@ function emitPages(input) {
|
|
|
1096
1463
|
const width = page.contentWidth;
|
|
1097
1464
|
const bleed = page.margins.right;
|
|
1098
1465
|
const bodyDependsOnPage = isPageDependent(resolved.body);
|
|
1099
|
-
const
|
|
1466
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
1467
|
+
const rendered = /* @__PURE__ */ new Map();
|
|
1468
|
+
const treeOf = (section, opts) => {
|
|
1469
|
+
if (bodyDependsOnPage) return renderNode2(section, opts, width);
|
|
1470
|
+
if (!rendered.has(section.key)) rendered.set(section.key, renderNode2(section, opts, width));
|
|
1471
|
+
return rendered.get(section.key);
|
|
1472
|
+
};
|
|
1100
1473
|
const translateSection = (section, opts, from, to) => {
|
|
1101
1474
|
const cacheKey = `${section.key}|${from}|${to}`;
|
|
1102
|
-
if (!bodyDependsOnPage &&
|
|
1103
|
-
const v =
|
|
1475
|
+
if (!bodyDependsOnPage && cache2.has(cacheKey)) return cache2.get(cacheKey);
|
|
1476
|
+
const v = treeOf(section, opts);
|
|
1104
1477
|
if (!v) return null;
|
|
1105
1478
|
const geom = geometry[section.key];
|
|
1106
1479
|
const cropped = geom ? cropToWindow(v, geom, from, to) : { node: v, offset: 0 };
|
|
1107
1480
|
const t = translate(cropped.node, ctx);
|
|
1108
1481
|
if (!t) return null;
|
|
1109
1482
|
const out = { node: t.node, offset: cropped.offset };
|
|
1110
|
-
if (!bodyDependsOnPage)
|
|
1483
|
+
if (!bodyDependsOnPage) cache2.set(cacheKey, out);
|
|
1111
1484
|
return out;
|
|
1112
1485
|
};
|
|
1486
|
+
const headers = /* @__PURE__ */ new Map();
|
|
1487
|
+
const repeatedHeader = (tableKey, table, opts, headerWidth) => {
|
|
1488
|
+
const key = `${tableKey}|${headerWidth}`;
|
|
1489
|
+
if (!bodyDependsOnPage && headers.has(key)) return headers.get(key);
|
|
1490
|
+
const v = renderNode2(table, opts, headerWidth);
|
|
1491
|
+
const t = v ? translate(headerOnly(v), ctx) : null;
|
|
1492
|
+
const node = t ? t.node : null;
|
|
1493
|
+
if (!bodyDependsOnPage) headers.set(key, node);
|
|
1494
|
+
return node;
|
|
1495
|
+
};
|
|
1113
1496
|
const region = (sections, opts) => {
|
|
1114
1497
|
const out = [];
|
|
1115
1498
|
for (const s of sections) {
|
|
@@ -1123,7 +1506,7 @@ function emitPages(input) {
|
|
|
1123
1506
|
return paged.pages.map((p) => {
|
|
1124
1507
|
const opts = { ...renderOpts, page: { number: p.number, total: paged.totalPages } };
|
|
1125
1508
|
const children = [];
|
|
1126
|
-
|
|
1509
|
+
for (const mark of input.watermarks ?? []) if (mark && (mark.placement ?? "center") === "center") children.push(watermarkNode(mark));
|
|
1127
1510
|
const headerSections = p.first && resolved.header.firstPage ? resolved.header.firstPage : resolved.header.default;
|
|
1128
1511
|
if (headerSections.length && page.headerHeight > 0) {
|
|
1129
1512
|
children.push(box(page.margins.left, page.headerTop, width, page.headerHeight, region(headerSections, opts)));
|
|
@@ -1137,10 +1520,8 @@ function emitPages(input) {
|
|
|
1137
1520
|
for (const r of f.repeatHeaders) {
|
|
1138
1521
|
const table = byKey.get(r.tableKey);
|
|
1139
1522
|
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]));
|
|
1523
|
+
const header = repeatedHeader(r.tableKey, table, opts, r.width);
|
|
1524
|
+
if (header) inner.push(box(r.left, 0, r.width, r.height, [header]));
|
|
1144
1525
|
}
|
|
1145
1526
|
const content = translateSection(section, opts, f.from, f.to);
|
|
1146
1527
|
if (content) {
|
|
@@ -1158,6 +1539,14 @@ function emitPages(input) {
|
|
|
1158
1539
|
if (footerSections.length && page.footerHeight > 0) {
|
|
1159
1540
|
children.push(box(page.margins.left, page.footerTop, width, page.footerHeight, region(footerSections, opts)));
|
|
1160
1541
|
}
|
|
1542
|
+
for (const mark of input.watermarks ?? []) {
|
|
1543
|
+
if (!mark || (mark.placement ?? "center") !== "bottom") continue;
|
|
1544
|
+
const node = creditNode(mark, width, ctx);
|
|
1545
|
+
if (!node) continue;
|
|
1546
|
+
const h = mark.fontSize * 1.6;
|
|
1547
|
+
const top = Math.max(page.height - h, page.height - page.margins.bottom + Math.max(0, (page.margins.bottom - h) / 2));
|
|
1548
|
+
children.push(box(page.margins.left, top, width, h, [node]));
|
|
1549
|
+
}
|
|
1161
1550
|
return {
|
|
1162
1551
|
kind: {
|
|
1163
1552
|
type: "Page",
|
|
@@ -1220,12 +1609,15 @@ async function buildFormeDocument(opts) {
|
|
|
1220
1609
|
return out;
|
|
1221
1610
|
};
|
|
1222
1611
|
const children = [];
|
|
1223
|
-
const
|
|
1224
|
-
if (mark) children.push(watermarkNode(mark));
|
|
1612
|
+
const marks = [watermarkFor(license), opts.watermark].filter((m) => Boolean(m));
|
|
1613
|
+
for (const mark of marks) if ((mark.placement ?? "center") === "center") children.push(watermarkNode(mark));
|
|
1225
1614
|
const header = translate2(resolved.header.default);
|
|
1226
1615
|
if (header.length) children.push({ kind: { type: "Fixed", position: "Header" }, style: {}, children: header });
|
|
1227
1616
|
const footer = translate2(resolved.footer.default);
|
|
1228
|
-
|
|
1617
|
+
const credits = marks.filter((m) => (m.placement ?? "center") === "bottom").map((m) => creditNode(m, geom.contentWidth, ctx)).filter((n) => Boolean(n));
|
|
1618
|
+
if (footer.length || credits.length) {
|
|
1619
|
+
children.push({ kind: { type: "Fixed", position: "Footer" }, style: {}, children: [...footer, ...credits] });
|
|
1620
|
+
}
|
|
1229
1621
|
children.push(...translate2(resolved.body));
|
|
1230
1622
|
const page = {
|
|
1231
1623
|
kind: {
|
|
@@ -1245,11 +1637,12 @@ async function buildFormeDocument(opts) {
|
|
|
1245
1637
|
defaultPage: { size: "A4", margin: { top: 54, right: 54, bottom: 54, left: 54 }, wrap: true },
|
|
1246
1638
|
defaultStyle: mapStyle4({ fontSize: pxToPt(theme.typography.baseFontSize ?? 11), fontFamily: theme.typography.bodyFont.split(",")[0].trim().replace(/^['"]|['"]$/g, "") })
|
|
1247
1639
|
};
|
|
1248
|
-
|
|
1640
|
+
const fonts = await resolveFonts({ fonts: opts.fonts, theme, sources: opts.fontSources });
|
|
1641
|
+
if (fonts.fonts.length) doc.fonts = fontEntries(fonts.fonts);
|
|
1249
1642
|
if (opts.tagged) doc.tagged = true;
|
|
1250
1643
|
if (opts.pdfUa) doc.pdfUa = true;
|
|
1251
1644
|
if (opts.pdfA) doc.pdfa = opts.pdfA;
|
|
1252
|
-
const warnings = [...ctx.warnings, ...resolved.warnings.map((w) => w.message)];
|
|
1645
|
+
const warnings = [...ctx.warnings, ...resolved.warnings.map((w) => w.message), ...fonts.warnings];
|
|
1253
1646
|
return { doc, warnings, license };
|
|
1254
1647
|
}
|
|
1255
1648
|
var engineLoader = null;
|
|
@@ -1282,6 +1675,20 @@ async function renderPdf(opts) {
|
|
|
1282
1675
|
}
|
|
1283
1676
|
async function renderPdfPaginated(opts) {
|
|
1284
1677
|
const started = Date.now();
|
|
1678
|
+
const layout = await layoutPaginated(opts);
|
|
1679
|
+
const drawn = await layout.draw();
|
|
1680
|
+
return {
|
|
1681
|
+
pdf: drawn.pdf,
|
|
1682
|
+
pages: drawn.pages,
|
|
1683
|
+
layout: drawn.layout,
|
|
1684
|
+
paged: layout.paged,
|
|
1685
|
+
geometry: layout.geometry,
|
|
1686
|
+
warnings: drawn.warnings,
|
|
1687
|
+
durationMs: Date.now() - started
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
var MEASURE_BATCH_ROWS = 1e3;
|
|
1691
|
+
async function layoutPaginated(opts) {
|
|
1285
1692
|
const license = await resolveLicenseStatus(opts.license !== void 0 ? { license: opts.license } : {});
|
|
1286
1693
|
const { template } = migrateTemplate(opts.template);
|
|
1287
1694
|
const theme = mergeTheme(opts.theme);
|
|
@@ -1316,17 +1723,16 @@ async function renderPdfPaginated(opts) {
|
|
|
1316
1723
|
};
|
|
1317
1724
|
const ctx = { warnings: /* @__PURE__ */ new Set(), suppressPageBreaks: true };
|
|
1318
1725
|
const render = opts.renderer ?? defaultRenderer;
|
|
1319
|
-
const
|
|
1320
|
-
|
|
1726
|
+
const fonts = await resolveFonts({ fonts: opts.fonts, theme, sources: opts.fontSources });
|
|
1727
|
+
const layoutSections = async (list) => {
|
|
1728
|
+
if (!list.length) return {};
|
|
1321
1729
|
const measured = [];
|
|
1322
|
-
for (const section of
|
|
1323
|
-
const
|
|
1324
|
-
if (!v) continue;
|
|
1325
|
-
const t = translate(v, ctx);
|
|
1730
|
+
for (const { section, tree } of list) {
|
|
1731
|
+
const t = translate(tree, ctx);
|
|
1326
1732
|
if (t) measured.push({ section, node: t.node, ann: t.ann });
|
|
1327
1733
|
}
|
|
1328
1734
|
if (!measured.length) return {};
|
|
1329
|
-
const
|
|
1735
|
+
const doc = {
|
|
1330
1736
|
children: [
|
|
1331
1737
|
{
|
|
1332
1738
|
kind: {
|
|
@@ -1345,53 +1751,86 @@ async function renderPdfPaginated(opts) {
|
|
|
1345
1751
|
defaultPage: { size: "A4", margin: { top: 54, right: 54, bottom: 54, left: 54 }, wrap: true },
|
|
1346
1752
|
defaultStyle: baseStyle(theme)
|
|
1347
1753
|
};
|
|
1348
|
-
if (
|
|
1349
|
-
const
|
|
1350
|
-
const
|
|
1754
|
+
if (fonts.fonts.length) doc.fonts = fontEntries(fonts.fonts);
|
|
1755
|
+
const result = await render(doc);
|
|
1756
|
+
const pages = result.layout?.pages ?? [];
|
|
1351
1757
|
const scale2 = 1 / PX_TO_PT_LOCAL;
|
|
1352
|
-
const scaled =
|
|
1758
|
+
const scaled = pages[0]?.elements.map((e) => scaleElement(e, scale2)) ?? [];
|
|
1353
1759
|
return geometryFromLayout(measured, scaled, geom.contentWidth);
|
|
1354
1760
|
};
|
|
1761
|
+
const batchRows = Math.max(1, opts.measureBatchRows ?? MEASURE_BATCH_ROWS);
|
|
1762
|
+
const measure = async (sections) => {
|
|
1763
|
+
const parts = [];
|
|
1764
|
+
for (const section of sections) {
|
|
1765
|
+
const tree = renderNode3(section, renderOpts, geom.contentWidth);
|
|
1766
|
+
if (tree) parts.push({ section, tree, long: longTables(tree, batchRows) });
|
|
1767
|
+
}
|
|
1768
|
+
const out = await layoutSections(parts.map((p) => ({ section: p.section, tree: p.long.length ? sliceTables(p.tree, p.long, 0, batchRows) : p.tree })));
|
|
1769
|
+
for (const p of parts) {
|
|
1770
|
+
const first = out[p.section.key];
|
|
1771
|
+
if (!p.long.length || !first) continue;
|
|
1772
|
+
const most = Math.max(...p.long.map((t) => t.bodyRows));
|
|
1773
|
+
const batches = [];
|
|
1774
|
+
for (let start = batchRows; start < most; start += batchRows) {
|
|
1775
|
+
batches.push({ section: { ...p.section, key: `${p.section.key}#rows-${start}` }, tree: sliceTables(p.tree, p.long, start, batchRows) });
|
|
1776
|
+
}
|
|
1777
|
+
const measured = [];
|
|
1778
|
+
for (let i = 0; i < batches.length; i += BATCHES_PER_PASS) {
|
|
1779
|
+
const chunk = batches.slice(i, i + BATCHES_PER_PASS);
|
|
1780
|
+
const got = await layoutSections(chunk);
|
|
1781
|
+
for (const b of chunk) {
|
|
1782
|
+
const g = got[b.section.key];
|
|
1783
|
+
if (g) measured.push(g);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
out[p.section.key] = stitchTables(first, p.long, measured);
|
|
1787
|
+
}
|
|
1788
|
+
return out;
|
|
1789
|
+
};
|
|
1355
1790
|
const geometry = await measure(resolved.body);
|
|
1356
1791
|
const regionGeometry = await measure([...resolved.header.default, ...resolved.header.firstPage ?? [], ...resolved.footer.default, ...resolved.footer.firstPage ?? []]);
|
|
1357
1792
|
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;
|
|
1793
|
+
const background = template.page.background ? resolveValue(template.page.background, theme, "#ffffff") : void 0;
|
|
1380
1794
|
return {
|
|
1381
|
-
pdf: result.pdf,
|
|
1382
|
-
pages: layout?.pages.length ?? paged.totalPages,
|
|
1383
|
-
layout,
|
|
1384
1795
|
paged,
|
|
1385
1796
|
geometry,
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1797
|
+
async draw(range) {
|
|
1798
|
+
const from = Math.max(0, range?.from ?? 0);
|
|
1799
|
+
const to = Math.min(paged.pages.length, range?.to ?? paged.pages.length);
|
|
1800
|
+
const pages = emitPages({
|
|
1801
|
+
resolved,
|
|
1802
|
+
// `totalPages` stays the whole document's, so a later file still counts
|
|
1803
|
+
// its pages as part of the whole.
|
|
1804
|
+
paged: { ...paged, pages: paged.pages.slice(from, to) },
|
|
1805
|
+
page: geom,
|
|
1806
|
+
renderOpts,
|
|
1807
|
+
ctx,
|
|
1808
|
+
geometry,
|
|
1809
|
+
background,
|
|
1810
|
+
watermarks: [watermarkFor(license), opts.watermark]
|
|
1811
|
+
});
|
|
1812
|
+
const doc = {
|
|
1813
|
+
children: pages,
|
|
1814
|
+
metadata: { title: template.name, creator: producerString(license), ...opts.metadata ?? {} },
|
|
1815
|
+
defaultPage: { size: "A4", margin: { top: 54, right: 54, bottom: 54, left: 54 }, wrap: true },
|
|
1816
|
+
defaultStyle: baseStyle(theme)
|
|
1817
|
+
};
|
|
1818
|
+
if (fonts.fonts.length) doc.fonts = fontEntries(fonts.fonts);
|
|
1819
|
+
if (opts.tagged) doc.tagged = true;
|
|
1820
|
+
if (opts.pdfUa) doc.pdfUa = true;
|
|
1821
|
+
if (opts.pdfA) doc.pdfa = opts.pdfA;
|
|
1822
|
+
const result = await render(doc);
|
|
1823
|
+
const layout = result.layout ?? null;
|
|
1824
|
+
return {
|
|
1825
|
+
pdf: result.pdf,
|
|
1826
|
+
pages: layout?.pages.length ?? to - from,
|
|
1827
|
+
layout,
|
|
1828
|
+
warnings: [...ctx.warnings, ...resolved.warnings.map((w) => w.message), ...paged.warnings.map((w) => w.message), ...fonts.warnings, ...result.warnings ?? []]
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1393
1831
|
};
|
|
1394
1832
|
}
|
|
1833
|
+
var BATCHES_PER_PASS = 2;
|
|
1395
1834
|
var PX_TO_PT_LOCAL = 72 / 96;
|
|
1396
1835
|
function scaleElement(el, scale2) {
|
|
1397
1836
|
return {
|
|
@@ -1424,15 +1863,19 @@ export {
|
|
|
1424
1863
|
UNBOUNDED_PAGE_HEIGHT,
|
|
1425
1864
|
geometryFromLayout,
|
|
1426
1865
|
FormeMeasurer,
|
|
1866
|
+
setFontFileReader,
|
|
1867
|
+
resolveFonts,
|
|
1427
1868
|
FormeEditorMeasurer,
|
|
1428
1869
|
buildFormeDocument,
|
|
1429
1870
|
setEngineLoader,
|
|
1430
1871
|
renderPdf,
|
|
1431
1872
|
renderPdfPaginated,
|
|
1873
|
+
MEASURE_BATCH_ROWS,
|
|
1874
|
+
layoutPaginated,
|
|
1432
1875
|
BroadPaper,
|
|
1433
1876
|
configure,
|
|
1434
1877
|
resolveLicenseStatus2 as resolveLicenseStatus,
|
|
1435
1878
|
verifyLicense,
|
|
1436
1879
|
parseLicense
|
|
1437
1880
|
};
|
|
1438
|
-
//# sourceMappingURL=chunk-
|
|
1881
|
+
//# sourceMappingURL=chunk-QA4APTWM.js.map
|