@worm-vue3-print/core 1.2.0 → 1.2.2

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.
@@ -0,0 +1,1920 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/browser/index.ts
31
+ var browser_exports = {};
32
+ __export(browser_exports, {
33
+ browserCodeRenderer: () => browserCodeRenderer,
34
+ renderHtmlPages: () => renderHtmlPages
35
+ });
36
+ module.exports = __toCommonJS(browser_exports);
37
+
38
+ // src/lexer.ts
39
+ var DANGEROUS_PROPS = /* @__PURE__ */ new Set([
40
+ "constructor",
41
+ "__proto__",
42
+ "prototype",
43
+ "__defineGetter__",
44
+ "__defineSetter__",
45
+ "__lookupGetter__",
46
+ "__lookupSetter__"
47
+ ]);
48
+ var RESERVED_WORDS = /* @__PURE__ */ new Set([
49
+ "new",
50
+ "function",
51
+ "class",
52
+ "this",
53
+ "delete",
54
+ "typeof",
55
+ "void",
56
+ "in",
57
+ "of",
58
+ "var",
59
+ "let",
60
+ "const",
61
+ "return",
62
+ "if",
63
+ "while",
64
+ "for"
65
+ ]);
66
+ function tokenize(input) {
67
+ const tokens = [];
68
+ let i = 0;
69
+ const n = input.length;
70
+ while (i < n) {
71
+ const ch = input[i];
72
+ if (/\s/.test(ch)) {
73
+ i++;
74
+ continue;
75
+ }
76
+ if (/[0-9]/.test(ch) || ch === "." && i + 1 < n && /[0-9]/.test(input[i + 1])) {
77
+ let j = i;
78
+ while (j < n && /[0-9.]/.test(input[j])) j++;
79
+ tokens.push({ type: "num", value: input.slice(i, j), position: i });
80
+ i = j;
81
+ continue;
82
+ }
83
+ if (ch === "'" || ch === '"') {
84
+ let j = i + 1;
85
+ let s = "";
86
+ while (j < n && input[j] !== ch) {
87
+ if (input[j] === "\\" && j + 1 < n) {
88
+ s += input[j + 1];
89
+ j += 2;
90
+ } else {
91
+ s += input[j];
92
+ j++;
93
+ }
94
+ }
95
+ if (j >= n) throw new Error("\u5B57\u7B26\u4E32\u672A\u95ED\u5408");
96
+ tokens.push({ type: "str", value: s, position: i });
97
+ i = j + 1;
98
+ continue;
99
+ }
100
+ if (/[A-Za-z_$]/.test(ch)) {
101
+ let j = i;
102
+ while (j < n && /[A-Za-z0-9_$]/.test(input[j])) j++;
103
+ const word = input.slice(i, j);
104
+ if (RESERVED_WORDS.has(word)) {
105
+ throw new Error(`\u4E0D\u5141\u8BB8\u4F7F\u7528\u5173\u952E\u5B57: ${word}`);
106
+ }
107
+ if (DANGEROUS_PROPS.has(word)) {
108
+ throw new Error(`\u7981\u6B62\u8BBF\u95EE: ${word}`);
109
+ }
110
+ if (word === "true" || word === "false") {
111
+ tokens.push({ type: "bool", value: word, position: i });
112
+ } else {
113
+ tokens.push({ type: "ident", value: word, position: i });
114
+ }
115
+ i = j;
116
+ continue;
117
+ }
118
+ const three = input.slice(i, i + 3);
119
+ if (three === "===" || three === "!==") {
120
+ tokens.push({ type: "punc", value: three, position: i });
121
+ i += 3;
122
+ continue;
123
+ }
124
+ const two = input.slice(i, i + 2);
125
+ if (two === "=>") throw new Error("\u4E0D\u5141\u8BB8\u51FD\u6570\u5B9A\u4E49");
126
+ if (["==", "!=", "<=", ">=", "&&", "||"].includes(two)) {
127
+ tokens.push({ type: "punc", value: two, position: i });
128
+ i += 2;
129
+ continue;
130
+ }
131
+ if (ch === "=") throw new Error("\u4E0D\u5141\u8BB8\u8D4B\u503C");
132
+ if (ch === ";") throw new Error("\u4E0D\u5141\u8BB8\u591A\u6761\u8BED\u53E5");
133
+ if ("+-*/%()[]{},:.?!<>".includes(ch)) {
134
+ tokens.push({ type: "punc", value: ch, position: i });
135
+ i++;
136
+ continue;
137
+ }
138
+ throw new Error(`\u975E\u6CD5\u5B57\u7B26: ${ch}`);
139
+ }
140
+ return tokens;
141
+ }
142
+
143
+ // src/parser.ts
144
+ function parse(tokens) {
145
+ const parser = new Parser(tokens);
146
+ return parser.parse();
147
+ }
148
+ var Parser = class {
149
+ constructor(tokens) {
150
+ this.pos = 0;
151
+ this.tokens = tokens;
152
+ }
153
+ parse() {
154
+ const node = this.parseTernary();
155
+ if (this.pos < this.tokens.length) {
156
+ throw new Error(`\u8868\u8FBE\u5F0F\u5B58\u5728\u591A\u4F59\u5185\u5BB9: ${this.tokens[this.pos].value}`);
157
+ }
158
+ return node;
159
+ }
160
+ peek() {
161
+ return this.tokens[this.pos];
162
+ }
163
+ next() {
164
+ const t = this.tokens[this.pos++];
165
+ if (!t) throw new Error("\u8868\u8FBE\u5F0F\u610F\u5916\u7ED3\u675F");
166
+ return t;
167
+ }
168
+ eatPunc(value) {
169
+ const t = this.peek();
170
+ if (t && t.type === "punc" && t.value === value) {
171
+ this.pos++;
172
+ return true;
173
+ }
174
+ return false;
175
+ }
176
+ expectPunc(value) {
177
+ if (!this.eatPunc(value)) {
178
+ const pos = this.peek()?.position ?? 0;
179
+ throw new Error(`\u4F4D\u7F6E ${pos}: \u671F\u671B "${value}"`);
180
+ }
181
+ }
182
+ // 三元运算符 ?: (优先级最低)
183
+ parseTernary() {
184
+ const cond = this.parseLogicalOr();
185
+ if (this.eatPunc("?")) {
186
+ const consequent = this.parseTernary();
187
+ this.expectPunc(":");
188
+ const alternate = this.parseTernary();
189
+ return { type: "ternary", cond, consequent, alternate };
190
+ }
191
+ return cond;
192
+ }
193
+ // ||
194
+ parseLogicalOr() {
195
+ let left = this.parseLogicalAnd();
196
+ while (this.eatPunc("||")) {
197
+ const right = this.parseLogicalAnd();
198
+ left = { type: "binary", op: "||", left, right };
199
+ }
200
+ return left;
201
+ }
202
+ // &&
203
+ parseLogicalAnd() {
204
+ let left = this.parseEquality();
205
+ while (this.eatPunc("&&")) {
206
+ const right = this.parseEquality();
207
+ left = { type: "binary", op: "&&", left, right };
208
+ }
209
+ return left;
210
+ }
211
+ // == != === !==
212
+ parseEquality() {
213
+ let left = this.parseRelational();
214
+ for (; ; ) {
215
+ const t = this.peek();
216
+ if (t?.type === "punc" && ["==", "!=", "===", "!=="].includes(t.value)) {
217
+ this.pos++;
218
+ const right = this.parseRelational();
219
+ left = { type: "binary", op: t.value, left, right };
220
+ } else {
221
+ return left;
222
+ }
223
+ }
224
+ }
225
+ // < > <= >=
226
+ parseRelational() {
227
+ let left = this.parseAdditive();
228
+ for (; ; ) {
229
+ const t = this.peek();
230
+ if (t?.type === "punc" && ["<", ">", "<=", ">="].includes(t.value)) {
231
+ this.pos++;
232
+ const right = this.parseAdditive();
233
+ left = { type: "binary", op: t.value, left, right };
234
+ } else {
235
+ return left;
236
+ }
237
+ }
238
+ }
239
+ // + - (二元)
240
+ parseAdditive() {
241
+ let left = this.parseMultiplicative();
242
+ for (; ; ) {
243
+ const t = this.peek();
244
+ if (t?.type === "punc" && (t.value === "+" || t.value === "-")) {
245
+ this.pos++;
246
+ const right = this.parseMultiplicative();
247
+ left = { type: "binary", op: t.value, left, right };
248
+ } else {
249
+ return left;
250
+ }
251
+ }
252
+ }
253
+ // * / %
254
+ parseMultiplicative() {
255
+ let left = this.parseUnary();
256
+ for (; ; ) {
257
+ const t = this.peek();
258
+ if (t?.type === "punc" && ["*", "/", "%"].includes(t.value)) {
259
+ this.pos++;
260
+ const right = this.parseUnary();
261
+ left = { type: "binary", op: t.value, left, right };
262
+ } else {
263
+ return left;
264
+ }
265
+ }
266
+ }
267
+ // 一元 ! - +
268
+ parseUnary() {
269
+ const t = this.peek();
270
+ if (t?.type === "punc" && ["!", "-", "+"].includes(t.value)) {
271
+ this.pos++;
272
+ const arg = this.parseUnary();
273
+ return { type: "unary", op: t.value, arg, prefix: true };
274
+ }
275
+ return this.parsePostfix();
276
+ }
277
+ // 成员访问 x.y, x[i], 函数调用 f()
278
+ parsePostfix() {
279
+ let node = this.parsePrimary();
280
+ for (; ; ) {
281
+ if (this.eatPunc(".")) {
282
+ const prop = this.next();
283
+ if (prop.type !== "ident") {
284
+ throw new Error(`\u4F4D\u7F6E ${prop.position}: \u6210\u5458\u8BBF\u95EE\u9700\u8981\u5C5E\u6027\u540D`);
285
+ }
286
+ node = {
287
+ type: "member",
288
+ object: node,
289
+ property: prop.value,
290
+ computed: false
291
+ };
292
+ } else if (this.eatPunc("[")) {
293
+ const index = this.parseTernary();
294
+ this.expectPunc("]");
295
+ if (index.type === "str") {
296
+ node = {
297
+ type: "member",
298
+ object: node,
299
+ property: index.value,
300
+ computed: false
301
+ };
302
+ } else {
303
+ node = {
304
+ type: "member",
305
+ object: node,
306
+ property: index.name ?? String(index.value),
307
+ computed: true
308
+ };
309
+ }
310
+ } else if (this.peek()?.type === "punc" && this.peek()?.value === "(" && (node.type === "ident" || node.type === "member")) {
311
+ this.pos++;
312
+ const args = this.parseArgs();
313
+ node = { type: "call", callee: node, args };
314
+ } else {
315
+ return node;
316
+ }
317
+ }
318
+ }
319
+ parseArgs() {
320
+ const args = [];
321
+ if (this.eatPunc(")")) return args;
322
+ for (; ; ) {
323
+ args.push(this.parseTernary());
324
+ if (this.eatPunc(")")) return args;
325
+ this.expectPunc(",");
326
+ }
327
+ }
328
+ parsePrimary() {
329
+ const t = this.next();
330
+ if (t.type === "num") {
331
+ const v = Number(t.value);
332
+ if (isNaN(v)) throw new Error(`\u975E\u6CD5\u6570\u5B57: ${t.value}`);
333
+ return { type: "num", value: v };
334
+ }
335
+ if (t.type === "str") {
336
+ return { type: "str", value: t.value };
337
+ }
338
+ if (t.type === "bool") {
339
+ return { type: "bool", value: t.value === "true" };
340
+ }
341
+ if (t.type === "ident") {
342
+ return { type: "ident", name: t.value };
343
+ }
344
+ if (t.value === "(") {
345
+ const inner = this.parseTernary();
346
+ this.expectPunc(")");
347
+ return inner;
348
+ }
349
+ if (t.value === "[") {
350
+ const elements = [];
351
+ if (!this.eatPunc("]")) {
352
+ for (; ; ) {
353
+ elements.push(this.parseTernary());
354
+ if (this.eatPunc("]")) break;
355
+ this.expectPunc(",");
356
+ }
357
+ }
358
+ return { type: "array", elements };
359
+ }
360
+ if (t.value === "{") {
361
+ const properties = [];
362
+ if (!this.eatPunc("}")) {
363
+ for (; ; ) {
364
+ const keyToken = this.next();
365
+ if (keyToken.type !== "ident" && keyToken.type !== "str") {
366
+ throw new Error(`\u4F4D\u7F6E ${keyToken.position}: \u5BF9\u8C61\u952E\u5FC5\u987B\u662F\u6807\u8BC6\u7B26\u6216\u5B57\u7B26\u4E32`);
367
+ }
368
+ this.expectPunc(":");
369
+ properties.push({ key: keyToken.value, value: this.parseTernary() });
370
+ if (this.eatPunc("}")) break;
371
+ this.expectPunc(",");
372
+ }
373
+ }
374
+ return { type: "object", properties };
375
+ }
376
+ throw new Error(`\u4F4D\u7F6E ${t.position}: \u610F\u5916\u7684\u7B26\u53F7: ${t.value}`);
377
+ }
378
+ };
379
+
380
+ // src/evaluator.ts
381
+ var SAFE_GLOBALS = {
382
+ Math,
383
+ Number,
384
+ String,
385
+ Boolean,
386
+ parseInt,
387
+ parseFloat,
388
+ isNaN,
389
+ true: true,
390
+ false: false,
391
+ null: null,
392
+ undefined: void 0
393
+ };
394
+ var DANGEROUS_PROPS2 = /* @__PURE__ */ new Set([
395
+ "constructor",
396
+ "__proto__",
397
+ "prototype",
398
+ "__defineGetter__",
399
+ "__defineSetter__",
400
+ "__lookupGetter__",
401
+ "__lookupSetter__"
402
+ ]);
403
+ var CALLABLE_GLOBALS = /* @__PURE__ */ new Set(["Number", "String", "Boolean", "parseInt", "parseFloat", "isNaN"]);
404
+ function evaluate(node, context, functions = {}) {
405
+ switch (node.type) {
406
+ case "num":
407
+ return node.value;
408
+ case "str":
409
+ return node.value;
410
+ case "bool":
411
+ return node.value;
412
+ case "ident":
413
+ return evalIdent(node.name, context, functions);
414
+ case "binary":
415
+ return evalBinary(node, context, functions);
416
+ case "unary":
417
+ return evalUnary(node, context, functions);
418
+ case "ternary":
419
+ return evaluate(node.cond, context, functions) ? evaluate(node.consequent, context, functions) : evaluate(node.alternate, context, functions);
420
+ case "member":
421
+ return evalMember(node, context, functions);
422
+ case "call":
423
+ return evalCall(node, context, functions);
424
+ case "array":
425
+ return node.elements.map((e) => evaluate(e, context, functions));
426
+ case "object":
427
+ return Object.fromEntries(
428
+ node.properties.map((p) => [p.key, evaluate(p.value, context, functions)])
429
+ );
430
+ }
431
+ }
432
+ function evalIdent(name, context, functions) {
433
+ if (Object.prototype.hasOwnProperty.call(context, name)) {
434
+ return context[name];
435
+ }
436
+ if (Object.prototype.hasOwnProperty.call(SAFE_GLOBALS, name)) {
437
+ return SAFE_GLOBALS[name];
438
+ }
439
+ throw new Error(`\u672A\u5B9A\u4E49\u7684\u6807\u8BC6\u7B26: ${name}`);
440
+ }
441
+ function evalBinary(node, context, functions) {
442
+ const left = evaluate(node.left, context, functions);
443
+ const right = evaluate(node.right, context, functions);
444
+ switch (node.op) {
445
+ case "+":
446
+ return left + right;
447
+ case "-":
448
+ return left - right;
449
+ case "*":
450
+ return left * right;
451
+ case "/":
452
+ return left / right;
453
+ case "%":
454
+ return left % right;
455
+ case "<":
456
+ return left < right;
457
+ case ">":
458
+ return left > right;
459
+ case "<=":
460
+ return left <= right;
461
+ case ">=":
462
+ return left >= right;
463
+ case "==":
464
+ return left == right;
465
+ case "!=":
466
+ return left != right;
467
+ case "===":
468
+ return left === right;
469
+ case "!==":
470
+ return left !== right;
471
+ case "&&":
472
+ return left && right;
473
+ case "||":
474
+ return left || right;
475
+ default:
476
+ throw new Error(`\u672A\u77E5\u8FD0\u7B97\u7B26: ${node.op}`);
477
+ }
478
+ }
479
+ function evalUnary(node, context, functions) {
480
+ const arg = evaluate(node.arg, context, functions);
481
+ switch (node.op) {
482
+ case "-":
483
+ return -arg;
484
+ case "+":
485
+ return +arg;
486
+ case "!":
487
+ return !arg;
488
+ default:
489
+ throw new Error(`\u672A\u77E5\u4E00\u5143\u8FD0\u7B97\u7B26: ${node.op}`);
490
+ }
491
+ }
492
+ function evalMember(node, context, functions) {
493
+ const obj = evaluate(node.object, context, functions);
494
+ if (obj == null) {
495
+ throw new Error("\u65E0\u6CD5\u8BBF\u95EE\u7A7A\u503C\u7684\u5C5E\u6027");
496
+ }
497
+ if (DANGEROUS_PROPS2.has(node.property)) {
498
+ throw new Error(`\u7981\u6B62\u8BBF\u95EE\u5C5E\u6027: ${node.property}`);
499
+ }
500
+ return obj[node.property];
501
+ }
502
+ function evalCall(node, context, functions) {
503
+ if (node.callee.type === "ident" && CALLABLE_GLOBALS.has(node.callee.name)) {
504
+ const fn = SAFE_GLOBALS[node.callee.name];
505
+ const args = node.args.map((a) => evaluate(a, context, functions));
506
+ return fn(...args);
507
+ }
508
+ if (node.callee.type === "member" && node.callee.object.type === "ident" && node.callee.object.name === "Math") {
509
+ const method = Math[node.callee.property];
510
+ if (typeof method !== "function") {
511
+ throw new Error(`Math.${node.callee.property} \u4E0D\u662F\u65B9\u6CD5`);
512
+ }
513
+ const args = node.args.map((a) => evaluate(a, context, functions));
514
+ return method(...args);
515
+ }
516
+ let fnName;
517
+ if (node.callee.type === "ident") {
518
+ fnName = node.callee.name;
519
+ } else if (node.callee.type === "member") {
520
+ throw new Error("\u4E0D\u652F\u6301\u65B9\u6CD5\u8C03\u7528\uFF0C\u8BF7\u4F7F\u7528\u9876\u7EA7\u51FD\u6570");
521
+ }
522
+ if (fnName && functions[fnName]) {
523
+ const args = node.args.map((a) => evaluate(a, context, functions));
524
+ return functions[fnName](...args);
525
+ }
526
+ if (fnName && Object.prototype.hasOwnProperty.call(context, fnName)) {
527
+ throw new Error(`${fnName} \u4E0D\u662F\u51FD\u6570`);
528
+ }
529
+ throw new Error(`\u4E0D\u5141\u8BB8\u8C03\u7528: ${fnName ?? "\u8868\u8FBE\u5F0F"}`);
530
+ }
531
+
532
+ // src/template-parser.ts
533
+ function parseTemplate(template) {
534
+ const parts = [];
535
+ let i = 0;
536
+ const n = template.length;
537
+ while (i < n) {
538
+ if (template[i] === "{") {
539
+ let depth = 1;
540
+ let j2 = i + 1;
541
+ while (j2 < n && depth > 0) {
542
+ if (template[j2] === "{") depth++;
543
+ else if (template[j2] === "}") depth--;
544
+ j2++;
545
+ }
546
+ if (depth !== 0) {
547
+ throw new Error(`\u4F4D\u7F6E ${i}: \u82B1\u62EC\u53F7\u672A\u95ED\u5408`);
548
+ }
549
+ const exprStr = template.slice(i + 1, j2 - 1);
550
+ if (exprStr.trim() === "") {
551
+ throw new Error(`\u4F4D\u7F6E ${i}: \u7A7A\u8868\u8FBE\u5F0F`);
552
+ }
553
+ const tokens = tokenize(exprStr);
554
+ const expression = parse(tokens);
555
+ parts.push({ type: "expr", expression });
556
+ i = j2;
557
+ continue;
558
+ }
559
+ let j = i;
560
+ while (j < n && template[j] !== "{") {
561
+ if (template[j] === "\\" && j + 1 < n && (template[j + 1] === "{" || template[j + 1] === "}")) {
562
+ j += 2;
563
+ } else {
564
+ j++;
565
+ }
566
+ }
567
+ parts.push({ type: "text", value: template.slice(i, j).replace(/\\([{}])/g, "$1") });
568
+ i = j;
569
+ }
570
+ return { type: "template", parts };
571
+ }
572
+ function renderTemplate(ast, context, functions = {}) {
573
+ let result = "";
574
+ for (const part of ast.parts) {
575
+ if (part.type === "text") {
576
+ result += part.value;
577
+ } else {
578
+ const value = evaluate(part.expression, context, functions);
579
+ result += String(value ?? "");
580
+ }
581
+ }
582
+ return result;
583
+ }
584
+ function compileTemplate(template, functions = {}) {
585
+ const ast = parseTemplate(template);
586
+ return (context) => renderTemplate(ast, context, functions);
587
+ }
588
+
589
+ // src/functions/format.ts
590
+ function formatMoney(value) {
591
+ const num = Number(value);
592
+ if (isNaN(num)) return String(value);
593
+ const sign = num < 0 ? "-" : "";
594
+ const abs = Math.abs(num);
595
+ const [intPart, decPart] = abs.toFixed(2).split(".");
596
+ const formatted = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
597
+ return `${sign}${formatted}.${decPart}`;
598
+ }
599
+ function formatDate(value, fmt) {
600
+ let date;
601
+ if (value instanceof Date) {
602
+ date = value;
603
+ } else if (typeof value === "number") {
604
+ date = new Date(value);
605
+ } else if (typeof value === "string") {
606
+ date = new Date(value);
607
+ } else {
608
+ return String(value);
609
+ }
610
+ if (isNaN(date.getTime())) return String(value);
611
+ const pad = (n) => String(n).padStart(2, "0");
612
+ return fmt.replace("YYYY", String(date.getFullYear())).replace("MM", pad(date.getMonth() + 1)).replace("DD", pad(date.getDate())).replace("HH", pad(date.getHours())).replace("mm", pad(date.getMinutes())).replace("ss", pad(date.getSeconds()));
613
+ }
614
+ function toUpperCaseAmount(value) {
615
+ const num = Number(value);
616
+ if (isNaN(num)) return String(value);
617
+ if (num === 0) return "\u96F6\u5143\u6574";
618
+ const digits = ["\u96F6", "\u58F9", "\u8D30", "\u53C1", "\u8086", "\u4F0D", "\u9646", "\u67D2", "\u634C", "\u7396"];
619
+ const units = ["", "\u62FE", "\u4F70", "\u4EDF"];
620
+ const bigUnits = ["", "\u4E07", "\u4EBF"];
621
+ const sign = num < 0 ? "\u8D1F" : "";
622
+ const abs = Math.abs(num);
623
+ const [intStr, decStr] = abs.toFixed(2).split(".");
624
+ const intNum = parseInt(intStr, 10);
625
+ let result = "";
626
+ if (intNum > 0) {
627
+ const intChars = intStr.split("").reverse();
628
+ let groupIdx = 0;
629
+ let lastDigit = 0;
630
+ let needZero = false;
631
+ for (let i = 0; i < intChars.length; i++) {
632
+ const d = parseInt(intChars[i], 10);
633
+ const unit = units[groupIdx % 4];
634
+ const bigUnit = bigUnits[Math.floor(groupIdx / 4)];
635
+ if (d === 0) {
636
+ if (lastDigit !== 0) needZero = true;
637
+ } else {
638
+ if (needZero) {
639
+ result = "\u96F6" + result;
640
+ needZero = false;
641
+ }
642
+ result = digits[d] + unit + result;
643
+ lastDigit = d;
644
+ }
645
+ if (groupIdx % 4 === 3 && groupIdx > 0) {
646
+ result = bigUnit + result;
647
+ }
648
+ groupIdx++;
649
+ }
650
+ result += "\u5143";
651
+ }
652
+ const jiao = parseInt(decStr[0], 10);
653
+ const fen = parseInt(decStr[1], 10);
654
+ if (jiao === 0 && fen === 0) {
655
+ result += "\u6574";
656
+ } else {
657
+ if (jiao > 0) {
658
+ result += digits[jiao] + "\u89D2";
659
+ }
660
+ if (fen > 0) {
661
+ result += digits[fen] + "\u5206";
662
+ }
663
+ }
664
+ return sign + result;
665
+ }
666
+ function ifFn(condition, trueVal, falseVal) {
667
+ return condition ? trueVal : falseVal;
668
+ }
669
+
670
+ // src/utils.ts
671
+ function getByPath(obj, path) {
672
+ if (obj == null || path == null) return void 0;
673
+ if (typeof obj === "object" && path in obj) return obj[path];
674
+ const parts = path.split(".");
675
+ let cur = obj;
676
+ for (const p of parts) {
677
+ if (cur == null) return void 0;
678
+ cur = cur[p];
679
+ }
680
+ return cur;
681
+ }
682
+
683
+ // src/functions/aggregate.ts
684
+ function sum(rows, field) {
685
+ return rows.reduce((acc, row) => acc + (Number(getByPath(row, field)) || 0), 0);
686
+ }
687
+ function avg(rows, field) {
688
+ if (rows.length === 0) return 0;
689
+ return sum(rows, field) / rows.length;
690
+ }
691
+ function count(rows, field) {
692
+ return rows.length;
693
+ }
694
+ function min(rows, field) {
695
+ if (rows.length === 0) return 0;
696
+ return Math.min(...rows.map((row) => Number(getByPath(row, field)) || 0));
697
+ }
698
+ function max(rows, field) {
699
+ if (rows.length === 0) return 0;
700
+ return Math.max(...rows.map((row) => Number(getByPath(row, field)) || 0));
701
+ }
702
+
703
+ // src/render/expression-eval.ts
704
+ var RenderEngine = class {
705
+ constructor() {
706
+ this.functions = {};
707
+ }
708
+ registerFunction(name, fn) {
709
+ this.functions[name] = fn;
710
+ }
711
+ evaluate(expression, context) {
712
+ const tokens = tokenize(expression);
713
+ const ast = parse(tokens);
714
+ return evaluate(ast, context, this.functions);
715
+ }
716
+ render(template, context) {
717
+ return compileTemplate(template, this.functions)(context);
718
+ }
719
+ };
720
+ var engine = new RenderEngine();
721
+ var FORMAT_FUNCTIONS = {
722
+ MONEY: formatMoney,
723
+ DATE: formatDate,
724
+ UPPER: toUpperCaseAmount,
725
+ IF: ifFn,
726
+ CONCAT: (...args) => args.filter((v) => v != null).map(String).join(""),
727
+ IFEMPTY: (v, d) => v != null && v !== "" ? String(v) : d,
728
+ ROUND: (n, d) => Number(Number(n).toFixed(d)),
729
+ LEN: (s) => String(s).length
730
+ };
731
+ for (const [name, fn] of Object.entries(FORMAT_FUNCTIONS)) {
732
+ engine.registerFunction(name, fn);
733
+ }
734
+ function aggregate(fn, field, rows) {
735
+ switch (fn.toUpperCase()) {
736
+ case "SUM":
737
+ return sum(rows, field);
738
+ case "AVG":
739
+ return avg(rows, field);
740
+ case "COUNT":
741
+ return count(rows, field);
742
+ case "MIN":
743
+ return min(rows, field);
744
+ case "MAX":
745
+ return max(rows, field);
746
+ default:
747
+ return 0;
748
+ }
749
+ }
750
+ function evaluateTemplate(text, ctx) {
751
+ if (!text) return "";
752
+ if (!text.includes("{")) return text;
753
+ const rows = Array.isArray(ctx.rows) ? ctx.rows : [];
754
+ const processedText = text.replace(/\{([^}]+)\}/g, (_match, inner) => {
755
+ let expr = inner.trim();
756
+ expr = expr.replace(/\b(SUM|AVG|COUNT|MIN|MAX)\(([^)]+)\)/g, (_m, fn, arg) => {
757
+ const field = arg.trim().replace(/^['"]|['"]$/g, "");
758
+ return String(aggregate(fn, field, rows));
759
+ });
760
+ return `{${expr}}`;
761
+ });
762
+ try {
763
+ return engine.render(processedText, ctx);
764
+ } catch {
765
+ return text;
766
+ }
767
+ }
768
+
769
+ // src/render/data-binder.ts
770
+ function bindData(template, printData, baseUrl) {
771
+ const raw = printData ?? {};
772
+ const data = Array.isArray(raw) ? raw[0] ?? {} : raw;
773
+ const bound = JSON.parse(JSON.stringify(template));
774
+ if (bound.header?.elements) {
775
+ bound.header.elements = bound.header.elements.map((el) => bindElement(el, data, baseUrl));
776
+ }
777
+ if (bound.footer?.elements) {
778
+ bound.footer.elements = bound.footer.elements.map((el) => bindElement(el, data, baseUrl));
779
+ }
780
+ if (bound.firstPageOverlay?.elements) {
781
+ bound.firstPageOverlay.elements = bound.firstPageOverlay.elements.map((el) => bindElement(el, data, baseUrl));
782
+ }
783
+ bound.elements = bound.elements.map((el) => bindElement(el, data, baseUrl));
784
+ return bound;
785
+ }
786
+ function bindElement(el, data, baseUrl) {
787
+ const cloned = { ...el, options: { ...el.options } };
788
+ if (typeof cloned.options.formatter === "string") {
789
+ cloned.options.formatter = evaluateTemplate(cloned.options.formatter, data);
790
+ }
791
+ const isImage = cloned.type === "image" || cloned.printElementType?.type === "image";
792
+ if (isImage && typeof cloned.options.src === "string") {
793
+ let src = cloned.options.src;
794
+ if (src.includes("{")) {
795
+ src = evaluateTemplate(src, data);
796
+ }
797
+ if (src && baseUrl && src.startsWith("/")) {
798
+ src = baseUrl.replace(/\/+$/, "") + src;
799
+ }
800
+ cloned.options.src = src;
801
+ }
802
+ if (cloned.type === "table" || cloned.printElementType?.type === "table") {
803
+ bindTableData(cloned, data);
804
+ }
805
+ return cloned;
806
+ }
807
+ function resolveListSource(opts, data) {
808
+ const explicitKey = opts.dataSource ?? opts.fields?.[0]?.dataSource ?? opts.fields?.[0]?.field;
809
+ if (explicitKey && Array.isArray(data[explicitKey])) {
810
+ return { list: data[explicitKey], key: explicitKey };
811
+ }
812
+ for (const [k, v] of Object.entries(data)) {
813
+ if (Array.isArray(v)) return { list: v, key: k };
814
+ }
815
+ return { list: [], key: explicitKey };
816
+ }
817
+ function bindTableData(el, data) {
818
+ const opts = el.options;
819
+ const rows = opts.tableRows;
820
+ if (!Array.isArray(rows) || rows.length === 0) return;
821
+ const mode = opts.tableMode ?? "dynamic";
822
+ const { list, key } = mode === "dynamic" ? resolveListSource(opts, data) : { list: [], key: void 0 };
823
+ const itemCtx = (item) => key ? { ...data, ...item, [key]: item } : { ...data, ...item };
824
+ const summaryRows = key ? list.map((item) => ({ ...item, [key]: item })) : list;
825
+ const renderRows = [];
826
+ const dataRowCtx = [];
827
+ const subtotalTemplates = [];
828
+ const summaryRenderRows = [];
829
+ let dataStartIdx = -1;
830
+ for (const row of rows) {
831
+ if (mode === "dynamic" && row.type === "data") {
832
+ for (const item of list) {
833
+ if (dataStartIdx < 0) dataStartIdx = renderRows.length;
834
+ const ctx = itemCtx(item);
835
+ renderRows.push(makeRenderRow(row, (cell) => {
836
+ const formatter = cell.formatter;
837
+ if (!formatter) return "";
838
+ return evaluateTemplate(formatter, ctx);
839
+ }));
840
+ dataRowCtx.push(ctx);
841
+ }
842
+ continue;
843
+ }
844
+ if (mode === "dynamic" && row.type === "subtotal") {
845
+ const tpl = makeRenderRow(row, (cell) => {
846
+ const formatter = cell.formatter;
847
+ if (!formatter) return "";
848
+ return evaluateTemplate(formatter, { rows: summaryRows, ...data });
849
+ }, true);
850
+ subtotalTemplates.push(tpl);
851
+ renderRows.push(tpl);
852
+ continue;
853
+ }
854
+ if (mode === "dynamic" && row.type === "summary") {
855
+ const summaryRow = makeRenderRow(row, (cell) => {
856
+ const formatter = cell.formatter;
857
+ if (!formatter) return "";
858
+ return evaluateTemplate(formatter, { rows: summaryRows, ...data });
859
+ });
860
+ summaryRenderRows.push(summaryRow);
861
+ renderRows.push(summaryRow);
862
+ continue;
863
+ }
864
+ renderRows.push(makeRenderRow(row, (cell) => {
865
+ const formatter = cell.formatter;
866
+ if (!formatter) return "";
867
+ return evaluateTemplate(formatter, data);
868
+ }));
869
+ }
870
+ opts._renderRows = renderRows;
871
+ opts._repeatHeaderCount = countRepeatHeader(rows);
872
+ opts._dataRowCtx = dataRowCtx;
873
+ opts._dataStartIdx = dataStartIdx < 0 ? renderRows.length : dataStartIdx;
874
+ opts._subtotalTemplates = subtotalTemplates;
875
+ opts._summaryRows = summaryRenderRows;
876
+ opts._mainData = data;
877
+ }
878
+ function makeRenderRow(row, resolve, keepRaw = false) {
879
+ return {
880
+ type: row.type,
881
+ height: row.height ?? 8,
882
+ cells: row.cells.map((cell) => ({
883
+ content: cell.merged ? "" : resolve(cell),
884
+ ...keepRaw ? { rawFormatter: cell.merged ? "" : cell.formatter ?? "" } : {},
885
+ cellType: cell.cellType,
886
+ barcodeType: cell.barcodeType,
887
+ qrCodeLevel: cell.qrCodeLevel,
888
+ showBarcodeText: cell.showBarcodeText,
889
+ fit: cell.fit,
890
+ maxWidth: cell.maxWidth,
891
+ maxHeight: cell.maxHeight,
892
+ rowspan: cell.rowspan ?? 1,
893
+ colspan: cell.colspan ?? 1,
894
+ merged: cell.merged === true,
895
+ align: cell.align,
896
+ valign: cell.valign,
897
+ fontSize: cell.fontSize,
898
+ fontWeight: cell.fontWeight,
899
+ color: cell.color,
900
+ backgroundColor: cell.backgroundColor,
901
+ borders: cell.borders,
902
+ padding: cell.padding,
903
+ wordWrap: cell.wordWrap
904
+ }))
905
+ };
906
+ }
907
+ function countRepeatHeader(rows) {
908
+ let n = 0;
909
+ for (const row of rows) {
910
+ if (row.type === "header" && row.repeatOnPage === true) {
911
+ n++;
912
+ } else {
913
+ break;
914
+ }
915
+ }
916
+ return n;
917
+ }
918
+ function injectSystemVariables(html) {
919
+ const now = /* @__PURE__ */ new Date();
920
+ const printDate = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
921
+ return html.replace(/\{printDate\}/g, printDate);
922
+ }
923
+
924
+ // src/render/types.ts
925
+ var PAPER_DIMENSIONS = {
926
+ A4: { width: 210, height: 297 },
927
+ A3: { width: 297, height: 420 },
928
+ A5: { width: 148, height: 210 },
929
+ Letter: { width: 216, height: 279 },
930
+ Legal: { width: 216, height: 356 },
931
+ CUSTOM: { width: 210, height: 297 }
932
+ };
933
+ function getPaperDimensions(template) {
934
+ const base = template.paperSize === "CUSTOM" ? { width: template.customWidth ?? 210, height: template.customHeight ?? 297 } : PAPER_DIMENSIONS[template.paperSize];
935
+ if (template.orientation === "landscape") {
936
+ return { width: base.height, height: base.width };
937
+ }
938
+ return { ...base };
939
+ }
940
+
941
+ // src/render/css-builder.ts
942
+ function mm(value) {
943
+ return `${value}mm`;
944
+ }
945
+ function buildPageCss(template) {
946
+ const paper = getPaperDimensions(template);
947
+ const { top: mt, right: mr, bottom: mb, left: ml } = template.margins;
948
+ const headerH = template.header?.height ?? 0;
949
+ const footerH = template.footer?.height ?? 0;
950
+ const overlayH = template.firstPageOverlay?.height ?? 0;
951
+ const contentWidth = paper.width - ml - mr;
952
+ return `
953
+ /* \u2500\u2500 \u6253\u5370\u7EB8\u5F20\uFF1A\u6D4F\u89C8\u5668\u539F\u751F\u6253\u5370\u6309\u6B64\u5C3A\u5BF8\u5206\u9875\uFF08Playwright page.pdf \u4EE5\u663E\u5F0F\u5BBD\u9AD8\u4E3A\u51C6\uFF0C\u65E0\u526F\u4F5C\u7528\uFF09 \u2500\u2500 */
954
+ @page { size: ${mm(paper.width)} ${mm(paper.height)}; margin: 0; }
955
+
956
+ /* \u2500\u2500 \u5168\u5C40\u91CD\u7F6E \u2500\u2500 */
957
+ * { margin: 0; padding: 0; box-sizing: border-box; }
958
+
959
+ /* \u6253\u5370\u5FC5\u987B\u4FDD\u7559\u5143\u7D20\u80CC\u666F\u8272\uFF08Chromium \u9ED8\u8BA4\u5254\u9664\u80CC\u666F\uFF0C\u9700\u663E\u5F0F\u58F0\u660E\uFF09 */
960
+ * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
961
+
962
+ body { font-family: "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif; }
963
+
964
+ /* \u2500\u2500 \u5C4F\u5E55\u9884\u89C8\uFF1A\u7070\u5E95 + \u7EB8\u5F20\u9634\u5F71/\u9875\u95F4\u8DDD\uFF1B\u6253\u5370\u65F6\u53BB\u9664 \u2500\u2500 */
965
+ @media screen {
966
+ body { background: #e9ebee; }
967
+ .print-page { margin: 12px auto; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18); }
968
+ }
969
+ @media print {
970
+ body { background: #fff; }
971
+ .print-page { margin: 0; box-shadow: none; }
972
+ }
973
+
974
+ /* \u2500\u2500 \u7EB8\u5F20\u9875\u9762 \u2500\u2500 */
975
+ .print-page {
976
+ width: ${mm(paper.width)};
977
+ min-height: ${mm(paper.height)};
978
+ background: ${template.pageBackground ?? "#fff"};
979
+ padding: ${mm(mt)} ${mm(mr)} ${mm(mb)} ${mm(ml)};
980
+ position: relative;
981
+ page-break-after: always;
982
+ overflow: hidden;
983
+ }
984
+ .print-page:last-child {
985
+ page-break-after: auto;
986
+ }
987
+
988
+ /* \u2500\u2500 \u9875\u7709 \u2500\u2500 */
989
+ .page-header {
990
+ width: ${mm(contentWidth)};
991
+ height: ${mm(headerH)};
992
+ position: relative;
993
+ }
994
+
995
+ /* \u2500\u2500 \u9875\u811A\uFF1A\u7EDD\u5BF9\u5B9A\u4F4D\u56FA\u5B9A\u5728\u9875\u9762\u5E95\u90E8\uFF08\u5185\u5BB9\u4E0D\u8DB3\u65F6\u4E0D\u968F\u6587\u6863\u6D41\u4E0A\u6D6E\uFF09 \u2500\u2500
996
+ \u7528\u663E\u5F0F top \u5B9A\u4F4D\u5230\u300C\u7EB8\u9AD8 - \u4E0B\u8FB9\u8DDD - \u9875\u811A\u9AD8\u300D\uFF0C\u4FDD\u8BC1\u4E0B\u8FB9\u8DDD\u751F\u6548\u3001
997
+ \u4E0E\u8BBE\u8BA1\u5668 CanvasPaper \u7684\u4E09\u533A\u51E0\u4F55\u4E00\u81F4\u3002 */
998
+ .page-footer {
999
+ width: ${mm(contentWidth)};
1000
+ height: ${mm(footerH)};
1001
+ position: absolute;
1002
+ top: ${mm(paper.height - mb - footerH)};
1003
+ left: 0;
1004
+ }
1005
+
1006
+ /* \u2500\u2500 \u5185\u5BB9\u533A \u2500\u2500 */
1007
+ .content-area {
1008
+ width: ${mm(contentWidth)};
1009
+ position: relative;
1010
+ overflow: visible;
1011
+ }
1012
+
1013
+ /* \u2500\u2500 \u9996\u9875\u53E0\u52A0\u533A\u57DF \u2500\u2500 */
1014
+ .first-page-overlay {
1015
+ width: ${mm(contentWidth)};
1016
+ height: ${mm(overlayH)};
1017
+ position: relative;
1018
+ }
1019
+
1020
+ /* \u2500\u2500 \u5143\u7D20\u901A\u7528\u5B9A\u4F4D \u2500\u2500 */
1021
+ .print-element {
1022
+ position: absolute;
1023
+ overflow: hidden;
1024
+ }
1025
+
1026
+ /* \u2500\u2500 \u8868\u683C\u6837\u5F0F \u2500\u2500 */
1027
+ .print-table {
1028
+ width: 100%;
1029
+ border-collapse: collapse;
1030
+ table-layout: fixed;
1031
+ }
1032
+ .print-table th,
1033
+ .print-table td {
1034
+ border: 1px solid #ccc;
1035
+ padding: 2mm 3mm;
1036
+ word-break: break-all;
1037
+ overflow: hidden;
1038
+ }
1039
+ .print-table thead th {
1040
+ background-color: #f5f5f5;
1041
+ font-weight: bold;
1042
+ }
1043
+
1044
+ /* \u2500\u2500 \u6D4B\u91CF\u6A21\u5F0F \u2500\u2500 */
1045
+ .measure-mode .print-page {
1046
+ height: auto;
1047
+ min-height: auto;
1048
+ overflow: visible;
1049
+ }
1050
+ .measure-mode .content-area {
1051
+ height: auto;
1052
+ overflow: visible;
1053
+ }
1054
+ `.trim();
1055
+ }
1056
+ function elementPositionStyle(left, top, width, height, zIndex) {
1057
+ const parts = [
1058
+ `position:absolute`,
1059
+ `left:${mm(left)}`,
1060
+ `top:${mm(top)}`,
1061
+ `width:${mm(width)}`
1062
+ ];
1063
+ if (height !== void 0 && height > 0) {
1064
+ parts.push(`height:${mm(height)}`);
1065
+ }
1066
+ if (zIndex !== void 0) {
1067
+ parts.push(`z-index:${zIndex}`);
1068
+ }
1069
+ return parts.join(";") + ";";
1070
+ }
1071
+
1072
+ // src/render/pagination-engine.ts
1073
+ function paginationOf(el) {
1074
+ return el.options?.pagination ?? el.pagination;
1075
+ }
1076
+ function tablePaginationOf(el) {
1077
+ return el.options?.tablePagination ?? el.tablePagination;
1078
+ }
1079
+ var SAFETY_MARGIN = 2;
1080
+ function isTableEl(el) {
1081
+ return el.type === "table" || el.printElementType?.type === "table";
1082
+ }
1083
+ function tableDesignBottom(el) {
1084
+ const opts = el.options ?? {};
1085
+ const top = opts.top ?? 0;
1086
+ const rows = opts.tableRows ?? [];
1087
+ if (rows.length > 0) {
1088
+ return top + rows.reduce((s, r) => s + (r.height ?? 0), 0);
1089
+ }
1090
+ return top + (opts.height ?? 0);
1091
+ }
1092
+ function buildFollowMap(sorted) {
1093
+ const map = /* @__PURE__ */ new Map();
1094
+ let currentTableId = null;
1095
+ let currentTableBottom = -1;
1096
+ for (const el of sorted) {
1097
+ if (isTableEl(el)) {
1098
+ currentTableId = el.id;
1099
+ currentTableBottom = tableDesignBottom(el);
1100
+ map.set(el.id, []);
1101
+ continue;
1102
+ }
1103
+ const top = el.options?.top ?? 0;
1104
+ if (currentTableId && top >= currentTableBottom) {
1105
+ map.get(currentTableId).push(el.id);
1106
+ }
1107
+ }
1108
+ return map;
1109
+ }
1110
+ function followGroupHeight(el, members, template, _measuredElements) {
1111
+ const tableBottom = tableDesignBottom(el);
1112
+ let maxBottom = tableBottom;
1113
+ for (const id of members) {
1114
+ const m = template.elements.find((e) => e.id === id);
1115
+ if (!m) continue;
1116
+ maxBottom = Math.max(maxBottom, (m.options?.top ?? 0) + (m.options?.height ?? 0));
1117
+ }
1118
+ return Math.max(maxBottom - tableBottom, 0);
1119
+ }
1120
+ function paginate(template, measuredElements) {
1121
+ const paper = getPaperDimensions(template);
1122
+ const { top: mt, bottom: mb } = template.margins;
1123
+ const headerH = template.header?.height ?? 0;
1124
+ const footerH = template.footer?.height ?? 0;
1125
+ const overlayH = template.firstPageOverlay?.height ?? 0;
1126
+ const contentHeight = paper.height - mt - mb - headerH - footerH;
1127
+ if (contentHeight <= 0) {
1128
+ throw new Error(
1129
+ `\u9875\u9762\u53EF\u7528\u9AD8\u5EA6\u4E0D\u8DB3: paper=${paper.height}mm, margins=${mt + mb}mm, header=${headerH}mm, footer=${footerH}mm`
1130
+ );
1131
+ }
1132
+ const sorted = [...template.elements].sort((a, b) => {
1133
+ const topA = a.options?.top ?? 0;
1134
+ const topB = b.options?.top ?? 0;
1135
+ return topA - topB;
1136
+ });
1137
+ const followMap = buildFollowMap(sorted);
1138
+ const followOwner = /* @__PURE__ */ new Map();
1139
+ for (const [tableId, members] of followMap) {
1140
+ for (const m of members) followOwner.set(m, tableId);
1141
+ }
1142
+ const pages = [];
1143
+ let currentPage = [];
1144
+ let remaining = contentHeight - overlayH - SAFETY_MARGIN;
1145
+ let isFirstPage = true;
1146
+ let pageBroken = false;
1147
+ function sectionTop(el) {
1148
+ return pageBroken ? 0 : el.options?.top ?? 0;
1149
+ }
1150
+ function fullPageHeight() {
1151
+ return isFirstPage ? contentHeight - overlayH - SAFETY_MARGIN : contentHeight - SAFETY_MARGIN;
1152
+ }
1153
+ function finishPage() {
1154
+ pages.push({ pageIndex: pages.length, sections: [...currentPage] });
1155
+ currentPage = [];
1156
+ remaining = contentHeight - SAFETY_MARGIN;
1157
+ isFirstPage = false;
1158
+ pageBroken = true;
1159
+ }
1160
+ let i = 0;
1161
+ while (i < sorted.length) {
1162
+ const el = sorted[i];
1163
+ if (followOwner.has(el.id)) {
1164
+ i++;
1165
+ continue;
1166
+ }
1167
+ if (paginationOf(el)?.pageable === false) {
1168
+ currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
1169
+ i++;
1170
+ continue;
1171
+ }
1172
+ if (isTableEl(el)) {
1173
+ i = paginateTable(el, measuredElements, i);
1174
+ } else {
1175
+ i = paginateNonTable(el, measuredElements, sorted, i);
1176
+ }
1177
+ }
1178
+ if (currentPage.length > 0) {
1179
+ pages.push({ pageIndex: pages.length, sections: [...currentPage] });
1180
+ }
1181
+ if (pages.length === 0) {
1182
+ pages.push({ pageIndex: 0, sections: [] });
1183
+ }
1184
+ return pages;
1185
+ function paginateNonTable(el, measured, sortedList, idx) {
1186
+ const elHeight = measured.get(el.id)?.measuredHeight ?? el.options?.height ?? 0;
1187
+ if (elHeight <= remaining) {
1188
+ currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
1189
+ remaining -= elHeight;
1190
+ if (paginationOf(el)?.keepWithNext && idx + 1 < sortedList.length) {
1191
+ const nextEl = sortedList[idx + 1];
1192
+ const nextHeight = measured.get(nextEl.id)?.measuredHeight ?? nextEl.options?.height ?? 0;
1193
+ if (elHeight + nextHeight > fullPageHeight()) {
1194
+ return idx + 1;
1195
+ }
1196
+ if (nextHeight > remaining) {
1197
+ currentPage.pop();
1198
+ finishPage();
1199
+ currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
1200
+ remaining -= elHeight;
1201
+ }
1202
+ }
1203
+ return idx + 1;
1204
+ }
1205
+ finishPage();
1206
+ currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
1207
+ remaining -= elHeight;
1208
+ return idx + 1;
1209
+ }
1210
+ function paginateTable(el, measured, idx) {
1211
+ const opts = el.options ?? {};
1212
+ const renderRows = opts._renderRows ?? [];
1213
+ const rowHeights = measured.get(el.id)?.measuredRowHeights ?? [];
1214
+ const hasSubtotal = (opts._subtotalTemplates?.length ?? 0) > 0;
1215
+ const hasSummary = (opts._summaryRows?.length ?? 0) > 0;
1216
+ const subtotalH = hasSubtotal ? specialRowHeight(renderRows, rowHeights, "subtotal") : 0;
1217
+ const summaryH = hasSummary ? specialRowHeight(renderRows, rowHeights, "summary") : 0;
1218
+ const isSpecial = (r) => hasSubtotal && r.type === "subtotal" || hasSummary && r.type === "summary";
1219
+ const bodyRows = renderRows.filter((r) => !isSpecial(r));
1220
+ const bodyHeights = rowHeights.filter((_, i2) => !isSpecial(renderRows[i2]));
1221
+ opts._renderRows = bodyRows;
1222
+ const rowCount = Math.min(bodyRows.length, bodyHeights.length);
1223
+ const repeatCount = opts._repeatHeaderCount ?? 0;
1224
+ const repeatH = measured.get(el.id)?.repeatHeaderHeight ?? rowHeights.slice(0, repeatCount).reduce((s, h) => s + h, 0);
1225
+ const freshAvail = contentHeight - SAFETY_MARGIN;
1226
+ if (repeatH > freshAvail) {
1227
+ throw new Error(
1228
+ `\u8868\u683C ${el.id} \u7684\u91CD\u590D\u8868\u5934\u9AD8\u5EA6 (${repeatH.toFixed(1)}mm) \u8D85\u8FC7\u9875\u9762\u53EF\u7528\u9AD8\u5EA6 (${freshAvail.toFixed(1)}mm)\uFF0C\u8BF7\u51CF\u5C11\u91CD\u590D\u8868\u5934\u884C`
1229
+ );
1230
+ }
1231
+ if (rowCount === 0) {
1232
+ currentPage.push({
1233
+ elementId: el.id,
1234
+ type: "table-slice",
1235
+ startRow: 0,
1236
+ endRow: 0,
1237
+ summary: summaryH > 0,
1238
+ renderTop: sectionTop(el)
1239
+ });
1240
+ remaining -= summaryH;
1241
+ applyFollowGroup(el, measured);
1242
+ return idx + 1;
1243
+ }
1244
+ const totalH = bodyHeights.slice(0, rowCount).reduce((s, h) => s + h, 0);
1245
+ const totalNeed = totalH + subtotalH + summaryH;
1246
+ if (tablePaginationOf(el)?.enabled === false) {
1247
+ if (totalNeed > remaining && currentPage.length > 0) finishPage();
1248
+ currentPage.push({
1249
+ elementId: el.id,
1250
+ type: "table-slice",
1251
+ startRow: 0,
1252
+ endRow: rowCount,
1253
+ subtotal: subtotalH > 0,
1254
+ summary: summaryH > 0,
1255
+ renderTop: sectionTop(el)
1256
+ });
1257
+ remaining -= totalNeed;
1258
+ applyFollowGroup(el, measured);
1259
+ return idx + 1;
1260
+ }
1261
+ const groups = buildRowGroups(bodyRows, rowCount);
1262
+ let sliceStart = 0;
1263
+ let firstSlice = true;
1264
+ for (const g of groups) {
1265
+ const gh = bodyHeights.slice(g.start, g.end).reduce((s, h) => s + h, 0);
1266
+ if (gh + subtotalH <= remaining) {
1267
+ remaining -= gh;
1268
+ continue;
1269
+ }
1270
+ if (g.start > sliceStart) {
1271
+ currentPage.push({
1272
+ elementId: el.id,
1273
+ type: "table-slice",
1274
+ startRow: sliceStart,
1275
+ endRow: g.start,
1276
+ subtotal: subtotalH > 0,
1277
+ ...!firstSlice && repeatCount > 0 ? { repeatHeader: true } : {},
1278
+ renderTop: sectionTop(el)
1279
+ });
1280
+ firstSlice = false;
1281
+ sliceStart = g.start;
1282
+ }
1283
+ finishPage();
1284
+ if (!firstSlice) remaining -= repeatH;
1285
+ remaining -= gh;
1286
+ }
1287
+ if (sliceStart < rowCount) {
1288
+ currentPage.push({
1289
+ elementId: el.id,
1290
+ type: "table-slice",
1291
+ startRow: sliceStart,
1292
+ endRow: rowCount,
1293
+ subtotal: subtotalH > 0,
1294
+ ...!firstSlice && repeatCount > 0 ? { repeatHeader: true } : {},
1295
+ renderTop: sectionTop(el)
1296
+ });
1297
+ }
1298
+ if (summaryH > 0) {
1299
+ if (summaryH <= remaining) {
1300
+ const lastIdx = currentPage.length - 1;
1301
+ const last = currentPage[lastIdx];
1302
+ if (last && last.type === "table-slice" && last.elementId === el.id) {
1303
+ currentPage[lastIdx] = { ...last, summary: true };
1304
+ remaining -= summaryH;
1305
+ } else {
1306
+ finishPage();
1307
+ currentPage.push({ elementId: el.id, type: "table-slice", startRow: rowCount, endRow: rowCount, summary: true, renderTop: 0 });
1308
+ remaining -= summaryH;
1309
+ }
1310
+ } else {
1311
+ finishPage();
1312
+ currentPage.push({ elementId: el.id, type: "table-slice", startRow: rowCount, endRow: rowCount, summary: true, renderTop: 0 });
1313
+ remaining -= summaryH;
1314
+ }
1315
+ }
1316
+ applyFollowGroup(el, measured);
1317
+ return idx + 1;
1318
+ }
1319
+ function applyFollowGroup(el, measured) {
1320
+ const members = followMap.get(el.id) ?? [];
1321
+ if (members.length === 0) return;
1322
+ const followH = followGroupHeight(el, members, template, measured);
1323
+ const opts = el.options ?? {};
1324
+ if (followH <= remaining) {
1325
+ const lastIdx = currentPage.length - 1;
1326
+ const last = currentPage[lastIdx];
1327
+ if (last && last.type === "table-slice" && last.elementId === el.id) {
1328
+ currentPage[lastIdx] = {
1329
+ ...last,
1330
+ type: "flow-group",
1331
+ followElementIds: members,
1332
+ // 沿用该切片在本页的定位 top(续片已 stamp 为 0)
1333
+ groupTop: last.renderTop ?? opts.top ?? 0
1334
+ };
1335
+ remaining -= followH;
1336
+ return;
1337
+ }
1338
+ }
1339
+ finishPage();
1340
+ currentPage.push({
1341
+ elementId: el.id,
1342
+ type: "flow-group",
1343
+ startRow: 0,
1344
+ endRow: 0,
1345
+ followElementIds: members,
1346
+ groupTop: 0
1347
+ });
1348
+ remaining -= followH;
1349
+ }
1350
+ }
1351
+ function buildRowGroups(renderRows, rowCount) {
1352
+ const groups = [];
1353
+ let start = 0;
1354
+ let end = 1;
1355
+ for (let r = 0; r < rowCount; r++) {
1356
+ for (const cell of renderRows[r]?.cells ?? []) {
1357
+ if (!cell.merged && (cell.rowspan ?? 1) > 1) end = Math.max(end, r + cell.rowspan);
1358
+ }
1359
+ if (r + 1 >= end) {
1360
+ groups.push({ start, end: r + 1 });
1361
+ start = r + 1;
1362
+ end = start + 1;
1363
+ }
1364
+ }
1365
+ return groups;
1366
+ }
1367
+ function specialRowHeight(renderRows, rowHeights, type) {
1368
+ let h = 0;
1369
+ for (let i = 0; i < renderRows.length; i++) {
1370
+ if (renderRows[i]?.type === type) h += rowHeights[i] ?? 0;
1371
+ }
1372
+ return h;
1373
+ }
1374
+
1375
+ // src/render/html-generator.ts
1376
+ function generateHtml(template, pageLayouts, printData, options) {
1377
+ const css = buildPageCss(template);
1378
+ const isMeasure = options?.isMeasurementPass === true;
1379
+ const totalPages = isMeasure ? 1 : pageLayouts.length;
1380
+ const ctx = { codeRenderer: options?.codeRenderer };
1381
+ if (isMeasure) {
1382
+ return generateMeasurementHtml(template, css, ctx);
1383
+ }
1384
+ return generateFinalHtml(template, pageLayouts, css, totalPages, ctx);
1385
+ }
1386
+ function generateMeasurementHtml(template, css, ctx) {
1387
+ const paper = getPaperDims(template);
1388
+ const contentWidth = paper.width - template.margins.left - template.margins.right;
1389
+ const elementsHtml = template.elements.map((el) => renderElement(el, true, void 0, void 0, ctx)).join("\n");
1390
+ const headerHtml = renderAreaElements(
1391
+ template.header?.elements ?? [],
1392
+ contentWidth,
1393
+ 0,
1394
+ // pageIndex placeholder
1395
+ 0,
1396
+ // totalPages placeholder
1397
+ ctx
1398
+ );
1399
+ const overlayHtml = renderAreaElements(template.firstPageOverlay?.elements ?? [], contentWidth, void 0, void 0, ctx);
1400
+ let html = `<!DOCTYPE html>
1401
+ <html lang="zh-CN">
1402
+ <head>
1403
+ <meta charset="UTF-8">
1404
+ <style>${css}</style>
1405
+ </head>
1406
+ <body class="measure-mode">
1407
+ <section class="print-page" data-measure-page="0">
1408
+ <div class="page-header">${headerHtml}</div>
1409
+ <div class="first-page-overlay">${overlayHtml}</div>
1410
+ <div class="content-area" style="height:auto;overflow:visible;">
1411
+ ${elementsHtml}
1412
+ </div>
1413
+ </section>
1414
+ </body>
1415
+ </html>`;
1416
+ html = injectSystemVariables(html);
1417
+ return html;
1418
+ }
1419
+ function generateFinalHtml(template, pageLayouts, css, totalPages, ctx) {
1420
+ const pagesHtml = pageLayouts.map((page) => {
1421
+ const pageNum = page.pageIndex + 1;
1422
+ return renderPage(template, page, pageNum, totalPages, ctx);
1423
+ }).join("\n");
1424
+ let html = `<!DOCTYPE html>
1425
+ <html lang="zh-CN">
1426
+ <head>
1427
+ <meta charset="UTF-8">
1428
+ <style>${css}</style>
1429
+ </head>
1430
+ <body>
1431
+ ${pagesHtml}
1432
+ </body>
1433
+ </html>`;
1434
+ html = injectSystemVariables(html);
1435
+ return html;
1436
+ }
1437
+ function renderPage(template, page, pageNum, totalPages, ctx) {
1438
+ const paper = getPaperDims(template);
1439
+ const contentWidth = paper.width - template.margins.left - template.margins.right;
1440
+ const headerHtml = renderAreaElements(
1441
+ template.header?.elements ?? [],
1442
+ contentWidth,
1443
+ pageNum,
1444
+ totalPages,
1445
+ ctx
1446
+ );
1447
+ const footerHtml = renderAreaElements(
1448
+ template.footer?.elements ?? [],
1449
+ contentWidth,
1450
+ pageNum,
1451
+ totalPages,
1452
+ ctx
1453
+ );
1454
+ const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(template.firstPageOverlay?.elements ?? [], contentWidth, void 0, void 0, ctx)}</div>` : "";
1455
+ let contentHtml = page.sections.map((section) => renderSection(section, template, ctx)).join("\n");
1456
+ contentHtml = contentHtml.replace(/\{pageIndex\}/g, String(pageNum));
1457
+ contentHtml = contentHtml.replace(/\{totalPages\}/g, String(totalPages));
1458
+ return `<section class="print-page" data-page="${pageNum}">
1459
+ <div class="page-header">${headerHtml}</div>
1460
+ ${overlayHtml}
1461
+ <div class="content-area">
1462
+ ${contentHtml}
1463
+ </div>
1464
+ <div class="page-footer">${footerHtml}</div>
1465
+ </section>`;
1466
+ }
1467
+ function renderSection(section, template, ctx) {
1468
+ const el = findElement(template, section.elementId);
1469
+ if (!el) {
1470
+ return `<!-- element not found: ${section.elementId} -->`;
1471
+ }
1472
+ if (section.type === "table-slice") {
1473
+ return renderTableSlice(el, section, ctx);
1474
+ }
1475
+ if (section.type === "flow-group") {
1476
+ return renderFlowGroup(el, section, template, ctx);
1477
+ }
1478
+ return renderElement(el, false, void 0, section.renderTop, ctx);
1479
+ }
1480
+ var V_ALIGN_FLEX = { top: "flex-start", middle: "center", bottom: "flex-end" };
1481
+ var H_ALIGN_FLEX = { left: "flex-start", center: "center", right: "flex-end" };
1482
+ function textStyle(opts) {
1483
+ const parts = [];
1484
+ if (opts.fontSize) parts.push(`font-size:${opts.fontSize}pt`);
1485
+ if (opts.fontFamily) parts.push(`font-family:${opts.fontFamily}`);
1486
+ if (opts.fontWeight) parts.push(`font-weight:${opts.fontWeight}`);
1487
+ if (opts.color) parts.push(`color:${opts.color}`);
1488
+ if (opts.backgroundColor) parts.push(`background-color:${opts.backgroundColor}`);
1489
+ if (opts.lineHeight) parts.push(`line-height:${opts.lineHeight}pt`);
1490
+ if (opts.letterSpacing) parts.push(`letter-spacing:${opts.letterSpacing}pt`);
1491
+ if (opts.verticalAlign) {
1492
+ parts.push(`display:flex;align-items:${V_ALIGN_FLEX[opts.verticalAlign] ?? "flex-start"}`);
1493
+ parts.push(`justify-content:${H_ALIGN_FLEX[opts.textAlign ?? "left"] ?? "flex-start"}`);
1494
+ }
1495
+ if (opts.textAlign) parts.push(`text-align:${opts.textAlign}`);
1496
+ return parts.length ? parts.join(";") + ";" : "";
1497
+ }
1498
+ function renderElement(el, isMeasure, containerStyle, overrideTop, ctx) {
1499
+ const opts = el.options ?? {};
1500
+ const left = opts.left ?? 0;
1501
+ const top = overrideTop ?? opts.top ?? 0;
1502
+ const width = opts.width ?? 100;
1503
+ const height = opts.height ?? void 0;
1504
+ const style = containerStyle ?? elementPositionStyle(left, top, width, height, opts.zIndex);
1505
+ const measureAttr = isMeasure ? ` data-measure-id="${el.id}"` : "";
1506
+ const type = el.type || el.printElementType?.type || "text";
1507
+ switch (type) {
1508
+ case "table":
1509
+ return renderTableElement(el, isMeasure, measureAttr, ctx);
1510
+ case "image":
1511
+ return `<div class="print-element" style="${style}"${measureAttr}>
1512
+ <img src="${esc(opts.src ?? "")}" style="width:100%;height:100%;object-fit:contain;" />
1513
+ </div>`;
1514
+ case "barcode":
1515
+ case "qrcode": {
1516
+ const codeValue = String(opts.formatter ?? opts.testData ?? "").trim();
1517
+ const fill = type === "barcode";
1518
+ return `<div class="print-element" style="${style}"${measureAttr}>
1519
+ ${codeImgHtml(codeValue, type, opts, `<span>${esc(codeValue)}</span>`, fill, ctx?.codeRenderer)}
1520
+ </div>`;
1521
+ }
1522
+ case "hline":
1523
+ return `<div class="print-element" style="${style};border-top:1px solid #000;height:0;"${measureAttr}></div>`;
1524
+ case "vline":
1525
+ return `<div class="print-element" style="${style};border-left:1px solid #000;width:0;"${measureAttr}></div>`;
1526
+ case "rect":
1527
+ return `<div class="print-element" style="${style};border:${opts.borderWidth ?? 1}px solid ${opts.borderColor ?? "#000"};"${measureAttr}></div>`;
1528
+ case "oval":
1529
+ return `<div class="print-element" style="${style};border:${opts.borderWidth ?? 1}px solid ${opts.borderColor ?? "#000"};border-radius:50%;"${measureAttr}></div>`;
1530
+ case "longText":
1531
+ return `<div class="print-element" style="${style}${textStyle(opts)}overflow:visible;"${measureAttr}>${esc(opts.formatter ?? opts.testData ?? "")}</div>`;
1532
+ case "html":
1533
+ return `<div class="print-element" style="${style}"${measureAttr}>${opts.testData ?? opts.title ?? ""}</div>`;
1534
+ default:
1535
+ return `<div class="print-element" style="${style}${textStyle(opts)}"${measureAttr}>${esc(opts.formatter ?? opts.testData ?? "")}</div>`;
1536
+ }
1537
+ }
1538
+ function codeImgHtml(value, cellType, opts, fallbackHtml, fill = false, codeRenderer, fit, maxWidth, maxHeight) {
1539
+ if (!value || !codeRenderer) return fallbackHtml;
1540
+ try {
1541
+ const svg = codeRenderer.render(value, cellType, {
1542
+ barcodeType: opts.barcodeType,
1543
+ qrCodeLevel: opts.qrCodeLevel != null ? String(opts.qrCodeLevel) : void 0,
1544
+ showText: opts.hideTitle !== void 0 ? !opts.hideTitle : opts.showBarcodeText,
1545
+ barWidth: typeof opts.barWidth === "number" ? opts.barWidth : void 0,
1546
+ fontSize: typeof opts.fontSize === "number" ? opts.fontSize : void 0
1547
+ });
1548
+ const src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
1549
+ const styleParts = [];
1550
+ if (fill) {
1551
+ styleParts.push("width:100%", "height:100%", "object-fit:contain", "display:block", "margin:auto");
1552
+ } else {
1553
+ styleParts.push("max-width:100%", "max-height:100%", "display:block", "margin:auto");
1554
+ }
1555
+ if (fit) {
1556
+ styleParts.push(`object-fit:${fit}`);
1557
+ }
1558
+ if (maxWidth) {
1559
+ styleParts.push(`max-width:${maxWidth}mm`);
1560
+ }
1561
+ if (maxHeight) {
1562
+ styleParts.push(`max-height:${maxHeight}mm`);
1563
+ }
1564
+ const style = styleParts.join(";");
1565
+ return `<img src="${src}" style="${style}" />`;
1566
+ } catch {
1567
+ return fallbackHtml;
1568
+ }
1569
+ }
1570
+ function matrixCellStyle(cell, opts) {
1571
+ const parts = [];
1572
+ const fontSize = cell.fontSize ?? opts.tableDefaultFontSize;
1573
+ const color = cell.color ?? opts.tableDefaultColor;
1574
+ const padding = cell.padding ?? opts.tableDefaultPadding ?? 1;
1575
+ if (fontSize) parts.push(`font-size:${fontSize}pt`);
1576
+ if (cell.fontWeight) parts.push(`font-weight:${cell.fontWeight}`);
1577
+ if (color) parts.push(`color:${color}`);
1578
+ if (cell.backgroundColor) parts.push(`background-color:${cell.backgroundColor}`);
1579
+ parts.push(`text-align:${cell.align ?? "left"}`);
1580
+ parts.push(`vertical-align:${cell.valign ?? "middle"}`);
1581
+ parts.push(`padding:${padding}mm`);
1582
+ parts.push(cell.wordWrap === false ? "white-space:nowrap;overflow:hidden" : "word-break:break-all");
1583
+ const b = cell.borders ?? {};
1584
+ for (const side of ["top", "right", "bottom", "left"]) {
1585
+ const border = b[side];
1586
+ parts.push(border ? `border-${side}:${border.width}pt ${border.style} ${border.color}` : `border-${side}:none`);
1587
+ }
1588
+ return parts.join(";");
1589
+ }
1590
+ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx) {
1591
+ const trs = [];
1592
+ for (let r = start; r < end; r++) {
1593
+ const row = renderRows[r];
1594
+ if (!row) continue;
1595
+ const idxAttr = withRowIndex ? ` data-row-index="${r}"` : "";
1596
+ const tds = row.cells.filter((cell) => !cell.merged).map((cell) => {
1597
+ const span = `${cell.rowspan > 1 ? ` rowspan="${cell.rowspan}"` : ""}${cell.colspan > 1 ? ` colspan="${cell.colspan}"` : ""}`;
1598
+ let inner;
1599
+ if (cell.cellType === "barcode" || cell.cellType === "qrcode") {
1600
+ const cellFill = cell.cellType === "barcode";
1601
+ inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;">${codeImgHtml(cell.content, cell.cellType, cell, esc(cell.content), cellFill, ctx?.codeRenderer, cell.fit, cell.maxWidth, cell.maxHeight)}</div>`;
1602
+ } else if (cell.cellType === "image") {
1603
+ const fit = cell.fit || "contain";
1604
+ const maxWidth = cell.maxWidth ? `max-width:${cell.maxWidth}mm;` : "max-width:100%;";
1605
+ const maxHeight = cell.maxHeight ? `max-height:${cell.maxHeight}mm;` : "max-height:100%;";
1606
+ inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;"><img src="${esc(cell.content)}" style="object-fit:${fit};${maxWidth}${maxHeight}display:block;" /></div>`;
1607
+ } else {
1608
+ inner = esc(cell.content);
1609
+ }
1610
+ return `<td${span} style="${matrixCellStyle(cell, opts)}">${inner}</td>`;
1611
+ }).join("");
1612
+ trs.push(`<tr${idxAttr} style="height:${row.height}mm;">${tds}</tr>`);
1613
+ }
1614
+ return trs.join("\n");
1615
+ }
1616
+ function matrixTableHtml(el, bodyHtml) {
1617
+ const opts = el.options;
1618
+ const colWidths = opts.tableColWidths ?? [];
1619
+ const colgroup = colWidths.map((w) => `<col style="width:${w}mm;">`).join("");
1620
+ const tableWidth = colWidths.reduce((s, w) => s + w, 0);
1621
+ return `<table class="print-table" data-element-id="${el.id}" style="border-collapse:collapse;table-layout:fixed;width:${tableWidth}mm;">
1622
+ <colgroup>${colgroup}</colgroup>
1623
+ <tbody>${bodyHtml}</tbody>
1624
+ </table>`;
1625
+ }
1626
+ function renderTableElement(el, isMeasure, measureAttr, ctx) {
1627
+ const opts = el.options;
1628
+ const style = elementPositionStyle(opts.left ?? 0, opts.top ?? 0, opts.width ?? 100, void 0, opts.zIndex);
1629
+ const renderRows = opts._renderRows ?? [];
1630
+ const bodyHtml = renderMatrixRows(renderRows, 0, renderRows.length, opts, isMeasure, ctx);
1631
+ return `<div class="print-element" style="${style};overflow:visible;"${measureAttr}>
1632
+ ${matrixTableHtml(el, bodyHtml)}
1633
+ </div>`;
1634
+ }
1635
+ function renderTableSlice(el, section, ctx) {
1636
+ const opts = el.options;
1637
+ const style = elementPositionStyle(opts.left ?? 0, section.renderTop ?? opts.top ?? 0, opts.width ?? 100, void 0, opts.zIndex);
1638
+ const renderRows = opts._renderRows ?? [];
1639
+ const startRow = section.startRow ?? 0;
1640
+ const endRow = section.endRow ?? renderRows.length;
1641
+ const repeatCount = opts._repeatHeaderCount ?? 0;
1642
+ const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx) : "";
1643
+ const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx);
1644
+ const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
1645
+ const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
1646
+ return `<div class="print-element" style="${style};overflow:visible;">
1647
+ ${matrixTableHtml(el, `${repeatHtml}
1648
+ ${bodyHtml}${subtotalHtml}${summaryHtml}`)}
1649
+ </div>`;
1650
+ }
1651
+ function renderSubtotalRows(el, section, opts, ctx) {
1652
+ const templates = opts._subtotalTemplates ?? [];
1653
+ if (templates.length === 0) return "";
1654
+ const dataRowCtx = opts._dataRowCtx ?? [];
1655
+ const dataStartIdx = opts._dataStartIdx ?? 0;
1656
+ const mainData = opts._mainData ?? {};
1657
+ const bodyLen = opts._renderRows?.length ?? 0;
1658
+ const startRow = section.startRow ?? 0;
1659
+ const endRow = section.endRow ?? bodyLen;
1660
+ const dataStart = Math.max(startRow, dataStartIdx);
1661
+ const dataEnd = Math.max(endRow, dataStart);
1662
+ const pageCtx = dataEnd > dataStartIdx ? dataRowCtx.slice(Math.max(dataStart - dataStartIdx, 0), dataEnd - dataStartIdx) : [];
1663
+ const rows = templates.map((tpl) => ({
1664
+ ...tpl,
1665
+ cells: tpl.cells.map((cell) => cell.rawFormatter ? { ...cell, content: evaluateTemplate(cell.rawFormatter, { rows: pageCtx, ...mainData }) } : cell)
1666
+ }));
1667
+ return renderMatrixRows(rows, 0, rows.length, opts, false, ctx);
1668
+ }
1669
+ function renderSummaryRows(el, opts, ctx) {
1670
+ const summaryRows = opts._summaryRows ?? [];
1671
+ if (summaryRows.length === 0) return "";
1672
+ return renderMatrixRows(summaryRows, 0, summaryRows.length, opts, false, ctx);
1673
+ }
1674
+ function renderFlowGroup(el, section, template, ctx) {
1675
+ const opts = el.options ?? {};
1676
+ const tableLeft = opts.left ?? 0;
1677
+ const tableWidth = opts.width ?? 100;
1678
+ const groupTop = section.groupTop ?? opts.top ?? 0;
1679
+ const tableBottom = tableDesignBottom(el);
1680
+ const zStyle = typeof opts.zIndex === "number" ? `z-index:${opts.zIndex};` : "";
1681
+ const style = `position:absolute;left:${mm(tableLeft)};top:${mm(groupTop)};width:${mm(tableWidth)};overflow:visible;${zStyle}`;
1682
+ const startRow = section.startRow ?? 0;
1683
+ const endRow = section.endRow ?? 0;
1684
+ let sliceHtml = "";
1685
+ if (endRow > startRow || section.subtotal || section.summary) {
1686
+ const renderRows = opts._renderRows ?? [];
1687
+ const repeatCount = opts._repeatHeaderCount ?? 0;
1688
+ const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx) : "";
1689
+ const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx);
1690
+ const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
1691
+ const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
1692
+ sliceHtml = `<div class="flow-slice" style="position:relative;width:${mm(tableWidth)};overflow:visible;">
1693
+ ${matrixTableHtml(el, `${repeatHtml}
1694
+ ${bodyHtml}${subtotalHtml}${summaryHtml}`)}
1695
+ </div>`;
1696
+ }
1697
+ const followIds = section.followElementIds ?? [];
1698
+ const followExtent = followIds.reduce((maxBottom, id) => {
1699
+ const m = findElement(template, id);
1700
+ if (!m) return maxBottom;
1701
+ return Math.max(maxBottom, (m.options?.top ?? 0) + (m.options?.height ?? 0));
1702
+ }, tableBottom);
1703
+ const followHtml = followIds.map((id) => {
1704
+ const m = findElement(template, id);
1705
+ if (!m) return "";
1706
+ const mTop = m.options?.top ?? 0;
1707
+ const mLeft = m.options?.left ?? 0;
1708
+ const mWidth = m.options?.width ?? tableWidth;
1709
+ const mHeight = m.options?.height ?? 0;
1710
+ const mZ = m.options?.zIndex;
1711
+ const type = m.type || m.printElementType?.type || "text";
1712
+ const needsHeight = type === "rect" || type === "oval" || type === "image";
1713
+ const flowStyle = [
1714
+ "position:absolute",
1715
+ `left:${mm(mLeft - tableLeft)}`,
1716
+ `top:${mm(mTop - tableBottom)}`,
1717
+ `width:${mm(mWidth)}`,
1718
+ ...needsHeight && mHeight > 0 ? [`height:${mm(mHeight)}`] : [],
1719
+ ...typeof mZ === "number" ? [`z-index:${mZ}`] : [],
1720
+ "overflow:visible"
1721
+ ].join(";") + ";";
1722
+ return renderElement(m, false, flowStyle, void 0, ctx);
1723
+ }).join("\n");
1724
+ const followWrap = followIds.length > 0 ? `<div class="flow-follow" style="position:relative;width:${mm(tableWidth)};height:${mm(Math.max(followExtent - tableBottom, 0))};overflow:visible;">
1725
+ ${followHtml}
1726
+ </div>` : "";
1727
+ return `<div class="flow-group" style="${style}">
1728
+ ${sliceHtml}
1729
+ ${followWrap}
1730
+ </div>`;
1731
+ }
1732
+ function renderAreaElements(elements, _contentWidth, pageIndex, totalPages, ctx) {
1733
+ return elements.map((el) => {
1734
+ let html = renderAreaElement(el, ctx);
1735
+ if (pageIndex !== void 0) {
1736
+ html = html.replace(/\{pageIndex\}/g, String(pageIndex));
1737
+ }
1738
+ if (totalPages !== void 0) {
1739
+ html = html.replace(/\{totalPages\}/g, String(totalPages));
1740
+ }
1741
+ return html;
1742
+ }).join("\n");
1743
+ }
1744
+ function renderAreaElement(el, ctx) {
1745
+ return renderElement(el, false, void 0, void 0, ctx);
1746
+ }
1747
+ function findElement(template, id) {
1748
+ return template.elements.find((el) => el.id === id);
1749
+ }
1750
+ function getPaperDims(template) {
1751
+ return getPaperDimensions(template);
1752
+ }
1753
+ function esc(str) {
1754
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1755
+ }
1756
+
1757
+ // src/browser/browser-pagination.ts
1758
+ var PX_PER_MM = 3.7795275591;
1759
+ var READY_TIMEOUT_MS = 3e3;
1760
+ async function renderHtmlPages(template, printData, baseUrl, codeRenderer) {
1761
+ const boundTemplate = bindData(template, printData, baseUrl);
1762
+ const measuredElements = await measureElements(boundTemplate, codeRenderer);
1763
+ const pageLayouts = paginate(boundTemplate, measuredElements);
1764
+ const html = generateHtml(boundTemplate, pageLayouts, printData, {
1765
+ codeRenderer
1766
+ });
1767
+ return { html, pageCount: pageLayouts.length, pageLayouts };
1768
+ }
1769
+ async function measureElements(template, codeRenderer) {
1770
+ const paper = getPaperDimensions(template);
1771
+ const measureHtml = generateHtml(template, [], void 0, {
1772
+ isMeasurementPass: true,
1773
+ codeRenderer
1774
+ });
1775
+ const iframe = document.createElement("iframe");
1776
+ iframe.setAttribute("aria-hidden", "true");
1777
+ iframe.style.cssText = `position:fixed;left:-10000px;top:0;width:${paper.width}mm;height:${paper.height}mm;border:0;visibility:hidden;pointer-events:none;`;
1778
+ document.body.appendChild(iframe);
1779
+ try {
1780
+ const win = iframe.contentWindow;
1781
+ const doc = iframe.contentDocument;
1782
+ if (!win || !doc) throw new Error("\u65E0\u6CD5\u521B\u5EFA\u6D4B\u91CF iframe");
1783
+ doc.open();
1784
+ doc.write(measureHtml);
1785
+ doc.close();
1786
+ await waitForRenderReady(win);
1787
+ const measurements = readMeasurements(doc);
1788
+ const measuredMap = /* @__PURE__ */ new Map();
1789
+ const elementIndex = new Map(template.elements.map((el) => [el.id, el]));
1790
+ for (const m of measurements) {
1791
+ const el = elementIndex.get(m.id);
1792
+ const repeatCount = el?.options?._repeatHeaderCount ?? 0;
1793
+ measuredMap.set(m.id, {
1794
+ id: m.id,
1795
+ measuredHeight: m.height,
1796
+ measuredRowHeights: m.rowHeights,
1797
+ repeatHeaderHeight: m.rowHeights && repeatCount > 0 ? m.rowHeights.slice(0, repeatCount).reduce((s, h) => s + h, 0) : 0
1798
+ });
1799
+ }
1800
+ return measuredMap;
1801
+ } finally {
1802
+ iframe.remove();
1803
+ }
1804
+ }
1805
+ function waitForRenderReady(win) {
1806
+ return new Promise((resolve) => {
1807
+ let settled = false;
1808
+ const finish = () => {
1809
+ if (!settled) {
1810
+ settled = true;
1811
+ resolve();
1812
+ }
1813
+ };
1814
+ const timer = setTimeout(finish, READY_TIMEOUT_MS);
1815
+ const run = async () => {
1816
+ try {
1817
+ const fonts = win.document.fonts;
1818
+ if (fonts?.ready) {
1819
+ await Promise.race([fonts.ready, timeout(READY_TIMEOUT_MS)]);
1820
+ }
1821
+ await Promise.race([waitForImages(win.document), timeout(READY_TIMEOUT_MS)]);
1822
+ } catch {
1823
+ } finally {
1824
+ clearTimeout(timer);
1825
+ finish();
1826
+ }
1827
+ };
1828
+ if (win.document.readyState === "complete") {
1829
+ void run();
1830
+ } else {
1831
+ win.addEventListener("load", () => void run(), { once: true });
1832
+ setTimeout(finish, READY_TIMEOUT_MS);
1833
+ }
1834
+ });
1835
+ }
1836
+ function timeout(ms) {
1837
+ return new Promise((_resolve, reject) => setTimeout(() => reject(new Error("timeout")), ms));
1838
+ }
1839
+ function waitForImages(doc) {
1840
+ const imgs = Array.from(doc.images ?? []);
1841
+ return Promise.all(
1842
+ imgs.map(
1843
+ (img) => img.complete ? Promise.resolve() : new Promise((resolve) => {
1844
+ img.addEventListener("load", () => resolve(), { once: true });
1845
+ img.addEventListener("error", () => resolve(), { once: true });
1846
+ })
1847
+ )
1848
+ ).then(() => void 0);
1849
+ }
1850
+ function readMeasurements(doc) {
1851
+ const result = [];
1852
+ const elements = doc.querySelectorAll("[data-measure-id]");
1853
+ for (const el of elements) {
1854
+ const htmlEl = el;
1855
+ const id = htmlEl.getAttribute("data-measure-id");
1856
+ if (!id) continue;
1857
+ const heightPx = htmlEl.offsetHeight;
1858
+ const table = htmlEl.querySelector("table.print-table");
1859
+ if (table) {
1860
+ const rowHeights = [];
1861
+ const rows = table.querySelectorAll("tbody > tr[data-row-index]");
1862
+ for (const row of rows) {
1863
+ rowHeights.push(row.offsetHeight / PX_PER_MM);
1864
+ }
1865
+ result.push({ id, height: heightPx / PX_PER_MM, rowHeights });
1866
+ } else {
1867
+ result.push({ id, height: heightPx / PX_PER_MM });
1868
+ }
1869
+ }
1870
+ return result;
1871
+ }
1872
+
1873
+ // src/browser/browser-code-renderer.ts
1874
+ var import_jsbarcode = __toESM(require("jsbarcode"), 1);
1875
+ var import_qrcode = __toESM(require("qrcode"), 1);
1876
+ var SVG_NS = "http://www.w3.org/2000/svg";
1877
+ function renderBarcodeSvg(value, opts) {
1878
+ const svg = document.createElementNS(SVG_NS, "svg");
1879
+ (0, import_jsbarcode.default)(svg, value, {
1880
+ format: opts.barcodeType || "CODE128",
1881
+ width: Math.max(1, (opts.barWidth ?? 2) / 2),
1882
+ height: 30,
1883
+ displayValue: opts.showText !== false,
1884
+ fontSize: opts.fontSize ?? 10,
1885
+ margin: 0,
1886
+ marginBottom: 2
1887
+ });
1888
+ return svg.outerHTML;
1889
+ }
1890
+ function renderQrSvg(value, opts) {
1891
+ const level = (opts.qrCodeLevel ?? "M").toUpperCase();
1892
+ const qr = import_qrcode.default.create(value, {
1893
+ errorCorrectionLevel: ["L", "M", "Q", "H"].includes(level) ? level : "M"
1894
+ });
1895
+ const modules = qr.modules;
1896
+ const size = modules.size;
1897
+ const dot = 4;
1898
+ const quiet = 4 * dot;
1899
+ const dim = size * dot + quiet * 2;
1900
+ let rects = "";
1901
+ for (let y = 0; y < size; y++) {
1902
+ for (let x = 0; x < size; x++) {
1903
+ if (modules.get(x, y)) {
1904
+ rects += `<rect x="${quiet + x * dot}" y="${quiet + y * dot}" width="${dot}" height="${dot}" fill="#000"/>`;
1905
+ }
1906
+ }
1907
+ }
1908
+ return `<svg xmlns="${SVG_NS}" viewBox="0 0 ${dim} ${dim}" width="${dim}" height="${dim}" shape-rendering="crispEdges">${rects}</svg>`;
1909
+ }
1910
+ var browserCodeRenderer = {
1911
+ render(value, cellType, opts = {}) {
1912
+ if (!value) throw new Error("empty barcode value");
1913
+ return cellType === "qrcode" ? renderQrSvg(value, opts) : renderBarcodeSvg(value, opts);
1914
+ }
1915
+ };
1916
+ // Annotate the CommonJS export names for ESM import in node:
1917
+ 0 && (module.exports = {
1918
+ browserCodeRenderer,
1919
+ renderHtmlPages
1920
+ });