diff --git a/README.md b/README.md index 9f102e7..7871bc1 100644 --- a/README.md +++ b/README.md @@ -34,14 +34,14 @@ The print resolution (dpmm = dots per millimeter) must match your printer. Commo Drag items from the left panel onto the canvas, or double-click them to add at the center. -Available objects: text, serial (auto-incrementing number fields), barcodes (27 symbologies including Code 128, QR, DataMatrix, PDF417, Maxicode), shapes (box, line, ellipse), and images. +Available objects: text, serial (auto-incrementing number fields), barcodes (28 symbologies including Code 128, QR, DataMatrix, PDF417, Maxicode), shapes (box, line, ellipse), and images.
Full list of supported barcode symbologies **1D linear:** Code 128, Code 39, Code 93, Code 11, Interleaved 2 of 5, Standard 2 of 5, Industrial 2 of 5, Codabar, LOGMARS, MSI, Plessey, GS1 Databar, Planet Code, Postal/POSTNET, EAN-13, EAN-8, UPC-A, UPC-E, UPC/EAN 2- or 5-digit supplement, Code 49 -**2D matrix:** QR Code, DataMatrix, PDF417, MicroPDF417, Aztec, Codablock F, Maxicode +**2D matrix:** QR Code, DataMatrix, PDF417, MicroPDF417, Aztec, Codablock F, Maxicode, TLC39
@@ -116,13 +116,13 @@ Both `.zpl` and `.json` round-trip cleanly. `.zpl` preserves all printable conte ## Coverage -93 of the 202 ZPL II commands tracked in the [roadmap](docs/zpl-roadmap.md) are supported today. Categorical breakdown: +94 of the 202 ZPL II commands tracked in the [roadmap](docs/zpl-roadmap.md) are supported today. Categorical breakdown: | Area | Supported | |---|---| | Layout & flow | 16 / 16 | | Templates & variables | 4 / 4 | -| Barcodes | 27 / 28 | +| Barcodes | 28 / 28 | | Fields | 15 / 20 | | Encoding & language | 3 / 4 | | Clock & time | 2 / 3 | diff --git a/docs/zpl-roadmap.md b/docs/zpl-roadmap.md index 7256123..1984281 100644 --- a/docs/zpl-roadmap.md +++ b/docs/zpl-roadmap.md @@ -115,7 +115,7 @@ The Printer Settings Modal (Media & Feed / Print Quality / Clock & Time / Encodi | `[x]` | `^B0` / `^BO` | Aztec | | | `[x]` | `^BB` | CODABLOCK F | | | `[x]` | `^BV` | UPS MaxiCode (also accepts `^BD` on some firmware generations as an alias) | | -| `[ ]` | `^BT` | TLC39 | `Coming soon` | +| `[x]` | `^BT` | TLC39 | | ## Graphics diff --git a/src/components/Canvas/BarcodeObject.tsx b/src/components/Canvas/BarcodeObject.tsx index e943385..b889a79 100644 --- a/src/components/Canvas/BarcodeObject.tsx +++ b/src/components/Canvas/BarcodeObject.tsx @@ -12,6 +12,7 @@ import { getDisplaySize, get1DBwipScale, getEanUpcLayout, + renderTlc39Canvas, type BarcodeDisplaySize, type EanUpcType, } from "./bwipHelpers"; @@ -61,21 +62,28 @@ export function BarcodeObject({ } }, []); - const opts = buildBwipOptions(obj, scale, dpmm); let barcodeCanvas: HTMLCanvasElement | null = null; let errorMsg: string | null = null; - if (opts) { - const canvas = document.createElement("canvas"); - try { - // buildBwipOptions returns Record on purpose: the - // option fields differ across barcode types (ean13 vs code128 vs …) - // and per-type narrowing would duplicate the switch already in - // buildBwipOptions. bwip-js' toCanvas signature uses a strict - // literal-string union, so the structural cast bridges the two. - bwipjs.toCanvas(canvas, opts as unknown as Parameters[1]); - barcodeCanvas = canvas; - } catch (e) { - errorMsg = cleanBwipError(e); + if (obj.type === "tlc39") { + // bwip-js has no native tlc39 encoder; render the Code 39 + MicroPDF417 + // composite ourselves. Goes straight to canvas, bypassing buildBwipOptions. + barcodeCanvas = renderTlc39Canvas(obj.props, scale, dpmm); + if (!barcodeCanvas) errorMsg = "TLC39 render failed"; + } else { + const opts = buildBwipOptions(obj, scale, dpmm); + if (opts) { + const canvas = document.createElement("canvas"); + try { + // buildBwipOptions returns Record on purpose: the + // option fields differ across barcode types (ean13 vs code128 vs …) + // and per-type narrowing would duplicate the switch already in + // buildBwipOptions. bwip-js' toCanvas signature uses a strict + // literal-string union, so the structural cast bridges the two. + bwipjs.toCanvas(canvas, opts as unknown as Parameters[1]); + barcodeCanvas = canvas; + } catch (e) { + errorMsg = cleanBwipError(e); + } } } diff --git a/src/components/Canvas/KonvaObject.tsx b/src/components/Canvas/KonvaObject.tsx index b6394fa..b57ce40 100644 --- a/src/components/Canvas/KonvaObject.tsx +++ b/src/components/Canvas/KonvaObject.tsx @@ -234,6 +234,7 @@ const BARCODE_TYPES = new Set([ "codablock", "upcEanExtension", "code49", + "tlc39", ]); export function KonvaObject(props_: Props) { diff --git a/src/components/Canvas/bwipConstants.ts b/src/components/Canvas/bwipConstants.ts index 7623d16..4b9d335 100644 --- a/src/components/Canvas/bwipConstants.ts +++ b/src/components/Canvas/bwipConstants.ts @@ -29,6 +29,9 @@ export const LOGMARS_TEXT_ZONE_DOTS = 20; // bwip-js adds 3 quiet-zone rows to MicroPDF417 canvas output. export const MICROPDF417_QUIET_ZONE_ROWS = 3; +// bwip-js renders MicroPDF417 at 2 internal px per data row, independent of `rowheight`. +export const MICROPDF417_PX_PER_ROW = 2; + /** * bwip-vs-Zebra width-correction constants for symbologies whose bar * pattern in bwip-js diverges from Zebra firmware. diff --git a/src/components/Canvas/bwipHelpers.ts b/src/components/Canvas/bwipHelpers.ts index 61f14e6..2ba7cb9 100644 --- a/src/components/Canvas/bwipHelpers.ts +++ b/src/components/Canvas/bwipHelpers.ts @@ -31,6 +31,7 @@ import { GS1_DATABAR_PADDING_ROWS, GS1_DATABAR_SPEC_HEIGHT_MODULES, LOGMARS_TEXT_ZONE_DOTS, + MICROPDF417_PX_PER_ROW, MICROPDF417_QUIET_ZONE_ROWS, PLESSEY_BWIP_TO_ZEBRA_WIDTH_RATIO, UPC_SUPP_TEXT_ZONE_DOTS, @@ -965,9 +966,7 @@ function getUprightDisplaySize( } case "micropdf417": { const p = obj.props; - // bwip-js ignores rowheight for micropdf417 and always uses 2 internal pixels per row. - // It also adds MICROPDF417_QUIET_ZONE_ROWS quiet-zone rows (top+bottom) to the canvas. - const numRows = Math.max(0, ch / (BWIP_SCALE * 2) - MICROPDF417_QUIET_ZONE_ROWS); + const numRows = micropdfDataRows(ch); const w = (cw / BWIP_SCALE) * dotsToPx(p.moduleWidth, scale, dpmm); const h = numRows * dotsToPx(p.rowHeight, scale, dpmm); @@ -991,3 +990,146 @@ function getUprightDisplaySize( } } } + +/** Valid MicroPDF417 row counts in TLC39's linked 4-column geometry. */ +export const TLC39_MICROPDF_ROW_COUNTS = [4, 6, 8, 10] as const; + +/** Snap to the nearest valid row count; bwip-js throws on any other value. */ +export function snapTlc39MicroPdfRows(requested: number): number { + if (!Number.isFinite(requested)) return 4; + for (const r of TLC39_MICROPDF_ROW_COUNTS) if (requested <= r) return r; + return 10; +} + +/** Data-row count from a bwip-js MicroPDF417 canvas height (assumes scale=BWIP_SCALE). */ +function micropdfDataRows(canvasHeight: number): number { + return Math.max( + 0, + canvasHeight / (BWIP_SCALE * MICROPDF417_PX_PER_ROW) + - MICROPDF417_QUIET_ZONE_ROWS, + ); +} + +/** TLC39 spec splits content on the first comma: ECI (6 digits) for + * the Code 39 base line, serial (≤25 alphanum) for the MicroPDF417 + * block stacked on top. The "T" linkage flag is appended to the + * Code 39 data when a MicroPDF417 follows; the leading "S" data + * identifier (if any) is stripped from the serial before MicroPDF + * encoding. */ +export function splitTlc39Content(content: string): { eci: string; serial: string } { + if (!content) return { eci: "", serial: "" }; + const comma = content.indexOf(","); + if (comma < 0) return { eci: content, serial: "" }; + const eci = content.slice(0, comma); + let serial = content.slice(comma + 1); + if (serial.startsWith("S")) serial = serial.slice(1); + return { eci, serial }; +} + +interface Tlc39RenderProps { + content: string; + moduleWidth: number; + height: number; + microPdfRowHeight: number; + microPdfRows: number; +} + +/** TLC39 composite (MicroPDF417 on top, Code 39 base below). bwip-js has + * no native encoder; composes two encoders at dpmm-aware display sizes + * per the TCIF spec (shared width, no separator). */ +export function renderTlc39Canvas( + props: Tlc39RenderProps, + scale: number, + dpmm: number, +): HTMLCanvasElement | null { + const { eci, serial } = splitTlc39Content(props.content); + const bwipScale = get1DBwipScale(props.moduleWidth, scale, dpmm); + const modulePx = dotsToPx(props.moduleWidth, scale, dpmm); + const code39H = dotsToPx(props.height, scale, dpmm); + + const renderCode39 = (text: string): HTMLCanvasElement | null => { + const c = document.createElement("canvas"); + try { + bwipjs.toCanvas(c, { + bcid: "code39", + text: text || " ", + scale: bwipScale, + // bwip `height` is in mm at 1x; constant source height + // (stretched to dpmm-correct dots below) matches the standalone + // 1D pattern. + height: 10, + includetext: false, + } as unknown as Parameters[1]); + } catch { + return null; + } + return c; + }; + + const stretchTo = ( + src: HTMLCanvasElement, + targetW: number, + targetH: number, + ): HTMLCanvasElement | null => { + const out = document.createElement("canvas"); + out.width = Math.max(1, Math.round(targetW)); + out.height = Math.max(1, Math.round(targetH)); + const c = out.getContext("2d"); + if (!c) return null; + c.fillStyle = "white"; + c.fillRect(0, 0, out.width, out.height); + c.imageSmoothingEnabled = false; + c.drawImage(src, 0, 0, out.width, out.height); + return out; + }; + + if (!serial) { + const src = renderCode39(eci); + if (!src) return null; + const w = (src.width / bwipScale) * modulePx; + return stretchTo(src, w, code39H); + } + + // Render MicroPDF first; the "T" linkage flag is only appended to + // Code 39 when the linked MicroPDF actually rendered, otherwise the + // flag would claim a link that does not exist. + const snappedRows = snapTlc39MicroPdfRows(props.microPdfRows); + const mpdfSrc = document.createElement("canvas"); + let mpdfOk = true; + try { + bwipjs.toCanvas(mpdfSrc, { + bcid: "micropdf417", + text: serial, + scale: BWIP_SCALE, + rows: snappedRows, + // TLC39 spec: linked MicroPDF417 is fixed at 4 columns. + columns: 4, + } as unknown as Parameters[1]); + } catch { + mpdfOk = false; + } + + const code39Src = renderCode39(mpdfOk ? `${eci}T` : eci); + if (!code39Src) return null; + const code39W = (code39Src.width / bwipScale) * modulePx; + + if (!mpdfOk) return stretchTo(code39Src, code39W, code39H); + + const mpdfW = (mpdfSrc.width / BWIP_SCALE) * modulePx; + const mpdfH = snappedRows * dotsToPx(props.microPdfRowHeight, scale, dpmm); + + const w = Math.max(1, Math.round(Math.max(code39W, mpdfW))); + const mpdfPxH = Math.max(1, Math.round(mpdfH)); + const code39PxH = Math.max(1, Math.round(code39H)); + const composite = document.createElement("canvas"); + composite.width = w; + composite.height = mpdfPxH + code39PxH; + const ctx = composite.getContext("2d"); + if (!ctx) return null; + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, composite.width, composite.height); + ctx.imageSmoothingEnabled = false; + ctx.drawImage(mpdfSrc, 0, 0, w, mpdfPxH); + ctx.drawImage(code39Src, 0, mpdfPxH, w, code39PxH); + return composite; +} diff --git a/src/lib/zplParser.test.ts b/src/lib/zplParser.test.ts index 8db60ba..cedf138 100644 --- a/src/lib/zplParser.test.ts +++ b/src/lib/zplParser.test.ts @@ -1388,6 +1388,117 @@ describe('parseZPL — change caret / tilde / delimiter (^CC ^CT ^CD)', () => { }); }); +// ── ^BT TLC39 ───────────────────────────────────────────────────────────────── + +describe('parseZPL — ^BT TLC39', () => { + it('parses ^BT with full param set into a tlc39 object', () => { + const zpl = '^XA^FO50,50^BTN,3,3,60,5,3^FD123456,ABC^FS^XZ'; + const { objects } = parseZPL(zpl, 8); + expect(objects).toHaveLength(1); + expect(objects[0]?.type).toBe('tlc39'); + const p = props(objects[0]); + expect(p.content).toBe('123456,ABC'); + expect(p.moduleWidth).toBe(3); + expect(p.height).toBe(60); + expect(p.microPdfRowHeight).toBe(5); + expect(p.microPdfRows).toBe(3); + expect(p.rotation).toBe('N'); + }); + + it('falls back to ^BY moduleWidth when ^BT w1 is empty', () => { + const zpl = '^XA^FO0,0^BY5^BTN,,3,40,4,4^FD123456,X^FS^XZ'; + const { objects } = parseZPL(zpl, 8); + expect(props(objects[0]).moduleWidth).toBe(5); + }); + + it('preserves microPdfRows mid-range', () => { + const zpl = '^XA^FO0,0^BTN,2,3,40,4,6^FD123456,X^FS^XZ'; + const { objects } = parseZPL(zpl, 8); + expect(props(objects[0]).microPdfRows).toBe(6); + }); + + it('accepts microPdfRows=1 and =10 (^BT spec bounds)', () => { + const z1 = parseZPL('^XA^FO0,0^BTN,2,3,40,4,1^FD123456,X^FS^XZ', 8).objects; + expect(props(z1[0]).microPdfRows).toBe(1); + const z10 = parseZPL('^XA^FO0,0^BTN,2,3,40,4,10^FD123456,X^FS^XZ', 8).objects; + expect(props(z10[0]).microPdfRows).toBe(10); + }); + + it('rejects microPdfRows outside ^BT spec range (-1, 0, 11, 20) and falls back to default 4', () => { + for (const v of [-1, 0, 11, 20]) { + const zpl = `^XA^FO0,0^BTN,2,3,40,4,${v}^FD123456,X^FS^XZ`; + const { objects } = parseZPL(zpl, 8); + expect(props(objects[0]).microPdfRows, `rows=${v}`).toBe(4); + } + }); + + it('drops non-canonical r1 on round-trip (re-emits as 2)', async () => { + const { ObjectRegistry } = await import('../registry'); + const { objects } = parseZPL('^XA^FO0,0^BTN,2,3,40,4,4^FD123,X^FS^XZ', 8); + const emitted = ObjectRegistry.tlc39.toZPL(objects[0] as never); + expect(emitted).toContain('^BTN,2,2,40,4,4'); + }); + + it('round-trips ^BT without serial (no MicroPDF block, no trailing comma)', async () => { + const { ObjectRegistry } = await import('../registry'); + const original = '^XA^FO10,20^BY2^BTN,2,2,40,4,4^FD123456^FS^XZ'; + const { objects } = parseZPL(original, 8); + expect(objects[0]?.type).toBe('tlc39'); + expect(props(objects[0]).content).toBe('123456'); + const emitted = ObjectRegistry.tlc39.toZPL(objects[0] as never); + expect(emitted).toMatch(/\^FD123456\^FS/); + const { objects: round2 } = parseZPL(`^XA${emitted}^XZ`, 8); + expect(props(round2[0]).content).toBe('123456'); + }); + + it('round-trips ^BT via parse → toZPL → parse', async () => { + const { ObjectRegistry } = await import('../registry'); + const original = '^XA^FO50,60^BY3^BTN,3,2,80,5,8^FD654321,ABCDEF^FS^XZ'; + const { objects } = parseZPL(original, 8); + expect(objects).toHaveLength(1); + const emitted = ObjectRegistry.tlc39.toZPL(objects[0] as never); + expect(emitted).toContain('^BTN,3,2,80,5,8'); + expect(emitted).toContain('^FD654321,ABCDEF'); + const { objects: round2 } = parseZPL(`^XA${emitted}^XZ`, 8); + expect(round2[0]?.type).toBe('tlc39'); + expect(props(round2[0])).toMatchObject(props(objects[0])); + }); +}); + +describe('snapTlc39MicroPdfRows', () => { + it('snaps in-range requests up to the nearest valid {4,6,8,10}', async () => { + const { snapTlc39MicroPdfRows } = await import('../components/Canvas/bwipHelpers'); + expect(snapTlc39MicroPdfRows(1)).toBe(4); + expect(snapTlc39MicroPdfRows(4)).toBe(4); + expect(snapTlc39MicroPdfRows(5)).toBe(6); + expect(snapTlc39MicroPdfRows(7)).toBe(8); + expect(snapTlc39MicroPdfRows(9)).toBe(10); + expect(snapTlc39MicroPdfRows(10)).toBe(10); + }); + + it('clamps above-range to the highest valid count', async () => { + const { snapTlc39MicroPdfRows } = await import('../components/Canvas/bwipHelpers'); + expect(snapTlc39MicroPdfRows(99)).toBe(10); + }); +}); + +describe('splitTlc39Content', () => { + it('splits on first comma; ECI before, serial after', async () => { + const { splitTlc39Content } = await import('../components/Canvas/bwipHelpers'); + expect(splitTlc39Content('123456,ABC123')).toEqual({ eci: '123456', serial: 'ABC123' }); + }); + + it('strips a leading S data identifier from the serial', async () => { + const { splitTlc39Content } = await import('../components/Canvas/bwipHelpers'); + expect(splitTlc39Content('123456,SXYZ789')).toEqual({ eci: '123456', serial: 'XYZ789' }); + }); + + it('returns empty serial when no comma is present', async () => { + const { splitTlc39Content } = await import('../components/Canvas/bwipHelpers'); + expect(splitTlc39Content('123456')).toEqual({ eci: '123456', serial: '' }); + }); +}); + // ── ^IM image reference ─────────────────────────────────────────────────────── describe('parseZPL — ^IM image reference', () => { diff --git a/src/lib/zplParser/context.ts b/src/lib/zplParser/context.ts index 1e95162..446660e 100644 --- a/src/lib/zplParser/context.ts +++ b/src/lib/zplParser/context.ts @@ -134,6 +134,11 @@ export interface FieldState { mpdfRowHeight: number; cbRowHeight: number; cbSecurity: CodablockProps["securityLevel"]; + // TLC39 pending + tlcModuleWidth: number | undefined; + tlcHeight: number; + tlcMicroPdfRowHeight: number; + tlcMicroPdfRows: number; // Pending font reference (^A@ or ^A{id}) — mutually exclusive pendingPrinterFontName: string | undefined; pendingFontId: string | undefined; @@ -260,6 +265,10 @@ export function createParserState(): ParserState { mpdfRowHeight: 10, cbRowHeight: 10, cbSecurity: "Y", + tlcModuleWidth: undefined, + tlcHeight: 40, + tlcMicroPdfRowHeight: 4, + tlcMicroPdfRows: 4, pendingPrinterFontName: undefined, pendingFontId: undefined, snPending: false, diff --git a/src/lib/zplParser/flushField.ts b/src/lib/zplParser/flushField.ts index 9fe2b17..19eea43 100644 --- a/src/lib/zplParser/flushField.ts +++ b/src/lib/zplParser/flushField.ts @@ -29,6 +29,7 @@ import type { AztecProps } from "../../registry/aztec"; import type { MaxicodeProps } from "../../registry/maxicode"; import type { MicroPdf417Props } from "../../registry/micropdf417"; import type { CodablockProps } from "../../registry/codablock"; +import type { Tlc39Props } from "../../registry/tlc39"; import { decodeFH, makeObj, variableNameFromComment } from "./helpers"; import { getPosType, type ParserState, REVERSE_BBOX_TOLERANCE_DOTS } from "./context"; @@ -477,6 +478,25 @@ export function createFlushField( ), ); break; + case "tlc39": + objects.push( + makeObj( + "tlc39", + s.field.x, + s.field.y, + { + content, + moduleWidth: s.field.tlcModuleWidth ?? s.defaults.byModuleWidth, + height: s.field.tlcHeight, + microPdfRowHeight: s.field.tlcMicroPdfRowHeight, + microPdfRows: s.field.tlcMicroPdfRows, + rotation: s.field.bcRotation, + } satisfies Tlc39Props, + posType, + comment, + ), + ); + break; case "symbol": { // ^GS payload is a single letter A..E selecting the glyph. // Anything else falls back to DEFAULT_GS_SYMBOL so a malformed diff --git a/src/lib/zplParser/handlers/barcodes.ts b/src/lib/zplParser/handlers/barcodes.ts index 64be4a5..b9e5345 100644 --- a/src/lib/zplParser/handlers/barcodes.ts +++ b/src/lib/zplParser/handlers/barcodes.ts @@ -151,5 +151,21 @@ export function createBarcodeHandlers( field.cbRowHeight = int(p[1], 10); field.cbSecurity = (p[2] ?? "Y") === "N" ? "N" : "Y"; }, + + // ^BTo,w1,r1,h1,w2,h2: TLC39 (Code 39 + optional MicroPDF417 stack) + BT(p) { + field.fieldType = "tlc39"; + field.bcRotation = readRotation(p[0]); + // w1 (Code 39 narrow bar) overrides ^BY when present; undefined + // means fall back to defaults.byModuleWidth at flush time. + field.tlcModuleWidth = int(p[1]) || undefined; + // p[2] (r1) intentionally dropped; canonicalized on emit. + field.tlcHeight = int(p[3], defaults.byHeight || 40); + field.tlcMicroPdfRowHeight = int(p[4], 4); + // Zebra ^BT h2 range is 1-10; firmware snaps to a valid linked + // MicroPDF417 row count {4,6,8,10} on print. + const rows = int(p[5], 4); + field.tlcMicroPdfRows = rows >= 1 && rows <= 10 ? rows : 4; + }, }; } diff --git a/src/locales/ar.ts b/src/locales/ar.ts index a15365e..a4b6d33 100644 --- a/src/locales/ar.ts +++ b/src/locales/ar.ts @@ -45,6 +45,7 @@ const ar = { maxicode: 'ماكسي كود', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'مجموعة', }, @@ -584,6 +585,13 @@ const ar = { moduleWidth: 'عرض الوحدة', security: 'فحص الأمان', }, + tlc39: { + content: 'المحتوى', + height: 'الارتفاع (نقاط)', + moduleWidth: 'عرض الوحدة', + microPdfRowHeight: 'ارتفاع صف MicroPDF', + microPdfRows: 'صفوف MicroPDF', + }, }, layers: { diff --git a/src/locales/bg.ts b/src/locales/bg.ts index 2d8fbf2..30746d9 100644 --- a/src/locales/bg.ts +++ b/src/locales/bg.ts @@ -45,6 +45,7 @@ const bg = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Група', }, @@ -584,6 +585,13 @@ const bg = { moduleWidth: 'Ширина на модул', security: 'Проверка за сигурност', }, + tlc39: { + content: 'Съдържание', + height: 'Височина (точки)', + moduleWidth: 'Ширина на модула', + microPdfRowHeight: 'Височина на ред MicroPDF', + microPdfRows: 'Редове MicroPDF', + }, }, layers: { diff --git a/src/locales/cs.ts b/src/locales/cs.ts index fa53a12..fb5690c 100644 --- a/src/locales/cs.ts +++ b/src/locales/cs.ts @@ -45,6 +45,7 @@ const cs = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Skupina', }, @@ -584,6 +585,13 @@ const cs = { moduleWidth: 'Šířka modulu', security: 'Bezpečnostní kontrola', }, + tlc39: { + content: 'Obsah', + height: 'Výška (body)', + moduleWidth: 'Šířka modulu', + microPdfRowHeight: 'Výška řádku MicroPDF', + microPdfRows: 'Řádky MicroPDF', + }, }, layers: { diff --git a/src/locales/da.ts b/src/locales/da.ts index a878006..ca4e542 100644 --- a/src/locales/da.ts +++ b/src/locales/da.ts @@ -45,6 +45,7 @@ const da = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Gruppe', }, @@ -584,6 +585,13 @@ const da = { moduleWidth: 'Modulbredde', security: 'Sikkerhedskontrol', }, + tlc39: { + content: 'Indhold', + height: 'Højde (punkter)', + moduleWidth: 'Modulbredde', + microPdfRowHeight: 'MicroPDF rækkehøjde', + microPdfRows: 'MicroPDF rækker', + }, }, layers: { diff --git a/src/locales/de.ts b/src/locales/de.ts index 971a79f..ee0bcb6 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -45,6 +45,7 @@ const de = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Gruppe', }, @@ -605,6 +606,13 @@ const de = { moduleWidth: 'Modulbreite', security: 'Sicherheitsprüfung', }, + tlc39: { + content: 'Inhalt', + height: 'Höhe (Punkte)', + moduleWidth: 'Modulbreite', + microPdfRowHeight: 'MicroPDF Zeilenhöhe', + microPdfRows: 'MicroPDF Zeilen', + }, }, layers: { diff --git a/src/locales/el.ts b/src/locales/el.ts index a439fdf..f712148 100644 --- a/src/locales/el.ts +++ b/src/locales/el.ts @@ -45,6 +45,7 @@ const el = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Ομάδα', }, @@ -584,6 +585,13 @@ const el = { moduleWidth: 'Πλάτος μονάδας', security: 'Έλεγχος ασφαλείας', }, + tlc39: { + content: 'Περιεχόμενο', + height: 'Ύψος (κουκκίδες)', + moduleWidth: 'Πλάτος μονάδας', + microPdfRowHeight: 'Ύψος γραμμής MicroPDF', + microPdfRows: 'Γραμμές MicroPDF', + }, }, layers: { diff --git a/src/locales/en.ts b/src/locales/en.ts index 2836242..f9c7532 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -45,6 +45,7 @@ const en = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Group', }, @@ -605,6 +606,13 @@ const en = { moduleWidth: 'Module width', security: 'Security check', }, + tlc39: { + content: 'Content', + height: 'Height (dots)', + moduleWidth: 'Module width', + microPdfRowHeight: 'MicroPDF row height', + microPdfRows: 'MicroPDF rows', + }, }, layers: { diff --git a/src/locales/es.ts b/src/locales/es.ts index 79ac4a4..ae27601 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -45,6 +45,7 @@ const es = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grupo', }, @@ -584,6 +585,13 @@ const es = { moduleWidth: 'Ancho módulo', security: 'Control de seguridad', }, + tlc39: { + content: 'Contenido', + height: 'Altura (puntos)', + moduleWidth: 'Ancho de módulo', + microPdfRowHeight: 'Altura de fila MicroPDF', + microPdfRows: 'Filas MicroPDF', + }, }, layers: { diff --git a/src/locales/et.ts b/src/locales/et.ts index 54d3518..71bbef3 100644 --- a/src/locales/et.ts +++ b/src/locales/et.ts @@ -45,6 +45,7 @@ const et = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Rühm', }, @@ -584,6 +585,13 @@ const et = { moduleWidth: 'Mooduli laius', security: 'Turvakontroll', }, + tlc39: { + content: 'Sisu', + height: 'Kõrgus (punktid)', + moduleWidth: 'Mooduli laius', + microPdfRowHeight: 'MicroPDF rea kõrgus', + microPdfRows: 'MicroPDF read', + }, }, layers: { diff --git a/src/locales/fa.ts b/src/locales/fa.ts index 918bf66..7cb2ad9 100644 --- a/src/locales/fa.ts +++ b/src/locales/fa.ts @@ -45,6 +45,7 @@ const fa = { maxicode: 'ماکسی‌کد', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'گروه', }, @@ -584,6 +585,13 @@ const fa = { moduleWidth: 'عرض ماژول', security: 'بررسی امنیتی', }, + tlc39: { + content: 'محتوا', + height: 'ارتفاع (نقطه)', + moduleWidth: 'عرض ماژول', + microPdfRowHeight: 'ارتفاع ردیف MicroPDF', + microPdfRows: 'ردیف‌های MicroPDF', + }, }, layers: { diff --git a/src/locales/fi.ts b/src/locales/fi.ts index bd9ec61..1f40a1c 100644 --- a/src/locales/fi.ts +++ b/src/locales/fi.ts @@ -45,6 +45,7 @@ const fi = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Ryhmä', }, @@ -584,6 +585,13 @@ const fi = { moduleWidth: 'Moduulin leveys', security: 'Turvatarkistus', }, + tlc39: { + content: 'Sisältö', + height: 'Korkeus (pisteet)', + moduleWidth: 'Moduulin leveys', + microPdfRowHeight: 'MicroPDF-rivin korkeus', + microPdfRows: 'MicroPDF-rivit', + }, }, layers: { diff --git a/src/locales/fr.ts b/src/locales/fr.ts index 41ddc6f..358ba39 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -45,6 +45,7 @@ const fr = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Groupe', }, @@ -584,6 +585,13 @@ const fr = { moduleWidth: 'Largeur module', security: 'Contrôle de sécurité', }, + tlc39: { + content: 'Contenu', + height: 'Hauteur (points)', + moduleWidth: 'Largeur de module', + microPdfRowHeight: 'Hauteur de ligne MicroPDF', + microPdfRows: 'Lignes MicroPDF', + }, }, layers: { diff --git a/src/locales/he.ts b/src/locales/he.ts index 167d326..b6436d0 100644 --- a/src/locales/he.ts +++ b/src/locales/he.ts @@ -45,6 +45,7 @@ const he = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'קבוצה', }, @@ -584,6 +585,13 @@ const he = { moduleWidth: 'רוחב מודול', security: 'בדיקת אבטחה', }, + tlc39: { + content: 'תוכן', + height: 'גובה (נקודות)', + moduleWidth: 'רוחב מודול', + microPdfRowHeight: 'גובה שורה של MicroPDF', + microPdfRows: 'שורות MicroPDF', + }, }, layers: { diff --git a/src/locales/hr.ts b/src/locales/hr.ts index 4889b75..1360a5a 100644 --- a/src/locales/hr.ts +++ b/src/locales/hr.ts @@ -45,6 +45,7 @@ const hr = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grupa', }, @@ -584,6 +585,13 @@ const hr = { moduleWidth: 'Širina modula', security: 'Sigurnosna provjera', }, + tlc39: { + content: 'Sadržaj', + height: 'Visina (točke)', + moduleWidth: 'Širina modula', + microPdfRowHeight: 'Visina retka MicroPDF', + microPdfRows: 'MicroPDF retci', + }, }, layers: { diff --git a/src/locales/hu.ts b/src/locales/hu.ts index ff8ecad..d95c537 100644 --- a/src/locales/hu.ts +++ b/src/locales/hu.ts @@ -45,6 +45,7 @@ const hu = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Csoport', }, @@ -584,6 +585,13 @@ const hu = { moduleWidth: 'Modulszélesség', security: 'Biztonsági ellenőrzés', }, + tlc39: { + content: 'Tartalom', + height: 'Magasság (pont)', + moduleWidth: 'Modulszélesség', + microPdfRowHeight: 'MicroPDF sormagasság', + microPdfRows: 'MicroPDF sorok', + }, }, layers: { diff --git a/src/locales/it.ts b/src/locales/it.ts index 30f8fe8..637f9f1 100644 --- a/src/locales/it.ts +++ b/src/locales/it.ts @@ -45,6 +45,7 @@ const it = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Gruppo', }, @@ -584,6 +585,13 @@ const it = { moduleWidth: 'Larghezza modulo', security: 'Controllo di sicurezza', }, + tlc39: { + content: 'Contenuto', + height: 'Altezza (punti)', + moduleWidth: 'Larghezza modulo', + microPdfRowHeight: 'Altezza riga MicroPDF', + microPdfRows: 'Righe MicroPDF', + }, }, layers: { diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 99bcf2f..d913780 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -45,6 +45,7 @@ const ja = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'グループ', }, @@ -584,6 +585,13 @@ const ja = { moduleWidth: 'モジュール幅', security: 'セキュリティチェック', }, + tlc39: { + content: 'コンテンツ', + height: '高さ (ドット)', + moduleWidth: 'モジュール幅', + microPdfRowHeight: 'MicroPDF行の高さ', + microPdfRows: 'MicroPDF行数', + }, }, layers: { diff --git a/src/locales/ko.ts b/src/locales/ko.ts index 47e392b..930efa2 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -45,6 +45,7 @@ const ko = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: '그룹', }, @@ -584,6 +585,13 @@ const ko = { moduleWidth: '모듈 폭', security: '보안 검사', }, + tlc39: { + content: '내용', + height: '높이 (도트)', + moduleWidth: '모듈 너비', + microPdfRowHeight: 'MicroPDF 행 높이', + microPdfRows: 'MicroPDF 행 수', + }, }, layers: { diff --git a/src/locales/lt.ts b/src/locales/lt.ts index fc6e00c..3474788 100644 --- a/src/locales/lt.ts +++ b/src/locales/lt.ts @@ -45,6 +45,7 @@ const lt = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grupė', }, @@ -584,6 +585,13 @@ const lt = { moduleWidth: 'Modulio plotis', security: 'Saugumo patikra', }, + tlc39: { + content: 'Turinys', + height: 'Aukštis (taškai)', + moduleWidth: 'Modulio plotis', + microPdfRowHeight: 'MicroPDF eilutės aukštis', + microPdfRows: 'MicroPDF eilutės', + }, }, layers: { diff --git a/src/locales/lv.ts b/src/locales/lv.ts index 56a5be6..12d02ee 100644 --- a/src/locales/lv.ts +++ b/src/locales/lv.ts @@ -45,6 +45,7 @@ const lv = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grupa', }, @@ -584,6 +585,13 @@ const lv = { moduleWidth: 'Moduļa platums', security: 'Drošības pārbaude', }, + tlc39: { + content: 'Saturs', + height: 'Augstums (punkti)', + moduleWidth: 'Moduļa platums', + microPdfRowHeight: 'MicroPDF rindas augstums', + microPdfRows: 'MicroPDF rindas', + }, }, layers: { diff --git a/src/locales/nl.ts b/src/locales/nl.ts index ec20ae5..8623e30 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -45,6 +45,7 @@ const nl = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Groep', }, @@ -584,6 +585,13 @@ const nl = { moduleWidth: 'Modulebreedte', security: 'Beveiligingscontrole', }, + tlc39: { + content: 'Inhoud', + height: 'Hoogte (punten)', + moduleWidth: 'Modulebreedte', + microPdfRowHeight: 'MicroPDF rijhoogte', + microPdfRows: 'MicroPDF rijen', + }, }, layers: { diff --git a/src/locales/no.ts b/src/locales/no.ts index 164f113..a179fce 100644 --- a/src/locales/no.ts +++ b/src/locales/no.ts @@ -45,6 +45,7 @@ const no = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Gruppe', }, @@ -584,6 +585,13 @@ const no = { moduleWidth: 'Modulbredde', security: 'Sikkerhetskontroll', }, + tlc39: { + content: 'Innhold', + height: 'Høyde (punkter)', + moduleWidth: 'Modulbredde', + microPdfRowHeight: 'MicroPDF radhøyde', + microPdfRows: 'MicroPDF rader', + }, }, layers: { diff --git a/src/locales/pl.ts b/src/locales/pl.ts index 0b8291f..49a2573 100644 --- a/src/locales/pl.ts +++ b/src/locales/pl.ts @@ -45,6 +45,7 @@ const pl = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grupa', }, @@ -584,6 +585,13 @@ const pl = { moduleWidth: 'Szerokość modułu', security: 'Kontrola bezpieczeństwa', }, + tlc39: { + content: 'Treść', + height: 'Wysokość (punkty)', + moduleWidth: 'Szerokość modułu', + microPdfRowHeight: 'Wysokość wiersza MicroPDF', + microPdfRows: 'Wiersze MicroPDF', + }, }, layers: { diff --git a/src/locales/pt.ts b/src/locales/pt.ts index e221fd3..c6fa70b 100644 --- a/src/locales/pt.ts +++ b/src/locales/pt.ts @@ -45,6 +45,7 @@ const pt = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grupo', }, @@ -584,6 +585,13 @@ const pt = { moduleWidth: 'Largura módulo', security: 'Verificação de segurança', }, + tlc39: { + content: 'Conteúdo', + height: 'Altura (pontos)', + moduleWidth: 'Largura do módulo', + microPdfRowHeight: 'Altura da linha MicroPDF', + microPdfRows: 'Linhas MicroPDF', + }, }, layers: { diff --git a/src/locales/ro.ts b/src/locales/ro.ts index bdafae5..b51d56f 100644 --- a/src/locales/ro.ts +++ b/src/locales/ro.ts @@ -45,6 +45,7 @@ const ro = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grup', }, @@ -584,6 +585,13 @@ const ro = { moduleWidth: 'Lățime modul', security: 'Verificare securitate', }, + tlc39: { + content: 'Conținut', + height: 'Înălțime (puncte)', + moduleWidth: 'Lățime modul', + microPdfRowHeight: 'Înălțime rând MicroPDF', + microPdfRows: 'Rânduri MicroPDF', + }, }, layers: { diff --git a/src/locales/sk.ts b/src/locales/sk.ts index 61161cb..ad1dcf1 100644 --- a/src/locales/sk.ts +++ b/src/locales/sk.ts @@ -45,6 +45,7 @@ const sk = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Skupina', }, @@ -584,6 +585,13 @@ const sk = { moduleWidth: 'Šírka modulu', security: 'Bezpečnostná kontrola', }, + tlc39: { + content: 'Obsah', + height: 'Výška (body)', + moduleWidth: 'Šírka modulu', + microPdfRowHeight: 'Výška riadku MicroPDF', + microPdfRows: 'Riadky MicroPDF', + }, }, layers: { diff --git a/src/locales/sl.ts b/src/locales/sl.ts index 370afaa..5ec165f 100644 --- a/src/locales/sl.ts +++ b/src/locales/sl.ts @@ -45,6 +45,7 @@ const sl = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Skupina', }, @@ -584,6 +585,13 @@ const sl = { moduleWidth: 'Širina modula', security: 'Varnostno preverjanje', }, + tlc39: { + content: 'Vsebina', + height: 'Višina (točke)', + moduleWidth: 'Širina modula', + microPdfRowHeight: 'Višina vrstice MicroPDF', + microPdfRows: 'Vrstice MicroPDF', + }, }, layers: { diff --git a/src/locales/sr.ts b/src/locales/sr.ts index d931271..50ad98b 100644 --- a/src/locales/sr.ts +++ b/src/locales/sr.ts @@ -45,6 +45,7 @@ const sr = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grupa', }, @@ -584,6 +585,13 @@ const sr = { moduleWidth: 'Ширина модула', security: 'Безбедносна провера', }, + tlc39: { + content: 'Sadržaj', + height: 'Visina (tačke)', + moduleWidth: 'Širina modula', + microPdfRowHeight: 'Visina reda MicroPDF', + microPdfRows: 'Redovi MicroPDF', + }, }, layers: { diff --git a/src/locales/sv.ts b/src/locales/sv.ts index 3232b60..2cd2aa9 100644 --- a/src/locales/sv.ts +++ b/src/locales/sv.ts @@ -45,6 +45,7 @@ const sv = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grupp', }, @@ -584,6 +585,13 @@ const sv = { moduleWidth: 'Modulbredd', security: 'Säkerhetskontroll', }, + tlc39: { + content: 'Innehåll', + height: 'Höjd (punkter)', + moduleWidth: 'Modulbredd', + microPdfRowHeight: 'MicroPDF radhöjd', + microPdfRows: 'MicroPDF rader', + }, }, layers: { diff --git a/src/locales/tr.ts b/src/locales/tr.ts index a039b2e..b230211 100644 --- a/src/locales/tr.ts +++ b/src/locales/tr.ts @@ -45,6 +45,7 @@ const tr = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: 'Grup', }, @@ -584,6 +585,13 @@ const tr = { moduleWidth: 'Modül genişliği', security: 'Güvenlik kontrolü', }, + tlc39: { + content: 'İçerik', + height: 'Yükseklik (nokta)', + moduleWidth: 'Modül genişliği', + microPdfRowHeight: 'MicroPDF satır yüksekliği', + microPdfRows: 'MicroPDF satırları', + }, }, layers: { diff --git a/src/locales/zh-hans.ts b/src/locales/zh-hans.ts index dfcb1a8..67ef0f3 100644 --- a/src/locales/zh-hans.ts +++ b/src/locales/zh-hans.ts @@ -45,6 +45,7 @@ const zhHans = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: '组', }, @@ -584,6 +585,13 @@ const zhHans = { moduleWidth: '模块宽度', security: '安全检查', }, + tlc39: { + content: '内容', + height: '高度 (点)', + moduleWidth: '模块宽度', + microPdfRowHeight: 'MicroPDF 行高', + microPdfRows: 'MicroPDF 行数', + }, }, layers: { diff --git a/src/locales/zh-hant.ts b/src/locales/zh-hant.ts index 5d5988d..ca3a27e 100644 --- a/src/locales/zh-hant.ts +++ b/src/locales/zh-hant.ts @@ -45,6 +45,7 @@ const zhHant = { maxicode: 'Maxicode', micropdf417: 'MicroPDF417', codablock: 'CODABLOCK', + tlc39: 'TLC39', group: '群組', }, @@ -584,6 +585,13 @@ const zhHant = { moduleWidth: '模組寬度', security: '安全檢查', }, + tlc39: { + content: '內容', + height: '高度 (點)', + moduleWidth: '模組寬度', + microPdfRowHeight: 'MicroPDF 列高', + microPdfRows: 'MicroPDF 列數', + }, }, layers: { diff --git a/src/registry/index.ts b/src/registry/index.ts index 55c26af..11af740 100644 --- a/src/registry/index.ts +++ b/src/registry/index.ts @@ -36,6 +36,7 @@ import { codablock } from './codablock'; import { upcEanExtension } from './upcEanExtension'; import { code49 } from './code49'; import { maxicode } from './maxicode'; +import { tlc39 } from './tlc39'; import { symbol } from './symbol'; export const BARCODE_1D_TYPES = new Set([ @@ -78,6 +79,7 @@ const _ObjectRegistry = { maxicode, micropdf417, codablock, + tlc39, // code-postal planet, postal, diff --git a/src/registry/leafObject.ts b/src/registry/leafObject.ts index 96326eb..421b5f1 100644 --- a/src/registry/leafObject.ts +++ b/src/registry/leafObject.ts @@ -33,6 +33,7 @@ import type { CodablockProps } from './codablock'; import type { UpcEanExtensionProps } from './upcEanExtension'; import type { Code49Props } from './code49'; import type { MaxicodeProps } from './maxicode'; +import type { Tlc39Props } from './tlc39'; import type { SymbolProps } from './symbol'; /** Single-branch shape for one registry type: the common base plus a @@ -75,6 +76,7 @@ export type LeafObject = | Leaf<'upcEanExtension', UpcEanExtensionProps> | Leaf<'code49', Code49Props> | Leaf<'maxicode', MaxicodeProps> + | Leaf<'tlc39', Tlc39Props> | Leaf<'symbol', SymbolProps>; /** Every registered leaf-type discriminator. */ diff --git a/src/registry/panels.ts b/src/registry/panels.ts index 69cb6be..1ce1ec7 100644 --- a/src/registry/panels.ts +++ b/src/registry/panels.ts @@ -28,6 +28,7 @@ import { aztecPanel } from './aztec.panel'; import { maxicodePanel } from './maxicode.panel'; import { micropdf417Panel } from './micropdf417.panel'; import { codablockPanel } from './codablock.panel'; +import { tlc39Panel } from './tlc39.panel'; import { planetPanel } from './planet.panel'; import { postalPanel } from './postal.panel'; import { boxPanel } from './box.panel'; @@ -65,6 +66,7 @@ const _ObjectPanels = { maxicode: maxicodePanel, micropdf417: micropdf417Panel, codablock: codablockPanel, + tlc39: tlc39Panel, planet: planetPanel, postal: postalPanel, box: boxPanel, diff --git a/src/registry/registry-isolation.test.ts b/src/registry/registry-isolation.test.ts index 049d4f8..048da35 100644 --- a/src/registry/registry-isolation.test.ts +++ b/src/registry/registry-isolation.test.ts @@ -3,8 +3,8 @@ import { ObjectRegistry, BARCODE_1D_TYPES, STACKED_2D_TYPES } from "./index"; import { ObjectPanels } from "./panels"; describe("registry isolation baseline", () => { - it("registers 34 object types", () => { - expect(Object.keys(ObjectRegistry)).toHaveLength(34); + it("registers 35 object types", () => { + expect(Object.keys(ObjectRegistry)).toHaveLength(35); }); it("classifies 20 1D barcodes", () => { diff --git a/src/registry/tlc39.panel.tsx b/src/registry/tlc39.panel.tsx new file mode 100644 index 0000000..667b4df --- /dev/null +++ b/src/registry/tlc39.panel.tsx @@ -0,0 +1,61 @@ +import type { ObjectTypeUi } from "../types/ObjectType"; +import { useT } from "../lib/useT"; +import { inputCls, labelCls } from "../components/Properties/styles"; +import { RotationSelect } from "../components/Properties/RotationSelect"; +import { NumberInput } from "../components/Properties/NumberInput"; +import type { Tlc39Props } from "./tlc39"; + +export const tlc39Panel: ObjectTypeUi = { + PropertiesPanel: ({ obj, onChange }) => { + const t = useT(); + const p = obj.props; + const loc = t.registry.tlc39; + return ( +
+
+ + onChange({ content: e.target.value })} + /> +
+ +
+ onChange({ height })} + /> + onChange({ moduleWidth })} + /> +
+ +
+ onChange({ microPdfRowHeight })} + /> + onChange({ microPdfRows })} + /> +
+ + onChange({ rotation })} /> +
+ ); + }, +}; diff --git a/src/registry/tlc39.ts b/src/registry/tlc39.ts new file mode 100644 index 0000000..c5770de --- /dev/null +++ b/src/registry/tlc39.ts @@ -0,0 +1,55 @@ +import type { ObjectTypeCore } from "../types/ObjectType"; +import { fieldPos, fdFieldFor } from "./zplHelpers"; +import { commitBarcodeWidthHeightTransform } from "./transformHelpers"; +import { type ZplRotation } from "./rotation"; + +export interface Tlc39Props { + /** TLC39 data: `,`. ECI is 6 digits; serial is up to + * 25 alphanumeric characters (rendered as MicroPDF417 stacked on + * top of the Code 39 base line). Comma is the canonical separator + * per spec. */ + content: string; + /** Code 39 narrow bar width in dots (1-10, default from ^BY). */ + moduleWidth: number; + /** Code 39 height in dots (the dominant visible component). */ + height: number; + /** MicroPDF417 row height in dots (1-255, default 4). */ + microPdfRowHeight: number; + /** MicroPDF417 row count. Zebra ^BT h2 accepts 1-10 (narrower than + * standalone ^BF since TLC39's linked MicroPDF is fixed at 4 columns); + * firmware snaps to a valid row count {4,6,8,10}. Default 4. */ + microPdfRows: number; + rotation: ZplRotation; +} + +export const tlc39: ObjectTypeCore = { + label: "TLC39", + icon: "▦T", + group: "code-2d", + bindable: true, + defaultProps: { + content: "123456,SERIAL", + moduleWidth: 2, + height: 40, + microPdfRowHeight: 4, + microPdfRows: 4, + rotation: "N", + }, + defaultSize: { width: 200, height: 80 }, + + commitTransform: commitBarcodeWidthHeightTransform, + + toZPL: (obj, ctx) => { + const p = obj.props; + return [ + `^BY${p.moduleWidth}`, + fieldPos(obj), + // r1 (wide:narrow ratio) is not exposed as a prop; emit the + // canonical "2" so the field round-trips. + `^BT${p.rotation},${p.moduleWidth},2,${p.height},${p.microPdfRowHeight},${p.microPdfRows}`, + fdFieldFor(obj, p.content, ctx), + ] + .filter(Boolean) + .join(""); + }, +};