68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
|
|
/**
|
||
|
|
* ZPL(Zebra Programming Language) 생성
|
||
|
|
* ZD421 등 Zebra 프린터용 라벨 데이터 생성 (200 DPI = 8 dots/mm 기준)
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { BarcodeLabelLayout } from "@/types/barcode";
|
||
|
|
|
||
|
|
const MM_TO_PX = 4;
|
||
|
|
const DOTS_PER_MM = 8; // 200 DPI
|
||
|
|
|
||
|
|
function pxToDots(px: number): number {
|
||
|
|
const mm = px / MM_TO_PX;
|
||
|
|
return Math.round(mm * DOTS_PER_MM);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function generateZPL(layout: BarcodeLabelLayout): string {
|
||
|
|
const { width_mm, height_mm, components } = layout;
|
||
|
|
const widthDots = Math.round(width_mm * DOTS_PER_MM);
|
||
|
|
const heightDots = Math.round(height_mm * DOTS_PER_MM);
|
||
|
|
|
||
|
|
const lines: string[] = [
|
||
|
|
"^XA",
|
||
|
|
"^PW" + widthDots,
|
||
|
|
"^LL" + heightDots,
|
||
|
|
"^LH0,0",
|
||
|
|
];
|
||
|
|
|
||
|
|
const sorted = [...components].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
||
|
|
|
||
|
|
for (const c of sorted) {
|
||
|
|
const x = pxToDots(c.x);
|
||
|
|
const y = pxToDots(c.y);
|
||
|
|
const w = pxToDots(c.width);
|
||
|
|
const h = pxToDots(c.height);
|
||
|
|
|
||
|
|
if (c.type === "text") {
|
||
|
|
const fontH = Math.max(10, Math.min(120, (c.fontSize || 10) * 4)); // 대략적 변환
|
||
|
|
const fontW = Math.round(fontH * 0.6);
|
||
|
|
lines.push(`^FO${x},${y}`);
|
||
|
|
lines.push(`^A0N,${fontH},${fontW}`);
|
||
|
|
lines.push(`^FD${escapeZPL(c.content || "")}^FS`);
|
||
|
|
} else if (c.type === "barcode") {
|
||
|
|
if (c.barcodeType === "QR") {
|
||
|
|
const size = Math.min(w, h);
|
||
|
|
const qrSize = Math.max(1, Math.min(10, Math.round(size / 20)));
|
||
|
|
lines.push(`^FO${x},${y}`);
|
||
|
|
lines.push(`^BQN,2,${qrSize}`);
|
||
|
|
lines.push(`^FDQA,${escapeZPL(c.barcodeValue || "")}^FS`);
|
||
|
|
} else {
|
||
|
|
// CODE128: ^BC, CODE39: ^B3
|
||
|
|
const mod = c.barcodeType === "CODE39" ? "^B3N" : "^BCN";
|
||
|
|
const showText = c.showBarcodeText !== false ? "Y" : "N";
|
||
|
|
lines.push(`^FO${x},${y}`);
|
||
|
|
lines.push(`${mod},${Math.max(20, h - 10)},${showText},N,N`);
|
||
|
|
lines.push(`^FD${escapeZPL(c.barcodeValue || "")}^FS`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// 이미지/선/사각형은 ZPL에서 비트맵 또는 ^GB 등으로 확장 가능 (생략)
|
||
|
|
}
|
||
|
|
|
||
|
|
lines.push("^XZ");
|
||
|
|
return lines.join("\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
function escapeZPL(s: string): string {
|
||
|
|
return s.replace(/\^/g, "^^").replace(/~/g, "~~");
|
||
|
|
}
|