@worm-vue3-print/core 1.0.1

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