templatePhysicalMm.ts
4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* 与 NativeTemplateCommandBuilder.toMillimeter 一致(px 按 96dpi 转 mm),
* 用于判断模板是否适合走 native-fast-printer 的 TSC 模板指令(常见标签幅宽约 4 英寸级)。
*/
import type { SystemLabelTemplate, SystemTemplateElementBase } from './types/printer'
const DESIGN_DPI = 96
const PX_PER_INCH = 96
const PX_PER_CM = 37.8
function toCanvasPx (value: number, unit: string): number {
const u = String(unit || 'inch').toLowerCase()
if (u === 'mm') return (value / 25.4) * PX_PER_INCH
if (u === 'cm') return value * PX_PER_CM
if (u === 'px') return value
return value * PX_PER_INCH
}
function fromCanvasPx (px: number, unit: string): number {
const u = String(unit || 'inch').toLowerCase()
if (u === 'mm') return (px * 25.4) / PX_PER_INCH
if (u === 'cm') return px / PX_PER_CM
if (u === 'px') return px
return px / PX_PER_INCH
}
function roundTemplateDim (value: number, unit: string): number {
const u = String(unit || 'inch').toLowerCase()
if (u === 'px') return Math.max(1, Math.round(value))
if (u === 'mm' || u === 'cm') return Math.round(value * 10) / 10
return Math.round(value * 1000) / 1000
}
function elementBottomPx (el: SystemTemplateElementBase): number {
const y = Number(el.y) || 0
const h = Math.max(0, Number(el.height) || 0)
let bottom = y + h
const type = String(el.type || '').toUpperCase()
const cfg = (el.config || {}) as Record<string, unknown>
if (type === 'BARCODE') {
const showText = String(cfg.showText ?? cfg.ShowText ?? 'true').toLowerCase() !== 'false'
if (showText) bottom += 28
else bottom += 4
}
if (type === 'QRCODE') bottom += 4
return bottom
}
/**
* 原生 printTemplate 的 SIZE 高度仅取自模板根 height,不会按元素自动增高;
* 条码/日期贴在底部时易被裁掉。与 systemTemplateAdapter.buildTscTemplate 增高逻辑对齐。
*/
export function ensureTemplateHeightCoversElements (
template: SystemLabelTemplate,
extraBottomPx = 14
): SystemLabelTemplate {
const unit = String(template.unit || 'inch')
const elements = template.elements || []
if (!elements.length) return template
let maxBottom = 0
for (const el of elements) {
maxBottom = Math.max(maxBottom, elementBottomPx(el))
}
const currentHpx = toCanvasPx(Number(template.height) || 0, unit)
const neededPx = maxBottom + extraBottomPx
if (neededPx <= currentHpx + 1) return template
return {
...template,
height: roundTemplateDim(fromCanvasPx(neededPx, unit), unit),
}
}
/** 常见 4″ 标签机安全上限(mm),略放宽 */
const NATIVE_FAST_MAX_WIDTH_MM = 112
const NATIVE_FAST_MAX_HEIGHT_MM = 320
/** 打印/预览统一:模板根 width/height + unit → 物理毫米(与 NativeTemplateCommandBuilder.toMillimeter 一致) */
export function getTemplatePhysicalSizeMm (
template: Pick<SystemLabelTemplate, 'unit' | 'width' | 'height'>
): { widthMm: number; heightMm: number; unit: string } {
const unit = String(template.unit || 'inch')
const w = Number(template.width) || 0
const h = Number(template.height) || 0
const { widthMm, heightMm } = templateSizeToMillimeters(unit, w, h)
return { widthMm, heightMm, unit }
}
export function templateSizeToMillimeters (
unit: string | undefined,
width: number,
height: number
): { widthMm: number; heightMm: number } {
const u = String(unit || 'inch').toLowerCase()
let widthMm = 0
let heightMm = 0
if (u === 'mm') {
widthMm = width
heightMm = height
} else if (u === 'cm') {
widthMm = width * 10
heightMm = height * 10
} else if (u === 'px') {
widthMm = (width / DESIGN_DPI) * 25.4
heightMm = (height / DESIGN_DPI) * 25.4
} else {
widthMm = width * 25.4
heightMm = height * 25.4
}
return { widthMm, heightMm }
}
export function isTemplateWithinNativeFastPrintBounds (
template: Pick<SystemLabelTemplate, 'unit' | 'width' | 'height'>
): boolean {
const w = Number(template.width) || 0
const h = Number(template.height) || 0
if (w <= 0 || h <= 0) return false
const { widthMm, heightMm } = templateSizeToMillimeters(template.unit, w, h)
return widthMm <= NATIVE_FAST_MAX_WIDTH_MM && heightMm <= NATIVE_FAST_MAX_HEIGHT_MM
}