@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.
@@ -20,13 +20,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/designer/index.ts
21
21
  var designer_exports = {};
22
22
  __export(designer_exports, {
23
+ BARCODE_BAR_HEIGHT_MODULES: () => BARCODE_BAR_HEIGHT_MODULES,
24
+ BARCODE_MARGIN_BOTTOM_MODULES: () => BARCODE_MARGIN_BOTTOM_MODULES,
25
+ BARCODE_MODULE_WIDTH_MM: () => BARCODE_MODULE_WIDTH_MM,
26
+ BARCODE_QUIET_ZONE_MODULES: () => BARCODE_QUIET_ZONE_MODULES,
27
+ BARCODE_TEXT_FONT_SIZE_MODULES: () => BARCODE_TEXT_FONT_SIZE_MODULES,
23
28
  DEFAULT_DEMO_DATA: () => DEFAULT_DEMO_DATA,
29
+ DEFAULT_SHRINK_MIN_FONT_SIZE_PT: () => DEFAULT_SHRINK_MIN_FONT_SIZE_PT,
24
30
  ELEMENT_BINDING_REGISTRY: () => ELEMENT_BINDING_REGISTRY,
25
31
  EXTENDED_FUNCTIONS: () => EXTENDED_FUNCTIONS,
26
32
  FIT_SCALE_MIN_PERCENT: () => FIT_SCALE_MIN_PERCENT,
27
33
  GHOST_BORDER_CSS: () => GHOST_BORDER_CSS,
34
+ LABEL_PAPER_GROUP: () => LABEL_PAPER_GROUP,
28
35
  MIN_COL_WIDTH_MM: () => MIN_COL_WIDTH_MM,
29
36
  MIN_SCALE_PERCENT: () => MIN_SCALE_PERCENT,
37
+ MIN_SHRINK_FONT_SIZE_PT: () => MIN_SHRINK_FONT_SIZE_PT,
38
+ MM_PER_INCH: () => MM_PER_INCH,
30
39
  PAPER_PRESETS: () => PAPER_PRESETS,
31
40
  PRESET_COLORS: () => PRESET_COLORS,
32
41
  PROPERTY_REGISTRY: () => PROPERTY_REGISTRY,
@@ -34,9 +43,15 @@ __export(designer_exports, {
34
43
  RULER_THICKNESS: () => RULER_THICKNESS,
35
44
  ZONE_ALLOWED_TYPES: () => ZONE_ALLOWED_TYPES,
36
45
  applyBorderPreset: () => applyBorderPreset,
46
+ barcodeAvailableBoxMm: () => barcodeAvailableBoxMm,
47
+ barcodePreferredModuleWidthMm: () => barcodePreferredModuleWidthMm,
48
+ barcodeUnitsPerModule: () => barcodeUnitsPerModule,
37
49
  buildRulerTicks: () => buildRulerTicks,
38
50
  calcResizeRect: () => calcResizeRect,
39
51
  canMergeReason: () => canMergeReason,
52
+ cellFitCapMm: () => cellFitCapMm,
53
+ cellFitKey: () => cellFitKey,
54
+ cellFitWidthMm: () => cellFitWidthMm,
40
55
  chooseMajorStepMM: () => chooseMajorStepMM,
41
56
  clampResizedColumnWidth: () => clampResizedColumnWidth,
42
57
  clampScalePercent: () => clampScalePercent,
@@ -55,6 +70,7 @@ __export(designer_exports, {
55
70
  filterGroups: () => filterGroups,
56
71
  finalizeElementZone: () => finalizeElementZone,
57
72
  findMainCell: () => findMainCell,
73
+ floorFontSize: () => floorFontSize,
58
74
  generateGroupId: () => generateGroupId,
59
75
  generateId: () => generateId,
60
76
  getDemoData: () => getDemoData,
@@ -67,6 +83,9 @@ __export(designer_exports, {
67
83
  groupFields: () => groupFields,
68
84
  insertCol: () => insertCol,
69
85
  insertRow: () => insertRow,
86
+ isContinuousPaperSize: () => isContinuousPaperSize,
87
+ isLabelPaperSize: () => isLabelPaperSize,
88
+ labelPaperDefaults: () => labelPaperDefaults,
70
89
  matchKeywords: () => matchKeywords,
71
90
  mergeCells: () => mergeCells,
72
91
  minorStepMM: () => minorStepMM,
@@ -74,13 +93,20 @@ __export(designer_exports, {
74
93
  nextWheelScale: () => nextWheelScale,
75
94
  normalizeSelection: () => normalizeSelection,
76
95
  normalizeTemplateUnits: () => normalizeTemplateUnits,
96
+ parseCellFitKey: () => parseCellFitKey,
77
97
  ptToMm: () => ptToMm,
78
98
  pxToMm: () => pxToMm,
79
99
  resolveBarcodeDesignValue: () => resolveBarcodeDesignValue,
100
+ resolveBarcodeSize: () => resolveBarcodeSize,
80
101
  resolveCellBorderCss: () => resolveCellBorderCss,
102
+ resolveCellTextFit: () => resolveCellTextFit,
103
+ resolveElementTextFit: () => resolveElementTextFit,
104
+ resolveShrinkMinFontSize: () => resolveShrinkMinFontSize,
81
105
  resolveTextBinding: () => resolveTextBinding,
106
+ roundFontSize: () => roundFontSize,
82
107
  safeEval: () => safeEval2,
83
108
  searchProperties: () => searchProperties,
109
+ setHeaderRepeat: () => setHeaderRepeat,
84
110
  setRowType: () => setRowType,
85
111
  splitCells: () => splitCells,
86
112
  syncTableElementSize: () => syncTableElementSize,
@@ -104,6 +130,122 @@ function mmToPx(mm2) {
104
130
  return mm2 * (96 / 25.4);
105
131
  }
106
132
 
133
+ // src/render/text-fit.ts
134
+ var DEFAULT_SHRINK_MIN_FONT_SIZE_PT = 6;
135
+ var MIN_SHRINK_FONT_SIZE_PT = 1;
136
+ var VALID_FITS = ["clip", "shrink", "autoHeight"];
137
+ function normalizeFit(value) {
138
+ return typeof value === "string" && VALID_FITS.includes(value) ? value : void 0;
139
+ }
140
+ var ELEMENT_DEFAULT_FIT = {
141
+ text: "clip",
142
+ longText: "autoHeight"
143
+ };
144
+ function resolveElementTextFit(type, opts) {
145
+ return normalizeFit(opts?.textFit) ?? ELEMENT_DEFAULT_FIT[type] ?? "clip";
146
+ }
147
+ function resolveCellTextFit(cell) {
148
+ return normalizeFit(cell?.textFit) ?? (cell?.wordWrap === false ? "clip" : "autoHeight");
149
+ }
150
+ function resolveShrinkMinFontSize(pt) {
151
+ if (typeof pt !== "number" || !Number.isFinite(pt) || pt <= 0) return DEFAULT_SHRINK_MIN_FONT_SIZE_PT;
152
+ return Math.max(pt, MIN_SHRINK_FONT_SIZE_PT);
153
+ }
154
+ function roundFontSize(pt) {
155
+ return Math.round(pt * 100) / 100;
156
+ }
157
+ function floorFontSize(pt) {
158
+ return Math.floor(pt * 100) / 100;
159
+ }
160
+ function cellFitKey(elementId, kind, rowIndex, colIndex) {
161
+ return `${elementId}#${kind}#${rowIndex}:${colIndex}`;
162
+ }
163
+ function parseCellFitKey(key) {
164
+ const first = key.indexOf("#");
165
+ if (first < 0) return void 0;
166
+ const last = key.lastIndexOf("#");
167
+ const kind = key.slice(first + 1, last);
168
+ if (kind !== "b" && kind !== "st" && kind !== "sm") return void 0;
169
+ const [rowIndex, colIndex] = key.slice(last + 1).split(":").map(Number);
170
+ if (!Number.isFinite(rowIndex) || !Number.isFinite(colIndex)) return void 0;
171
+ return { elementId: key.slice(0, first), kind, rowIndex, colIndex };
172
+ }
173
+ function cellFitWidthMm(colWidths, colIndex, cell, defaultPadding = 1) {
174
+ const span = Math.max(cell?.colspan ?? 1, 1);
175
+ let width = 0;
176
+ for (let i = 0; i < span; i++) {
177
+ width += colWidths[colIndex + i] ?? 0;
178
+ }
179
+ if (width <= 0) return 0;
180
+ const padding = cell?.padding ?? defaultPadding;
181
+ const borderMm = (ptToMm(cell?.borders?.left?.width ?? 0) + ptToMm(cell?.borders?.right?.width ?? 0)) / 2;
182
+ return Math.max(width - padding * 2 - borderMm, 0.5);
183
+ }
184
+ function cellFitCapMm(rows, rowIndex, cell, defaultPadding = 1) {
185
+ const span = Math.max(cell?.rowspan ?? 1, 1);
186
+ let height = 0;
187
+ for (let i = 0; i < span; i++) {
188
+ height += rows[rowIndex + i]?.height ?? 8;
189
+ }
190
+ const padding = cell?.padding ?? defaultPadding;
191
+ const borderMm = (ptToMm(cell?.borders?.top?.width ?? 0) + ptToMm(cell?.borders?.bottom?.width ?? 0)) / 2;
192
+ return Math.max(height - padding * 2 - borderMm, 0.5);
193
+ }
194
+
195
+ // src/render/barcode-dot.ts
196
+ var MM_PER_INCH = 25.4;
197
+ var BARCODE_QUIET_ZONE_MODULES = 10;
198
+ var BARCODE_BAR_HEIGHT_MODULES = 30;
199
+ var BARCODE_TEXT_FONT_SIZE_MODULES = 10;
200
+ var BARCODE_MARGIN_BOTTOM_MODULES = 2;
201
+ var BARCODE_MODULE_WIDTH_MM = 0.25;
202
+ function barcodeUnitsPerModule(barWidth) {
203
+ const raw = typeof barWidth === "number" && Number.isFinite(barWidth) ? barWidth : 2;
204
+ return Math.max(1, raw / 2);
205
+ }
206
+ function barcodePreferredModuleWidthMm(barWidth) {
207
+ return barcodeUnitsPerModule(barWidth) * BARCODE_MODULE_WIDTH_MM;
208
+ }
209
+ function barcodeAvailableBoxMm(boxMm, maxMm) {
210
+ const box = typeof boxMm === "number" && boxMm > 0 ? boxMm : Number.POSITIVE_INFINITY;
211
+ const limit = typeof maxMm === "number" && maxMm > 0 ? Math.min(box, maxMm) : box;
212
+ return Number.isFinite(limit) ? limit : 0;
213
+ }
214
+ function resolveBarcodeSize(input) {
215
+ const unitWidth = Math.max(1, Math.ceil(input.unitWidth));
216
+ const unitHeight = Math.max(1, Math.ceil(input.unitHeight));
217
+ const preferredModuleMm = barcodePreferredModuleWidthMm(input.barWidth);
218
+ const maxModuleMm = Math.min(
219
+ input.boxWidthMm > 0 ? input.boxWidthMm / unitWidth : Number.POSITIVE_INFINITY,
220
+ input.boxHeightMm > 0 ? input.boxHeightMm / unitHeight : Number.POSITIVE_INFINITY
221
+ );
222
+ const dpi = input.dpi;
223
+ if (typeof dpi === "number" && Number.isFinite(dpi) && dpi > 0) {
224
+ const dotsPerMm = dpi / MM_PER_INCH;
225
+ const preferredDots = Math.max(1, Math.round(preferredModuleMm * dotsPerMm));
226
+ const maxDots = Math.floor(maxModuleMm * dotsPerMm);
227
+ if (maxDots >= 1) {
228
+ const dotsPerModule = Math.min(preferredDots, maxDots);
229
+ const moduleWidthMm2 = dotsPerModule / dotsPerMm;
230
+ return {
231
+ moduleWidthMm: moduleWidthMm2,
232
+ widthMm: unitWidth * moduleWidthMm2,
233
+ heightMm: unitHeight * moduleWidthMm2,
234
+ dotsPerModule,
235
+ scaledDown: dotsPerModule < preferredDots
236
+ };
237
+ }
238
+ }
239
+ const moduleWidthMm = Math.min(preferredModuleMm, maxModuleMm);
240
+ return {
241
+ moduleWidthMm,
242
+ widthMm: unitWidth * moduleWidthMm,
243
+ heightMm: unitHeight * moduleWidthMm,
244
+ dotsPerModule: null,
245
+ scaledDown: moduleWidthMm < preferredModuleMm
246
+ };
247
+ }
248
+
107
249
  // src/designer/utils/scale.ts
108
250
  var MIN_SCALE_PERCENT = 25;
109
251
  var FIT_SCALE_MIN_PERCENT = 5;
@@ -151,24 +293,55 @@ function normalizeTemplateUnits(data) {
151
293
  }
152
294
 
153
295
  // src/designer/utils/default-config.ts
296
+ var LABEL_PAPER_GROUP = "\u6807\u7B7E\u7EB8";
154
297
  var PAPER_PRESETS = {
155
- A4: { width: 210, height: 297 },
156
- A3: { width: 297, height: 420 },
157
- A5: { width: 148, height: 210 },
158
- Letter: { width: 216, height: 279 },
159
- Legal: { width: 216, height: 356 }
298
+ A4: { width: 210, height: 297, label: "A4 210\xD7297mm", group: "\u5E38\u7528\u7EB8\u5F20" },
299
+ A3: { width: 297, height: 420, label: "A3 297\xD7420mm", group: "\u5E38\u7528\u7EB8\u5F20" },
300
+ A5: { width: 148, height: 210, label: "A5 148\xD7210mm", group: "\u5E38\u7528\u7EB8\u5F20" },
301
+ Letter: { width: 216, height: 279, label: "Letter 216\xD7279mm", group: "\u5E38\u7528\u7EB8\u5F20" },
302
+ Legal: { width: 216, height: 356, label: "Legal 216\xD7356mm", group: "\u5E38\u7528\u7EB8\u5F20" },
303
+ // 针式打印纸(241 系列等分):11 英寸整张 279.4mm 按等分取整
304
+ DOT_FULL: { width: 241, height: 279.4, label: "\u5168\u7B49\u5206 241\xD7279.4mm", group: "\u9488\u5F0F\u6253\u5370\u7EB8" },
305
+ DOT_HALF: { width: 241, height: 139.7, label: "\u4E8C\u7B49\u5206 241\xD7139.7mm", group: "\u9488\u5F0F\u6253\u5370\u7EB8" },
306
+ DOT_THIRD: { width: 241, height: 93.1, label: "\u4E09\u7B49\u5206 241\xD793.1mm", group: "\u9488\u5F0F\u6253\u5370\u7EB8" },
307
+ // 标签纸
308
+ LABEL_80X60: { width: 80, height: 60, label: "80\xD760mm", group: LABEL_PAPER_GROUP },
309
+ LABEL_60X40: { width: 60, height: 40, label: "60\xD740mm", group: LABEL_PAPER_GROUP },
310
+ LABEL_40X30: { width: 40, height: 30, label: "40\xD730mm", group: LABEL_PAPER_GROUP },
311
+ // 小票纸(热敏卷纸):高度仅为设计画布高度,出纸按内容推导
312
+ THERMAL_57: { width: 57, height: 297, continuous: true, label: "57mm", group: "\u5C0F\u7968\u7EB8" },
313
+ THERMAL_80: { width: 80, height: 297, continuous: true, label: "80mm", group: "\u5C0F\u7968\u7EB8" },
314
+ THERMAL_110: { width: 110, height: 297, continuous: true, label: "110mm", group: "\u5C0F\u7968\u7EB8" },
315
+ // 连续纸:默认 80mm 热敏;高度仅为设计画布高度,出纸按内容推导
316
+ CONTINUOUS: { width: 80, height: 297, continuous: true, label: "\u8FDE\u7EED\u7EB8\uFF08\u81EA\u5B9A\u4E49\u5BBD\u5EA6\uFF09", group: "\u8FDE\u7EED\u7EB8" }
160
317
  };
318
+ function isContinuousPaperSize(paperSize) {
319
+ return !!paperSize && PAPER_PRESETS[paperSize]?.continuous === true;
320
+ }
321
+ function isLabelPaperSize(paperSize) {
322
+ return !!paperSize && PAPER_PRESETS[paperSize]?.group === LABEL_PAPER_GROUP;
323
+ }
324
+ function labelPaperDefaults(t) {
325
+ return {
326
+ margins: { top: 0, right: 0, bottom: 0, left: 0 },
327
+ header: { ...t.header, height: 0 },
328
+ footer: { ...t.footer, height: 0 }
329
+ };
330
+ }
161
331
  function getPaperDimensions(t) {
332
+ const continuous = isContinuousPaperSize(t.paperSize);
162
333
  let base;
163
- if (t.paperSize === "CUSTOM") {
334
+ if (t.paperSize === "CUSTOM" || continuous) {
335
+ const preset = PAPER_PRESETS[t.paperSize];
164
336
  base = {
165
- width: t.customWidth ?? 210,
166
- height: t.customHeight ?? 297
337
+ width: t.customWidth ?? preset?.width ?? 210,
338
+ height: t.customHeight ?? preset?.height ?? 297
167
339
  };
168
340
  } else {
169
- base = PAPER_PRESETS[t.paperSize] || PAPER_PRESETS["A4"];
341
+ const preset = PAPER_PRESETS[t.paperSize] || PAPER_PRESETS["A4"];
342
+ base = { width: preset.width, height: preset.height };
170
343
  }
171
- return t.orientation === "landscape" ? { width: base.height, height: base.width } : base;
344
+ return t.orientation === "landscape" && !continuous ? { width: base.height, height: base.width } : base;
172
345
  }
173
346
 
174
347
  // src/designer/utils/table-matrix.ts
@@ -348,6 +521,7 @@ function copyCellStyle(src, dst) {
348
521
  dst.align = src.align;
349
522
  dst.valign = src.valign;
350
523
  dst.fontSize = src.fontSize;
524
+ dst.fontFamily = src.fontFamily;
351
525
  dst.fontWeight = src.fontWeight;
352
526
  dst.color = src.color;
353
527
  dst.backgroundColor = src.backgroundColor;
@@ -364,6 +538,8 @@ function insertRow(rows, index, position) {
364
538
  id: `row-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
365
539
  type,
366
540
  height: ref.height,
541
+ // 新表头行沿用相邻表头行的重复设置:漏勾会让续片只重复部分表头,多级表头结构断裂
542
+ ...type === "header" ? { repeatOnPage: [rows[insertAt - 1], rows[insertAt]].find((r) => r?.type === "header")?.repeatOnPage === true } : {},
367
543
  cells: Array.from({ length: colCount }, () => createCell())
368
544
  };
369
545
  for (let c = 0; c < colCount; c++) {
@@ -487,11 +663,20 @@ function setRowType(rows, index, type) {
487
663
  }
488
664
  }
489
665
  rows[index].type = type;
490
- if (type !== "header") {
666
+ if (type === "header") {
667
+ const neighbour = [rows[index - 1], rows[index + 1]].find((r) => r?.type === "header");
668
+ rows[index].repeatOnPage = rows[index].repeatOnPage !== void 0 ? rows[index].repeatOnPage === true : neighbour?.repeatOnPage === true;
669
+ } else {
491
670
  delete rows[index].repeatOnPage;
492
671
  }
493
672
  return null;
494
673
  }
674
+ function setHeaderRepeat(rows, enabled) {
675
+ for (const row of rows) {
676
+ if (row.type !== "header") break;
677
+ row.repeatOnPage = enabled;
678
+ }
679
+ }
495
680
  function applyBorderPreset(rows, rect, preset, border) {
496
681
  for (let r = rect.r1; r <= rect.r2; r++) {
497
682
  for (let c = rect.c1; c <= rect.c2; c++) {
@@ -771,6 +956,7 @@ var PROPERTY_REGISTRY = [
771
956
  group: "appearance",
772
957
  groupLabel: "\u5916\u89C2",
773
958
  items: [
959
+ { key: "ap-font-family", keywords: ["\u5B57\u4F53", "\u5B57\u4F53\u540D", "\u5B57\u4F53\u65CF", "fontfamily"] },
774
960
  { key: "ap-font-size", keywords: ["\u5B57\u4F53\u5927\u5C0F", "fontsize", "\u5B57\u53F7"] },
775
961
  { key: "ap-font-weight", keywords: ["\u5B57\u4F53\u7C97\u7EC6", "\u7C97\u4F53", "bold"] },
776
962
  { key: "ap-align", keywords: ["\u5BF9\u9F50", "align"] },
@@ -778,7 +964,25 @@ var PROPERTY_REGISTRY = [
778
964
  { key: "ap-color", keywords: ["\u989C\u8272", "color"] },
779
965
  { key: "ap-bg-color", keywords: ["\u80CC\u666F\u8272", "background"] },
780
966
  { key: "ap-line-height", keywords: ["\u884C\u9AD8", "lineheight"] },
781
- { key: "ap-letter-spacing", keywords: ["\u5B57\u95F4\u8DDD", "letterspacing"] }
967
+ { key: "ap-letter-spacing", keywords: ["\u5B57\u95F4\u8DDD", "letterspacing"] },
968
+ { key: "ap-text-fit", keywords: ["\u6EA2\u51FA", "\u6EA2\u51FA\u663E\u793A", "\u622A\u65AD", "\u81EA\u52A8\u7F29\u5C0F", "\u81EA\u9002\u5E94\u884C\u9AD8", "textfit", "overflow"] },
969
+ { key: "ap-shrink-min", keywords: ["\u6700\u5C0F\u5B57\u53F7", "\u7F29\u653E\u4E0B\u9650", "shrinkminfontsize"] },
970
+ { key: "ap-word-wrap", keywords: ["\u81EA\u52A8\u6362\u884C", "\u4E0D\u6362\u884C", "wordwrap"] }
971
+ ]
972
+ },
973
+ {
974
+ group: "code",
975
+ groupLabel: "\u6761\u7801\u8BBE\u7F6E",
976
+ items: [
977
+ { key: "cd-type", keywords: ["\u7801\u5236", "\u6761\u7801\u7C7B\u578B", "\u6761\u5F62\u7801\u7C7B\u578B", "barcodetype", "code128", "ean13", "code39", "itf14"] },
978
+ { key: "cd-dpi", keywords: ["\u6253\u5370\u673A\u5206\u8FA8\u7387", "dpi", "\u6253\u5370\u70B9", "\u70B9\u9635\u5BF9\u9F50", "203", "300"] },
979
+ { key: "cd-bar-width", keywords: ["\u6761\u5BBD", "\u6761\u7C97\u7EC6", "barwidth", "\u6A21\u5757\u5BBD"] },
980
+ { key: "cd-show-text", keywords: ["\u663E\u793A\u6587\u672C", "\u6761\u7801\u6587\u672C", "\u663E\u793A\u6570\u5B57"] },
981
+ { key: "cd-font-size", keywords: ["\u6761\u7801\u5B57\u53F7", "\u6587\u672C\u5B57\u53F7"] },
982
+ { key: "cd-ec-level", keywords: ["\u7EA0\u9519\u7EA7\u522B", "\u5BB9\u9519", "qrcodelevel"] },
983
+ { key: "cd-fit", keywords: ["\u7F29\u653E\u6A21\u5F0F", "\u62C9\u4F38", "\u7B49\u6BD4", "fit", "contain", "cover"] },
984
+ { key: "cd-max-width", keywords: ["\u6700\u5927\u5BBD\u5EA6", "\u6700\u5927\u5BBD", "maxwidth"] },
985
+ { key: "cd-max-height", keywords: ["\u6700\u5927\u9AD8\u5EA6", "\u6700\u5927\u9AD8", "maxheight"] }
782
986
  ]
783
987
  },
784
988
  {
@@ -1382,6 +1586,113 @@ var Parser = class {
1382
1586
  }
1383
1587
  };
1384
1588
 
1589
+ // src/numeric.ts
1590
+ var MAX_DIGITS = 20;
1591
+ var EXTRA_SCALE = 8;
1592
+ function toNumber(value, fallback = 0) {
1593
+ if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
1594
+ if (typeof value === "string") {
1595
+ const s = value.trim();
1596
+ if (s === "") return fallback;
1597
+ const n = Number(s);
1598
+ return Number.isFinite(n) ? n : fallback;
1599
+ }
1600
+ if (value == null) return fallback;
1601
+ if (typeof value === "boolean") return value ? 1 : 0;
1602
+ return fallback;
1603
+ }
1604
+ function isNumericLike(value) {
1605
+ if (typeof value === "number") return Number.isFinite(value);
1606
+ if (typeof value === "string") {
1607
+ const s = value.trim();
1608
+ return s !== "" && Number.isFinite(Number(s));
1609
+ }
1610
+ return false;
1611
+ }
1612
+ function normalizeFloat(value) {
1613
+ if (!Number.isFinite(value) || Number.isInteger(value)) return value;
1614
+ if (Math.abs(value) >= 1e15) return value;
1615
+ return Number(value.toPrecision(12));
1616
+ }
1617
+ function decimalsOf(n) {
1618
+ if (!Number.isFinite(n) || Number.isInteger(n)) return 0;
1619
+ const s = String(n);
1620
+ if (s.includes("e") || s.includes("E")) return 0;
1621
+ const i = s.indexOf(".");
1622
+ return i === -1 ? 0 : s.length - i - 1;
1623
+ }
1624
+ function rescale(value, decimals) {
1625
+ if (!Number.isFinite(value)) return 0;
1626
+ const d = Math.min(Math.max(decimals, 0), 12);
1627
+ if (d === 0) return Math.round(value);
1628
+ const f = 10 ** d;
1629
+ const scaled = value * f;
1630
+ if (!Number.isFinite(scaled) || Math.abs(scaled) >= 1e15) return normalizeFloat(value);
1631
+ return Math.round(scaled) / f;
1632
+ }
1633
+ function decimalAdd(left, right) {
1634
+ const a = toNumber(left);
1635
+ const b = toNumber(right);
1636
+ const raw = a + b;
1637
+ return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
1638
+ }
1639
+ function decimalSubtract(left, right) {
1640
+ const a = toNumber(left);
1641
+ const b = toNumber(right);
1642
+ const raw = a - b;
1643
+ return rescale(raw, Math.max(decimalsOf(a), decimalsOf(b)));
1644
+ }
1645
+ function decimalMultiply(left, right) {
1646
+ const a = toNumber(left);
1647
+ const b = toNumber(right);
1648
+ const raw = a * b;
1649
+ return rescale(raw, decimalsOf(a) + decimalsOf(b));
1650
+ }
1651
+ function decimalDivide(left, right) {
1652
+ const a = toNumber(left);
1653
+ const b = toNumber(right);
1654
+ if (b === 0) return 0;
1655
+ return normalizeFloat(a / b);
1656
+ }
1657
+ function decimalModulo(left, right) {
1658
+ const a = toNumber(left);
1659
+ const b = toNumber(right);
1660
+ if (b === 0) return 0;
1661
+ return normalizeFloat(a % b);
1662
+ }
1663
+ function normalizeDigits(digits) {
1664
+ if (digits === void 0 || digits === null) return 2;
1665
+ const d = Math.trunc(toNumber(digits, 2));
1666
+ if (!Number.isFinite(d)) return 2;
1667
+ return Math.min(Math.max(d, -MAX_DIGITS), MAX_DIGITS);
1668
+ }
1669
+ function roundTo(value, digits, mode) {
1670
+ const n = toNumber(value, 0);
1671
+ const d = normalizeDigits(digits);
1672
+ if (n === 0) return 0;
1673
+ if (Math.abs(n) >= 1e21) return normalizeFloat(n);
1674
+ const scale = Math.max(d, 0) + EXTRA_SCALE;
1675
+ const fixed = n.toFixed(scale);
1676
+ if (!/^-?\d+(\.\d+)?$/.test(fixed)) return normalizeFloat(n);
1677
+ const negative = fixed.startsWith("-");
1678
+ const digitsStr = fixed.replace("-", "").replace(".", "");
1679
+ let scaled = BigInt(digitsStr || "0");
1680
+ if (negative) scaled = -scaled;
1681
+ const pow10 = 10n ** BigInt(scale - d);
1682
+ const sign = scaled < 0n ? -1n : 1n;
1683
+ const abs = scaled < 0n ? -scaled : scaled;
1684
+ let q = abs / pow10;
1685
+ const r = abs % pow10;
1686
+ const doubled = 2n * r;
1687
+ if (mode === "up") {
1688
+ if (r !== 0n) q += 1n;
1689
+ } else if (mode !== "down" && doubled >= pow10) {
1690
+ if (doubled > pow10 || mode === "half-up" || q % 2n === 1n) q += 1n;
1691
+ }
1692
+ const result = sign * q;
1693
+ return normalizeFloat(d >= 0 ? Number(result) / 10 ** d : Number(result) * 10 ** -d);
1694
+ }
1695
+
1385
1696
  // src/evaluator.ts
1386
1697
  var SAFE_GLOBALS = {
1387
1698
  Math,
@@ -1447,16 +1758,17 @@ function evalBinary(node, context, functions) {
1447
1758
  const left = evaluate(node.left, context, functions);
1448
1759
  const right = evaluate(node.right, context, functions);
1449
1760
  switch (node.op) {
1761
+ // 数值运算统一走十进制精确实现:消除 0.1 + 0.2 类浮点噪声、字符串数字按数值处理、除零兜底 0
1450
1762
  case "+":
1451
- return left + right;
1763
+ return isNumericOperands(left, right) ? decimalAdd(left, right) : concatOperands(left, right);
1452
1764
  case "-":
1453
- return left - right;
1765
+ return decimalSubtract(left, right);
1454
1766
  case "*":
1455
- return left * right;
1767
+ return decimalMultiply(left, right);
1456
1768
  case "/":
1457
- return left / right;
1769
+ return decimalDivide(left, right);
1458
1770
  case "%":
1459
- return left % right;
1771
+ return decimalModulo(left, right);
1460
1772
  case "<":
1461
1773
  return left < right;
1462
1774
  case ">":
@@ -1485,15 +1797,29 @@ function evalUnary(node, context, functions) {
1485
1797
  const arg = evaluate(node.arg, context, functions);
1486
1798
  switch (node.op) {
1487
1799
  case "-":
1488
- return -arg;
1800
+ return decimalSubtract(0, arg);
1489
1801
  case "+":
1490
- return +arg;
1802
+ return toNumber(arg);
1491
1803
  case "!":
1492
1804
  return !arg;
1493
1805
  default:
1494
1806
  throw new Error(`\u672A\u77E5\u4E00\u5143\u8FD0\u7B97\u7B26: ${node.op}`);
1495
1807
  }
1496
1808
  }
1809
+ function concatOperands(left, right) {
1810
+ return `${left == null ? "" : String(left)}${right == null ? "" : String(right)}`;
1811
+ }
1812
+ function isNumericOperands(left, right) {
1813
+ const l = operandKind(left);
1814
+ const r = operandKind(right);
1815
+ if (l === "other" || r === "other") return false;
1816
+ return l === "num" || r === "num";
1817
+ }
1818
+ function operandKind(value) {
1819
+ if (value == null || value === "") return "neutral";
1820
+ if (isNumericLike(value)) return "num";
1821
+ return "other";
1822
+ }
1497
1823
  function evalMember(node, context, functions) {
1498
1824
  const obj = evaluate(node.object, context, functions);
1499
1825
  if (obj == null) {
@@ -1705,6 +2031,34 @@ function max(rows, field) {
1705
2031
  return Math.max(...rows.map((row) => Number(getByPath(row, field)) || 0));
1706
2032
  }
1707
2033
 
2034
+ // src/functions/math.ts
2035
+ function addNumbers(...values) {
2036
+ return values.reduce((acc, v) => decimalAdd(acc, v), 0);
2037
+ }
2038
+ function subtractNumbers(...values) {
2039
+ if (values.length === 0) return 0;
2040
+ return values.slice(1).reduce((acc, v) => decimalSubtract(acc, v), decimalAdd(values[0], 0));
2041
+ }
2042
+ function multiplyNumbers(...values) {
2043
+ if (values.length === 0) return 0;
2044
+ return values.reduce((acc, v) => decimalMultiply(acc, v), 1);
2045
+ }
2046
+ function divideNumbers(a, b) {
2047
+ return decimalDivide(a, b);
2048
+ }
2049
+ function round(value, digits) {
2050
+ return roundTo(value, digits, "half-up");
2051
+ }
2052
+ function roundUp(value, digits) {
2053
+ return roundTo(value, digits, "up");
2054
+ }
2055
+ function roundDown(value, digits) {
2056
+ return roundTo(value, digits, "down");
2057
+ }
2058
+ function roundHalfEven(value, digits) {
2059
+ return roundTo(value, digits, "half-even");
2060
+ }
2061
+
1708
2062
  // src/render/expression-eval.ts
1709
2063
  var RenderEngine = class {
1710
2064
  constructor() {
@@ -1730,13 +2084,28 @@ var FORMAT_FUNCTIONS = {
1730
2084
  IF: ifFn,
1731
2085
  CONCAT: (...args) => args.filter((v) => v != null).map(String).join(""),
1732
2086
  IFEMPTY: (v, d) => v != null && v !== "" ? String(v) : d,
1733
- ROUND: (n, d) => Number(Number(n).toFixed(d)),
1734
- LEN: (s) => String(s).length
2087
+ LEN: (s) => String(s).length,
2088
+ // 四则运算(与 +/-/*// 运算符同一套数值语义,供不方便写运算符的场景使用)
2089
+ ADD: addNumbers,
2090
+ SUB: subtractNumbers,
2091
+ MUL: multiplyNumbers,
2092
+ DIV: divideNumbers,
2093
+ // 数值修约
2094
+ ROUND: round,
2095
+ ROUNDUP: roundUp,
2096
+ CEIL: roundUp,
2097
+ ROUNDDOWN: roundDown,
2098
+ FLOOR: roundDown,
2099
+ ROUNDBANK: roundHalfEven
1735
2100
  };
1736
2101
  for (const [name, fn] of Object.entries(FORMAT_FUNCTIONS)) {
1737
2102
  engine.registerFunction(name, fn);
1738
2103
  }
1739
2104
 
2105
+ // src/render/watermark.ts
2106
+ var PX_PER_MM = 96 / 25.4;
2107
+ var MM_PER_PX = 25.4 / 96;
2108
+
1740
2109
  // src/index.ts
1741
2110
  var TemplateEngine = class {
1742
2111
  constructor(options) {
@@ -1772,7 +2141,18 @@ var EXTENDED_FUNCTIONS = {
1772
2141
  },
1773
2142
  SUBSTR: (str, start, len) => String(str).slice(start, len ? start + len : void 0),
1774
2143
  LEN: (str) => String(str).length,
1775
- ROUND: (num, decimals) => Number(Number(num).toFixed(decimals)),
2144
+ // 四则运算(与 +/-/*// 运算符同一套数值语义,供不方便写运算符的场景使用)
2145
+ ADD: addNumbers,
2146
+ SUB: subtractNumbers,
2147
+ MUL: multiplyNumbers,
2148
+ DIV: divideNumbers,
2149
+ // 数值修约
2150
+ ROUND: round,
2151
+ ROUNDUP: roundUp,
2152
+ CEIL: roundUp,
2153
+ ROUNDDOWN: roundDown,
2154
+ FLOOR: roundDown,
2155
+ ROUNDBANK: roundHalfEven,
1776
2156
  NOW: () => (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
1777
2157
  IFEMPTY: (value, defaultVal) => value != null && value !== "" ? String(value) : defaultVal,
1778
2158
  PAD: (value, len, char) => String(value).padStart(len, char),
@@ -2274,6 +2654,7 @@ function useResize(options) {
2274
2654
  let startRect = { left: 0, top: 0, width: 0, height: 0 };
2275
2655
  let activePoint = null;
2276
2656
  function startResize(point, e) {
2657
+ if (e.button !== 0) return;
2277
2658
  activePoint = point;
2278
2659
  startX = e.clientX;
2279
2660
  startY = e.clientY;
@@ -2346,13 +2727,22 @@ function useBindingDisplay() {
2346
2727
  }
2347
2728
  // Annotate the CommonJS export names for ESM import in node:
2348
2729
  0 && (module.exports = {
2730
+ BARCODE_BAR_HEIGHT_MODULES,
2731
+ BARCODE_MARGIN_BOTTOM_MODULES,
2732
+ BARCODE_MODULE_WIDTH_MM,
2733
+ BARCODE_QUIET_ZONE_MODULES,
2734
+ BARCODE_TEXT_FONT_SIZE_MODULES,
2349
2735
  DEFAULT_DEMO_DATA,
2736
+ DEFAULT_SHRINK_MIN_FONT_SIZE_PT,
2350
2737
  ELEMENT_BINDING_REGISTRY,
2351
2738
  EXTENDED_FUNCTIONS,
2352
2739
  FIT_SCALE_MIN_PERCENT,
2353
2740
  GHOST_BORDER_CSS,
2741
+ LABEL_PAPER_GROUP,
2354
2742
  MIN_COL_WIDTH_MM,
2355
2743
  MIN_SCALE_PERCENT,
2744
+ MIN_SHRINK_FONT_SIZE_PT,
2745
+ MM_PER_INCH,
2356
2746
  PAPER_PRESETS,
2357
2747
  PRESET_COLORS,
2358
2748
  PROPERTY_REGISTRY,
@@ -2360,9 +2750,15 @@ function useBindingDisplay() {
2360
2750
  RULER_THICKNESS,
2361
2751
  ZONE_ALLOWED_TYPES,
2362
2752
  applyBorderPreset,
2753
+ barcodeAvailableBoxMm,
2754
+ barcodePreferredModuleWidthMm,
2755
+ barcodeUnitsPerModule,
2363
2756
  buildRulerTicks,
2364
2757
  calcResizeRect,
2365
2758
  canMergeReason,
2759
+ cellFitCapMm,
2760
+ cellFitKey,
2761
+ cellFitWidthMm,
2366
2762
  chooseMajorStepMM,
2367
2763
  clampResizedColumnWidth,
2368
2764
  clampScalePercent,
@@ -2381,6 +2777,7 @@ function useBindingDisplay() {
2381
2777
  filterGroups,
2382
2778
  finalizeElementZone,
2383
2779
  findMainCell,
2780
+ floorFontSize,
2384
2781
  generateGroupId,
2385
2782
  generateId,
2386
2783
  getDemoData,
@@ -2393,6 +2790,9 @@ function useBindingDisplay() {
2393
2790
  groupFields,
2394
2791
  insertCol,
2395
2792
  insertRow,
2793
+ isContinuousPaperSize,
2794
+ isLabelPaperSize,
2795
+ labelPaperDefaults,
2396
2796
  matchKeywords,
2397
2797
  mergeCells,
2398
2798
  minorStepMM,
@@ -2400,13 +2800,20 @@ function useBindingDisplay() {
2400
2800
  nextWheelScale,
2401
2801
  normalizeSelection,
2402
2802
  normalizeTemplateUnits,
2803
+ parseCellFitKey,
2403
2804
  ptToMm,
2404
2805
  pxToMm,
2405
2806
  resolveBarcodeDesignValue,
2807
+ resolveBarcodeSize,
2406
2808
  resolveCellBorderCss,
2809
+ resolveCellTextFit,
2810
+ resolveElementTextFit,
2811
+ resolveShrinkMinFontSize,
2407
2812
  resolveTextBinding,
2813
+ roundFontSize,
2408
2814
  safeEval,
2409
2815
  searchProperties,
2816
+ setHeaderRepeat,
2410
2817
  setRowType,
2411
2818
  splitCells,
2412
2819
  syncTableElementSize,