@worm-vue3-print/core 1.2.2 → 1.3.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/README.md +24 -7
- package/dist/browser/index.cjs +2338 -340
- package/dist/browser/index.d.cts +79 -6
- package/dist/browser/index.d.ts +79 -6
- package/dist/browser/index.js +283 -128
- package/dist/{chunk-HLCZHPUF.js → chunk-AKWV6SID.js} +1 -1
- package/dist/chunk-KFRPTLFV.js +3819 -0
- package/dist/client/index.cjs +381 -0
- package/dist/client/index.d.cts +241 -0
- package/dist/client/index.d.ts +241 -0
- package/dist/client/index.js +346 -0
- package/dist/designer/index.cjs +429 -22
- package/dist/designer/index.d.cts +100 -289
- package/dist/designer/index.d.ts +100 -289
- package/dist/designer/index.js +147 -26
- package/dist/dom-executor.iife.global.js +8 -0
- package/dist/driver-Dn_YAzO5.d.cts +935 -0
- package/dist/driver-Dn_YAzO5.d.ts +935 -0
- package/dist/index.cjs +2226 -163
- package/dist/index.d.cts +374 -6
- package/dist/index.d.ts +374 -6
- package/dist/index.js +180 -4
- package/dist/node/index.cjs +41 -0
- package/dist/node/index.d.cts +6 -0
- package/dist/node/index.d.ts +6 -0
- package/dist/node/index.js +10 -0
- package/dist/ports-BvwlA_km.d.ts +102 -0
- package/dist/ports-Cf5sjwls.d.cts +102 -0
- package/package.json +13 -2
- package/dist/chunk-JZIVNXZ7.js +0 -1777
- package/dist/types-BGBAbQD5.d.cts +0 -184
- package/dist/types-BGBAbQD5.d.ts +0 -184
package/dist/index.cjs
CHANGED
|
@@ -20,38 +20,126 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
DEFAULT_READINESS_MS: () => DEFAULT_READINESS_MS,
|
|
24
|
+
DEFAULT_SHRINK_MIN_FONT_SIZE_PT: () => DEFAULT_SHRINK_MIN_FONT_SIZE_PT,
|
|
25
|
+
DEFAULT_TIMEOUT_MS: () => DEFAULT_TIMEOUT_MS,
|
|
26
|
+
EXECUTOR_TARGETS: () => EXECUTOR_TARGETS,
|
|
27
|
+
FALLBACK_FONT_STACK: () => FALLBACK_FONT_STACK,
|
|
28
|
+
MAX_BATCH_COPIES: () => MAX_BATCH_COPIES,
|
|
29
|
+
MICROMETERS_PER_INCH: () => MICROMETERS_PER_INCH,
|
|
30
|
+
MICROMETERS_PER_MM: () => MICROMETERS_PER_MM,
|
|
31
|
+
MIN_CONTINUOUS_HEIGHT_MM: () => MIN_CONTINUOUS_HEIGHT_MM,
|
|
32
|
+
MIN_SHRINK_FONT_SIZE_PT: () => MIN_SHRINK_FONT_SIZE_PT,
|
|
33
|
+
MM_PER_INCH: () => MM_PER_INCH,
|
|
34
|
+
MM_PER_PX: () => MM_PER_PX,
|
|
23
35
|
PAPER_DIMENSIONS: () => PAPER_DIMENSIONS,
|
|
36
|
+
PX_PER_MM: () => PX_PER_MM,
|
|
37
|
+
PrintFailure: () => PrintFailure,
|
|
38
|
+
TILE_DEFAULTS: () => TILE_DEFAULTS,
|
|
24
39
|
TemplateEngine: () => TemplateEngine,
|
|
40
|
+
TilingError: () => TilingError,
|
|
41
|
+
WATERMARK_DEFAULTS: () => WATERMARK_DEFAULTS,
|
|
42
|
+
WATERMARK_DENSITY_PRESETS: () => WATERMARK_DENSITY_PRESETS,
|
|
43
|
+
addNumbers: () => addNumbers,
|
|
44
|
+
applyTextFitSizes: () => applyTextFitSizes,
|
|
25
45
|
avg: () => avg,
|
|
26
46
|
bindData: () => bindData,
|
|
47
|
+
buildBasePageCss: () => buildBasePageCss,
|
|
48
|
+
buildFontFaceCss: () => buildFontFaceCss,
|
|
27
49
|
buildPageCss: () => buildPageCss,
|
|
50
|
+
buildPageGeometryCss: () => buildPageGeometryCss,
|
|
51
|
+
buildPageRuleCss: () => buildPageRuleCss,
|
|
52
|
+
buildPdfTargetSpec: () => buildPdfTargetSpec,
|
|
53
|
+
buildScreenshotTargetSpec: () => buildScreenshotTargetSpec,
|
|
54
|
+
cellFitCapMm: () => cellFitCapMm,
|
|
55
|
+
cellFitKey: () => cellFitKey,
|
|
56
|
+
codeSpecKey: () => codeSpecKey,
|
|
28
57
|
compileTemplate: () => compileTemplate,
|
|
58
|
+
composeBatchHtml: () => composeBatchHtml,
|
|
59
|
+
composeContinuousHeight: () => composeContinuousHeight,
|
|
60
|
+
composeMultiPageDocument: () => composeMultiPageDocument,
|
|
61
|
+
composeTiledHtml: () => composeTiledHtml,
|
|
62
|
+
computeMaxColumns: () => computeMaxColumns,
|
|
63
|
+
computeTileLayout: () => computeTileLayout,
|
|
29
64
|
count: () => count,
|
|
65
|
+
createCollectingCodeRenderer: () => createCollectingCodeRenderer,
|
|
66
|
+
createDomHostRuntime: () => createDomHostRuntime,
|
|
67
|
+
createMapCodeRenderer: () => createMapCodeRenderer,
|
|
68
|
+
divideNumbers: () => divideNumbers,
|
|
30
69
|
elementPositionStyle: () => elementPositionStyle,
|
|
70
|
+
escapeHeightMm: () => escapeHeightMm,
|
|
71
|
+
escapeInlineStyleValue: () => escapeInlineStyleValue,
|
|
31
72
|
evaluate: () => evaluate,
|
|
32
73
|
evaluateTemplate: () => evaluateTemplate,
|
|
74
|
+
floorFontSize: () => floorFontSize,
|
|
33
75
|
formatDate: () => formatDate,
|
|
34
76
|
formatMoney: () => formatMoney,
|
|
77
|
+
formatTimestamp: () => formatTimestamp,
|
|
35
78
|
generateHtml: () => generateHtml,
|
|
36
79
|
getByPath: () => getByPath,
|
|
37
80
|
getPaperDimensions: () => getPaperDimensions,
|
|
38
81
|
ifFn: () => ifFn,
|
|
39
82
|
injectSystemVariables: () => injectSystemVariables,
|
|
83
|
+
isContinuousPaper: () => isContinuousPaper,
|
|
84
|
+
isContinuousPaperSize: () => isContinuousPaperSize,
|
|
85
|
+
isMultiPageTemplate: () => isMultiPageTemplate,
|
|
86
|
+
isWatermarkVisible: () => isWatermarkVisible,
|
|
40
87
|
max: () => max,
|
|
88
|
+
mergeCodeMaps: () => mergeCodeMaps,
|
|
89
|
+
mergeFontDeclarations: () => mergeFontDeclarations,
|
|
90
|
+
micrometersToMillimeters: () => micrometersToMillimeters,
|
|
91
|
+
millimetersToInches: () => millimetersToInches,
|
|
92
|
+
millimetersToMicrometers: () => millimetersToMicrometers,
|
|
41
93
|
min: () => min,
|
|
42
94
|
mm: () => mm,
|
|
95
|
+
mmToPx: () => mmToPx,
|
|
96
|
+
multiplyNumbers: () => multiplyNumbers,
|
|
97
|
+
normalizeMeasurements: () => normalizeMeasurements,
|
|
98
|
+
normalizePrintData: () => normalizePrintData,
|
|
99
|
+
normalizeTemplate: () => normalizeTemplate,
|
|
100
|
+
normalizeTilingOptions: () => normalizeTilingOptions,
|
|
43
101
|
paginate: () => paginate,
|
|
102
|
+
paperViewportPx: () => paperViewportPx,
|
|
44
103
|
parse: () => parse,
|
|
104
|
+
parseCellFitKey: () => parseCellFitKey,
|
|
45
105
|
parseTemplate: () => parseTemplate,
|
|
106
|
+
prepareDocument: () => prepareDocument,
|
|
107
|
+
pxToMm: () => pxToMm,
|
|
108
|
+
renderPdf: () => renderPdf,
|
|
109
|
+
renderScreenshot: () => renderScreenshot,
|
|
46
110
|
renderTemplate: () => renderTemplate,
|
|
111
|
+
renderWatermarkLayerHtml: () => renderWatermarkLayerHtml,
|
|
112
|
+
renderWatermarkTileSvg: () => renderWatermarkTileSvg,
|
|
113
|
+
resolveCellTextFit: () => resolveCellTextFit,
|
|
114
|
+
resolveElementTextFit: () => resolveElementTextFit,
|
|
115
|
+
resolvePaperMm: () => resolvePaperMm,
|
|
116
|
+
resolveSheetMm: () => resolveSheetMm,
|
|
117
|
+
resolveShrinkMinFontSize: () => resolveShrinkMinFontSize,
|
|
118
|
+
resolveSystemVariables: () => resolveSystemVariables,
|
|
119
|
+
resolveWatermarkLayout: () => resolveWatermarkLayout,
|
|
120
|
+
resolveWatermarkText: () => resolveWatermarkText,
|
|
121
|
+
round: () => round,
|
|
122
|
+
roundDown: () => roundDown,
|
|
123
|
+
roundFontSize: () => roundFontSize,
|
|
124
|
+
roundHalfEven: () => roundHalfEven,
|
|
125
|
+
roundMm: () => roundMm,
|
|
126
|
+
roundUp: () => roundUp,
|
|
47
127
|
safeEval: () => safeEval,
|
|
48
128
|
setPageIndex: () => setPageIndex,
|
|
49
129
|
setTotalPages: () => setTotalPages,
|
|
130
|
+
subtractNumbers: () => subtractNumbers,
|
|
50
131
|
sum: () => sum,
|
|
51
132
|
systemVars: () => systemVars,
|
|
52
133
|
tableDesignBottom: () => tableDesignBottom,
|
|
134
|
+
tilePosition: () => tilePosition,
|
|
135
|
+
toElectronPrintToPdfOptions: () => toElectronPrintToPdfOptions,
|
|
136
|
+
toFontFamilyStack: () => toFontFamilyStack,
|
|
137
|
+
toPlaywrightPdfOptions: () => toPlaywrightPdfOptions,
|
|
138
|
+
toPrintFailure: () => toPrintFailure,
|
|
53
139
|
toUpperCaseAmount: () => toUpperCaseAmount,
|
|
54
|
-
tokenize: () => tokenize
|
|
140
|
+
tokenize: () => tokenize,
|
|
141
|
+
validateTiling: () => validateTiling,
|
|
142
|
+
withTimeout: () => withTimeout
|
|
55
143
|
});
|
|
56
144
|
module.exports = __toCommonJS(index_exports);
|
|
57
145
|
|
|
@@ -397,6 +485,113 @@ var Parser = class {
|
|
|
397
485
|
}
|
|
398
486
|
};
|
|
399
487
|
|
|
488
|
+
// src/numeric.ts
|
|
489
|
+
var MAX_DIGITS = 20;
|
|
490
|
+
var EXTRA_SCALE = 8;
|
|
491
|
+
function toNumber(value, fallback = 0) {
|
|
492
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
|
|
493
|
+
if (typeof value === "string") {
|
|
494
|
+
const s = value.trim();
|
|
495
|
+
if (s === "") return fallback;
|
|
496
|
+
const n = Number(s);
|
|
497
|
+
return Number.isFinite(n) ? n : fallback;
|
|
498
|
+
}
|
|
499
|
+
if (value == null) return fallback;
|
|
500
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
501
|
+
return fallback;
|
|
502
|
+
}
|
|
503
|
+
function isNumericLike(value) {
|
|
504
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
505
|
+
if (typeof value === "string") {
|
|
506
|
+
const s = value.trim();
|
|
507
|
+
return s !== "" && Number.isFinite(Number(s));
|
|
508
|
+
}
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
function normalizeFloat(value) {
|
|
512
|
+
if (!Number.isFinite(value) || Number.isInteger(value)) return value;
|
|
513
|
+
if (Math.abs(value) >= 1e15) return value;
|
|
514
|
+
return Number(value.toPrecision(12));
|
|
515
|
+
}
|
|
516
|
+
function decimalsOf(n) {
|
|
517
|
+
if (!Number.isFinite(n) || Number.isInteger(n)) return 0;
|
|
518
|
+
const s = String(n);
|
|
519
|
+
if (s.includes("e") || s.includes("E")) return 0;
|
|
520
|
+
const i = s.indexOf(".");
|
|
521
|
+
return i === -1 ? 0 : s.length - i - 1;
|
|
522
|
+
}
|
|
523
|
+
function rescale(value, decimals) {
|
|
524
|
+
if (!Number.isFinite(value)) return 0;
|
|
525
|
+
const d = Math.min(Math.max(decimals, 0), 12);
|
|
526
|
+
if (d === 0) return Math.round(value);
|
|
527
|
+
const f = 10 ** d;
|
|
528
|
+
const scaled = value * f;
|
|
529
|
+
if (!Number.isFinite(scaled) || Math.abs(scaled) >= 1e15) return normalizeFloat(value);
|
|
530
|
+
return Math.round(scaled) / f;
|
|
531
|
+
}
|
|
532
|
+
function decimalAdd(left, right) {
|
|
533
|
+
const a = toNumber(left);
|
|
534
|
+
const b = toNumber(right);
|
|
535
|
+
const raw = a + b;
|
|
536
|
+
return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
|
|
537
|
+
}
|
|
538
|
+
function decimalSubtract(left, right) {
|
|
539
|
+
const a = toNumber(left);
|
|
540
|
+
const b = toNumber(right);
|
|
541
|
+
const raw = a - b;
|
|
542
|
+
return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
|
|
543
|
+
}
|
|
544
|
+
function decimalMultiply(left, right) {
|
|
545
|
+
const a = toNumber(left);
|
|
546
|
+
const b = toNumber(right);
|
|
547
|
+
const raw = a * b;
|
|
548
|
+
return rescale(raw, decimalsOf(a) + decimalsOf(b));
|
|
549
|
+
}
|
|
550
|
+
function decimalDivide(left, right) {
|
|
551
|
+
const a = toNumber(left);
|
|
552
|
+
const b = toNumber(right);
|
|
553
|
+
if (b === 0) return 0;
|
|
554
|
+
return normalizeFloat(a / b);
|
|
555
|
+
}
|
|
556
|
+
function decimalModulo(left, right) {
|
|
557
|
+
const a = toNumber(left);
|
|
558
|
+
const b = toNumber(right);
|
|
559
|
+
if (b === 0) return 0;
|
|
560
|
+
return normalizeFloat(a % b);
|
|
561
|
+
}
|
|
562
|
+
function normalizeDigits(digits) {
|
|
563
|
+
if (digits === void 0 || digits === null) return 2;
|
|
564
|
+
const d = Math.trunc(toNumber(digits, 2));
|
|
565
|
+
if (!Number.isFinite(d)) return 2;
|
|
566
|
+
return Math.min(Math.max(d, -MAX_DIGITS), MAX_DIGITS);
|
|
567
|
+
}
|
|
568
|
+
function roundTo(value, digits, mode) {
|
|
569
|
+
const n = toNumber(value, 0);
|
|
570
|
+
const d = normalizeDigits(digits);
|
|
571
|
+
if (n === 0) return 0;
|
|
572
|
+
if (Math.abs(n) >= 1e21) return normalizeFloat(n);
|
|
573
|
+
const scale = Math.max(d, 0) + EXTRA_SCALE;
|
|
574
|
+
const fixed = n.toFixed(scale);
|
|
575
|
+
if (!/^-?\d+(\.\d+)?$/.test(fixed)) return normalizeFloat(n);
|
|
576
|
+
const negative = fixed.startsWith("-");
|
|
577
|
+
const digitsStr = fixed.replace("-", "").replace(".", "");
|
|
578
|
+
let scaled = BigInt(digitsStr || "0");
|
|
579
|
+
if (negative) scaled = -scaled;
|
|
580
|
+
const pow10 = 10n ** BigInt(scale - d);
|
|
581
|
+
const sign = scaled < 0n ? -1n : 1n;
|
|
582
|
+
const abs = scaled < 0n ? -scaled : scaled;
|
|
583
|
+
let q = abs / pow10;
|
|
584
|
+
const r = abs % pow10;
|
|
585
|
+
const doubled = 2n * r;
|
|
586
|
+
if (mode === "up") {
|
|
587
|
+
if (r !== 0n) q += 1n;
|
|
588
|
+
} else if (mode !== "down" && doubled >= pow10) {
|
|
589
|
+
if (doubled > pow10 || mode === "half-up" || q % 2n === 1n) q += 1n;
|
|
590
|
+
}
|
|
591
|
+
const result = sign * q;
|
|
592
|
+
return normalizeFloat(d >= 0 ? Number(result) / 10 ** d : Number(result) * 10 ** -d);
|
|
593
|
+
}
|
|
594
|
+
|
|
400
595
|
// src/evaluator.ts
|
|
401
596
|
var SAFE_GLOBALS = {
|
|
402
597
|
Math,
|
|
@@ -462,16 +657,17 @@ function evalBinary(node, context, functions) {
|
|
|
462
657
|
const left = evaluate(node.left, context, functions);
|
|
463
658
|
const right = evaluate(node.right, context, functions);
|
|
464
659
|
switch (node.op) {
|
|
660
|
+
// 数值运算统一走十进制精确实现:消除 0.1 + 0.2 类浮点噪声、字符串数字按数值处理、除零兜底 0
|
|
465
661
|
case "+":
|
|
466
|
-
return left
|
|
662
|
+
return isNumericOperands(left, right) ? decimalAdd(left, right) : concatOperands(left, right);
|
|
467
663
|
case "-":
|
|
468
|
-
return left
|
|
664
|
+
return decimalSubtract(left, right);
|
|
469
665
|
case "*":
|
|
470
|
-
return left
|
|
666
|
+
return decimalMultiply(left, right);
|
|
471
667
|
case "/":
|
|
472
|
-
return left
|
|
668
|
+
return decimalDivide(left, right);
|
|
473
669
|
case "%":
|
|
474
|
-
return left
|
|
670
|
+
return decimalModulo(left, right);
|
|
475
671
|
case "<":
|
|
476
672
|
return left < right;
|
|
477
673
|
case ">":
|
|
@@ -500,15 +696,29 @@ function evalUnary(node, context, functions) {
|
|
|
500
696
|
const arg = evaluate(node.arg, context, functions);
|
|
501
697
|
switch (node.op) {
|
|
502
698
|
case "-":
|
|
503
|
-
return
|
|
699
|
+
return decimalSubtract(0, arg);
|
|
504
700
|
case "+":
|
|
505
|
-
return
|
|
701
|
+
return toNumber(arg);
|
|
506
702
|
case "!":
|
|
507
703
|
return !arg;
|
|
508
704
|
default:
|
|
509
705
|
throw new Error(`\u672A\u77E5\u4E00\u5143\u8FD0\u7B97\u7B26: ${node.op}`);
|
|
510
706
|
}
|
|
511
707
|
}
|
|
708
|
+
function concatOperands(left, right) {
|
|
709
|
+
return `${left == null ? "" : String(left)}${right == null ? "" : String(right)}`;
|
|
710
|
+
}
|
|
711
|
+
function isNumericOperands(left, right) {
|
|
712
|
+
const l = operandKind(left);
|
|
713
|
+
const r = operandKind(right);
|
|
714
|
+
if (l === "other" || r === "other") return false;
|
|
715
|
+
return l === "num" || r === "num";
|
|
716
|
+
}
|
|
717
|
+
function operandKind(value) {
|
|
718
|
+
if (value == null || value === "") return "neutral";
|
|
719
|
+
if (isNumericLike(value)) return "num";
|
|
720
|
+
return "other";
|
|
721
|
+
}
|
|
512
722
|
function evalMember(node, context, functions) {
|
|
513
723
|
const obj = evaluate(node.object, context, functions);
|
|
514
724
|
if (obj == null) {
|
|
@@ -641,8 +851,8 @@ function formatDate(value, fmt) {
|
|
|
641
851
|
return String(value);
|
|
642
852
|
}
|
|
643
853
|
if (isNaN(date.getTime())) return String(value);
|
|
644
|
-
const
|
|
645
|
-
return fmt.replace("YYYY", String(date.getFullYear())).replace("MM",
|
|
854
|
+
const pad3 = (n) => String(n).padStart(2, "0");
|
|
855
|
+
return fmt.replace("YYYY", String(date.getFullYear())).replace("MM", pad3(date.getMonth() + 1)).replace("DD", pad3(date.getDate())).replace("HH", pad3(date.getHours())).replace("mm", pad3(date.getMinutes())).replace("ss", pad3(date.getSeconds()));
|
|
646
856
|
}
|
|
647
857
|
function toUpperCaseAmount(value) {
|
|
648
858
|
const num = Number(value);
|
|
@@ -742,6 +952,34 @@ var systemVars = {
|
|
|
742
952
|
printTime: () => Date.now()
|
|
743
953
|
};
|
|
744
954
|
|
|
955
|
+
// src/functions/math.ts
|
|
956
|
+
function addNumbers(...values) {
|
|
957
|
+
return values.reduce((acc, v) => decimalAdd(acc, v), 0);
|
|
958
|
+
}
|
|
959
|
+
function subtractNumbers(...values) {
|
|
960
|
+
if (values.length === 0) return 0;
|
|
961
|
+
return values.slice(1).reduce((acc, v) => decimalSubtract(acc, v), decimalAdd(values[0], 0));
|
|
962
|
+
}
|
|
963
|
+
function multiplyNumbers(...values) {
|
|
964
|
+
if (values.length === 0) return 0;
|
|
965
|
+
return values.reduce((acc, v) => decimalMultiply(acc, v), 1);
|
|
966
|
+
}
|
|
967
|
+
function divideNumbers(a, b) {
|
|
968
|
+
return decimalDivide(a, b);
|
|
969
|
+
}
|
|
970
|
+
function round(value, digits) {
|
|
971
|
+
return roundTo(value, digits, "half-up");
|
|
972
|
+
}
|
|
973
|
+
function roundUp(value, digits) {
|
|
974
|
+
return roundTo(value, digits, "up");
|
|
975
|
+
}
|
|
976
|
+
function roundDown(value, digits) {
|
|
977
|
+
return roundTo(value, digits, "down");
|
|
978
|
+
}
|
|
979
|
+
function roundHalfEven(value, digits) {
|
|
980
|
+
return roundTo(value, digits, "half-even");
|
|
981
|
+
}
|
|
982
|
+
|
|
745
983
|
// src/render/expression-eval.ts
|
|
746
984
|
var RenderEngine = class {
|
|
747
985
|
constructor() {
|
|
@@ -767,8 +1005,19 @@ var FORMAT_FUNCTIONS = {
|
|
|
767
1005
|
IF: ifFn,
|
|
768
1006
|
CONCAT: (...args) => args.filter((v) => v != null).map(String).join(""),
|
|
769
1007
|
IFEMPTY: (v, d) => v != null && v !== "" ? String(v) : d,
|
|
770
|
-
|
|
771
|
-
|
|
1008
|
+
LEN: (s) => String(s).length,
|
|
1009
|
+
// 四则运算(与 +/-/*// 运算符同一套数值语义,供不方便写运算符的场景使用)
|
|
1010
|
+
ADD: addNumbers,
|
|
1011
|
+
SUB: subtractNumbers,
|
|
1012
|
+
MUL: multiplyNumbers,
|
|
1013
|
+
DIV: divideNumbers,
|
|
1014
|
+
// 数值修约
|
|
1015
|
+
ROUND: round,
|
|
1016
|
+
ROUNDUP: roundUp,
|
|
1017
|
+
CEIL: roundUp,
|
|
1018
|
+
ROUNDDOWN: roundDown,
|
|
1019
|
+
FLOOR: roundDown,
|
|
1020
|
+
ROUNDBANK: roundHalfEven
|
|
772
1021
|
};
|
|
773
1022
|
for (const [name, fn] of Object.entries(FORMAT_FUNCTIONS)) {
|
|
774
1023
|
engine.registerFunction(name, fn);
|
|
@@ -812,9 +1061,8 @@ function evaluateTemplate(text, ctx) {
|
|
|
812
1061
|
}
|
|
813
1062
|
|
|
814
1063
|
// src/render/data-binder.ts
|
|
815
|
-
function bindData(template, printData, baseUrl) {
|
|
816
|
-
const
|
|
817
|
-
const data = Array.isArray(raw) ? raw[0] ?? {} : raw;
|
|
1064
|
+
function bindData(template, printData, baseUrl, fontBaseUrl) {
|
|
1065
|
+
const data = { ...resolveSystemVariables(), ...printData ?? {} };
|
|
818
1066
|
const bound = JSON.parse(JSON.stringify(template));
|
|
819
1067
|
if (bound.header?.elements) {
|
|
820
1068
|
bound.header.elements = bound.header.elements.map((el) => bindElement(el, data, baseUrl));
|
|
@@ -826,12 +1074,29 @@ function bindData(template, printData, baseUrl) {
|
|
|
826
1074
|
bound.firstPageOverlay.elements = bound.firstPageOverlay.elements.map((el) => bindElement(el, data, baseUrl));
|
|
827
1075
|
}
|
|
828
1076
|
bound.elements = bound.elements.map((el) => bindElement(el, data, baseUrl));
|
|
1077
|
+
const fontBase = fontBaseUrl ?? baseUrl;
|
|
1078
|
+
if (fontBase && bound.fonts?.length) {
|
|
1079
|
+
const prefix = fontBase.replace(/\/+$/, "");
|
|
1080
|
+
bound.fonts = bound.fonts.map((font) => ({
|
|
1081
|
+
...font,
|
|
1082
|
+
files: (font.files ?? []).map((file) => ({
|
|
1083
|
+
...file,
|
|
1084
|
+
// 绝对 URL(含协议相对 //)原样使用
|
|
1085
|
+
url: file.url?.startsWith("/") && !file.url.startsWith("//") ? prefix + file.url : file.url
|
|
1086
|
+
}))
|
|
1087
|
+
}));
|
|
1088
|
+
}
|
|
829
1089
|
return bound;
|
|
830
1090
|
}
|
|
831
1091
|
function bindElement(el, data, baseUrl) {
|
|
832
1092
|
const cloned = { ...el, options: { ...el.options } };
|
|
833
1093
|
if (typeof cloned.options.formatter === "string") {
|
|
834
|
-
|
|
1094
|
+
const raw = cloned.options.formatter;
|
|
1095
|
+
if (referencesPageNumbers(raw)) {
|
|
1096
|
+
cloned.options.rawFormatter = raw;
|
|
1097
|
+
} else {
|
|
1098
|
+
cloned.options.formatter = evaluateTemplate(raw, data);
|
|
1099
|
+
}
|
|
835
1100
|
}
|
|
836
1101
|
const isImage = cloned.type === "image" || cloned.printElementType?.type === "image";
|
|
837
1102
|
if (isImage && typeof cloned.options.src === "string") {
|
|
@@ -878,39 +1143,25 @@ function bindTableData(el, data) {
|
|
|
878
1143
|
if (dataStartIdx < 0) dataStartIdx = renderRows.length;
|
|
879
1144
|
const ctx = itemCtx(item);
|
|
880
1145
|
renderRows.push(makeRenderRow(row, (cell) => {
|
|
881
|
-
|
|
882
|
-
if (!formatter) return "";
|
|
883
|
-
return evaluateTemplate(formatter, ctx);
|
|
1146
|
+
return resolveCellText(cell, ctx);
|
|
884
1147
|
}));
|
|
885
1148
|
dataRowCtx.push(ctx);
|
|
886
1149
|
}
|
|
887
1150
|
continue;
|
|
888
1151
|
}
|
|
889
1152
|
if (mode === "dynamic" && row.type === "subtotal") {
|
|
890
|
-
const tpl = makeRenderRow(row, (cell) => {
|
|
891
|
-
const formatter = cell.formatter;
|
|
892
|
-
if (!formatter) return "";
|
|
893
|
-
return evaluateTemplate(formatter, { rows: summaryRows, ...data });
|
|
894
|
-
}, true);
|
|
1153
|
+
const tpl = makeRenderRow(row, (cell) => resolveCellText(cell, { rows: summaryRows, ...data }), true);
|
|
895
1154
|
subtotalTemplates.push(tpl);
|
|
896
1155
|
renderRows.push(tpl);
|
|
897
1156
|
continue;
|
|
898
1157
|
}
|
|
899
1158
|
if (mode === "dynamic" && row.type === "summary") {
|
|
900
|
-
const summaryRow = makeRenderRow(row, (cell) => {
|
|
901
|
-
const formatter = cell.formatter;
|
|
902
|
-
if (!formatter) return "";
|
|
903
|
-
return evaluateTemplate(formatter, { rows: summaryRows, ...data });
|
|
904
|
-
});
|
|
1159
|
+
const summaryRow = makeRenderRow(row, (cell) => resolveCellText(cell, { rows: summaryRows, ...data }));
|
|
905
1160
|
summaryRenderRows.push(summaryRow);
|
|
906
1161
|
renderRows.push(summaryRow);
|
|
907
1162
|
continue;
|
|
908
1163
|
}
|
|
909
|
-
renderRows.push(makeRenderRow(row, (cell) =>
|
|
910
|
-
const formatter = cell.formatter;
|
|
911
|
-
if (!formatter) return "";
|
|
912
|
-
return evaluateTemplate(formatter, data);
|
|
913
|
-
}));
|
|
1164
|
+
renderRows.push(makeRenderRow(row, (cell) => resolveCellText(cell, data)));
|
|
914
1165
|
}
|
|
915
1166
|
opts._renderRows = renderRows;
|
|
916
1167
|
opts._repeatHeaderCount = countRepeatHeader(rows);
|
|
@@ -920,17 +1171,25 @@ function bindTableData(el, data) {
|
|
|
920
1171
|
opts._summaryRows = summaryRenderRows;
|
|
921
1172
|
opts._mainData = data;
|
|
922
1173
|
}
|
|
1174
|
+
function resolveCellText(cell, ctx) {
|
|
1175
|
+
const formatter = cell.formatter;
|
|
1176
|
+
if (!formatter) return "";
|
|
1177
|
+
if (referencesPageNumbers(formatter)) return formatter;
|
|
1178
|
+
return evaluateTemplate(formatter, ctx);
|
|
1179
|
+
}
|
|
923
1180
|
function makeRenderRow(row, resolve, keepRaw = false) {
|
|
924
1181
|
return {
|
|
925
1182
|
type: row.type,
|
|
926
1183
|
height: row.height ?? 8,
|
|
927
1184
|
cells: row.cells.map((cell) => ({
|
|
928
1185
|
content: cell.merged ? "" : resolve(cell),
|
|
929
|
-
|
|
1186
|
+
// 小计行(keepRaw)与引用页码的单元格都保留原始表达式:前者按当页数据行重算,后者按当页页码重算
|
|
1187
|
+
...(keepRaw || referencesPageNumbers(cell.formatter)) && !cell.merged ? { rawFormatter: cell.formatter ?? "" } : {},
|
|
930
1188
|
cellType: cell.cellType,
|
|
931
1189
|
barcodeType: cell.barcodeType,
|
|
932
1190
|
qrCodeLevel: cell.qrCodeLevel,
|
|
933
1191
|
showBarcodeText: cell.showBarcodeText,
|
|
1192
|
+
printerDpi: cell.printerDpi,
|
|
934
1193
|
fit: cell.fit,
|
|
935
1194
|
maxWidth: cell.maxWidth,
|
|
936
1195
|
maxHeight: cell.maxHeight,
|
|
@@ -940,30 +1199,108 @@ function makeRenderRow(row, resolve, keepRaw = false) {
|
|
|
940
1199
|
align: cell.align,
|
|
941
1200
|
valign: cell.valign,
|
|
942
1201
|
fontSize: cell.fontSize,
|
|
1202
|
+
fontFamily: cell.fontFamily,
|
|
943
1203
|
fontWeight: cell.fontWeight,
|
|
944
1204
|
color: cell.color,
|
|
945
1205
|
backgroundColor: cell.backgroundColor,
|
|
946
1206
|
borders: cell.borders,
|
|
947
1207
|
padding: cell.padding,
|
|
948
|
-
wordWrap: cell.wordWrap
|
|
1208
|
+
wordWrap: cell.wordWrap,
|
|
1209
|
+
textFit: cell.textFit,
|
|
1210
|
+
shrinkMinFontSize: cell.shrinkMinFontSize
|
|
949
1211
|
}))
|
|
950
1212
|
};
|
|
951
1213
|
}
|
|
952
1214
|
function countRepeatHeader(rows) {
|
|
1215
|
+
const headerCount = countLeadingHeaderRows(rows);
|
|
1216
|
+
if (headerCount === 0) return 0;
|
|
1217
|
+
const enabled = rows.slice(0, headerCount).some((row) => row.repeatOnPage === true);
|
|
1218
|
+
if (!enabled) return 0;
|
|
1219
|
+
return alignToRowspanBoundary(rows, headerCount);
|
|
1220
|
+
}
|
|
1221
|
+
function countLeadingHeaderRows(rows) {
|
|
953
1222
|
let n = 0;
|
|
954
1223
|
for (const row of rows) {
|
|
955
|
-
if (row
|
|
956
|
-
|
|
957
|
-
} else {
|
|
958
|
-
break;
|
|
959
|
-
}
|
|
1224
|
+
if (row?.type !== "header") break;
|
|
1225
|
+
n++;
|
|
960
1226
|
}
|
|
961
1227
|
return n;
|
|
962
1228
|
}
|
|
963
|
-
function
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1229
|
+
function isRowspanComplete(rows, n) {
|
|
1230
|
+
for (let r = 0; r < n; r++) {
|
|
1231
|
+
for (const cell of rows[r]?.cells ?? []) {
|
|
1232
|
+
if (cell?.merged) continue;
|
|
1233
|
+
if (r + (cell?.rowspan ?? 1) > n) return false;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return true;
|
|
1237
|
+
}
|
|
1238
|
+
function alignToRowspanBoundary(rows, max2) {
|
|
1239
|
+
for (let n = max2; n >= 1; n--) {
|
|
1240
|
+
if (isRowspanComplete(rows, n)) return n;
|
|
1241
|
+
}
|
|
1242
|
+
return 1;
|
|
1243
|
+
}
|
|
1244
|
+
var pad2 = (n) => String(n).padStart(2, "0");
|
|
1245
|
+
function resolveSystemVariables(now = /* @__PURE__ */ new Date(), page = {}) {
|
|
1246
|
+
return {
|
|
1247
|
+
printDate: `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())}`,
|
|
1248
|
+
printTime: `${pad2(now.getHours())}:${pad2(now.getMinutes())}:${pad2(now.getSeconds())}`,
|
|
1249
|
+
pageIndex: page.pageIndex ?? 1,
|
|
1250
|
+
totalPages: page.totalPages ?? 1
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
function referencesPageNumbers(expr) {
|
|
1254
|
+
return typeof expr === "string" && /\b(pageIndex|totalPages)\b/.test(expr);
|
|
1255
|
+
}
|
|
1256
|
+
function injectSystemVariables(html, now = /* @__PURE__ */ new Date()) {
|
|
1257
|
+
const { printDate, printTime } = resolveSystemVariables(now);
|
|
1258
|
+
return html.replace(/\{printDate\}/g, printDate).replace(/\{printTime\}/g, printTime);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// src/print/fonts.ts
|
|
1262
|
+
function buildFontFaceCss(fonts) {
|
|
1263
|
+
const blocks = [];
|
|
1264
|
+
for (const font of fonts ?? []) {
|
|
1265
|
+
const family = font?.family?.trim();
|
|
1266
|
+
if (!family) continue;
|
|
1267
|
+
const quoted = `"${family.replace(/"/g, "")}"`;
|
|
1268
|
+
for (const file of font.files ?? []) {
|
|
1269
|
+
const url = file?.url?.trim();
|
|
1270
|
+
if (!url) continue;
|
|
1271
|
+
const weight = Number.isFinite(file.weight) ? Number(file.weight) : 400;
|
|
1272
|
+
const style = file.style === "italic" ? "italic" : "normal";
|
|
1273
|
+
blocks.push(
|
|
1274
|
+
`@font-face{font-family:${quoted};src:url("${url}")${fontFormatHint(url)};font-weight:${weight};font-style:${style};font-display:block;}`
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return blocks.length ? `${blocks.join("")}
|
|
1279
|
+
` : "";
|
|
1280
|
+
}
|
|
1281
|
+
function fontFormatHint(url) {
|
|
1282
|
+
const path = url.split(/[?#]/)[0].toLowerCase();
|
|
1283
|
+
if (path.endsWith(".woff2")) return ' format("woff2")';
|
|
1284
|
+
if (path.endsWith(".woff")) return ' format("woff")';
|
|
1285
|
+
if (path.endsWith(".ttf")) return ' format("truetype")';
|
|
1286
|
+
if (path.endsWith(".otf")) return ' format("opentype")';
|
|
1287
|
+
return "";
|
|
1288
|
+
}
|
|
1289
|
+
var FALLBACK_FONT_STACK = [
|
|
1290
|
+
'"Microsoft YaHei"',
|
|
1291
|
+
'"PingFang SC"',
|
|
1292
|
+
'"Helvetica Neue"',
|
|
1293
|
+
"Arial",
|
|
1294
|
+
"sans-serif"
|
|
1295
|
+
];
|
|
1296
|
+
function escapeInlineStyleValue(cssValue) {
|
|
1297
|
+
return cssValue.replace(/&/g, "&").replace(/"/g, """);
|
|
1298
|
+
}
|
|
1299
|
+
function toFontFamilyStack(family) {
|
|
1300
|
+
const name = family?.trim();
|
|
1301
|
+
if (!name) return FALLBACK_FONT_STACK.join(", ");
|
|
1302
|
+
const head = name.includes(",") ? name : `"${name.replace(/"/g, "")}"`;
|
|
1303
|
+
return [head, ...FALLBACK_FONT_STACK].join(", ");
|
|
967
1304
|
}
|
|
968
1305
|
|
|
969
1306
|
// src/render/types.ts
|
|
@@ -973,38 +1310,81 @@ var PAPER_DIMENSIONS = {
|
|
|
973
1310
|
A5: { width: 148, height: 210 },
|
|
974
1311
|
Letter: { width: 216, height: 279 },
|
|
975
1312
|
Legal: { width: 216, height: 356 },
|
|
976
|
-
|
|
1313
|
+
// 针式打印纸(241 系列等分):11 英寸整张 279.4mm 按等分取整
|
|
1314
|
+
DOT_FULL: { width: 241, height: 279.4 },
|
|
1315
|
+
DOT_HALF: { width: 241, height: 139.7 },
|
|
1316
|
+
DOT_THIRD: { width: 241, height: 93.1 },
|
|
1317
|
+
// 标签纸
|
|
1318
|
+
LABEL_80X60: { width: 80, height: 60 },
|
|
1319
|
+
LABEL_60X40: { width: 60, height: 40 },
|
|
1320
|
+
LABEL_40X30: { width: 40, height: 30 },
|
|
1321
|
+
// 小票纸(热敏卷纸):高度仅为设计画布高度,出纸按内容推导
|
|
1322
|
+
THERMAL_57: { width: 57, height: 297 },
|
|
1323
|
+
THERMAL_80: { width: 80, height: 297 },
|
|
1324
|
+
THERMAL_110: { width: 110, height: 297 },
|
|
1325
|
+
CUSTOM: { width: 210, height: 297 },
|
|
1326
|
+
// 连续纸:默认 80mm 热敏;高度仅为设计画布高度,出纸按内容推导
|
|
1327
|
+
CONTINUOUS: { width: 80, height: 297 }
|
|
977
1328
|
};
|
|
1329
|
+
var CONTINUOUS_PAPER_SIZES = /* @__PURE__ */ new Set([
|
|
1330
|
+
"CONTINUOUS",
|
|
1331
|
+
"THERMAL_57",
|
|
1332
|
+
"THERMAL_80",
|
|
1333
|
+
"THERMAL_110"
|
|
1334
|
+
]);
|
|
1335
|
+
function isContinuousPaperSize(paperSize) {
|
|
1336
|
+
return CONTINUOUS_PAPER_SIZES.has(paperSize);
|
|
1337
|
+
}
|
|
978
1338
|
function getPaperDimensions(template) {
|
|
979
|
-
const
|
|
980
|
-
|
|
1339
|
+
const continuous = isContinuousPaperSize(template.paperSize);
|
|
1340
|
+
const base = template.paperSize === "CUSTOM" || continuous ? {
|
|
1341
|
+
width: template.customWidth ?? PAPER_DIMENSIONS[template.paperSize].width,
|
|
1342
|
+
height: template.customHeight ?? PAPER_DIMENSIONS[template.paperSize].height
|
|
1343
|
+
} : PAPER_DIMENSIONS[template.paperSize];
|
|
1344
|
+
if (template.orientation === "landscape" && !continuous) {
|
|
981
1345
|
return { width: base.height, height: base.width };
|
|
982
1346
|
}
|
|
983
1347
|
return { ...base };
|
|
984
1348
|
}
|
|
1349
|
+
function getOutputPaperDimensions(template) {
|
|
1350
|
+
const continuous = isContinuousPaperSize(template.paperSize);
|
|
1351
|
+
if (continuous || template.tiling?.enabled === true) {
|
|
1352
|
+
return getPaperDimensions(template);
|
|
1353
|
+
}
|
|
1354
|
+
const design = getPaperDimensions(template);
|
|
1355
|
+
const swap = template.outputRotation === 90 || template.outputRotation === 270;
|
|
1356
|
+
return swap ? { width: design.height, height: design.width } : design;
|
|
1357
|
+
}
|
|
1358
|
+
function getOutputRotationAngle(template) {
|
|
1359
|
+
const continuous = isContinuousPaperSize(template.paperSize);
|
|
1360
|
+
if (continuous || template.tiling?.enabled === true) return 0;
|
|
1361
|
+
return template.outputRotation ?? 0;
|
|
1362
|
+
}
|
|
1363
|
+
function isContinuousPaper(template) {
|
|
1364
|
+
return isContinuousPaperSize(template.paperSize);
|
|
1365
|
+
}
|
|
985
1366
|
|
|
986
1367
|
// src/render/css-builder.ts
|
|
987
1368
|
function mm(value) {
|
|
988
1369
|
return `${value}mm`;
|
|
989
1370
|
}
|
|
990
|
-
function
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
|
|
1371
|
+
function resolveOuterPaper(template, pageHeightMm) {
|
|
1372
|
+
const out = getOutputPaperDimensions(template);
|
|
1373
|
+
return pageHeightMm && pageHeightMm > 0 && isContinuousPaper(template) ? { width: out.width, height: pageHeightMm } : out;
|
|
1374
|
+
}
|
|
1375
|
+
function resolveAreaPaper(template, pageHeightMm) {
|
|
1376
|
+
const design = getPaperDimensions(template);
|
|
1377
|
+
return pageHeightMm && pageHeightMm > 0 && isContinuousPaper(template) ? { width: design.width, height: pageHeightMm } : design;
|
|
1378
|
+
}
|
|
1379
|
+
function screenBlock() {
|
|
997
1380
|
return `
|
|
998
|
-
/* \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 */
|
|
999
|
-
@page { size: ${mm(paper.width)} ${mm(paper.height)}; margin: 0; }
|
|
1000
|
-
|
|
1001
1381
|
/* \u2500\u2500 \u5168\u5C40\u91CD\u7F6E \u2500\u2500 */
|
|
1002
1382
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1003
1383
|
|
|
1004
1384
|
/* \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 */
|
|
1005
1385
|
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
1006
1386
|
|
|
1007
|
-
body { font-family: "
|
|
1387
|
+
body { font-family: ${FALLBACK_FONT_STACK.join(", ")}; }
|
|
1008
1388
|
|
|
1009
1389
|
/* \u2500\u2500 \u5C4F\u5E55\u9884\u89C8\uFF1A\u7070\u5E95 + \u7EB8\u5F20\u9634\u5F71/\u9875\u95F4\u8DDD\uFF1B\u6253\u5370\u65F6\u53BB\u9664 \u2500\u2500 */
|
|
1010
1390
|
@media screen {
|
|
@@ -1015,13 +1395,110 @@ body { font-family: "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, s
|
|
|
1015
1395
|
body { background: #fff; }
|
|
1016
1396
|
.print-page { margin: 0; box-shadow: none; }
|
|
1017
1397
|
}
|
|
1398
|
+
`;
|
|
1399
|
+
}
|
|
1400
|
+
function watermarkBlock() {
|
|
1401
|
+
return `
|
|
1402
|
+
/* \u2500\u2500 \u6C34\u5370\u5C42\uFF1A\u8986\u76D6\u6574\u9875\u3001\u4F4D\u4E8E\u6240\u6709\u5185\u5BB9\u4E4B\u4E0B\uFF08\u663E\u5F0F\u77E2\u91CF\u74E6\u7247\uFF0C\u7981\u6B62\u7528 CSS \u5E73\u94FA\u80CC\u666F\uFF1A
|
|
1403
|
+
Chromium \u4F1A\u628A\u5B83\u7F16\u8BD1\u6210 PDF \u5E73\u94FA\u56FE\u6848\uFF0C\u51FA\u7EB8\u94FE\u8DEF\u7684 RIP \u4F1A\u5FFD\u7565\u56FE\u6848\u77E9\u9635\u5BFC\u81F4\u6C34\u5370\u653E\u5927/\u9519\u4F4D\uFF09 \u2500\u2500 */
|
|
1404
|
+
.watermark-layer {
|
|
1405
|
+
position: absolute;
|
|
1406
|
+
top: 0;
|
|
1407
|
+
left: 0;
|
|
1408
|
+
width: 100%;
|
|
1409
|
+
height: 100%;
|
|
1410
|
+
z-index: 0;
|
|
1411
|
+
pointer-events: none;
|
|
1412
|
+
overflow: hidden;
|
|
1413
|
+
}
|
|
1414
|
+
/* \u5355\u5757\u6C34\u5370\u74E6\u7247\uFF1A\u4F4D\u7F6E/\u5C3A\u5BF8\u7531 core \u7684\u74E6\u7247\u7F51\u683C\u7ED9\u51FA\uFF08mm\uFF09 */
|
|
1415
|
+
.watermark-tile {
|
|
1416
|
+
position: absolute;
|
|
1417
|
+
pointer-events: none;
|
|
1418
|
+
}
|
|
1419
|
+
`;
|
|
1420
|
+
}
|
|
1421
|
+
function elementBlock() {
|
|
1422
|
+
return `
|
|
1423
|
+
/* \u2500\u2500 \u5143\u7D20\u901A\u7528\u5B9A\u4F4D \u2500\u2500 */
|
|
1424
|
+
.print-element {
|
|
1425
|
+
position: absolute;
|
|
1426
|
+
overflow: hidden;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/* \u2500\u2500 \u8868\u683C\u6837\u5F0F \u2500\u2500 */
|
|
1430
|
+
.print-table {
|
|
1431
|
+
width: 100%;
|
|
1432
|
+
border-collapse: collapse;
|
|
1433
|
+
table-layout: fixed;
|
|
1434
|
+
}
|
|
1435
|
+
.print-table th,
|
|
1436
|
+
.print-table td {
|
|
1437
|
+
border: 1px solid #ccc;
|
|
1438
|
+
padding: 2mm 3mm;
|
|
1439
|
+
word-break: break-all;
|
|
1440
|
+
overflow: hidden;
|
|
1441
|
+
}
|
|
1442
|
+
.print-table thead th {
|
|
1443
|
+
background-color: #f5f5f5;
|
|
1444
|
+
font-weight: bold;
|
|
1445
|
+
}
|
|
1018
1446
|
|
|
1447
|
+
/* \u2500\u2500 \u6D4B\u91CF\u6A21\u5F0F \u2500\u2500 */
|
|
1448
|
+
.measure-mode .print-page {
|
|
1449
|
+
height: auto;
|
|
1450
|
+
min-height: auto;
|
|
1451
|
+
overflow: visible;
|
|
1452
|
+
}
|
|
1453
|
+
.measure-mode .content-area {
|
|
1454
|
+
height: auto;
|
|
1455
|
+
overflow: visible;
|
|
1456
|
+
}
|
|
1457
|
+
`;
|
|
1458
|
+
}
|
|
1459
|
+
function pageRuleBlock(paper) {
|
|
1460
|
+
return `
|
|
1461
|
+
/* \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 */
|
|
1462
|
+
@page { size: ${mm(paper.width)} ${mm(paper.height)}; margin: 0; }
|
|
1463
|
+
`;
|
|
1464
|
+
}
|
|
1465
|
+
function buildRotorCss(area, margins, angle) {
|
|
1466
|
+
const base = `
|
|
1467
|
+
/* \u2500\u2500 \u51FA\u7EB8\u8F6C\u5B50\uFF1A\u8BBE\u8BA1\u7A3F\u6574\u9875\u65CB\u8F6C ${angle}\xB0 \u586B\u5165\u51FA\u7EB8\u7EB8\u5F20 \u2500\u2500 */
|
|
1468
|
+
.print-page-rotor {
|
|
1469
|
+
position: absolute;
|
|
1470
|
+
top: 0;
|
|
1471
|
+
left: 0;
|
|
1472
|
+
width: ${mm(area.width)};
|
|
1473
|
+
height: ${mm(area.height)};
|
|
1474
|
+
padding: ${mm(margins.top)} ${mm(margins.right)} ${mm(margins.bottom)} ${mm(margins.left)};
|
|
1475
|
+
box-sizing: border-box;
|
|
1476
|
+
transform-origin: 0 0;
|
|
1477
|
+
}`;
|
|
1478
|
+
if (angle === 0) return base.trim();
|
|
1479
|
+
const transforms = {
|
|
1480
|
+
90: `translate(${mm(area.height)}, 0mm) rotate(90deg)`,
|
|
1481
|
+
180: `translate(${mm(area.width)}, ${mm(area.height)}) rotate(180deg)`,
|
|
1482
|
+
270: `translate(0mm, ${mm(area.width)}) rotate(270deg)`
|
|
1483
|
+
};
|
|
1484
|
+
const tf = transforms[angle];
|
|
1485
|
+
return [
|
|
1486
|
+
base.trim(),
|
|
1487
|
+
`.print-page-rotor-${angle} {
|
|
1488
|
+
transform: ${tf};
|
|
1489
|
+
}`
|
|
1490
|
+
].join("\n");
|
|
1491
|
+
}
|
|
1492
|
+
function pageGeometryBlock(template, outerPaper, pageSel, rotated) {
|
|
1493
|
+
const { top: mt, right: mr, bottom: mb, left: ml } = template.margins;
|
|
1494
|
+
const padding = rotated ? "0" : `${mm(mt)} ${mm(mr)} ${mm(mb)} ${mm(ml)}`;
|
|
1495
|
+
return `
|
|
1019
1496
|
/* \u2500\u2500 \u7EB8\u5F20\u9875\u9762 \u2500\u2500 */
|
|
1020
|
-
|
|
1021
|
-
width: ${mm(
|
|
1022
|
-
min-height: ${mm(
|
|
1497
|
+
${pageSel} {
|
|
1498
|
+
width: ${mm(outerPaper.width)};
|
|
1499
|
+
min-height: ${mm(outerPaper.height)};
|
|
1023
1500
|
background: ${template.pageBackground ?? "#fff"};
|
|
1024
|
-
padding: ${
|
|
1501
|
+
padding: ${padding};
|
|
1025
1502
|
position: relative;
|
|
1026
1503
|
page-break-after: always;
|
|
1027
1504
|
overflow: hidden;
|
|
@@ -1029,9 +1506,18 @@ body { font-family: "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, s
|
|
|
1029
1506
|
.print-page:last-child {
|
|
1030
1507
|
page-break-after: auto;
|
|
1031
1508
|
}
|
|
1032
|
-
|
|
1509
|
+
`;
|
|
1510
|
+
}
|
|
1511
|
+
function areaGeometryBlock(template, paper, desc) {
|
|
1512
|
+
const { right: mr, left: ml } = template.margins;
|
|
1513
|
+
const { bottom: mb } = template.margins;
|
|
1514
|
+
const headerH = template.header?.height ?? 0;
|
|
1515
|
+
const footerH = template.footer?.height ?? 0;
|
|
1516
|
+
const overlayH = template.firstPageOverlay?.height ?? 0;
|
|
1517
|
+
const contentWidth = paper.width - ml - mr;
|
|
1518
|
+
return `
|
|
1033
1519
|
/* \u2500\u2500 \u9875\u7709 \u2500\u2500 */
|
|
1034
|
-
.page-header {
|
|
1520
|
+
${desc}.page-header {
|
|
1035
1521
|
width: ${mm(contentWidth)};
|
|
1036
1522
|
height: ${mm(headerH)};
|
|
1037
1523
|
position: relative;
|
|
@@ -1040,7 +1526,7 @@ body { font-family: "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, s
|
|
|
1040
1526
|
/* \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
|
|
1041
1527
|
\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
|
|
1042
1528
|
\u4E0E\u8BBE\u8BA1\u5668 CanvasPaper \u7684\u4E09\u533A\u51E0\u4F55\u4E00\u81F4\u3002 */
|
|
1043
|
-
.page-footer {
|
|
1529
|
+
${desc}.page-footer {
|
|
1044
1530
|
width: ${mm(contentWidth)};
|
|
1045
1531
|
height: ${mm(footerH)};
|
|
1046
1532
|
position: absolute;
|
|
@@ -1049,54 +1535,115 @@ body { font-family: "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, s
|
|
|
1049
1535
|
}
|
|
1050
1536
|
|
|
1051
1537
|
/* \u2500\u2500 \u5185\u5BB9\u533A \u2500\u2500 */
|
|
1052
|
-
.content-area {
|
|
1538
|
+
${desc}.content-area {
|
|
1053
1539
|
width: ${mm(contentWidth)};
|
|
1054
1540
|
position: relative;
|
|
1055
1541
|
overflow: visible;
|
|
1056
1542
|
}
|
|
1057
1543
|
|
|
1058
1544
|
/* \u2500\u2500 \u9996\u9875\u53E0\u52A0\u533A\u57DF \u2500\u2500 */
|
|
1059
|
-
.first-page-overlay {
|
|
1545
|
+
${desc}.first-page-overlay {
|
|
1060
1546
|
width: ${mm(contentWidth)};
|
|
1061
1547
|
height: ${mm(overlayH)};
|
|
1062
1548
|
position: relative;
|
|
1063
1549
|
}
|
|
1550
|
+
`;
|
|
1551
|
+
}
|
|
1552
|
+
function buildBasePageCss() {
|
|
1553
|
+
return [screenBlock(), watermarkBlock(), elementBlock()].join("").trim();
|
|
1554
|
+
}
|
|
1555
|
+
function buildPageRuleCss(template, pageHeightMm) {
|
|
1556
|
+
return pageRuleBlock(resolveOuterPaper(template, pageHeightMm)).trim();
|
|
1557
|
+
}
|
|
1558
|
+
function buildPageGeometryCss(template, scope, pageHeightMm) {
|
|
1559
|
+
const angle = getOutputRotationAngle(template);
|
|
1560
|
+
const rotated = angle !== 0;
|
|
1561
|
+
const outer = resolveOuterPaper(template, pageHeightMm);
|
|
1562
|
+
const area = resolveAreaPaper(template, pageHeightMm);
|
|
1563
|
+
const pageSel = scope ? `${scope}.print-page` : ".print-page";
|
|
1564
|
+
const desc = scope ? `${scope} ` : "";
|
|
1565
|
+
return [
|
|
1566
|
+
pageGeometryBlock(template, outer, pageSel, rotated),
|
|
1567
|
+
areaGeometryBlock(template, area, desc),
|
|
1568
|
+
...rotated ? [buildRotorCss(area, template.margins, angle)] : []
|
|
1569
|
+
].join("").trim();
|
|
1570
|
+
}
|
|
1571
|
+
function buildPageCss(template, pageHeightMm) {
|
|
1572
|
+
const angle = getOutputRotationAngle(template);
|
|
1573
|
+
const rotated = angle !== 0;
|
|
1574
|
+
const outer = resolveOuterPaper(template, pageHeightMm);
|
|
1575
|
+
const area = resolveAreaPaper(template, pageHeightMm);
|
|
1576
|
+
return [
|
|
1577
|
+
pageRuleBlock(outer),
|
|
1578
|
+
screenBlock(),
|
|
1579
|
+
pageGeometryBlock(template, outer, ".print-page", rotated),
|
|
1580
|
+
watermarkBlock(),
|
|
1581
|
+
areaGeometryBlock(template, area, ""),
|
|
1582
|
+
elementBlock(),
|
|
1583
|
+
...rotated ? [buildRotorCss(area, template.margins, angle)] : []
|
|
1584
|
+
].join("").trim();
|
|
1585
|
+
}
|
|
1586
|
+
var COPY_BREAK_CSS = ".print-copy:not(:last-child){break-after:page;page-break-after:always;}";
|
|
1587
|
+
function buildBatchPageCss(template, copies) {
|
|
1588
|
+
if (!isContinuousPaper(template)) {
|
|
1589
|
+
return `${buildPageCss(template)}
|
|
1590
|
+
${COPY_BREAK_CSS}`;
|
|
1591
|
+
}
|
|
1592
|
+
const { bottom: mb } = template.margins;
|
|
1593
|
+
const footerH = template.footer?.height ?? 0;
|
|
1594
|
+
const width = getPaperDimensions(template).width;
|
|
1595
|
+
const base = buildPageCss(template, copies[0]?.heightMm);
|
|
1596
|
+
const scoped = copies.map((copy, i) => {
|
|
1597
|
+
const h = copy.heightMm;
|
|
1598
|
+
const rules = [
|
|
1599
|
+
`@page copy${i} { size: ${mm(width)} ${h ? mm(h) : "auto"}; margin: 0; }`,
|
|
1600
|
+
`.print-copy-${i} { page: copy${i}; }`
|
|
1601
|
+
];
|
|
1602
|
+
if (h) {
|
|
1603
|
+
rules.push(`.print-copy-${i} .print-page { min-height: ${mm(h)}; }`);
|
|
1604
|
+
rules.push(`.print-copy-${i} .page-footer { top: ${mm(h - mb - footerH)}; }`);
|
|
1605
|
+
}
|
|
1606
|
+
return rules.join("\n");
|
|
1607
|
+
}).join("\n");
|
|
1608
|
+
return `${base}
|
|
1609
|
+
${scoped}
|
|
1610
|
+
${COPY_BREAK_CSS}`;
|
|
1611
|
+
}
|
|
1612
|
+
function buildSheetPageCss(layout) {
|
|
1613
|
+
return `
|
|
1614
|
+
/* \u2500\u2500 \u62FC\u7248\u7EB8\u5F20\uFF1A@page \u5C3A\u5BF8 = \u76EE\u6807\u7EB8\u3002\u670D\u52A1\u7AEF/\u5BA2\u6237\u7AEF\u94FE\u8DEF\u4EE5 paperMm \u663E\u5F0F\u5B9A\u5C3A\u5BF8\u3001\u4E0D\u770B\u8FD9\u91CC\uFF0C
|
|
1615
|
+
\u4F46\u6D4F\u89C8\u5668\u94FE\u8DEF\u53EA\u770B @page\uFF0C\u6545\u8FD9\u6761\u662F\u786C\u9700\u6C42 \u2500\u2500 */
|
|
1616
|
+
@page { size: ${mm(layout.sheet.width)} ${mm(layout.sheet.height)}; margin: 0; }
|
|
1064
1617
|
|
|
1065
|
-
/* \u2500\u2500 \
|
|
1066
|
-
.print-
|
|
1067
|
-
|
|
1618
|
+
/* \u2500\u2500 \u4E00\u5F20\u76EE\u6807\u7EB8 \u2500\u2500 */
|
|
1619
|
+
.print-sheet {
|
|
1620
|
+
width: ${mm(layout.sheet.width)};
|
|
1621
|
+
height: ${mm(layout.sheet.height)};
|
|
1622
|
+
position: relative;
|
|
1068
1623
|
overflow: hidden;
|
|
1624
|
+
break-after: page;
|
|
1625
|
+
page-break-after: always;
|
|
1069
1626
|
}
|
|
1627
|
+
.print-sheet:last-child { break-after: auto; page-break-after: auto; }
|
|
1070
1628
|
|
|
1071
|
-
/* \u2500\u2500 \
|
|
1072
|
-
.print-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
}
|
|
1077
|
-
.print-table th,
|
|
1078
|
-
.print-table td {
|
|
1079
|
-
border: 1px solid #ccc;
|
|
1080
|
-
padding: 2mm 3mm;
|
|
1081
|
-
word-break: break-all;
|
|
1629
|
+
/* \u2500\u2500 \u4E00\u683C\uFF1A\u7EDD\u5BF9\u5B9A\u4F4D\uFF0C\u4F4D\u7F6E\u7531 tilePosition() \u4EE5\u884C\u5185 style \u7ED9\u51FA \u2500\u2500 */
|
|
1630
|
+
.print-tile {
|
|
1631
|
+
position: absolute;
|
|
1632
|
+
width: ${mm(layout.tile.width)};
|
|
1633
|
+
height: ${mm(layout.tile.height)};
|
|
1082
1634
|
overflow: hidden;
|
|
1083
1635
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
}
|
|
1636
|
+
/* \u9632\u5FA1\u6027\u58F0\u660E\uFF1A\u7EDD\u5BF9\u5B9A\u4F4D + overflow:hidden \u5BB9\u5668\u5185\u7684\u540E\u4EE3\u4E0D\u4EA7\u751F\u5206\u9875\u70B9\uFF0C\u5F53\u524D\u5E03\u5C40\u4E0B\u65E0\u5B9E\u9645\u4F5C\u7528\uFF1B
|
|
1637
|
+
\u82E5\u5C06\u6765\u6539\u7528 flex/grid \u5E03\u5C40\uFF0C\u683C\u5185\u6574\u9875\u4F1A\u91CD\u65B0\u53C2\u4E0E\u5206\u9875\uFF0C\u6545\u4FDD\u7559 */
|
|
1638
|
+
.print-tile > .print-page { break-after: auto; page-break-after: auto; }
|
|
1088
1639
|
|
|
1089
|
-
|
|
1090
|
-
.
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
}
|
|
1095
|
-
.measure-mode .content-area {
|
|
1096
|
-
height: auto;
|
|
1097
|
-
overflow: visible;
|
|
1640
|
+
@media screen {
|
|
1641
|
+
.print-sheet { margin: 12px auto; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18); }
|
|
1642
|
+
/* \u5FC5\u9700\uFF1A\u62B5\u6D88\u6807\u7B7E CSS \u7684 @media screen{.print-page{margin:12px auto}}\uFF0C
|
|
1643
|
+
\u5426\u5219\u8BBE\u8BA1\u5668\u9884\u89C8\u9519\u4F4D 3.17mm\u3001\u4E0E\u51FA\u7EB8\u4E0D\u4E00\u81F4 */
|
|
1644
|
+
.print-tile > .print-page { margin: 0; box-shadow: none; }
|
|
1098
1645
|
}
|
|
1099
|
-
|
|
1646
|
+
`;
|
|
1100
1647
|
}
|
|
1101
1648
|
function elementPositionStyle(left, top, width, height, zIndex) {
|
|
1102
1649
|
const parts = [
|
|
@@ -1114,6 +1661,223 @@ function elementPositionStyle(left, top, width, height, zIndex) {
|
|
|
1114
1661
|
return parts.join(";") + ";";
|
|
1115
1662
|
}
|
|
1116
1663
|
|
|
1664
|
+
// src/designer/utils/units.ts
|
|
1665
|
+
function ptToMm(pt) {
|
|
1666
|
+
return pt / 2.83464566929;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// src/render/text-fit.ts
|
|
1670
|
+
var DEFAULT_SHRINK_MIN_FONT_SIZE_PT = 6;
|
|
1671
|
+
var MIN_SHRINK_FONT_SIZE_PT = 1;
|
|
1672
|
+
var VALID_FITS = ["clip", "shrink", "autoHeight"];
|
|
1673
|
+
function normalizeFit(value) {
|
|
1674
|
+
return typeof value === "string" && VALID_FITS.includes(value) ? value : void 0;
|
|
1675
|
+
}
|
|
1676
|
+
var ELEMENT_DEFAULT_FIT = {
|
|
1677
|
+
text: "clip",
|
|
1678
|
+
longText: "autoHeight"
|
|
1679
|
+
};
|
|
1680
|
+
function resolveElementTextFit(type, opts) {
|
|
1681
|
+
return normalizeFit(opts?.textFit) ?? ELEMENT_DEFAULT_FIT[type] ?? "clip";
|
|
1682
|
+
}
|
|
1683
|
+
function resolveCellTextFit(cell) {
|
|
1684
|
+
return normalizeFit(cell?.textFit) ?? (cell?.wordWrap === false ? "clip" : "autoHeight");
|
|
1685
|
+
}
|
|
1686
|
+
function resolveShrinkMinFontSize(pt) {
|
|
1687
|
+
if (typeof pt !== "number" || !Number.isFinite(pt) || pt <= 0) return DEFAULT_SHRINK_MIN_FONT_SIZE_PT;
|
|
1688
|
+
return Math.max(pt, MIN_SHRINK_FONT_SIZE_PT);
|
|
1689
|
+
}
|
|
1690
|
+
function roundFontSize(pt) {
|
|
1691
|
+
return Math.round(pt * 100) / 100;
|
|
1692
|
+
}
|
|
1693
|
+
function floorFontSize(pt) {
|
|
1694
|
+
return Math.floor(pt * 100) / 100;
|
|
1695
|
+
}
|
|
1696
|
+
function cellFitKey(elementId, kind, rowIndex, colIndex) {
|
|
1697
|
+
return `${elementId}#${kind}#${rowIndex}:${colIndex}`;
|
|
1698
|
+
}
|
|
1699
|
+
function parseCellFitKey(key) {
|
|
1700
|
+
const first = key.indexOf("#");
|
|
1701
|
+
if (first < 0) return void 0;
|
|
1702
|
+
const last = key.lastIndexOf("#");
|
|
1703
|
+
const kind = key.slice(first + 1, last);
|
|
1704
|
+
if (kind !== "b" && kind !== "st" && kind !== "sm") return void 0;
|
|
1705
|
+
const [rowIndex, colIndex] = key.slice(last + 1).split(":").map(Number);
|
|
1706
|
+
if (!Number.isFinite(rowIndex) || !Number.isFinite(colIndex)) return void 0;
|
|
1707
|
+
return { elementId: key.slice(0, first), kind, rowIndex, colIndex };
|
|
1708
|
+
}
|
|
1709
|
+
function cellFitWidthMm(colWidths, colIndex, cell, defaultPadding = 1) {
|
|
1710
|
+
const span = Math.max(cell?.colspan ?? 1, 1);
|
|
1711
|
+
let width = 0;
|
|
1712
|
+
for (let i = 0; i < span; i++) {
|
|
1713
|
+
width += colWidths[colIndex + i] ?? 0;
|
|
1714
|
+
}
|
|
1715
|
+
if (width <= 0) return 0;
|
|
1716
|
+
const padding = cell?.padding ?? defaultPadding;
|
|
1717
|
+
const borderMm = (ptToMm(cell?.borders?.left?.width ?? 0) + ptToMm(cell?.borders?.right?.width ?? 0)) / 2;
|
|
1718
|
+
return Math.max(width - padding * 2 - borderMm, 0.5);
|
|
1719
|
+
}
|
|
1720
|
+
function cellFitCapMm(rows, rowIndex, cell, defaultPadding = 1) {
|
|
1721
|
+
const span = Math.max(cell?.rowspan ?? 1, 1);
|
|
1722
|
+
let height = 0;
|
|
1723
|
+
for (let i = 0; i < span; i++) {
|
|
1724
|
+
height += rows[rowIndex + i]?.height ?? 8;
|
|
1725
|
+
}
|
|
1726
|
+
const padding = cell?.padding ?? defaultPadding;
|
|
1727
|
+
const borderMm = (ptToMm(cell?.borders?.top?.width ?? 0) + ptToMm(cell?.borders?.bottom?.width ?? 0)) / 2;
|
|
1728
|
+
return Math.max(height - padding * 2 - borderMm, 0.5);
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// src/render/watermark.ts
|
|
1732
|
+
var PX_PER_MM = 96 / 25.4;
|
|
1733
|
+
var MM_PER_PX = 25.4 / 96;
|
|
1734
|
+
var WATERMARK_DEFAULTS = {
|
|
1735
|
+
color: "#cccccc",
|
|
1736
|
+
opacity: 0.15,
|
|
1737
|
+
rotate: -30,
|
|
1738
|
+
/** 瓦片默认尺寸(px):决定平铺疏密,默认 260×180 */
|
|
1739
|
+
tileWidth: 260,
|
|
1740
|
+
tileHeight: 180,
|
|
1741
|
+
/** 瓦片下限(px),防止文字裁剪/异常平铺 */
|
|
1742
|
+
minTileWidth: 140,
|
|
1743
|
+
minTileHeight: 100,
|
|
1744
|
+
/** 瓦片字号固定 16px:密度只改变平铺疏密,不改字体大小 */
|
|
1745
|
+
fontSize: 16
|
|
1746
|
+
};
|
|
1747
|
+
var WATERMARK_DENSITY_PRESETS = {
|
|
1748
|
+
dense: { width: 200, height: 120, label: "\u5BC6" },
|
|
1749
|
+
medium: { width: 260, height: 180, label: "\u4E2D\uFF08\u9ED8\u8BA4\uFF09" },
|
|
1750
|
+
loose: { width: 340, height: 260, label: "\u758F" }
|
|
1751
|
+
};
|
|
1752
|
+
function clampTile(value, fallback, min2) {
|
|
1753
|
+
const n = typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
1754
|
+
return Math.max(min2, Math.round(n));
|
|
1755
|
+
}
|
|
1756
|
+
function resolveTileSize(wm) {
|
|
1757
|
+
return {
|
|
1758
|
+
width: clampTile(wm?.tileWidth, WATERMARK_DEFAULTS.tileWidth, WATERMARK_DEFAULTS.minTileWidth),
|
|
1759
|
+
height: clampTile(wm?.tileHeight, WATERMARK_DEFAULTS.tileHeight, WATERMARK_DEFAULTS.minTileHeight)
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
function isWatermarkVisible(wm) {
|
|
1763
|
+
if (!wm) return false;
|
|
1764
|
+
if (!wm.mode || wm.mode === "fixed") {
|
|
1765
|
+
return !!wm.content && wm.content.trim().length > 0;
|
|
1766
|
+
}
|
|
1767
|
+
return !!wm.binding && wm.binding.trim().length > 0;
|
|
1768
|
+
}
|
|
1769
|
+
function resolveWatermarkText(wm, printData, systemVars2) {
|
|
1770
|
+
if (!wm) return "";
|
|
1771
|
+
let text = "";
|
|
1772
|
+
if (!wm.mode || wm.mode === "fixed") {
|
|
1773
|
+
text = wm.content ?? "";
|
|
1774
|
+
} else if (typeof wm.binding === "string" && wm.binding.trim()) {
|
|
1775
|
+
const binding = wm.binding.trim();
|
|
1776
|
+
const data = Array.isArray(printData) ? printData[0] ?? {} : printData ?? {};
|
|
1777
|
+
const ctx = { ...resolveSystemVariables(), ...systemVars2, ...data };
|
|
1778
|
+
let resolved;
|
|
1779
|
+
if (binding.includes("{")) {
|
|
1780
|
+
resolved = evaluateTemplate(binding, ctx);
|
|
1781
|
+
} else {
|
|
1782
|
+
try {
|
|
1783
|
+
resolved = safeEval(binding, ctx);
|
|
1784
|
+
} catch {
|
|
1785
|
+
resolved = void 0;
|
|
1786
|
+
}
|
|
1787
|
+
if (resolved == null || typeof resolved === "object") {
|
|
1788
|
+
resolved = getByPath(data, binding);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
if (resolved != null && typeof resolved !== "object" && String(resolved) !== binding) {
|
|
1792
|
+
text = String(resolved);
|
|
1793
|
+
} else {
|
|
1794
|
+
text = wm.testData ?? `[${wm.binding}]`;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
if (wm.timestamp && text) {
|
|
1798
|
+
text = `${text} ${formatTimestamp(wm.format)}`;
|
|
1799
|
+
}
|
|
1800
|
+
return text;
|
|
1801
|
+
}
|
|
1802
|
+
function pad(n) {
|
|
1803
|
+
return String(n).padStart(2, "0");
|
|
1804
|
+
}
|
|
1805
|
+
function formatTimestamp(format) {
|
|
1806
|
+
const fmt = format && format.trim() ? format : "YYYY-MM-DD HH:mm";
|
|
1807
|
+
const d = /* @__PURE__ */ new Date();
|
|
1808
|
+
const map = {
|
|
1809
|
+
YYYY: String(d.getFullYear()),
|
|
1810
|
+
MM: pad(d.getMonth() + 1),
|
|
1811
|
+
DD: pad(d.getDate()),
|
|
1812
|
+
HH: pad(d.getHours()),
|
|
1813
|
+
mm: pad(d.getMinutes()),
|
|
1814
|
+
ss: pad(d.getSeconds())
|
|
1815
|
+
};
|
|
1816
|
+
return fmt.replace(/YYYY|MM|DD|HH|mm|ss/g, (k) => map[k] ?? k);
|
|
1817
|
+
}
|
|
1818
|
+
function round4(n) {
|
|
1819
|
+
return Math.round(n * 1e4) / 1e4;
|
|
1820
|
+
}
|
|
1821
|
+
function escXml(text) {
|
|
1822
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1823
|
+
}
|
|
1824
|
+
function resolveWatermarkLayout(wm, printData, paperMm, systemVars2) {
|
|
1825
|
+
if (!isWatermarkVisible(wm)) return null;
|
|
1826
|
+
const text = resolveWatermarkText(wm, printData, systemVars2);
|
|
1827
|
+
if (!text) return null;
|
|
1828
|
+
const tile = resolveTileSize(wm);
|
|
1829
|
+
const tileWidthMm = round4(tile.width * MM_PER_PX);
|
|
1830
|
+
const tileHeightMm = round4(tile.height * MM_PER_PX);
|
|
1831
|
+
const paper = {
|
|
1832
|
+
width: Math.max(0, Number.isFinite(paperMm?.width) ? paperMm.width : 0),
|
|
1833
|
+
height: Math.max(0, Number.isFinite(paperMm?.height) ? paperMm.height : 0)
|
|
1834
|
+
};
|
|
1835
|
+
const columns = Math.max(1, Math.ceil(paper.width / tileWidthMm));
|
|
1836
|
+
const rows = Math.max(1, Math.ceil(paper.height / tileHeightMm));
|
|
1837
|
+
const tiles = [];
|
|
1838
|
+
for (let r = 0; r < rows; r++) {
|
|
1839
|
+
for (let c = 0; c < columns; c++) {
|
|
1840
|
+
tiles.push({
|
|
1841
|
+
leftMm: round4(c * tileWidthMm),
|
|
1842
|
+
topMm: round4(r * tileHeightMm),
|
|
1843
|
+
widthMm: tileWidthMm,
|
|
1844
|
+
heightMm: tileHeightMm
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
return {
|
|
1849
|
+
text,
|
|
1850
|
+
color: wm?.color || WATERMARK_DEFAULTS.color,
|
|
1851
|
+
rotate: wm?.rotate ?? WATERMARK_DEFAULTS.rotate,
|
|
1852
|
+
opacity: wm?.opacity ?? WATERMARK_DEFAULTS.opacity,
|
|
1853
|
+
fontSizePx: WATERMARK_DEFAULTS.fontSize,
|
|
1854
|
+
tileWidthPx: tile.width,
|
|
1855
|
+
tileHeightPx: tile.height,
|
|
1856
|
+
tileWidthMm,
|
|
1857
|
+
tileHeightMm,
|
|
1858
|
+
columns,
|
|
1859
|
+
rows,
|
|
1860
|
+
tiles
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
function renderWatermarkTileSvg(layout, tile) {
|
|
1864
|
+
const tileWidthPx = layout.tileWidthPx;
|
|
1865
|
+
const tileHeightPx = layout.tileHeightPx;
|
|
1866
|
+
const cx = tileWidthPx / 2;
|
|
1867
|
+
const cy = tileHeightPx / 2;
|
|
1868
|
+
const style = `left:${tile.leftMm}mm;top:${tile.topMm}mm;width:${tile.widthMm}mm;height:${tile.heightMm}mm`;
|
|
1869
|
+
return `<svg class="watermark-tile" style="${style}" viewBox="0 0 ${tileWidthPx} ${tileHeightPx}" xmlns="http://www.w3.org/2000/svg"><text x="${cx}" y="${cy}" font-size="${layout.fontSizePx}" fill="${layout.color}" text-anchor="middle" dominant-baseline="middle" transform="rotate(${layout.rotate},${cx},${cy})">${escXml(layout.text)}</text></svg>`;
|
|
1870
|
+
}
|
|
1871
|
+
function renderWatermarkLayerHtml(wm, printData, paperMm, systemVars2) {
|
|
1872
|
+
const layout = resolveWatermarkLayout(wm, printData, paperMm, systemVars2);
|
|
1873
|
+
if (!layout) return "";
|
|
1874
|
+
const tiles = layout.tiles.map((t) => renderWatermarkTileSvg(layout, t)).join("\n");
|
|
1875
|
+
return `
|
|
1876
|
+
<div class="watermark-layer" style="opacity:${layout.opacity}">
|
|
1877
|
+
${tiles}
|
|
1878
|
+
</div>`;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1117
1881
|
// src/render/pagination-engine.ts
|
|
1118
1882
|
function paginationOf(el) {
|
|
1119
1883
|
return el.options?.pagination ?? el.pagination;
|
|
@@ -1134,7 +1898,7 @@ function tableDesignBottom(el) {
|
|
|
1134
1898
|
}
|
|
1135
1899
|
return top + (opts.height ?? 0);
|
|
1136
1900
|
}
|
|
1137
|
-
function buildFollowMap(sorted) {
|
|
1901
|
+
function buildFollowMap(sorted, excludedIds) {
|
|
1138
1902
|
const map = /* @__PURE__ */ new Map();
|
|
1139
1903
|
let currentTableId = null;
|
|
1140
1904
|
let currentTableBottom = -1;
|
|
@@ -1145,6 +1909,7 @@ function buildFollowMap(sorted) {
|
|
|
1145
1909
|
map.set(el.id, []);
|
|
1146
1910
|
continue;
|
|
1147
1911
|
}
|
|
1912
|
+
if (excludedIds?.has(el.id)) continue;
|
|
1148
1913
|
const top = el.options?.top ?? 0;
|
|
1149
1914
|
if (currentTableId && top >= currentTableBottom) {
|
|
1150
1915
|
map.get(currentTableId).push(el.id);
|
|
@@ -1162,28 +1927,110 @@ function followGroupHeight(el, members, template, _measuredElements) {
|
|
|
1162
1927
|
}
|
|
1163
1928
|
return Math.max(maxBottom - tableBottom, 0);
|
|
1164
1929
|
}
|
|
1930
|
+
function effectiveHeight(el, measured) {
|
|
1931
|
+
return Math.max(el.options?.height ?? 0, measured.get(el.id)?.measuredHeight ?? 0);
|
|
1932
|
+
}
|
|
1933
|
+
function buildPaginationUnits(sorted, measured, groupMap) {
|
|
1934
|
+
const unitOf = /* @__PURE__ */ new Map();
|
|
1935
|
+
for (const members of groupMap.values()) {
|
|
1936
|
+
if (members.length < 2) continue;
|
|
1937
|
+
const unit = {
|
|
1938
|
+
anchorId: members[0].id,
|
|
1939
|
+
ids: members.map((m) => m.id),
|
|
1940
|
+
forceFirstPage: members.some((m) => paginationOf(m)?.pageable === false)
|
|
1941
|
+
};
|
|
1942
|
+
for (const m of members) unitOf.set(m.id, unit);
|
|
1943
|
+
}
|
|
1944
|
+
const eligible = sorted.filter(
|
|
1945
|
+
(el) => !isTableEl(el) && !el.options?.groupId && paginationOf(el)?.pageable !== false
|
|
1946
|
+
);
|
|
1947
|
+
const parent = /* @__PURE__ */ new Map();
|
|
1948
|
+
eligible.forEach((el) => parent.set(el.id, el.id));
|
|
1949
|
+
const find = (id) => {
|
|
1950
|
+
let root = id;
|
|
1951
|
+
while (parent.get(root) !== root) root = parent.get(root);
|
|
1952
|
+
let cur = id;
|
|
1953
|
+
while (parent.get(cur) !== root) {
|
|
1954
|
+
const next = parent.get(cur);
|
|
1955
|
+
parent.set(cur, root);
|
|
1956
|
+
cur = next;
|
|
1957
|
+
}
|
|
1958
|
+
return root;
|
|
1959
|
+
};
|
|
1960
|
+
const union = (a, b) => {
|
|
1961
|
+
const ra = find(a);
|
|
1962
|
+
const rb = find(b);
|
|
1963
|
+
if (ra !== rb) parent.set(rb, ra);
|
|
1964
|
+
};
|
|
1965
|
+
const active = [];
|
|
1966
|
+
for (const el of eligible) {
|
|
1967
|
+
const top = el.options?.top ?? 0;
|
|
1968
|
+
for (let i = active.length - 1; i >= 0; i--) {
|
|
1969
|
+
if (active[i].bottom <= top) active.splice(i, 1);
|
|
1970
|
+
}
|
|
1971
|
+
const bottom = top + effectiveHeight(el, measured);
|
|
1972
|
+
for (const a of active) {
|
|
1973
|
+
if (a.bottom > top) union(el.id, a.id);
|
|
1974
|
+
}
|
|
1975
|
+
active.push({ id: el.id, top, bottom });
|
|
1976
|
+
}
|
|
1977
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
1978
|
+
for (const el of eligible) {
|
|
1979
|
+
const root = find(el.id);
|
|
1980
|
+
const arr = clusters.get(root);
|
|
1981
|
+
if (arr) arr.push(el.id);
|
|
1982
|
+
else clusters.set(root, [el.id]);
|
|
1983
|
+
}
|
|
1984
|
+
for (const ids of clusters.values()) {
|
|
1985
|
+
if (ids.length < 2) continue;
|
|
1986
|
+
const unit = { anchorId: ids[0], ids, forceFirstPage: false };
|
|
1987
|
+
for (const id of ids) unitOf.set(id, unit);
|
|
1988
|
+
}
|
|
1989
|
+
return unitOf;
|
|
1990
|
+
}
|
|
1165
1991
|
function paginate(template, measuredElements) {
|
|
1166
1992
|
const paper = getPaperDimensions(template);
|
|
1167
1993
|
const { top: mt, bottom: mb } = template.margins;
|
|
1168
1994
|
const headerH = template.header?.height ?? 0;
|
|
1169
1995
|
const footerH = template.footer?.height ?? 0;
|
|
1170
1996
|
const overlayH = template.firstPageOverlay?.height ?? 0;
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1997
|
+
const continuous = isContinuousPaper(template);
|
|
1998
|
+
const contentHeight = continuous ? Number.POSITIVE_INFINITY : paper.height - mt - mb - headerH - footerH;
|
|
1999
|
+
if (!continuous && contentHeight <= 0) {
|
|
1173
2000
|
throw new Error(
|
|
1174
2001
|
`\u9875\u9762\u53EF\u7528\u9AD8\u5EA6\u4E0D\u8DB3: paper=${paper.height}mm, margins=${mt + mb}mm, header=${headerH}mm, footer=${footerH}mm`
|
|
1175
2002
|
);
|
|
1176
2003
|
}
|
|
2004
|
+
const orderIndex = /* @__PURE__ */ new Map();
|
|
2005
|
+
template.elements.forEach((e, idx) => orderIndex.set(e.id, idx));
|
|
1177
2006
|
const sorted = [...template.elements].sort((a, b) => {
|
|
1178
2007
|
const topA = a.options?.top ?? 0;
|
|
1179
2008
|
const topB = b.options?.top ?? 0;
|
|
1180
|
-
return topA - topB;
|
|
2009
|
+
if (topA !== topB) return topA - topB;
|
|
2010
|
+
const zA = a.options?.zIndex ?? 0;
|
|
2011
|
+
const zB = b.options?.zIndex ?? 0;
|
|
2012
|
+
if (zA !== zB) return zA - zB;
|
|
2013
|
+
return (orderIndex.get(a.id) ?? 0) - (orderIndex.get(b.id) ?? 0);
|
|
1181
2014
|
});
|
|
1182
|
-
const
|
|
2015
|
+
const explicitGroupIds = /* @__PURE__ */ new Set();
|
|
2016
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
2017
|
+
for (const el of sorted) {
|
|
2018
|
+
if (isTableEl(el)) continue;
|
|
2019
|
+
const gid = el.options?.groupId;
|
|
2020
|
+
if (!gid) continue;
|
|
2021
|
+
explicitGroupIds.add(el.id);
|
|
2022
|
+
const arr = groupMap.get(gid);
|
|
2023
|
+
if (arr) arr.push(el);
|
|
2024
|
+
else groupMap.set(gid, [el]);
|
|
2025
|
+
}
|
|
2026
|
+
const followMap = buildFollowMap(sorted, explicitGroupIds);
|
|
1183
2027
|
const followOwner = /* @__PURE__ */ new Map();
|
|
1184
2028
|
for (const [tableId, members] of followMap) {
|
|
1185
2029
|
for (const m of members) followOwner.set(m, tableId);
|
|
1186
2030
|
}
|
|
2031
|
+
const elById = /* @__PURE__ */ new Map();
|
|
2032
|
+
for (const el of sorted) elById.set(el.id, el);
|
|
2033
|
+
const unitOf = buildPaginationUnits(sorted, measuredElements, groupMap);
|
|
1187
2034
|
const pages = [];
|
|
1188
2035
|
let currentPage = [];
|
|
1189
2036
|
let remaining = contentHeight - overlayH - SAFETY_MARGIN;
|
|
@@ -1195,12 +2042,26 @@ function paginate(template, measuredElements) {
|
|
|
1195
2042
|
function fullPageHeight() {
|
|
1196
2043
|
return isFirstPage ? contentHeight - overlayH - SAFETY_MARGIN : contentHeight - SAFETY_MARGIN;
|
|
1197
2044
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
2045
|
+
let overflowOnCurrent = false;
|
|
2046
|
+
function pushPage() {
|
|
2047
|
+
pages.push(
|
|
2048
|
+
overflowOnCurrent ? { pageIndex: pages.length, sections: [...currentPage], overflow: true } : { pageIndex: pages.length, sections: [...currentPage] }
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
function noteOverflow(bottom) {
|
|
2052
|
+
if (bottom > contentHeight) overflowOnCurrent = true;
|
|
2053
|
+
}
|
|
2054
|
+
function finishPage(overflow = false) {
|
|
2055
|
+
if (currentPage.length === 0) {
|
|
2056
|
+
overflowOnCurrent = overflowOnCurrent || overflow;
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
pushPage();
|
|
1200
2060
|
currentPage = [];
|
|
1201
2061
|
remaining = contentHeight - SAFETY_MARGIN;
|
|
1202
2062
|
isFirstPage = false;
|
|
1203
2063
|
pageBroken = true;
|
|
2064
|
+
overflowOnCurrent = false;
|
|
1204
2065
|
}
|
|
1205
2066
|
let i = 0;
|
|
1206
2067
|
while (i < sorted.length) {
|
|
@@ -1209,19 +2070,32 @@ function paginate(template, measuredElements) {
|
|
|
1209
2070
|
i++;
|
|
1210
2071
|
continue;
|
|
1211
2072
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
2073
|
+
const unit = unitOf.get(el.id);
|
|
2074
|
+
if (unit && unit.anchorId !== el.id) {
|
|
2075
|
+
i++;
|
|
2076
|
+
continue;
|
|
2077
|
+
}
|
|
2078
|
+
if (paginationOf(el)?.pageable === false || unit?.forceFirstPage) {
|
|
2079
|
+
if (unit) {
|
|
2080
|
+
for (const id of unit.ids) {
|
|
2081
|
+
currentPage.push({ elementId: id, type: "element", renderTop: elById.get(id)?.options?.top ?? 0 });
|
|
2082
|
+
}
|
|
2083
|
+
} else {
|
|
2084
|
+
currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
|
|
2085
|
+
}
|
|
1214
2086
|
i++;
|
|
1215
2087
|
continue;
|
|
1216
2088
|
}
|
|
1217
2089
|
if (isTableEl(el)) {
|
|
1218
2090
|
i = paginateTable(el, measuredElements, i);
|
|
2091
|
+
} else if (unit) {
|
|
2092
|
+
i = paginateUnit(unit, i);
|
|
1219
2093
|
} else {
|
|
1220
2094
|
i = paginateNonTable(el, measuredElements, sorted, i);
|
|
1221
2095
|
}
|
|
1222
2096
|
}
|
|
1223
2097
|
if (currentPage.length > 0) {
|
|
1224
|
-
|
|
2098
|
+
pushPage();
|
|
1225
2099
|
}
|
|
1226
2100
|
if (pages.length === 0) {
|
|
1227
2101
|
pages.push({ pageIndex: 0, sections: [] });
|
|
@@ -1230,6 +2104,8 @@ function paginate(template, measuredElements) {
|
|
|
1230
2104
|
function paginateNonTable(el, measured, sortedList, idx) {
|
|
1231
2105
|
const elHeight = measured.get(el.id)?.measuredHeight ?? el.options?.height ?? 0;
|
|
1232
2106
|
if (elHeight <= remaining) {
|
|
2107
|
+
const top2 = pageBroken ? 0 : el.options?.top ?? 0;
|
|
2108
|
+
noteOverflow(top2 + elHeight);
|
|
1233
2109
|
currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
|
|
1234
2110
|
remaining -= elHeight;
|
|
1235
2111
|
if (paginationOf(el)?.keepWithNext && idx + 1 < sortedList.length) {
|
|
@@ -1247,11 +2123,43 @@ function paginate(template, measuredElements) {
|
|
|
1247
2123
|
}
|
|
1248
2124
|
return idx + 1;
|
|
1249
2125
|
}
|
|
1250
|
-
|
|
2126
|
+
const top = pageBroken ? 0 : el.options?.top ?? 0;
|
|
2127
|
+
finishPage(top + elHeight > contentHeight);
|
|
1251
2128
|
currentPage.push({ elementId: el.id, type: "element", renderTop: sectionTop(el) });
|
|
1252
2129
|
remaining -= elHeight;
|
|
1253
2130
|
return idx + 1;
|
|
1254
2131
|
}
|
|
2132
|
+
function paginateUnit(unit, idx) {
|
|
2133
|
+
const members = unit.ids.map((id) => elById.get(id)).filter((m) => !!m);
|
|
2134
|
+
let minTop = Number.POSITIVE_INFINITY;
|
|
2135
|
+
let maxBottom = Number.NEGATIVE_INFINITY;
|
|
2136
|
+
for (const m of members) {
|
|
2137
|
+
const top2 = m.options?.top ?? 0;
|
|
2138
|
+
minTop = Math.min(minTop, top2);
|
|
2139
|
+
maxBottom = Math.max(maxBottom, top2 + effectiveHeight(m, measuredElements));
|
|
2140
|
+
}
|
|
2141
|
+
const unitHeight = Math.max(maxBottom - minTop, 0);
|
|
2142
|
+
const place = () => {
|
|
2143
|
+
const offset = pageBroken ? -minTop : 0;
|
|
2144
|
+
noteOverflow(minTop + offset + unitHeight);
|
|
2145
|
+
for (const m of members) {
|
|
2146
|
+
currentPage.push({
|
|
2147
|
+
elementId: m.id,
|
|
2148
|
+
type: "element",
|
|
2149
|
+
renderTop: (m.options?.top ?? 0) + offset
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
remaining -= unitHeight;
|
|
2153
|
+
};
|
|
2154
|
+
if (unitHeight <= remaining) {
|
|
2155
|
+
place();
|
|
2156
|
+
return idx + 1;
|
|
2157
|
+
}
|
|
2158
|
+
const top = pageBroken ? 0 : minTop;
|
|
2159
|
+
finishPage(top + unitHeight > contentHeight);
|
|
2160
|
+
place();
|
|
2161
|
+
return idx + 1;
|
|
2162
|
+
}
|
|
1255
2163
|
function paginateTable(el, measured, idx) {
|
|
1256
2164
|
const opts = el.options ?? {};
|
|
1257
2165
|
const renderRows = opts._renderRows ?? [];
|
|
@@ -1417,18 +2325,35 @@ function specialRowHeight(renderRows, rowHeights, type) {
|
|
|
1417
2325
|
return h;
|
|
1418
2326
|
}
|
|
1419
2327
|
|
|
2328
|
+
// src/render/barcode-dot.ts
|
|
2329
|
+
function barcodeAvailableBoxMm(boxMm, maxMm) {
|
|
2330
|
+
const box = typeof boxMm === "number" && boxMm > 0 ? boxMm : Number.POSITIVE_INFINITY;
|
|
2331
|
+
const limit = typeof maxMm === "number" && maxMm > 0 ? Math.min(box, maxMm) : box;
|
|
2332
|
+
return Number.isFinite(limit) ? limit : 0;
|
|
2333
|
+
}
|
|
2334
|
+
|
|
1420
2335
|
// src/render/html-generator.ts
|
|
2336
|
+
function pageVarsContext(vars) {
|
|
2337
|
+
return { ...vars.data ?? {}, pageIndex: vars.pageIndex, totalPages: vars.totalPages };
|
|
2338
|
+
}
|
|
2339
|
+
function pageVarsData(printData) {
|
|
2340
|
+
if (Array.isArray(printData)) return printData[0] ?? {};
|
|
2341
|
+
return printData ?? {};
|
|
2342
|
+
}
|
|
1421
2343
|
function generateHtml(template, pageLayouts, printData, options) {
|
|
1422
|
-
const css = buildPageCss(template);
|
|
2344
|
+
const css = buildFontFaceCss(template.fonts) + buildPageCss(template, options?.pageHeightMm);
|
|
1423
2345
|
const isMeasure = options?.isMeasurementPass === true;
|
|
1424
2346
|
const totalPages = isMeasure ? 1 : pageLayouts.length;
|
|
1425
|
-
const ctx = {
|
|
2347
|
+
const ctx = {
|
|
2348
|
+
codeRenderer: options?.codeRenderer,
|
|
2349
|
+
pageHeightMm: options?.pageHeightMm
|
|
2350
|
+
};
|
|
1426
2351
|
if (isMeasure) {
|
|
1427
|
-
return generateMeasurementHtml(template, css, ctx);
|
|
2352
|
+
return generateMeasurementHtml(template, css, ctx, printData);
|
|
1428
2353
|
}
|
|
1429
|
-
return generateFinalHtml(template, pageLayouts, css, totalPages, ctx);
|
|
2354
|
+
return generateFinalHtml(template, pageLayouts, css, totalPages, ctx, printData);
|
|
1430
2355
|
}
|
|
1431
|
-
function generateMeasurementHtml(template, css, ctx) {
|
|
2356
|
+
function generateMeasurementHtml(template, css, ctx, printData) {
|
|
1432
2357
|
const paper = getPaperDims(template);
|
|
1433
2358
|
const contentWidth = paper.width - template.margins.left - template.margins.right;
|
|
1434
2359
|
const elementsHtml = template.elements.map((el) => renderElement(el, true, void 0, void 0, ctx)).join("\n");
|
|
@@ -1450,6 +2375,7 @@ function generateMeasurementHtml(template, css, ctx) {
|
|
|
1450
2375
|
</head>
|
|
1451
2376
|
<body class="measure-mode">
|
|
1452
2377
|
<section class="print-page" data-measure-page="0">
|
|
2378
|
+
${renderWatermarkLayerHtml(template.watermark, printData, getPaperDims(template))}
|
|
1453
2379
|
<div class="page-header">${headerHtml}</div>
|
|
1454
2380
|
<div class="first-page-overlay">${overlayHtml}</div>
|
|
1455
2381
|
<div class="content-area" style="height:auto;overflow:visible;">
|
|
@@ -1461,54 +2387,111 @@ function generateMeasurementHtml(template, css, ctx) {
|
|
|
1461
2387
|
html = injectSystemVariables(html);
|
|
1462
2388
|
return html;
|
|
1463
2389
|
}
|
|
1464
|
-
function
|
|
2390
|
+
function renderFinalPages(template, pageLayouts, printData, ctx, options) {
|
|
2391
|
+
const pageOffset = options?.pageOffset ?? 0;
|
|
2392
|
+
const totalPages = options?.totalPages ?? pageLayouts.length;
|
|
1465
2393
|
const pagesHtml = pageLayouts.map((page) => {
|
|
1466
|
-
const pageNum = page.pageIndex + 1;
|
|
1467
|
-
return renderPage(template, page, pageNum, totalPages, ctx);
|
|
2394
|
+
const pageNum = page.pageIndex + 1 + pageOffset;
|
|
2395
|
+
return renderPage(template, page, pageNum, totalPages, ctx, printData, options?.pageClass);
|
|
1468
2396
|
}).join("\n");
|
|
1469
|
-
|
|
2397
|
+
return injectSystemVariables(pagesHtml);
|
|
2398
|
+
}
|
|
2399
|
+
function wrapHtmlDocument(css, bodyInnerHtml, bodyClass) {
|
|
2400
|
+
const html = `<!DOCTYPE html>
|
|
1470
2401
|
<html lang="zh-CN">
|
|
1471
2402
|
<head>
|
|
1472
2403
|
<meta charset="UTF-8">
|
|
1473
2404
|
<style>${css}</style>
|
|
1474
2405
|
</head>
|
|
1475
|
-
<body>
|
|
1476
|
-
${
|
|
2406
|
+
<body${bodyClass ? ` class="${bodyClass}"` : ""}>
|
|
2407
|
+
${bodyInnerHtml}
|
|
1477
2408
|
</body>
|
|
1478
2409
|
</html>`;
|
|
1479
|
-
|
|
1480
|
-
|
|
2410
|
+
return injectSystemVariables(html);
|
|
2411
|
+
}
|
|
2412
|
+
function generateFinalHtml(template, pageLayouts, css, _totalPages2, ctx, printData) {
|
|
2413
|
+
const bodyInner = renderFinalPages(template, pageLayouts, printData ?? {}, ctx);
|
|
2414
|
+
return wrapHtmlDocument(
|
|
2415
|
+
css,
|
|
2416
|
+
bodyInner,
|
|
2417
|
+
isContinuousPaper(template) ? "continuous" : void 0
|
|
2418
|
+
);
|
|
1481
2419
|
}
|
|
1482
|
-
function renderPage(template, page, pageNum, totalPages, ctx) {
|
|
2420
|
+
function renderPage(template, page, pageNum, totalPages, ctx, printData, pageClass) {
|
|
1483
2421
|
const paper = getPaperDims(template);
|
|
2422
|
+
const paperMm = {
|
|
2423
|
+
width: paper.width,
|
|
2424
|
+
height: ctx.pageHeightMm && ctx.pageHeightMm > 0 ? ctx.pageHeightMm : paper.height
|
|
2425
|
+
};
|
|
1484
2426
|
const contentWidth = paper.width - template.margins.left - template.margins.right;
|
|
2427
|
+
const pageVars = { pageIndex: pageNum, totalPages, data: pageVarsData(printData) };
|
|
2428
|
+
const scoped = withPageNumbers(template, pageVars);
|
|
2429
|
+
const pageCtx = { ...ctx, pageVars };
|
|
1485
2430
|
const headerHtml = renderAreaElements(
|
|
1486
|
-
|
|
2431
|
+
scoped.header?.elements ?? [],
|
|
1487
2432
|
contentWidth,
|
|
1488
2433
|
pageNum,
|
|
1489
2434
|
totalPages,
|
|
1490
|
-
|
|
2435
|
+
pageCtx
|
|
1491
2436
|
);
|
|
1492
2437
|
const footerHtml = renderAreaElements(
|
|
1493
|
-
|
|
2438
|
+
scoped.footer?.elements ?? [],
|
|
1494
2439
|
contentWidth,
|
|
1495
2440
|
pageNum,
|
|
1496
2441
|
totalPages,
|
|
1497
|
-
|
|
2442
|
+
pageCtx
|
|
1498
2443
|
);
|
|
1499
|
-
const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(
|
|
1500
|
-
let contentHtml = page.sections.map((section) => renderSection(section,
|
|
2444
|
+
const overlayHtml = page.pageIndex === 0 ? `<div class="first-page-overlay">${renderAreaElements(scoped.firstPageOverlay?.elements ?? [], contentWidth, pageNum, totalPages, pageCtx)}</div>` : "";
|
|
2445
|
+
let contentHtml = page.sections.map((section) => renderSection(section, scoped, pageCtx)).join("\n");
|
|
1501
2446
|
contentHtml = contentHtml.replace(/\{pageIndex\}/g, String(pageNum));
|
|
1502
2447
|
contentHtml = contentHtml.replace(/\{totalPages\}/g, String(totalPages));
|
|
1503
|
-
|
|
2448
|
+
const pageInner = `
|
|
2449
|
+
${renderWatermarkLayerHtml(template.watermark, printData, paperMm, { pageIndex: pageNum, totalPages })}
|
|
1504
2450
|
<div class="page-header">${headerHtml}</div>
|
|
1505
2451
|
${overlayHtml}
|
|
1506
2452
|
<div class="content-area">
|
|
1507
2453
|
${contentHtml}
|
|
1508
2454
|
</div>
|
|
1509
|
-
<div class="page-footer">${footerHtml}</div
|
|
2455
|
+
<div class="page-footer">${footerHtml}</div>`;
|
|
2456
|
+
const rot = getOutputRotationAngle(template);
|
|
2457
|
+
const pageBody = rot !== 0 ? `<div class="print-page-rotor print-page-rotor-${rot}">${pageInner}</div>` : pageInner;
|
|
2458
|
+
return `<section class="print-page${pageClass ? ` ${pageClass}` : ""}" data-page="${pageNum}">
|
|
2459
|
+
${pageBody}
|
|
1510
2460
|
</section>`;
|
|
1511
2461
|
}
|
|
2462
|
+
function withPageNumbers(template, vars) {
|
|
2463
|
+
if (!templateReferencesPageNumbers(template)) return template;
|
|
2464
|
+
const context = pageVarsContext(vars);
|
|
2465
|
+
const mapEl = (el) => {
|
|
2466
|
+
const raw = el.options?.rawFormatter;
|
|
2467
|
+
if (typeof raw !== "string") return el;
|
|
2468
|
+
return { ...el, options: { ...el.options, formatter: evaluateTemplate(raw, context) } };
|
|
2469
|
+
};
|
|
2470
|
+
const mapArea = (area) => area ? { ...area, elements: (area.elements ?? []).map(mapEl) } : area;
|
|
2471
|
+
return {
|
|
2472
|
+
...template,
|
|
2473
|
+
elements: template.elements.map(mapEl),
|
|
2474
|
+
header: mapArea(template.header),
|
|
2475
|
+
footer: mapArea(template.footer),
|
|
2476
|
+
firstPageOverlay: mapArea(template.firstPageOverlay)
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
function templateReferencesPageNumbers(template) {
|
|
2480
|
+
const areas = [
|
|
2481
|
+
template.elements,
|
|
2482
|
+
template.header?.elements,
|
|
2483
|
+
template.footer?.elements,
|
|
2484
|
+
template.firstPageOverlay?.elements
|
|
2485
|
+
];
|
|
2486
|
+
return areas.some((list) => (list ?? []).some((el) => typeof el.options?.rawFormatter === "string"));
|
|
2487
|
+
}
|
|
2488
|
+
function resolveCellContent(cell, ctx) {
|
|
2489
|
+
const raw = cell.rawFormatter;
|
|
2490
|
+
if (typeof raw === "string" && raw !== "" && ctx?.pageVars) {
|
|
2491
|
+
return evaluateTemplate(raw, pageVarsContext(ctx.pageVars));
|
|
2492
|
+
}
|
|
2493
|
+
return cell.content;
|
|
2494
|
+
}
|
|
1512
2495
|
function renderSection(section, template, ctx) {
|
|
1513
2496
|
const el = findElement(template, section.elementId);
|
|
1514
2497
|
if (!el) {
|
|
@@ -1526,8 +2509,9 @@ var V_ALIGN_FLEX = { top: "flex-start", middle: "center", bottom: "flex-end" };
|
|
|
1526
2509
|
var H_ALIGN_FLEX = { left: "flex-start", center: "center", right: "flex-end" };
|
|
1527
2510
|
function textStyle(opts) {
|
|
1528
2511
|
const parts = [];
|
|
1529
|
-
|
|
1530
|
-
if (
|
|
2512
|
+
const fontSize = opts._fitFontSize === void 0 ? opts.fontSize : roundFontSize(opts._fitFontSize);
|
|
2513
|
+
if (fontSize) parts.push(`font-size:${fontSize}pt`);
|
|
2514
|
+
if (opts.fontFamily) parts.push(`font-family:${escapeInlineStyleValue(toFontFamilyStack(opts.fontFamily))}`);
|
|
1531
2515
|
if (opts.fontWeight) parts.push(`font-weight:${opts.fontWeight}`);
|
|
1532
2516
|
if (opts.color) parts.push(`color:${opts.color}`);
|
|
1533
2517
|
if (opts.backgroundColor) parts.push(`background-color:${opts.backgroundColor}`);
|
|
@@ -1540,15 +2524,28 @@ function textStyle(opts) {
|
|
|
1540
2524
|
if (opts.textAlign) parts.push(`text-align:${opts.textAlign}`);
|
|
1541
2525
|
return parts.length ? parts.join(";") + ";" : "";
|
|
1542
2526
|
}
|
|
2527
|
+
function elementFitStyle(fit, opts) {
|
|
2528
|
+
if (fit === "autoHeight") return "overflow:visible;";
|
|
2529
|
+
if (opts.wordWrap === false) return "white-space:nowrap;text-overflow:ellipsis;";
|
|
2530
|
+
return "";
|
|
2531
|
+
}
|
|
2532
|
+
function fitAttrs(fit, key, baseFontSizePt, opts) {
|
|
2533
|
+
if (fit !== "shrink") return "";
|
|
2534
|
+
const min2 = resolveShrinkMinFontSize(opts.shrinkMinFontSize);
|
|
2535
|
+
return ` data-fit="shrink" data-fit-key="${esc(key)}" data-fit-base="${baseFontSizePt}" data-fit-min="${min2}"`;
|
|
2536
|
+
}
|
|
1543
2537
|
function renderElement(el, isMeasure, containerStyle, overrideTop, ctx) {
|
|
1544
2538
|
const opts = el.options ?? {};
|
|
1545
2539
|
const left = opts.left ?? 0;
|
|
1546
2540
|
const top = overrideTop ?? opts.top ?? 0;
|
|
1547
2541
|
const width = opts.width ?? 100;
|
|
1548
2542
|
const height = opts.height ?? void 0;
|
|
1549
|
-
const style = containerStyle ?? elementPositionStyle(left, top, width, height, opts.zIndex);
|
|
1550
|
-
const measureAttr = isMeasure ? ` data-measure-id="${el.id}"` : "";
|
|
1551
2543
|
const type = el.type || el.printElementType?.type || "text";
|
|
2544
|
+
const fit = resolveElementTextFit(type, opts);
|
|
2545
|
+
const fitHeight = fit === "autoHeight" ? void 0 : height;
|
|
2546
|
+
const style = containerStyle ?? elementPositionStyle(left, top, width, fitHeight, opts.zIndex);
|
|
2547
|
+
const measureAttr = isMeasure ? ` data-measure-id="${el.id}"` : "";
|
|
2548
|
+
const fitAttr = fitAttrs(fit, el.id, opts.fontSize ?? 12, opts);
|
|
1552
2549
|
switch (type) {
|
|
1553
2550
|
case "table":
|
|
1554
2551
|
return renderTableElement(el, isMeasure, measureAttr, ctx);
|
|
@@ -1559,9 +2556,17 @@ function renderElement(el, isMeasure, containerStyle, overrideTop, ctx) {
|
|
|
1559
2556
|
case "barcode":
|
|
1560
2557
|
case "qrcode": {
|
|
1561
2558
|
const codeValue = String(opts.formatter ?? opts.testData ?? "").trim();
|
|
1562
|
-
const fill = type === "barcode";
|
|
1563
2559
|
return `<div class="print-element" style="${style}"${measureAttr}>
|
|
1564
|
-
${codeImgHtml(codeValue, type, opts,
|
|
2560
|
+
${codeImgHtml(codeValue, type, opts, {
|
|
2561
|
+
fallback: `<span>${esc(codeValue)}</span>`,
|
|
2562
|
+
codeRenderer: ctx?.codeRenderer,
|
|
2563
|
+
fit: opts.fit,
|
|
2564
|
+
maxWidth: opts.maxWidth,
|
|
2565
|
+
maxHeight: opts.maxHeight,
|
|
2566
|
+
printerDpi: opts.printerDpi,
|
|
2567
|
+
targetWidthMm: opts.width,
|
|
2568
|
+
targetHeightMm: opts.height
|
|
2569
|
+
})}
|
|
1565
2570
|
</div>`;
|
|
1566
2571
|
}
|
|
1567
2572
|
case "hline":
|
|
@@ -1573,30 +2578,32 @@ function renderElement(el, isMeasure, containerStyle, overrideTop, ctx) {
|
|
|
1573
2578
|
case "oval":
|
|
1574
2579
|
return `<div class="print-element" style="${style};border:${opts.borderWidth ?? 1}px solid ${opts.borderColor ?? "#000"};border-radius:50%;"${measureAttr}></div>`;
|
|
1575
2580
|
case "longText":
|
|
1576
|
-
return `<div class="print-element" style="${style}${textStyle(opts)}
|
|
2581
|
+
return `<div class="print-element" style="${style}${textStyle(opts)}${elementFitStyle(fit, opts)}"${measureAttr}${fitAttr}>${esc(opts.formatter ?? opts.testData ?? "")}</div>`;
|
|
1577
2582
|
case "html":
|
|
1578
2583
|
return `<div class="print-element" style="${style}"${measureAttr}>${opts.testData ?? opts.title ?? ""}</div>`;
|
|
1579
2584
|
default:
|
|
1580
|
-
return `<div class="print-element" style="${style}${textStyle(opts)}"${measureAttr}>${esc(opts.formatter ?? opts.testData ?? "")}</div>`;
|
|
2585
|
+
return `<div class="print-element" style="${style}${textStyle(opts)}${elementFitStyle(fit, opts)}"${measureAttr}${fitAttr}>${esc(opts.formatter ?? opts.testData ?? "")}</div>`;
|
|
1581
2586
|
}
|
|
1582
2587
|
}
|
|
1583
|
-
function codeImgHtml(value, cellType, opts,
|
|
1584
|
-
if (!value || !codeRenderer) return
|
|
2588
|
+
function codeImgHtml(value, cellType, opts, io) {
|
|
2589
|
+
if (!value || !io.codeRenderer) return io.fallback;
|
|
2590
|
+
const { fallback, codeRenderer, fit, maxWidth, maxHeight } = io;
|
|
2591
|
+
const isBarcode = cellType === "barcode";
|
|
1585
2592
|
try {
|
|
1586
2593
|
const svg = codeRenderer.render(value, cellType, {
|
|
1587
2594
|
barcodeType: opts.barcodeType,
|
|
1588
2595
|
qrCodeLevel: opts.qrCodeLevel != null ? String(opts.qrCodeLevel) : void 0,
|
|
1589
2596
|
showText: opts.hideTitle !== void 0 ? !opts.hideTitle : opts.showBarcodeText,
|
|
1590
2597
|
barWidth: typeof opts.barWidth === "number" ? opts.barWidth : void 0,
|
|
1591
|
-
fontSize: typeof opts.fontSize === "number" ? opts.fontSize : void 0
|
|
2598
|
+
fontSize: typeof opts.fontSize === "number" ? opts.fontSize : void 0,
|
|
2599
|
+
printerDpi: isBarcode ? io.printerDpi : void 0,
|
|
2600
|
+
targetWidthMm: isBarcode ? barcodeAvailableBoxMm(io.targetWidthMm, maxWidth) : void 0,
|
|
2601
|
+
targetHeightMm: isBarcode ? barcodeAvailableBoxMm(io.targetHeightMm, maxHeight) : void 0
|
|
1592
2602
|
});
|
|
2603
|
+
if (isBarcode && /^<svg[\s>]/i.test(svg)) return inlineCodeSvgHtml(svg);
|
|
1593
2604
|
const src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
1594
2605
|
const styleParts = [];
|
|
1595
|
-
|
|
1596
|
-
styleParts.push("width:100%", "height:100%", "object-fit:contain", "display:block", "margin:auto");
|
|
1597
|
-
} else {
|
|
1598
|
-
styleParts.push("max-width:100%", "max-height:100%", "display:block", "margin:auto");
|
|
1599
|
-
}
|
|
2606
|
+
styleParts.push("max-width:100%", "max-height:100%", "display:block", "margin:auto");
|
|
1600
2607
|
if (fit) {
|
|
1601
2608
|
styleParts.push(`object-fit:${fit}`);
|
|
1602
2609
|
}
|
|
@@ -1609,15 +2616,20 @@ function codeImgHtml(value, cellType, opts, fallbackHtml, fill = false, codeRend
|
|
|
1609
2616
|
const style = styleParts.join(";");
|
|
1610
2617
|
return `<img src="${src}" style="${style}" />`;
|
|
1611
2618
|
} catch {
|
|
1612
|
-
return
|
|
2619
|
+
return fallback;
|
|
1613
2620
|
}
|
|
1614
2621
|
}
|
|
2622
|
+
function inlineCodeSvgHtml(svg) {
|
|
2623
|
+
const styled = svg.replace(/<svg\b/, '<svg style="display:block"');
|
|
2624
|
+
return `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;">${styled}</div>`;
|
|
2625
|
+
}
|
|
1615
2626
|
function matrixCellStyle(cell, opts) {
|
|
1616
2627
|
const parts = [];
|
|
1617
|
-
const fontSize = cell.fontSize ?? opts.tableDefaultFontSize;
|
|
2628
|
+
const fontSize = cell.fittedFontSize ?? cell.fontSize ?? opts.tableDefaultFontSize;
|
|
1618
2629
|
const color = cell.color ?? opts.tableDefaultColor;
|
|
1619
2630
|
const padding = cell.padding ?? opts.tableDefaultPadding ?? 1;
|
|
1620
2631
|
if (fontSize) parts.push(`font-size:${fontSize}pt`);
|
|
2632
|
+
if (cell.fontFamily) parts.push(`font-family:${escapeInlineStyleValue(toFontFamilyStack(cell.fontFamily))}`);
|
|
1621
2633
|
if (cell.fontWeight) parts.push(`font-weight:${cell.fontWeight}`);
|
|
1622
2634
|
if (color) parts.push(`color:${color}`);
|
|
1623
2635
|
if (cell.backgroundColor) parts.push(`background-color:${cell.backgroundColor}`);
|
|
@@ -1632,25 +2644,38 @@ function matrixCellStyle(cell, opts) {
|
|
|
1632
2644
|
}
|
|
1633
2645
|
return parts.join(";");
|
|
1634
2646
|
}
|
|
1635
|
-
function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx) {
|
|
2647
|
+
function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx, fitOwner = { elementId: "", kind: "b" }, rowLimit) {
|
|
1636
2648
|
const trs = [];
|
|
2649
|
+
const defaultPadding = opts.tableDefaultPadding ?? 1;
|
|
1637
2650
|
for (let r = start; r < end; r++) {
|
|
1638
2651
|
const row = renderRows[r];
|
|
1639
2652
|
if (!row) continue;
|
|
1640
2653
|
const idxAttr = withRowIndex ? ` data-row-index="${r}"` : "";
|
|
1641
|
-
const tds = row.cells.filter((cell) => !cell.merged).map((cell) => {
|
|
1642
|
-
const
|
|
2654
|
+
const tds = row.cells.map((cell, ci) => ({ cell, ci })).filter(({ cell }) => !cell.merged).map(({ cell, ci }) => {
|
|
2655
|
+
const rowspan = rowLimit === void 0 ? cell.rowspan ?? 1 : Math.max(1, Math.min(cell.rowspan ?? 1, rowLimit - r));
|
|
2656
|
+
const span = `${rowspan > 1 ? ` rowspan="${rowspan}"` : ""}${cell.colspan > 1 ? ` colspan="${cell.colspan}"` : ""}`;
|
|
2657
|
+
const colIndex = ci;
|
|
1643
2658
|
let inner;
|
|
1644
2659
|
if (cell.cellType === "barcode" || cell.cellType === "qrcode") {
|
|
1645
|
-
const
|
|
1646
|
-
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;">${codeImgHtml(
|
|
2660
|
+
const codeValue = resolveCellContent(cell, ctx);
|
|
2661
|
+
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;">${codeImgHtml(codeValue, cell.cellType, cell, {
|
|
2662
|
+
fallback: esc(codeValue),
|
|
2663
|
+
codeRenderer: ctx?.codeRenderer,
|
|
2664
|
+
fit: cell.fit,
|
|
2665
|
+
maxWidth: cell.maxWidth,
|
|
2666
|
+
maxHeight: cell.maxHeight,
|
|
2667
|
+
printerDpi: cell.printerDpi,
|
|
2668
|
+
// 可用框 = 单元格内容区(列宽/行高扣除内边距与塌陷边框);列宽缺失时宽度为 0 → 不约束宽度
|
|
2669
|
+
targetWidthMm: cellFitWidthMm(opts.tableColWidths ?? [], ci, cell, defaultPadding),
|
|
2670
|
+
targetHeightMm: cellFitCapMm(renderRows, r, cell, defaultPadding)
|
|
2671
|
+
})}</div>`;
|
|
1647
2672
|
} else if (cell.cellType === "image") {
|
|
1648
2673
|
const fit = cell.fit || "contain";
|
|
1649
2674
|
const maxWidth = cell.maxWidth ? `max-width:${cell.maxWidth}mm;` : "max-width:100%;";
|
|
1650
2675
|
const maxHeight = cell.maxHeight ? `max-height:${cell.maxHeight}mm;` : "max-height:100%;";
|
|
1651
|
-
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;"><img src="${esc(cell
|
|
2676
|
+
inner = `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;"><img src="${esc(resolveCellContent(cell, ctx))}" style="object-fit:${fit};${maxWidth}${maxHeight}display:block;" /></div>`;
|
|
1652
2677
|
} else {
|
|
1653
|
-
inner =
|
|
2678
|
+
inner = renderTextCellInner(renderRows, r, colIndex, cell, opts, fitOwner, defaultPadding, ctx);
|
|
1654
2679
|
}
|
|
1655
2680
|
return `<td${span} style="${matrixCellStyle(cell, opts)}">${inner}</td>`;
|
|
1656
2681
|
}).join("");
|
|
@@ -1658,6 +2683,20 @@ function renderMatrixRows(renderRows, start, end, opts, withRowIndex, ctx) {
|
|
|
1658
2683
|
}
|
|
1659
2684
|
return trs.join("\n");
|
|
1660
2685
|
}
|
|
2686
|
+
function renderTextCellInner(renderRows, rowIndex, colIndex, cell, opts, fitOwner, defaultPadding, ctx) {
|
|
2687
|
+
const fit = resolveCellTextFit(cell);
|
|
2688
|
+
const text = esc(resolveCellContent(cell, ctx));
|
|
2689
|
+
if (fit === "autoHeight") return text;
|
|
2690
|
+
const capMm = cellFitCapMm(renderRows, rowIndex, cell, defaultPadding);
|
|
2691
|
+
const nowrap = cell.wordWrap === false;
|
|
2692
|
+
const style = [
|
|
2693
|
+
`max-height:${capMm}mm`,
|
|
2694
|
+
"overflow:hidden",
|
|
2695
|
+
...nowrap ? ["white-space:nowrap", "text-overflow:ellipsis"] : []
|
|
2696
|
+
].join(";");
|
|
2697
|
+
const attrs = fit === "shrink" && fitOwner.elementId ? ` data-fit="shrink" data-fit-key="${esc(cellFitKey(fitOwner.elementId, fitOwner.kind, rowIndex, colIndex))}" data-fit-base="${cell.fontSize ?? opts.tableDefaultFontSize ?? 12}" data-fit-min="${resolveShrinkMinFontSize(cell.shrinkMinFontSize)}" data-fit-mm="${capMm}"` : "";
|
|
2698
|
+
return `<div class="cell-fit" style="${style}"${attrs}>${text}</div>`;
|
|
2699
|
+
}
|
|
1661
2700
|
function matrixTableHtml(el, bodyHtml) {
|
|
1662
2701
|
const opts = el.options;
|
|
1663
2702
|
const colWidths = opts.tableColWidths ?? [];
|
|
@@ -1672,7 +2711,7 @@ function renderTableElement(el, isMeasure, measureAttr, ctx) {
|
|
|
1672
2711
|
const opts = el.options;
|
|
1673
2712
|
const style = elementPositionStyle(opts.left ?? 0, opts.top ?? 0, opts.width ?? 100, void 0, opts.zIndex);
|
|
1674
2713
|
const renderRows = opts._renderRows ?? [];
|
|
1675
|
-
const bodyHtml = renderMatrixRows(renderRows, 0, renderRows.length, opts, isMeasure, ctx);
|
|
2714
|
+
const bodyHtml = renderMatrixRows(renderRows, 0, renderRows.length, opts, isMeasure, ctx, { elementId: el.id, kind: "b" });
|
|
1676
2715
|
return `<div class="print-element" style="${style};overflow:visible;"${measureAttr}>
|
|
1677
2716
|
${matrixTableHtml(el, bodyHtml)}
|
|
1678
2717
|
</div>`;
|
|
@@ -1684,8 +2723,8 @@ function renderTableSlice(el, section, ctx) {
|
|
|
1684
2723
|
const startRow = section.startRow ?? 0;
|
|
1685
2724
|
const endRow = section.endRow ?? renderRows.length;
|
|
1686
2725
|
const repeatCount = opts._repeatHeaderCount ?? 0;
|
|
1687
|
-
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx) : "";
|
|
1688
|
-
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx);
|
|
2726
|
+
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }, repeatCount) : "";
|
|
2727
|
+
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx, { elementId: el.id, kind: "b" });
|
|
1689
2728
|
const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
|
|
1690
2729
|
const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
|
|
1691
2730
|
return `<div class="print-element" style="${style};overflow:visible;">
|
|
@@ -1705,16 +2744,22 @@ function renderSubtotalRows(el, section, opts, ctx) {
|
|
|
1705
2744
|
const dataStart = Math.max(startRow, dataStartIdx);
|
|
1706
2745
|
const dataEnd = Math.max(endRow, dataStart);
|
|
1707
2746
|
const pageCtx = dataEnd > dataStartIdx ? dataRowCtx.slice(Math.max(dataStart - dataStartIdx, 0), dataEnd - dataStartIdx) : [];
|
|
2747
|
+
const pageVars = ctx?.pageVars ? pageVarsContext(ctx.pageVars) : {};
|
|
1708
2748
|
const rows = templates.map((tpl) => ({
|
|
1709
2749
|
...tpl,
|
|
1710
|
-
cells: tpl.cells.map((cell) => cell.rawFormatter ? {
|
|
2750
|
+
cells: tpl.cells.map((cell) => cell.rawFormatter ? {
|
|
2751
|
+
...cell,
|
|
2752
|
+
content: evaluateTemplate(cell.rawFormatter, { rows: pageCtx, ...mainData, ...pageVars }),
|
|
2753
|
+
// 已按本页上下文求值,清标记避免渲染时再按页码重算一次(会丢掉 rows)
|
|
2754
|
+
rawFormatter: ""
|
|
2755
|
+
} : cell)
|
|
1711
2756
|
}));
|
|
1712
|
-
return renderMatrixRows(rows, 0, rows.length, opts, false, ctx);
|
|
2757
|
+
return renderMatrixRows(rows, 0, rows.length, opts, false, ctx, { elementId: el.id, kind: "st" });
|
|
1713
2758
|
}
|
|
1714
2759
|
function renderSummaryRows(el, opts, ctx) {
|
|
1715
2760
|
const summaryRows = opts._summaryRows ?? [];
|
|
1716
2761
|
if (summaryRows.length === 0) return "";
|
|
1717
|
-
return renderMatrixRows(summaryRows, 0, summaryRows.length, opts, false, ctx);
|
|
2762
|
+
return renderMatrixRows(summaryRows, 0, summaryRows.length, opts, false, ctx, { elementId: el.id, kind: "sm" });
|
|
1718
2763
|
}
|
|
1719
2764
|
function renderFlowGroup(el, section, template, ctx) {
|
|
1720
2765
|
const opts = el.options ?? {};
|
|
@@ -1730,8 +2775,8 @@ function renderFlowGroup(el, section, template, ctx) {
|
|
|
1730
2775
|
if (endRow > startRow || section.subtotal || section.summary) {
|
|
1731
2776
|
const renderRows = opts._renderRows ?? [];
|
|
1732
2777
|
const repeatCount = opts._repeatHeaderCount ?? 0;
|
|
1733
|
-
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx) : "";
|
|
1734
|
-
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx);
|
|
2778
|
+
const repeatHtml = section.repeatHeader && repeatCount > 0 ? renderMatrixRows(renderRows, 0, repeatCount, opts, false, ctx, { elementId: el.id, kind: "b" }, repeatCount) : "";
|
|
2779
|
+
const bodyHtml = renderMatrixRows(renderRows, startRow, endRow, opts, false, ctx, { elementId: el.id, kind: "b" });
|
|
1735
2780
|
const subtotalHtml = section.subtotal ? renderSubtotalRows(el, section, opts, ctx) : "";
|
|
1736
2781
|
const summaryHtml = section.summary ? renderSummaryRows(el, opts, ctx) : "";
|
|
1737
2782
|
sliceHtml = `<div class="flow-slice" style="position:relative;width:${mm(tableWidth)};overflow:visible;">
|
|
@@ -1799,6 +2844,936 @@ function esc(str) {
|
|
|
1799
2844
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1800
2845
|
}
|
|
1801
2846
|
|
|
2847
|
+
// src/render/continuous-paper.ts
|
|
2848
|
+
var MIN_CONTINUOUS_HEIGHT_MM = 25.4;
|
|
2849
|
+
function composeContinuousHeight(template, contentBottomMm) {
|
|
2850
|
+
const mb = template.margins?.bottom ?? 0;
|
|
2851
|
+
const footerH = template.footer?.height ?? 0;
|
|
2852
|
+
const height = Math.max(contentBottomMm, 0) + footerH + mb;
|
|
2853
|
+
return Math.max(MIN_CONTINUOUS_HEIGHT_MM, Math.round(height * 100) / 100);
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
// src/print/units.ts
|
|
2857
|
+
var PX_PER_MM2 = 3.7795275591;
|
|
2858
|
+
var MICROMETERS_PER_MM = 1e3;
|
|
2859
|
+
var MM_PER_INCH = 25.4;
|
|
2860
|
+
var MICROMETERS_PER_INCH = 25400;
|
|
2861
|
+
function pxToMm(px) {
|
|
2862
|
+
return px / PX_PER_MM2;
|
|
2863
|
+
}
|
|
2864
|
+
function mmToPx(mm2) {
|
|
2865
|
+
return Math.round(mm2 * PX_PER_MM2);
|
|
2866
|
+
}
|
|
2867
|
+
function millimetersToMicrometers(mm2) {
|
|
2868
|
+
return Math.round(mm2 * MICROMETERS_PER_MM);
|
|
2869
|
+
}
|
|
2870
|
+
function micrometersToMillimeters(um) {
|
|
2871
|
+
return um / MICROMETERS_PER_MM;
|
|
2872
|
+
}
|
|
2873
|
+
function millimetersToInches(mm2) {
|
|
2874
|
+
return mm2 / MM_PER_INCH;
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
// src/print/errors.ts
|
|
2878
|
+
var PrintFailure = class extends Error {
|
|
2879
|
+
constructor(code, message, cause) {
|
|
2880
|
+
super(message);
|
|
2881
|
+
this.name = "PrintFailure";
|
|
2882
|
+
this.code = code;
|
|
2883
|
+
this.cause = cause;
|
|
2884
|
+
}
|
|
2885
|
+
};
|
|
2886
|
+
function toPrintFailure(err, fallbackCode, context) {
|
|
2887
|
+
if (err instanceof PrintFailure) return err;
|
|
2888
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2889
|
+
return new PrintFailure(fallbackCode, `${context}\uFF1A${detail}`, err);
|
|
2890
|
+
}
|
|
2891
|
+
async function withTimeout(task, ms, code, message) {
|
|
2892
|
+
let timer;
|
|
2893
|
+
try {
|
|
2894
|
+
return await Promise.race([
|
|
2895
|
+
task,
|
|
2896
|
+
new Promise((_resolve, reject) => {
|
|
2897
|
+
timer = setTimeout(() => reject(new PrintFailure(code, message)), ms);
|
|
2898
|
+
})
|
|
2899
|
+
]);
|
|
2900
|
+
} finally {
|
|
2901
|
+
if (timer) clearTimeout(timer);
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
// src/print/paper.ts
|
|
2906
|
+
function positive(value) {
|
|
2907
|
+
return typeof value === "number" && value > 0 ? value : void 0;
|
|
2908
|
+
}
|
|
2909
|
+
function escapeHeightMm(input) {
|
|
2910
|
+
return positive(input.paperHeightMm) ?? positive(input.override?.height);
|
|
2911
|
+
}
|
|
2912
|
+
function resolvePaperMm(input) {
|
|
2913
|
+
const width = positive(input.override?.width) ?? input.paperMm.width;
|
|
2914
|
+
const overrideHeight = positive(input.override?.height);
|
|
2915
|
+
if (input.continuous) {
|
|
2916
|
+
return overrideHeight ? { paperMm: { width, height: overrideHeight }, heightSource: "config" } : { paperMm: { width, height: input.paperMm.height }, heightSource: "derived" };
|
|
2917
|
+
}
|
|
2918
|
+
return { paperMm: { width, height: overrideHeight ?? input.paperMm.height }, heightSource: "config" };
|
|
2919
|
+
}
|
|
2920
|
+
function paperViewportPx(paper) {
|
|
2921
|
+
return { width: mmToPx(paper.width), height: mmToPx(paper.height) };
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
// src/print/pdf-spec.ts
|
|
2925
|
+
var ZERO_MARGINS_MM = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
2926
|
+
function buildPdfTargetSpec(paperMm) {
|
|
2927
|
+
return {
|
|
2928
|
+
paperMm,
|
|
2929
|
+
marginsMm: { ...ZERO_MARGINS_MM },
|
|
2930
|
+
printBackground: true,
|
|
2931
|
+
scale: 1,
|
|
2932
|
+
preferCSSPageSize: false
|
|
2933
|
+
};
|
|
2934
|
+
}
|
|
2935
|
+
function toElectronPrintToPdfOptions(spec) {
|
|
2936
|
+
return {
|
|
2937
|
+
margins: { ...spec.marginsMm },
|
|
2938
|
+
pageSize: {
|
|
2939
|
+
width: millimetersToInches(spec.paperMm.width),
|
|
2940
|
+
height: millimetersToInches(spec.paperMm.height)
|
|
2941
|
+
},
|
|
2942
|
+
printBackground: spec.printBackground,
|
|
2943
|
+
scale: spec.scale,
|
|
2944
|
+
preferCSSPageSize: spec.preferCSSPageSize
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
function toPlaywrightPdfOptions(spec) {
|
|
2948
|
+
const mm2 = (value) => `${value}mm`;
|
|
2949
|
+
return {
|
|
2950
|
+
width: mm2(spec.paperMm.width),
|
|
2951
|
+
height: mm2(spec.paperMm.height),
|
|
2952
|
+
margin: {
|
|
2953
|
+
top: mm2(spec.marginsMm.top),
|
|
2954
|
+
right: mm2(spec.marginsMm.right),
|
|
2955
|
+
bottom: mm2(spec.marginsMm.bottom),
|
|
2956
|
+
left: mm2(spec.marginsMm.left)
|
|
2957
|
+
},
|
|
2958
|
+
printBackground: spec.printBackground,
|
|
2959
|
+
preferCSSPageSize: spec.preferCSSPageSize
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
function buildScreenshotTargetSpec() {
|
|
2963
|
+
return { type: "png", fullPage: true, omitBackground: false };
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
// src/print/measure.ts
|
|
2967
|
+
function normalizeMeasurements(raw, template) {
|
|
2968
|
+
const index = new Map(template.elements.map((el) => [el.id, el]));
|
|
2969
|
+
const measured = /* @__PURE__ */ new Map();
|
|
2970
|
+
for (const item of raw) {
|
|
2971
|
+
const element = index.get(item.id);
|
|
2972
|
+
const repeatCount = element?.options?._repeatHeaderCount ?? 0;
|
|
2973
|
+
const rowHeights = item.rowHeightsPx?.map(pxToMm);
|
|
2974
|
+
measured.set(item.id, {
|
|
2975
|
+
id: item.id,
|
|
2976
|
+
measuredHeight: pxToMm(item.heightPx),
|
|
2977
|
+
measuredRowHeights: rowHeights,
|
|
2978
|
+
repeatHeaderHeight: rowHeights && rowHeights.length > 0 && repeatCount > 0 ? rowHeights.slice(0, repeatCount).reduce((sum2, h) => sum2 + h, 0) : 0
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
return measured;
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
// src/print/apply-text-fit.ts
|
|
2985
|
+
var CELL_ROW_SOURCES = {
|
|
2986
|
+
b: "_renderRows",
|
|
2987
|
+
st: "_subtotalTemplates",
|
|
2988
|
+
sm: "_summaryRows"
|
|
2989
|
+
};
|
|
2990
|
+
function applyTextFitSizes(template, fits) {
|
|
2991
|
+
if (!fits || fits.length === 0) return;
|
|
2992
|
+
const index = /* @__PURE__ */ new Map();
|
|
2993
|
+
const collect = (elements) => {
|
|
2994
|
+
for (const el of elements ?? []) index.set(el.id, el);
|
|
2995
|
+
};
|
|
2996
|
+
collect(template.elements);
|
|
2997
|
+
collect(template.header?.elements);
|
|
2998
|
+
collect(template.footer?.elements);
|
|
2999
|
+
collect(template.firstPageOverlay?.elements);
|
|
3000
|
+
for (const fit of fits) {
|
|
3001
|
+
const parsed = parseCellFitKey(fit.key);
|
|
3002
|
+
if (!parsed) {
|
|
3003
|
+
const el2 = index.get(fit.key);
|
|
3004
|
+
if (el2) el2.options._fitFontSize = roundFontSize(fit.fontSizePt);
|
|
3005
|
+
continue;
|
|
3006
|
+
}
|
|
3007
|
+
const el = index.get(parsed.elementId);
|
|
3008
|
+
const rows = el?.options?.[CELL_ROW_SOURCES[parsed.kind]];
|
|
3009
|
+
const cell = rows?.[parsed.rowIndex]?.cells?.[parsed.colIndex];
|
|
3010
|
+
if (cell) cell.fittedFontSize = roundFontSize(fit.fontSizePt);
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
// src/print/codes.ts
|
|
3015
|
+
function codeSpecKey(value, cellType, opts = {}) {
|
|
3016
|
+
return JSON.stringify([
|
|
3017
|
+
value,
|
|
3018
|
+
cellType,
|
|
3019
|
+
opts.barcodeType ?? null,
|
|
3020
|
+
opts.qrCodeLevel ?? null,
|
|
3021
|
+
opts.showText ?? null,
|
|
3022
|
+
opts.barWidth ?? null,
|
|
3023
|
+
opts.fontSize ?? null
|
|
3024
|
+
]);
|
|
3025
|
+
}
|
|
3026
|
+
function createMapCodeRenderer(map) {
|
|
3027
|
+
return {
|
|
3028
|
+
render(value, cellType, opts) {
|
|
3029
|
+
const svg = map.get(codeSpecKey(value, cellType, opts));
|
|
3030
|
+
if (!svg) throw new Error(`\u7801\u503C\u672A\u6E32\u67D3\uFF1A${value}`);
|
|
3031
|
+
return svg;
|
|
3032
|
+
}
|
|
3033
|
+
};
|
|
3034
|
+
}
|
|
3035
|
+
function createCollectingCodeRenderer(base) {
|
|
3036
|
+
const collected = /* @__PURE__ */ new Map();
|
|
3037
|
+
return {
|
|
3038
|
+
renderer: {
|
|
3039
|
+
render(value, cellType, opts = {}) {
|
|
3040
|
+
const key = codeSpecKey(value, cellType, opts);
|
|
3041
|
+
const hit = base?.get(key);
|
|
3042
|
+
if (hit) return hit;
|
|
3043
|
+
collected.set(key, { key, value, cellType, opts });
|
|
3044
|
+
throw new Error("collect");
|
|
3045
|
+
}
|
|
3046
|
+
},
|
|
3047
|
+
takeSpecs() {
|
|
3048
|
+
const specs = [...collected.values()];
|
|
3049
|
+
collected.clear();
|
|
3050
|
+
return specs;
|
|
3051
|
+
}
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
function mergeCodeMaps(...maps) {
|
|
3055
|
+
const merged = /* @__PURE__ */ new Map();
|
|
3056
|
+
for (const map of maps) {
|
|
3057
|
+
for (const [key, svg] of map) merged.set(key, svg);
|
|
3058
|
+
}
|
|
3059
|
+
return merged;
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
// src/print/driver.ts
|
|
3063
|
+
var EXECUTOR_TARGETS = {
|
|
3064
|
+
waitReady: "window",
|
|
3065
|
+
readMeasurements: "document",
|
|
3066
|
+
readContentBottom: "document",
|
|
3067
|
+
renderCodes: "none",
|
|
3068
|
+
applyTextFit: "document"
|
|
3069
|
+
};
|
|
3070
|
+
|
|
3071
|
+
// src/print/ports.ts
|
|
3072
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
3073
|
+
var DEFAULT_READINESS_MS = 5e3;
|
|
3074
|
+
|
|
3075
|
+
// src/print/dom-host-runtime.ts
|
|
3076
|
+
function createDomHostRuntime(factory, bundle) {
|
|
3077
|
+
return {
|
|
3078
|
+
async withSession(options, fn) {
|
|
3079
|
+
const driver = await factory.createDriver();
|
|
3080
|
+
const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
3081
|
+
const readinessMs = options.readinessMs ?? DEFAULT_READINESS_MS;
|
|
3082
|
+
const budget = () => Math.max(1, deadline - Date.now());
|
|
3083
|
+
const fail = (err, code, context) => toPrintFailure(err, code, context);
|
|
3084
|
+
let injected = false;
|
|
3085
|
+
const ensureExecutor = async () => {
|
|
3086
|
+
if (injected) return;
|
|
3087
|
+
if (driver.requiresExecutor === false) {
|
|
3088
|
+
injected = true;
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
if (!bundle) throw new PrintFailure("INTERNAL", "\u7F3A\u5C11 core DOM \u6267\u884C\u5668\u4EA7\u7269");
|
|
3092
|
+
await driver.injectExecutor(bundle);
|
|
3093
|
+
injected = true;
|
|
3094
|
+
};
|
|
3095
|
+
const load = async (html, viewport) => {
|
|
3096
|
+
await driver.open(viewport);
|
|
3097
|
+
await driver.setContent(html);
|
|
3098
|
+
injected = false;
|
|
3099
|
+
await ensureExecutor();
|
|
3100
|
+
await driver.evaluate("waitReady", [readinessMs]);
|
|
3101
|
+
};
|
|
3102
|
+
const session = {
|
|
3103
|
+
async renderCodes(specs) {
|
|
3104
|
+
if (specs.length === 0) return /* @__PURE__ */ new Map();
|
|
3105
|
+
const ms = budget();
|
|
3106
|
+
return withTimeout(
|
|
3107
|
+
(async () => {
|
|
3108
|
+
try {
|
|
3109
|
+
await ensureExecutor();
|
|
3110
|
+
const map = await driver.evaluate("renderCodes", [specs]);
|
|
3111
|
+
return new Map(Object.entries(map));
|
|
3112
|
+
} catch (err) {
|
|
3113
|
+
throw fail(err, "INTERNAL", "\u7801\u503C\u6E32\u67D3\u5931\u8D25");
|
|
3114
|
+
}
|
|
3115
|
+
})(),
|
|
3116
|
+
ms,
|
|
3117
|
+
"RENDER_TIMEOUT",
|
|
3118
|
+
`\u7801\u503C\u6E32\u67D3\u8D85\u8FC7 ${ms}ms`
|
|
3119
|
+
);
|
|
3120
|
+
},
|
|
3121
|
+
async measure(html, viewport) {
|
|
3122
|
+
const ms = budget();
|
|
3123
|
+
return withTimeout(
|
|
3124
|
+
(async () => {
|
|
3125
|
+
try {
|
|
3126
|
+
await load(html, viewport);
|
|
3127
|
+
const fits = await driver.evaluate("applyTextFit") ?? [];
|
|
3128
|
+
const measurements = await driver.evaluate("readMeasurements");
|
|
3129
|
+
return { measurements, fits };
|
|
3130
|
+
} catch (err) {
|
|
3131
|
+
throw fail(err, "MEASURE_FAILED", "\u6D4B\u91CF\u5931\u8D25");
|
|
3132
|
+
}
|
|
3133
|
+
})(),
|
|
3134
|
+
ms,
|
|
3135
|
+
"RENDER_TIMEOUT",
|
|
3136
|
+
`\u6D4B\u91CF\u8D85\u8FC7 ${ms}ms`
|
|
3137
|
+
);
|
|
3138
|
+
},
|
|
3139
|
+
async probeContentBottom(html, viewport) {
|
|
3140
|
+
const ms = budget();
|
|
3141
|
+
return withTimeout(
|
|
3142
|
+
(async () => {
|
|
3143
|
+
try {
|
|
3144
|
+
await load(html, viewport);
|
|
3145
|
+
return await driver.evaluate("readContentBottom");
|
|
3146
|
+
} catch (err) {
|
|
3147
|
+
throw fail(err, "MEASURE_FAILED", "\u8FDE\u7EED\u7EB8\u63A2\u9488\u5931\u8D25");
|
|
3148
|
+
}
|
|
3149
|
+
})(),
|
|
3150
|
+
ms,
|
|
3151
|
+
"RENDER_TIMEOUT",
|
|
3152
|
+
`\u8FDE\u7EED\u7EB8\u63A2\u9488\u8D85\u8FC7 ${ms}ms`
|
|
3153
|
+
);
|
|
3154
|
+
},
|
|
3155
|
+
async toPdf(html, spec, viewport) {
|
|
3156
|
+
if (!driver.pdf) throw new PrintFailure("UNSUPPORTED_RUNTIME", "\u5F53\u524D\u5BBF\u4E3B\u4E0D\u652F\u6301\u751F\u6210 PDF");
|
|
3157
|
+
const ms = budget();
|
|
3158
|
+
return withTimeout(
|
|
3159
|
+
(async () => {
|
|
3160
|
+
try {
|
|
3161
|
+
await load(html, viewport);
|
|
3162
|
+
return await driver.pdf(html, spec);
|
|
3163
|
+
} catch (err) {
|
|
3164
|
+
throw fail(err, "PDF_FAILED", "PDF \u751F\u6210\u5931\u8D25");
|
|
3165
|
+
}
|
|
3166
|
+
})(),
|
|
3167
|
+
ms,
|
|
3168
|
+
"RENDER_TIMEOUT",
|
|
3169
|
+
`PDF \u751F\u6210\u8D85\u8FC7 ${ms}ms`
|
|
3170
|
+
);
|
|
3171
|
+
},
|
|
3172
|
+
async toScreenshot(html, spec, viewport) {
|
|
3173
|
+
if (!driver.screenshot) throw new PrintFailure("UNSUPPORTED_RUNTIME", "\u5F53\u524D\u5BBF\u4E3B\u4E0D\u652F\u6301\u622A\u56FE");
|
|
3174
|
+
const ms = budget();
|
|
3175
|
+
return withTimeout(
|
|
3176
|
+
(async () => {
|
|
3177
|
+
try {
|
|
3178
|
+
await load(html, viewport);
|
|
3179
|
+
return await driver.screenshot(html, spec);
|
|
3180
|
+
} catch (err) {
|
|
3181
|
+
throw fail(err, "SCREENSHOT_FAILED", "\u622A\u56FE\u5931\u8D25");
|
|
3182
|
+
}
|
|
3183
|
+
})(),
|
|
3184
|
+
ms,
|
|
3185
|
+
"RENDER_TIMEOUT",
|
|
3186
|
+
`\u622A\u56FE\u8D85\u8FC7 ${ms}ms`
|
|
3187
|
+
);
|
|
3188
|
+
}
|
|
3189
|
+
};
|
|
3190
|
+
try {
|
|
3191
|
+
return await fn(session);
|
|
3192
|
+
} finally {
|
|
3193
|
+
try {
|
|
3194
|
+
await driver.close();
|
|
3195
|
+
} catch {
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
// src/print/normalize-print-data.ts
|
|
3203
|
+
var MAX_BATCH_COPIES = 500;
|
|
3204
|
+
function isPlainRecord(v) {
|
|
3205
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3206
|
+
}
|
|
3207
|
+
function normalizePrintData(raw) {
|
|
3208
|
+
if (raw === void 0) return { mode: "single", data: {} };
|
|
3209
|
+
if (!Array.isArray(raw)) return { mode: "single", data: raw };
|
|
3210
|
+
if (raw.length === 0) {
|
|
3211
|
+
throw new Error("\u6279\u91CF\u6253\u5370\u6570\u636E\u5FC5\u987B\u662F\u975E\u7A7A\u5BF9\u8C61\u6570\u7EC4");
|
|
3212
|
+
}
|
|
3213
|
+
if (raw.length > MAX_BATCH_COPIES) {
|
|
3214
|
+
throw new Error(`\u6279\u91CF\u6253\u5370\u6700\u591A\u652F\u6301 ${MAX_BATCH_COPIES} \u4EFD\uFF0C\u5F53\u524D ${raw.length} \u4EFD`);
|
|
3215
|
+
}
|
|
3216
|
+
for (let i = 0; i < raw.length; i++) {
|
|
3217
|
+
if (!isPlainRecord(raw[i])) {
|
|
3218
|
+
throw new Error(`\u6279\u91CF\u6253\u5370\u6570\u636E\u7B2C ${i + 1} \u9879\u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
return { mode: "batch", dataList: raw };
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
// src/print/batch-compose.ts
|
|
3225
|
+
function composeBatchHtml(copies) {
|
|
3226
|
+
const bound = copies[0].bound;
|
|
3227
|
+
const continuous = isContinuousPaper(bound);
|
|
3228
|
+
const bodyInner = copies.map((copy, i) => {
|
|
3229
|
+
const cls = continuous ? `print-copy print-copy-${i}` : "print-copy";
|
|
3230
|
+
const pages = renderFinalPages(copy.bound, copy.pageLayouts, copy.data, {
|
|
3231
|
+
codeRenderer: copy.codeRenderer,
|
|
3232
|
+
pageHeightMm: copy.derivedHeightMm
|
|
3233
|
+
});
|
|
3234
|
+
return `<section class="${cls}">
|
|
3235
|
+
${pages}
|
|
3236
|
+
</section>`;
|
|
3237
|
+
}).join("\n");
|
|
3238
|
+
const pageCss = continuous ? buildBatchPageCss(bound, copies.map((c) => ({ heightMm: c.derivedHeightMm }))) : `${buildPageCss(bound)}
|
|
3239
|
+
.print-copy:not(:last-child){break-after:page;page-break-after:always;}`;
|
|
3240
|
+
const css = buildFontFaceCss(bound.fonts) + pageCss;
|
|
3241
|
+
return {
|
|
3242
|
+
html: wrapHtmlDocument(
|
|
3243
|
+
css,
|
|
3244
|
+
bodyInner,
|
|
3245
|
+
continuous ? "continuous" : void 0
|
|
3246
|
+
),
|
|
3247
|
+
pageCount: copies.reduce((sum2, c) => sum2 + c.pageLayouts.length, 0),
|
|
3248
|
+
copyPaperMm: copies.map((c) => c.paperMm)
|
|
3249
|
+
};
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
// src/print/tiling.ts
|
|
3253
|
+
var TILE_DEFAULTS = {
|
|
3254
|
+
enabled: true,
|
|
3255
|
+
sheetPaperSize: "A4",
|
|
3256
|
+
sheetOrientation: "portrait",
|
|
3257
|
+
sheetMargin: { top: 10, right: 10, bottom: 10, left: 10 },
|
|
3258
|
+
gapX: 2,
|
|
3259
|
+
gapY: 2,
|
|
3260
|
+
columns: 2
|
|
3261
|
+
};
|
|
3262
|
+
var TilingError = class extends Error {
|
|
3263
|
+
constructor(code, message) {
|
|
3264
|
+
super(message);
|
|
3265
|
+
this.name = "TilingError";
|
|
3266
|
+
this.code = code;
|
|
3267
|
+
}
|
|
3268
|
+
};
|
|
3269
|
+
function roundMm(value) {
|
|
3270
|
+
return Math.round(value * 10) / 10;
|
|
3271
|
+
}
|
|
3272
|
+
function normalizeTilingOptions(t) {
|
|
3273
|
+
const raw = t.tiling;
|
|
3274
|
+
return {
|
|
3275
|
+
...TILE_DEFAULTS,
|
|
3276
|
+
...raw,
|
|
3277
|
+
sheetMargin: { ...TILE_DEFAULTS.sheetMargin, ...raw?.sheetMargin ?? {} }
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3280
|
+
function resolveSheetMm(t, opts) {
|
|
3281
|
+
const cfg = normalizeTilingOptions(t);
|
|
3282
|
+
const ov = opts?.paperOverride;
|
|
3283
|
+
if (ov && typeof ov.width === "number" && ov.width > 0 && typeof ov.height === "number" && ov.height > 0) {
|
|
3284
|
+
return { width: ov.width, height: ov.height };
|
|
3285
|
+
}
|
|
3286
|
+
if (cfg.sheetPaperSize === "CUSTOM") {
|
|
3287
|
+
return {
|
|
3288
|
+
width: cfg.sheetCustomWidth ?? PAPER_DIMENSIONS.A4.width,
|
|
3289
|
+
height: cfg.sheetCustomHeight ?? PAPER_DIMENSIONS.A4.height
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
const preset = cfg.sheetPaperSize ?? "A4";
|
|
3293
|
+
const base = PAPER_DIMENSIONS[preset] ?? PAPER_DIMENSIONS.A4;
|
|
3294
|
+
return cfg.sheetOrientation === "landscape" ? { width: base.height, height: base.width } : { ...base };
|
|
3295
|
+
}
|
|
3296
|
+
function availableArea(sheet, margin) {
|
|
3297
|
+
return {
|
|
3298
|
+
w: sheet.width - margin.left - margin.right,
|
|
3299
|
+
h: sheet.height - margin.top - margin.bottom
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
function calcMaxColumns(availW, labelW, gapX) {
|
|
3303
|
+
return Math.max(0, Math.floor((availW + gapX) / (labelW + gapX)));
|
|
3304
|
+
}
|
|
3305
|
+
function computeTileLayout(t, opts) {
|
|
3306
|
+
const issues = validateTiling(t, opts);
|
|
3307
|
+
if (issues.length) throw new TilingError(issues[0].code, issues[0].message);
|
|
3308
|
+
const cfg = normalizeTilingOptions(t);
|
|
3309
|
+
const sheet = resolveSheetMm(t, opts);
|
|
3310
|
+
const label = getPaperDimensions(t);
|
|
3311
|
+
const avail = availableArea(sheet, cfg.sheetMargin);
|
|
3312
|
+
const rows = Math.floor((avail.h + cfg.gapY) / (label.height + cfg.gapY));
|
|
3313
|
+
return {
|
|
3314
|
+
tile: { width: label.width, height: label.height },
|
|
3315
|
+
sheet,
|
|
3316
|
+
columns: cfg.columns,
|
|
3317
|
+
rows,
|
|
3318
|
+
perSheet: cfg.columns * rows,
|
|
3319
|
+
maxColumns: calcMaxColumns(avail.w, label.width, cfg.gapX),
|
|
3320
|
+
margin: { ...cfg.sheetMargin },
|
|
3321
|
+
gapX: cfg.gapX,
|
|
3322
|
+
gapY: cfg.gapY
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
function tilePosition(layout, index) {
|
|
3326
|
+
const slot = index % layout.perSheet;
|
|
3327
|
+
const col = slot % layout.columns;
|
|
3328
|
+
const row = Math.floor(slot / layout.columns);
|
|
3329
|
+
return {
|
|
3330
|
+
left: roundMm(layout.margin.left + col * (layout.tile.width + layout.gapX)),
|
|
3331
|
+
top: roundMm(layout.margin.top + row * (layout.tile.height + layout.gapY))
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
function computeMaxColumns(t, opts) {
|
|
3335
|
+
const cfg = normalizeTilingOptions(t);
|
|
3336
|
+
const sheet = resolveSheetMm(t, opts);
|
|
3337
|
+
const label = getPaperDimensions(t);
|
|
3338
|
+
const availW = sheet.width - cfg.sheetMargin.left - cfg.sheetMargin.right;
|
|
3339
|
+
return calcMaxColumns(availW, label.width, cfg.gapX);
|
|
3340
|
+
}
|
|
3341
|
+
function validateTiling(t, opts) {
|
|
3342
|
+
const issues = [];
|
|
3343
|
+
const cfg = normalizeTilingOptions(t);
|
|
3344
|
+
if (isContinuousPaper(t)) {
|
|
3345
|
+
issues.push({
|
|
3346
|
+
code: "CONTINUOUS_UNSUPPORTED",
|
|
3347
|
+
message: "\u8FDE\u7EED\u7EB8\u4E0D\u652F\u6301\u62FC\u7248\u6253\u5370\uFF0C\u8BF7\u5C06\u6A21\u677F\u7EB8\u5F20\u6539\u4E3A\u56FA\u5B9A\u7EB8\u5F20\u6216\u5173\u95ED\u62FC\u7248"
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
const sheetPaperSize = cfg.sheetPaperSize;
|
|
3351
|
+
const sheetContinuous = isContinuousPaperSize(sheetPaperSize ?? "");
|
|
3352
|
+
if (sheetContinuous) {
|
|
3353
|
+
issues.push({ code: "SHEET_CONTINUOUS", message: "\u62FC\u7248\u76EE\u6807\u7EB8\u5F20\u4E0D\u80FD\u662F\u8FDE\u7EED\u7EB8" });
|
|
3354
|
+
}
|
|
3355
|
+
const customSizeInvalid = cfg.sheetPaperSize === "CUSTOM" && !((cfg.sheetCustomWidth ?? 0) > 0 && (cfg.sheetCustomHeight ?? 0) > 0);
|
|
3356
|
+
if (customSizeInvalid) {
|
|
3357
|
+
issues.push({
|
|
3358
|
+
code: "SHEET_SIZE_INVALID",
|
|
3359
|
+
message: "\u62FC\u7248\u81EA\u5B9A\u4E49\u7EB8\u5F20\u5BBD\u9AD8\u5FC5\u987B\u662F\u5927\u4E8E 0 \u7684\u6570\u503C\uFF08mm\uFF09"
|
|
3360
|
+
});
|
|
3361
|
+
}
|
|
3362
|
+
if (customSizeInvalid || sheetContinuous) return issues;
|
|
3363
|
+
const sheet = resolveSheetMm(t, opts);
|
|
3364
|
+
const label = getPaperDimensions(t);
|
|
3365
|
+
const avail = availableArea(sheet, cfg.sheetMargin);
|
|
3366
|
+
const marginLR = roundMm(cfg.sheetMargin.left + cfg.sheetMargin.right);
|
|
3367
|
+
if (!Number.isInteger(cfg.columns) || cfg.columns < 1) {
|
|
3368
|
+
issues.push({ code: "COLUMNS_INVALID", message: "\u62FC\u7248\u5217\u6570\u5FC5\u987B\u662F\u5927\u4E8E 0 \u7684\u6574\u6570" });
|
|
3369
|
+
return issues;
|
|
3370
|
+
}
|
|
3371
|
+
const maxColumns = calcMaxColumns(avail.w, label.width, cfg.gapX);
|
|
3372
|
+
if (cfg.columns > maxColumns) {
|
|
3373
|
+
const needW = roundMm(cfg.columns * label.width + (cfg.columns - 1) * cfg.gapX);
|
|
3374
|
+
issues.push({
|
|
3375
|
+
code: "COLUMNS_OVERFLOW",
|
|
3376
|
+
message: "\u62FC\u7248\u5217\u6570 " + cfg.columns + " \u8D85\u51FA\u7EB8\u9762\u53EF\u7528\u5BBD\u5EA6\uFF1A" + roundMm(sheet.width) + "mm \u2212 \u5DE6\u53F3\u7559\u767D " + marginLR + "mm = " + roundMm(avail.w) + "mm\uFF0C\u6700\u591A\u53EF\u653E " + maxColumns + " \u5217\uFF1B\u5F53\u524D " + cfg.columns + " \u5217 " + label.width + "mm \u6807\u7B7E\u542B\u95F4\u8DDD\u9700\u8981 " + needW + "mm"
|
|
3377
|
+
});
|
|
3378
|
+
}
|
|
3379
|
+
if (Math.floor((avail.h + cfg.gapY) / (label.height + cfg.gapY)) < 1) {
|
|
3380
|
+
issues.push({
|
|
3381
|
+
code: "LABEL_TOO_TALL",
|
|
3382
|
+
message: "\u6807\u7B7E\u9AD8\u5EA6 " + label.height + "mm \u8D85\u51FA\u7EB8\u9762\u53EF\u7528\u9AD8\u5EA6 " + roundMm(avail.h) + "mm\uFF0C\u62FC\u7248\u6BCF\u5F20 0 \u884C\uFF1B\u8BF7\u7F29\u5C0F\u6807\u7B7E\u9AD8\u5EA6\u6216\u6539\u7528\u6A2A\u5411\u7EB8"
|
|
3383
|
+
});
|
|
3384
|
+
}
|
|
3385
|
+
return issues;
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
// src/print/tile-compose.ts
|
|
3389
|
+
function composeTiledHtml(input) {
|
|
3390
|
+
const { copies, layout } = input;
|
|
3391
|
+
const bound = copies[0].bound;
|
|
3392
|
+
const sheets = [];
|
|
3393
|
+
for (let start = 0; start < copies.length; start += layout.perSheet) {
|
|
3394
|
+
const tiles = copies.slice(start, start + layout.perSheet).map((copy, i) => {
|
|
3395
|
+
const pos = tilePosition(layout, i);
|
|
3396
|
+
const page = renderFinalPages(copy.bound, copy.pageLayouts, copy.data, {
|
|
3397
|
+
codeRenderer: copy.codeRenderer
|
|
3398
|
+
});
|
|
3399
|
+
return `<div class="print-tile" style="left:${pos.left}mm;top:${pos.top}mm">
|
|
3400
|
+
${page}
|
|
3401
|
+
</div>`;
|
|
3402
|
+
}).join("\n");
|
|
3403
|
+
sheets.push(`<section class="print-sheet">
|
|
3404
|
+
${tiles}
|
|
3405
|
+
</section>`);
|
|
3406
|
+
}
|
|
3407
|
+
const css = buildFontFaceCss(bound.fonts) + buildPageCss(bound) + "\n" + buildSheetPageCss(layout);
|
|
3408
|
+
return {
|
|
3409
|
+
html: wrapHtmlDocument(css, sheets.join("\n")),
|
|
3410
|
+
sheetCount: sheets.length,
|
|
3411
|
+
perSheet: layout.perSheet
|
|
3412
|
+
};
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
// src/print/multi-template.ts
|
|
3416
|
+
function pageLabel(p, index) {
|
|
3417
|
+
return p.name ? `\u7B2C ${index + 1} \u9875\u300C${p.name}\u300D` : `\u7B2C ${index + 1} \u9875`;
|
|
3418
|
+
}
|
|
3419
|
+
function normalizeTemplate(templateJson) {
|
|
3420
|
+
const pages = isMultiPageTemplate(templateJson) ? templateJson.pages : [templateJson];
|
|
3421
|
+
if (pages.length === 0) {
|
|
3422
|
+
throw new Error("\u591A\u9875\u9762\u6A21\u677F\u81F3\u5C11\u9700\u8981\u4E00\u9875");
|
|
3423
|
+
}
|
|
3424
|
+
if (pages.length === 1) return pages;
|
|
3425
|
+
const firstPaper = getPaperDimensions(pages[0]);
|
|
3426
|
+
pages.forEach((p, i) => {
|
|
3427
|
+
if (isContinuousPaper(p)) {
|
|
3428
|
+
throw new Error(`\u591A\u9875\u9762\u6A21\u677F\u4E0D\u652F\u6301\u8FDE\u7EED\u7EB8\uFF08${pageLabel(p, i)}\uFF09`);
|
|
3429
|
+
}
|
|
3430
|
+
if (p.tiling?.enabled === true) {
|
|
3431
|
+
throw new Error(`\u591A\u9875\u9762\u6A21\u677F\u4E0D\u652F\u6301\u6807\u7B7E\u62FC\u7248\uFF08${pageLabel(p, i)}\uFF09`);
|
|
3432
|
+
}
|
|
3433
|
+
const d = getPaperDimensions(p);
|
|
3434
|
+
if (d.width !== firstPaper.width || d.height !== firstPaper.height) {
|
|
3435
|
+
throw new Error(
|
|
3436
|
+
`\u591A\u9875\u9762\u6A21\u677F\u5404\u9875\u7EB8\u5F20\u5C3A\u5BF8\u5FC5\u987B\u4E00\u81F4\uFF08${pageLabel(p, i)} ${d.width}\xD7${d.height}mm \u4E0E\u9996\u9875 ${firstPaper.width}\xD7${firstPaper.height}mm \u4E0D\u540C\uFF09`
|
|
3437
|
+
);
|
|
3438
|
+
}
|
|
3439
|
+
});
|
|
3440
|
+
return pages;
|
|
3441
|
+
}
|
|
3442
|
+
function isMultiPageTemplate(templateJson) {
|
|
3443
|
+
return Array.isArray(templateJson.pages);
|
|
3444
|
+
}
|
|
3445
|
+
function mergeFontDeclarations(pages) {
|
|
3446
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3447
|
+
const merged = [];
|
|
3448
|
+
for (const p of pages) {
|
|
3449
|
+
for (const f of p.fonts ?? []) {
|
|
3450
|
+
const key = f.family;
|
|
3451
|
+
if (seen.has(key)) continue;
|
|
3452
|
+
seen.add(key);
|
|
3453
|
+
merged.push(f);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
return merged;
|
|
3457
|
+
}
|
|
3458
|
+
function composeMultiPageDocument(copies) {
|
|
3459
|
+
if (copies.length === 0) {
|
|
3460
|
+
throw new Error("\u591A\u9875\u9762\u6A21\u677F\u81F3\u5C11\u9700\u8981\u4E00\u4EFD\u6570\u636E");
|
|
3461
|
+
}
|
|
3462
|
+
const templates = copies[0].boundPages;
|
|
3463
|
+
const css = buildFontFaceCss(mergeFontDeclarations(templates)) + [
|
|
3464
|
+
buildPageRuleCss(templates[0]),
|
|
3465
|
+
buildBasePageCss(),
|
|
3466
|
+
...templates.map((t, i) => buildPageGeometryCss(t, `.mt-${i}`)),
|
|
3467
|
+
...copies.length > 1 ? [COPY_BREAK_CSS] : []
|
|
3468
|
+
].join("\n\n");
|
|
3469
|
+
const bodyInner = copies.map((copy) => {
|
|
3470
|
+
const totalPages = copy.layoutsPerPage.reduce((s, ls) => s + ls.length, 0);
|
|
3471
|
+
let offset = 0;
|
|
3472
|
+
const fragments = copy.layoutsPerPage.map((layouts, i) => {
|
|
3473
|
+
const html = renderFinalPages(
|
|
3474
|
+
copy.boundPages[i],
|
|
3475
|
+
layouts,
|
|
3476
|
+
copy.data,
|
|
3477
|
+
{ codeRenderer: copy.codeRenderers?.[i] },
|
|
3478
|
+
{
|
|
3479
|
+
pageOffset: offset,
|
|
3480
|
+
totalPages,
|
|
3481
|
+
pageClass: `mt-${i}`
|
|
3482
|
+
}
|
|
3483
|
+
);
|
|
3484
|
+
offset += layouts.length;
|
|
3485
|
+
return html;
|
|
3486
|
+
});
|
|
3487
|
+
const pages = fragments.join("\n");
|
|
3488
|
+
return copies.length > 1 ? `<section class="print-copy">
|
|
3489
|
+
${pages}
|
|
3490
|
+
</section>` : pages;
|
|
3491
|
+
}).join("\n");
|
|
3492
|
+
const pageCount = copies.reduce(
|
|
3493
|
+
(sum2, c) => sum2 + c.layoutsPerPage.reduce((s, ls) => s + ls.length, 0),
|
|
3494
|
+
0
|
|
3495
|
+
);
|
|
3496
|
+
const pageLayouts = [];
|
|
3497
|
+
let g = 0;
|
|
3498
|
+
for (const c of copies) {
|
|
3499
|
+
for (const ls of c.layoutsPerPage) {
|
|
3500
|
+
for (const p of ls) pageLayouts.push({ ...p, pageIndex: g++ });
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
return {
|
|
3504
|
+
html: wrapHtmlDocument(css, bodyInner),
|
|
3505
|
+
pageCount,
|
|
3506
|
+
pageLayouts
|
|
3507
|
+
};
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
// src/print/pipeline.ts
|
|
3511
|
+
async function prepareDocument(job, runtime) {
|
|
3512
|
+
return runtime.withSession(job, (session) => prepareWithSession(job, session));
|
|
3513
|
+
}
|
|
3514
|
+
async function renderPdf(job, runtime) {
|
|
3515
|
+
return runtime.withSession(job, async (session) => {
|
|
3516
|
+
const prepared = await prepareWithSession(job, session);
|
|
3517
|
+
const viewport = paperViewportPx(prepared.paperMm);
|
|
3518
|
+
const pdf = await session.toPdf(prepared.html, buildPdfTargetSpec(prepared.paperMm), viewport);
|
|
3519
|
+
return { pdf, prepared };
|
|
3520
|
+
});
|
|
3521
|
+
}
|
|
3522
|
+
async function renderScreenshot(job, runtime) {
|
|
3523
|
+
return runtime.withSession(job, async (session) => {
|
|
3524
|
+
const pageTemplates = normalizeTemplate(job.templateJson);
|
|
3525
|
+
if (pageTemplates.length > 1) {
|
|
3526
|
+
const normalized2 = normalizePrintData(job.printData);
|
|
3527
|
+
const data2 = normalized2.mode === "batch" ? normalized2.dataList[0] : normalized2.data;
|
|
3528
|
+
const copy = await prepareMultiCopy(job, session, data2);
|
|
3529
|
+
const doc = composeMultiPageDocument([copy]);
|
|
3530
|
+
const viewport2 = paperViewportPx(getPaperDimensions(pageTemplates[0]));
|
|
3531
|
+
return session.toScreenshot(doc.html, buildScreenshotTargetSpec(), viewport2);
|
|
3532
|
+
}
|
|
3533
|
+
const singleJob = pageTemplates[0] !== job.templateJson ? { ...job, templateJson: pageTemplates[0] } : job;
|
|
3534
|
+
const normalized = normalizePrintData(singleJob.printData);
|
|
3535
|
+
const data = normalized.mode === "batch" ? normalized.dataList[0] : normalized.data;
|
|
3536
|
+
const bound = bindData(singleJob.templateJson, data, singleJob.baseUrl, singleJob.fontBaseUrl);
|
|
3537
|
+
const built = await buildHtmlWithCodes({
|
|
3538
|
+
bound,
|
|
3539
|
+
job: singleJob,
|
|
3540
|
+
session,
|
|
3541
|
+
data,
|
|
3542
|
+
pageLayouts: [],
|
|
3543
|
+
isMeasurementPass: true
|
|
3544
|
+
});
|
|
3545
|
+
const viewport = paperViewportPx(getPaperDimensions(bound));
|
|
3546
|
+
return session.toScreenshot(built.html, buildScreenshotTargetSpec(), viewport);
|
|
3547
|
+
});
|
|
3548
|
+
}
|
|
3549
|
+
async function prepareWithSession(job, session) {
|
|
3550
|
+
const pageTemplates = normalizeTemplate(job.templateJson);
|
|
3551
|
+
if (pageTemplates.length > 1) {
|
|
3552
|
+
const normalized2 = normalizePrintData(job.printData);
|
|
3553
|
+
const rows = normalized2.mode === "batch" ? normalized2.dataList : [normalized2.data];
|
|
3554
|
+
const copies2 = [];
|
|
3555
|
+
for (let i = 0; i < rows.length; i++) {
|
|
3556
|
+
try {
|
|
3557
|
+
copies2.push(await prepareMultiCopy(job, session, rows[i]));
|
|
3558
|
+
} catch (err) {
|
|
3559
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3560
|
+
throw new Error(`\u7B2C ${i + 1} \u4EFD\u6E32\u67D3\u5931\u8D25\uFF1A${reason}`);
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
const doc = composeMultiPageDocument(copies2);
|
|
3564
|
+
return {
|
|
3565
|
+
html: doc.html,
|
|
3566
|
+
pageCount: doc.pageCount,
|
|
3567
|
+
paperMm: getOutputPaperDimensions(pageTemplates[0]),
|
|
3568
|
+
continuous: false,
|
|
3569
|
+
heightSource: "config",
|
|
3570
|
+
pageLayouts: doc.pageLayouts,
|
|
3571
|
+
copies: copies2.length
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
const singleJob = pageTemplates[0] !== job.templateJson ? { ...job, templateJson: pageTemplates[0] } : job;
|
|
3575
|
+
const template = pageTemplates[0];
|
|
3576
|
+
const normalized = normalizePrintData(singleJob.printData);
|
|
3577
|
+
if (normalized.mode === "single") {
|
|
3578
|
+
const single = await prepareSingleWithSession(singleJob, session, normalized.data);
|
|
3579
|
+
if (template.tiling?.enabled === true) {
|
|
3580
|
+
return composeTiledPrepared(singleJob, [{
|
|
3581
|
+
bound: single.bound,
|
|
3582
|
+
pageLayouts: single.pageLayouts,
|
|
3583
|
+
data: normalized.data,
|
|
3584
|
+
codeRenderer: single.codeRenderer,
|
|
3585
|
+
derivedHeightMm: single.derivedHeightMm,
|
|
3586
|
+
paperMm: single.paperMm,
|
|
3587
|
+
heightSource: single.heightSource
|
|
3588
|
+
}]);
|
|
3589
|
+
}
|
|
3590
|
+
return toPreparedDocument(single);
|
|
3591
|
+
}
|
|
3592
|
+
const copies = [];
|
|
3593
|
+
for (let i = 0; i < normalized.dataList.length; i++) {
|
|
3594
|
+
try {
|
|
3595
|
+
const single = await prepareSingleWithSession(singleJob, session, normalized.dataList[i]);
|
|
3596
|
+
copies.push({
|
|
3597
|
+
bound: single.bound,
|
|
3598
|
+
pageLayouts: single.pageLayouts,
|
|
3599
|
+
data: normalized.dataList[i],
|
|
3600
|
+
codeRenderer: single.codeRenderer,
|
|
3601
|
+
derivedHeightMm: single.derivedHeightMm,
|
|
3602
|
+
paperMm: single.paperMm,
|
|
3603
|
+
heightSource: single.heightSource
|
|
3604
|
+
});
|
|
3605
|
+
} catch (err) {
|
|
3606
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3607
|
+
throw new Error(`\u7B2C ${i + 1} \u4EFD\u6E32\u67D3\u5931\u8D25\uFF1A${reason}`);
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
if (template.tiling?.enabled === true) {
|
|
3611
|
+
return composeTiledPrepared(singleJob, copies);
|
|
3612
|
+
}
|
|
3613
|
+
const merged = composeBatchHtml(copies);
|
|
3614
|
+
return {
|
|
3615
|
+
html: merged.html,
|
|
3616
|
+
pageCount: merged.pageCount,
|
|
3617
|
+
paperMm: copies[0].paperMm,
|
|
3618
|
+
continuous: isContinuousPaper(copies[0].bound),
|
|
3619
|
+
// 同模板同参数各份来源必然一致;不能用 derivedHeightMm 是否存在判断——
|
|
3620
|
+
// 连续纸逃生门时该字段有值但来源是 config
|
|
3621
|
+
heightSource: copies[0].heightSource,
|
|
3622
|
+
// 各份 pageIndex 均从 0 开始,批量拼接后该字段仅作调试用途
|
|
3623
|
+
pageLayouts: copies.flatMap((c) => c.pageLayouts),
|
|
3624
|
+
copies: copies.length,
|
|
3625
|
+
copyPaperMm: merged.copyPaperMm
|
|
3626
|
+
};
|
|
3627
|
+
}
|
|
3628
|
+
function toPreparedDocument(s) {
|
|
3629
|
+
return {
|
|
3630
|
+
html: s.html,
|
|
3631
|
+
pageCount: s.pageCount,
|
|
3632
|
+
paperMm: s.paperMm,
|
|
3633
|
+
continuous: s.continuous,
|
|
3634
|
+
heightSource: s.heightSource,
|
|
3635
|
+
pageLayouts: s.pageLayouts,
|
|
3636
|
+
copies: 1
|
|
3637
|
+
};
|
|
3638
|
+
}
|
|
3639
|
+
function composeTiledPrepared(job, copies) {
|
|
3640
|
+
const bound = copies[0].bound;
|
|
3641
|
+
const layout = computeTileLayout(bound, { paperOverride: job.paperOverride });
|
|
3642
|
+
copies.forEach((copy, i) => {
|
|
3643
|
+
const overflow = copy.pageLayouts[0]?.overflow === true;
|
|
3644
|
+
if (copy.pageLayouts.length !== 1 || overflow) {
|
|
3645
|
+
const detail = overflow ? "\u5185\u5BB9\u8D85\u51FA\u7EB8\u5F20\u9AD8\u5EA6" : `\u6E32\u67D3\u51FA ${copy.pageLayouts.length} \u9875`;
|
|
3646
|
+
throw new Error(
|
|
3647
|
+
`\u62FC\u7248\u8981\u6C42\u6BCF\u4EFD\u6807\u7B7E\u6070\u597D 1 \u9875\uFF0C\u7B2C ${i + 1} \u4EFD${detail}\uFF1B\u8BF7\u7F29\u5C0F\u5185\u5BB9\u6216\u8C03\u6574\u6807\u7B7E\u7EB8\u5F20\u9AD8\u5EA6`
|
|
3648
|
+
);
|
|
3649
|
+
}
|
|
3650
|
+
});
|
|
3651
|
+
const tiled = composeTiledHtml({
|
|
3652
|
+
copies: copies.map((c) => ({
|
|
3653
|
+
bound: c.bound,
|
|
3654
|
+
pageLayouts: c.pageLayouts,
|
|
3655
|
+
data: c.data,
|
|
3656
|
+
codeRenderer: c.codeRenderer
|
|
3657
|
+
})),
|
|
3658
|
+
layout
|
|
3659
|
+
});
|
|
3660
|
+
return {
|
|
3661
|
+
html: tiled.html,
|
|
3662
|
+
// 语义变更:pageCount = 实际输出张数(客户端任务历史、预览「共 N 页」都按此口径)
|
|
3663
|
+
pageCount: tiled.sheetCount,
|
|
3664
|
+
paperMm: { width: layout.sheet.width, height: layout.sheet.height },
|
|
3665
|
+
continuous: false,
|
|
3666
|
+
heightSource: "config",
|
|
3667
|
+
// 调试用途;各份 pageIndex 均从 0 开始
|
|
3668
|
+
pageLayouts: copies.flatMap((c) => c.pageLayouts),
|
|
3669
|
+
copies: copies.length
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
async function prepareSingleWithSession(job, session, data) {
|
|
3673
|
+
const bound = bindData(job.templateJson, data, job.baseUrl, job.fontBaseUrl);
|
|
3674
|
+
const continuous = isContinuousPaper(bound);
|
|
3675
|
+
const designPaper = getPaperDimensions(bound);
|
|
3676
|
+
const viewport = paperViewportPx(designPaper);
|
|
3677
|
+
const heightEscape = escapeHeightMm({ paperHeightMm: job.paperHeightMm, override: job.paperOverride });
|
|
3678
|
+
const measurement = await buildHtmlWithCodes({
|
|
3679
|
+
bound,
|
|
3680
|
+
job,
|
|
3681
|
+
session,
|
|
3682
|
+
data,
|
|
3683
|
+
pageLayouts: [],
|
|
3684
|
+
isMeasurementPass: true
|
|
3685
|
+
});
|
|
3686
|
+
const measurements = await session.measure(measurement.html, viewport);
|
|
3687
|
+
applyTextFitSizes(bound, measurements.fits);
|
|
3688
|
+
const pageLayouts = paginate(bound, normalizeMeasurements(measurements.measurements, bound));
|
|
3689
|
+
const finalBuild = await buildHtmlWithCodes({
|
|
3690
|
+
bound,
|
|
3691
|
+
job,
|
|
3692
|
+
session,
|
|
3693
|
+
data,
|
|
3694
|
+
pageLayouts,
|
|
3695
|
+
isMeasurementPass: false,
|
|
3696
|
+
baseMap: measurement.map
|
|
3697
|
+
});
|
|
3698
|
+
let html = finalBuild.html;
|
|
3699
|
+
let derivedHeightMm;
|
|
3700
|
+
if (continuous) {
|
|
3701
|
+
if (heightEscape && heightEscape > 0) {
|
|
3702
|
+
derivedHeightMm = heightEscape;
|
|
3703
|
+
} else {
|
|
3704
|
+
const bottomPx = await session.probeContentBottom(html, viewport);
|
|
3705
|
+
derivedHeightMm = composeContinuousHeight(bound, pxToMm(bottomPx));
|
|
3706
|
+
}
|
|
3707
|
+
html = generateHtml(bound, pageLayouts, data, {
|
|
3708
|
+
codeRenderer: finalBuild.codeRenderer,
|
|
3709
|
+
pageHeightMm: derivedHeightMm
|
|
3710
|
+
});
|
|
3711
|
+
}
|
|
3712
|
+
const overrideForPaper = continuous && heightEscape ? { ...job.paperOverride, height: heightEscape } : job.paperOverride;
|
|
3713
|
+
const outputPaper = getOutputPaperDimensions(bound);
|
|
3714
|
+
const { paperMm, heightSource } = resolvePaperMm({
|
|
3715
|
+
paperMm: { width: outputPaper.width, height: derivedHeightMm ?? outputPaper.height },
|
|
3716
|
+
continuous,
|
|
3717
|
+
override: overrideForPaper
|
|
3718
|
+
});
|
|
3719
|
+
return {
|
|
3720
|
+
html,
|
|
3721
|
+
pageCount: pageLayouts.length,
|
|
3722
|
+
paperMm,
|
|
3723
|
+
continuous,
|
|
3724
|
+
heightSource,
|
|
3725
|
+
pageLayouts,
|
|
3726
|
+
copies: 1,
|
|
3727
|
+
bound,
|
|
3728
|
+
codeRenderer: finalBuild.codeRenderer,
|
|
3729
|
+
derivedHeightMm
|
|
3730
|
+
};
|
|
3731
|
+
}
|
|
3732
|
+
async function prepareMultiCopy(job, session, data) {
|
|
3733
|
+
const templates = normalizeTemplate(job.templateJson);
|
|
3734
|
+
const viewport = paperViewportPx(getPaperDimensions(templates[0]));
|
|
3735
|
+
const boundPages = [];
|
|
3736
|
+
const layoutsPerPage = [];
|
|
3737
|
+
const codeRenderers = [];
|
|
3738
|
+
for (const t of templates) {
|
|
3739
|
+
const bound = bindData(t, data, job.baseUrl, job.fontBaseUrl);
|
|
3740
|
+
const measurement = await buildHtmlWithCodes({ bound, job, session, data, pageLayouts: [], isMeasurementPass: true });
|
|
3741
|
+
const measurements = await session.measure(measurement.html, viewport);
|
|
3742
|
+
applyTextFitSizes(bound, measurements.fits);
|
|
3743
|
+
const layouts = paginate(bound, normalizeMeasurements(measurements.measurements, bound));
|
|
3744
|
+
const final = await buildHtmlWithCodes({ bound, job, session, data, pageLayouts: layouts, isMeasurementPass: false, baseMap: measurement.map });
|
|
3745
|
+
boundPages.push(bound);
|
|
3746
|
+
layoutsPerPage.push(layouts);
|
|
3747
|
+
codeRenderers.push(final.codeRenderer);
|
|
3748
|
+
}
|
|
3749
|
+
return { boundPages, layoutsPerPage, data, codeRenderers };
|
|
3750
|
+
}
|
|
3751
|
+
async function buildHtmlWithCodes(input) {
|
|
3752
|
+
const baseMap = input.baseMap ?? /* @__PURE__ */ new Map();
|
|
3753
|
+
if (input.job.codeRenderer) {
|
|
3754
|
+
const html2 = generateHtml(input.bound, input.pageLayouts, input.data, {
|
|
3755
|
+
isMeasurementPass: input.isMeasurementPass,
|
|
3756
|
+
codeRenderer: input.job.codeRenderer
|
|
3757
|
+
});
|
|
3758
|
+
return { html: html2, codeRenderer: input.job.codeRenderer, map: baseMap };
|
|
3759
|
+
}
|
|
3760
|
+
const collector = createCollectingCodeRenderer(baseMap);
|
|
3761
|
+
const draft = generateHtml(input.bound, input.pageLayouts, input.data, {
|
|
3762
|
+
isMeasurementPass: input.isMeasurementPass,
|
|
3763
|
+
codeRenderer: collector.renderer
|
|
3764
|
+
});
|
|
3765
|
+
const extra = collector.takeSpecs();
|
|
3766
|
+
const rendered = extra.length > 0 ? await input.session.renderCodes(extra) : /* @__PURE__ */ new Map();
|
|
3767
|
+
const map = mergeCodeMaps(baseMap, rendered);
|
|
3768
|
+
const codeRenderer = map.size > 0 ? createMapCodeRenderer(map) : void 0;
|
|
3769
|
+
if (extra.length === 0) return { html: draft, codeRenderer, map };
|
|
3770
|
+
const html = generateHtml(input.bound, input.pageLayouts, input.data, {
|
|
3771
|
+
isMeasurementPass: input.isMeasurementPass,
|
|
3772
|
+
codeRenderer
|
|
3773
|
+
});
|
|
3774
|
+
return { html, codeRenderer, map };
|
|
3775
|
+
}
|
|
3776
|
+
|
|
1802
3777
|
// src/index.ts
|
|
1803
3778
|
var TemplateEngine = class {
|
|
1804
3779
|
constructor(options) {
|
|
@@ -1821,36 +3796,124 @@ var TemplateEngine = class {
|
|
|
1821
3796
|
};
|
|
1822
3797
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1823
3798
|
0 && (module.exports = {
|
|
3799
|
+
DEFAULT_READINESS_MS,
|
|
3800
|
+
DEFAULT_SHRINK_MIN_FONT_SIZE_PT,
|
|
3801
|
+
DEFAULT_TIMEOUT_MS,
|
|
3802
|
+
EXECUTOR_TARGETS,
|
|
3803
|
+
FALLBACK_FONT_STACK,
|
|
3804
|
+
MAX_BATCH_COPIES,
|
|
3805
|
+
MICROMETERS_PER_INCH,
|
|
3806
|
+
MICROMETERS_PER_MM,
|
|
3807
|
+
MIN_CONTINUOUS_HEIGHT_MM,
|
|
3808
|
+
MIN_SHRINK_FONT_SIZE_PT,
|
|
3809
|
+
MM_PER_INCH,
|
|
3810
|
+
MM_PER_PX,
|
|
1824
3811
|
PAPER_DIMENSIONS,
|
|
3812
|
+
PX_PER_MM,
|
|
3813
|
+
PrintFailure,
|
|
3814
|
+
TILE_DEFAULTS,
|
|
1825
3815
|
TemplateEngine,
|
|
3816
|
+
TilingError,
|
|
3817
|
+
WATERMARK_DEFAULTS,
|
|
3818
|
+
WATERMARK_DENSITY_PRESETS,
|
|
3819
|
+
addNumbers,
|
|
3820
|
+
applyTextFitSizes,
|
|
1826
3821
|
avg,
|
|
1827
3822
|
bindData,
|
|
3823
|
+
buildBasePageCss,
|
|
3824
|
+
buildFontFaceCss,
|
|
1828
3825
|
buildPageCss,
|
|
3826
|
+
buildPageGeometryCss,
|
|
3827
|
+
buildPageRuleCss,
|
|
3828
|
+
buildPdfTargetSpec,
|
|
3829
|
+
buildScreenshotTargetSpec,
|
|
3830
|
+
cellFitCapMm,
|
|
3831
|
+
cellFitKey,
|
|
3832
|
+
codeSpecKey,
|
|
1829
3833
|
compileTemplate,
|
|
3834
|
+
composeBatchHtml,
|
|
3835
|
+
composeContinuousHeight,
|
|
3836
|
+
composeMultiPageDocument,
|
|
3837
|
+
composeTiledHtml,
|
|
3838
|
+
computeMaxColumns,
|
|
3839
|
+
computeTileLayout,
|
|
1830
3840
|
count,
|
|
3841
|
+
createCollectingCodeRenderer,
|
|
3842
|
+
createDomHostRuntime,
|
|
3843
|
+
createMapCodeRenderer,
|
|
3844
|
+
divideNumbers,
|
|
1831
3845
|
elementPositionStyle,
|
|
3846
|
+
escapeHeightMm,
|
|
3847
|
+
escapeInlineStyleValue,
|
|
1832
3848
|
evaluate,
|
|
1833
3849
|
evaluateTemplate,
|
|
3850
|
+
floorFontSize,
|
|
1834
3851
|
formatDate,
|
|
1835
3852
|
formatMoney,
|
|
3853
|
+
formatTimestamp,
|
|
1836
3854
|
generateHtml,
|
|
1837
3855
|
getByPath,
|
|
1838
3856
|
getPaperDimensions,
|
|
1839
3857
|
ifFn,
|
|
1840
3858
|
injectSystemVariables,
|
|
3859
|
+
isContinuousPaper,
|
|
3860
|
+
isContinuousPaperSize,
|
|
3861
|
+
isMultiPageTemplate,
|
|
3862
|
+
isWatermarkVisible,
|
|
1841
3863
|
max,
|
|
3864
|
+
mergeCodeMaps,
|
|
3865
|
+
mergeFontDeclarations,
|
|
3866
|
+
micrometersToMillimeters,
|
|
3867
|
+
millimetersToInches,
|
|
3868
|
+
millimetersToMicrometers,
|
|
1842
3869
|
min,
|
|
1843
3870
|
mm,
|
|
3871
|
+
mmToPx,
|
|
3872
|
+
multiplyNumbers,
|
|
3873
|
+
normalizeMeasurements,
|
|
3874
|
+
normalizePrintData,
|
|
3875
|
+
normalizeTemplate,
|
|
3876
|
+
normalizeTilingOptions,
|
|
1844
3877
|
paginate,
|
|
3878
|
+
paperViewportPx,
|
|
1845
3879
|
parse,
|
|
3880
|
+
parseCellFitKey,
|
|
1846
3881
|
parseTemplate,
|
|
3882
|
+
prepareDocument,
|
|
3883
|
+
pxToMm,
|
|
3884
|
+
renderPdf,
|
|
3885
|
+
renderScreenshot,
|
|
1847
3886
|
renderTemplate,
|
|
3887
|
+
renderWatermarkLayerHtml,
|
|
3888
|
+
renderWatermarkTileSvg,
|
|
3889
|
+
resolveCellTextFit,
|
|
3890
|
+
resolveElementTextFit,
|
|
3891
|
+
resolvePaperMm,
|
|
3892
|
+
resolveSheetMm,
|
|
3893
|
+
resolveShrinkMinFontSize,
|
|
3894
|
+
resolveSystemVariables,
|
|
3895
|
+
resolveWatermarkLayout,
|
|
3896
|
+
resolveWatermarkText,
|
|
3897
|
+
round,
|
|
3898
|
+
roundDown,
|
|
3899
|
+
roundFontSize,
|
|
3900
|
+
roundHalfEven,
|
|
3901
|
+
roundMm,
|
|
3902
|
+
roundUp,
|
|
1848
3903
|
safeEval,
|
|
1849
3904
|
setPageIndex,
|
|
1850
3905
|
setTotalPages,
|
|
3906
|
+
subtractNumbers,
|
|
1851
3907
|
sum,
|
|
1852
3908
|
systemVars,
|
|
1853
3909
|
tableDesignBottom,
|
|
3910
|
+
tilePosition,
|
|
3911
|
+
toElectronPrintToPdfOptions,
|
|
3912
|
+
toFontFamilyStack,
|
|
3913
|
+
toPlaywrightPdfOptions,
|
|
3914
|
+
toPrintFailure,
|
|
1854
3915
|
toUpperCaseAmount,
|
|
1855
|
-
tokenize
|
|
3916
|
+
tokenize,
|
|
3917
|
+
validateTiling,
|
|
3918
|
+
withTimeout
|
|
1856
3919
|
});
|