Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/components/Canvas/BarcodeObject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ export function BarcodeObject({
bwipjs.toCanvas(canvas, opts as unknown as Parameters<typeof bwipjs.toCanvas>[1]);
barcodeCanvas = canvas;
} catch (e) {
errorMsg = e instanceof Error ? e.message : String(e);
const raw = e instanceof Error ? e.message : String(e);
// bwip-js prefixes its errors like `bwip-js: bwipp.code39badCharacter: ...`
// Strip the library/identifier head so the placeholder shows only the
// human-readable reason.
errorMsg = raw.replace(/^bwip-js:\s*/i, '').replace(/^bwipp\.[^:]+:\s*/i, '');
}
}

Expand Down Expand Up @@ -589,9 +593,13 @@ export function BarcodeObject({
<Text
x={6}
y={6}
width={fbW - 12}
height={fbH - 12}
wrap="word"
ellipsis
text={errorMsg ? `⚠ ${errorMsg}` : obj.type}
fontSize={Math.max(dotsToPx(10, scale, dpmm), 8)}
fill="#374151"
fill={errorMsg ? "#b91c1c" : "#374151"}
/>
</Group>
);
Expand Down
13 changes: 11 additions & 2 deletions src/components/Canvas/bwipHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,15 @@ export function buildBwipOptions(
case "codabar":
case "plessey": {
const p = obj.props;
opts = { bcid, text: p.content || "0", scale: scale1D, height: 10 };
// Code39/Codabar/Plessey only encode uppercase letters. Zebra firmware
// (and Labelary) silently uppercase lowercase input; bwip-js does not and
// throws instead, which would crash the canvas for any imported ZPL with
// lowercase content. Uppercase here so the lib matches firmware behaviour
// without rewriting the user's source.
const needsUpper = obj.type === "code39" || obj.type === "codabar" || obj.type === "plessey";
const raw = p.content || "0";
const text = needsUpper ? raw.toUpperCase() : raw;
opts = { bcid, text, scale: scale1D, height: 10 };
break;
}
case "msi": {
Expand All @@ -221,9 +229,10 @@ export function buildBwipOptions(
}
case "logmars": {
const p = obj.props;
// LOGMARS is a Code39 subset; same uppercase rule applies.
opts = {
bcid,
text: p.content || "0",
text: (p.content || "0").toUpperCase(),
scale: scale1D,
height: 10,
includecheck: true,
Expand Down
4 changes: 2 additions & 2 deletions src/registry/aztec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ObjectTypeDefinition } from "../types/ObjectType";
import { useT } from "../lib/useT";
import { inputCls, labelCls } from "../components/Properties/styles";
import { fieldPos } from "./zplHelpers";
import { fieldPos, fdField } from "./zplHelpers";

export interface AztecProps {
content: string;
Expand All @@ -27,7 +27,7 @@ export const aztec: ObjectTypeDefinition<AztecProps> = {
return [
fieldPos(obj),
`^B0N,${p.magnification},N,N,N,N`,
`^FD${p.content}^FS`,
fdField(p.content),
].join("");
},

Expand Down
12 changes: 7 additions & 5 deletions src/registry/barcode1d.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { ObjectTypeDefinition, ObjectGroup, LabelObjectBase } from '../types/ObjectType';
import { useT } from '../lib/useT';
import { inputCls, labelCls } from '../components/Properties/styles';
import { fieldPos } from './zplHelpers';
import { fieldPos, fdField } from './zplHelpers';
import { commitHeightTransform } from './transformHelpers';
import { filterContent, type ContentSpec } from './contentSpec';

export interface Barcode1DProps {
content: string;
Expand All @@ -16,7 +17,6 @@ interface Barcode1DConfig {
label: string;
icon: string;
defaultContent: string;
contentMaxLength?: number;
hasCheckDigit: boolean;
/** Build the ZPL barcode command (e.g. `^BUN,100,Y,N,N`). */
zplCommand: (p: Barcode1DProps) => string;
Expand All @@ -34,6 +34,8 @@ interface Barcode1DConfig {
heightLocked?: boolean;
/** See {@link ObjectTypeDefinition.interpretationLocked}. */
interpretationLocked?: boolean;
/** Restrict allowed input characters; see {@link ContentSpec}. */
contentSpec?: ContentSpec;
}

interface BarcodeLocale {
Expand Down Expand Up @@ -77,7 +79,7 @@ export function createBarcode1D(config: Barcode1DConfig): ObjectTypeDefinition<B
byCmd,
fieldPos(obj),
config.zplCommand(p),
`^FD${p.content}^FS`,
fdField(p.content),
].filter(Boolean).join('');
},

Expand All @@ -92,9 +94,9 @@ export function createBarcode1D(config: Barcode1DConfig): ObjectTypeDefinition<B
<input
className={inputCls}
value={p.content}
maxLength={config.contentMaxLength}
maxLength={config.contentSpec?.maxLength}
placeholder={loc.placeholder}
onChange={(e) => onChange({ content: e.target.value })}
onChange={(e) => onChange({ content: filterContent(e.target.value, config.contentSpec) })}
/>
</div>

Expand Down
1 change: 1 addition & 0 deletions src/registry/codabar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const codabar = createBarcode1D({
hasCheckDigit: true,
localeKey: "codabar",
group: 'code-1d',
contentSpec: { charset: '0-9A-Da-d\\-$:/.+' },
zplCommand: (p) => {
const interp = p.printInterpretation ? "Y" : "N";
const check = p.checkDigit ? "Y" : "N";
Expand Down
4 changes: 2 additions & 2 deletions src/registry/codablock.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ObjectTypeDefinition } from "../types/ObjectType";
import { useT } from "../lib/useT";
import { inputCls, labelCls } from "../components/Properties/styles";
import { fieldPos } from "./zplHelpers";
import { fieldPos, fdField } from "./zplHelpers";
import { commitStacked2DTransform } from "./transformHelpers";

export interface CodablockProps {
Expand Down Expand Up @@ -32,7 +32,7 @@ export const codablock: ObjectTypeDefinition<CodablockProps> = {
`^BY${p.moduleWidth}`,
fieldPos(obj),
`^BBN,${p.rowHeight},${p.securityLevel}`,
`^FD${p.content}^FS`,
fdField(p.content),
]
.filter(Boolean)
.join("");
Expand Down
1 change: 1 addition & 0 deletions src/registry/code11.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const code11 = createBarcode1D({
hasCheckDigit: true,
localeKey: "code11",
group: 'code-1d',
contentSpec: { charset: '0-9\\-' },
zplCommand: (p) => {
const interp = p.printInterpretation ? "Y" : "N";
const check = p.checkDigit ? "Y" : "N";
Expand Down
4 changes: 2 additions & 2 deletions src/registry/code128.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ObjectTypeDefinition } from '../types/ObjectType';
import { useT } from '../lib/useT';
import { inputCls, labelCls } from '../components/Properties/styles';
import { fieldPos } from './zplHelpers';
import { fieldPos, fdField } from './zplHelpers';
import { commitHeightTransform } from './transformHelpers';

export interface Code128Props {
Expand Down Expand Up @@ -35,7 +35,7 @@ export const code128: ObjectTypeDefinition<Code128Props> = {
`^BY${p.moduleWidth}`,
fieldPos(obj),
`^BCN,${p.height},${interp},N,${check}`,
`^FD${p.content}^FS`,
fdField(p.content),
].filter(Boolean).join('');
},

Expand Down
9 changes: 6 additions & 3 deletions src/registry/code39.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { ObjectTypeDefinition } from '../types/ObjectType';
import { useT } from '../lib/useT';
import { inputCls, labelCls } from '../components/Properties/styles';
import { fieldPos } from './zplHelpers';
import { fieldPos, fdField } from './zplHelpers';
import { commitHeightTransform } from './transformHelpers';
import { filterContent, type ContentSpec } from './contentSpec';

const code39Spec: ContentSpec = { charset: '0-9A-Za-z\\-. $/+%' };

export interface Code39Props {
content: string;
Expand Down Expand Up @@ -35,7 +38,7 @@ export const code39: ObjectTypeDefinition<Code39Props> = {
`^BY${p.moduleWidth}`,
fieldPos(obj),
`^B3N,${check},${p.height},${interp},N`,
`^FD${p.content}^FS`,
fdField(p.content),
].filter(Boolean).join('');
},

Expand All @@ -49,7 +52,7 @@ export const code39: ObjectTypeDefinition<Code39Props> = {
<input
className={inputCls}
value={p.content}
onChange={(e) => onChange({ content: e.target.value })}
onChange={(e) => onChange({ content: filterContent(e.target.value, code39Spec) })}
/>
</div>

Expand Down
30 changes: 30 additions & 0 deletions src/registry/contentSpec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Per-symbology content rules. Applied on input so the canvas preview
* (bwip-js) does not throw on characters the renderer rejects.
*
* Case normalisation is intentionally not done here: the source ZPL
* is preserved verbatim, and `bwipHelpers` uppercases at the lib call
* site for the symbologies that require it.
*/
export interface ContentSpec {
/** Character-class body (no surrounding `[]`), e.g. `0-9` or `0-9A-Z\\-. $/+%`. */
charset: string;
maxLength?: number;
}

const rejectCache = new WeakMap<ContentSpec, RegExp>();

function rejectPattern(spec: ContentSpec): RegExp {
let re = rejectCache.get(spec);
if (!re) {
re = new RegExp(`[^${spec.charset}]`, 'g');
rejectCache.set(spec, re);
}
return re;
}

export function filterContent(raw: string, spec?: ContentSpec): string {
if (!spec) return raw;
const filtered = raw.replace(rejectPattern(spec), '');
return spec.maxLength ? filtered.slice(0, spec.maxLength) : filtered;
}
4 changes: 2 additions & 2 deletions src/registry/datamatrix.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ObjectTypeDefinition } from '../types/ObjectType';
import { useT } from '../lib/useT';
import { inputCls, labelCls } from '../components/Properties/styles';
import { fieldPos } from './zplHelpers';
import { fieldPos, fdField } from './zplHelpers';
import { clamp } from './transformHelpers';

export interface DataMatrixProps {
Expand Down Expand Up @@ -30,7 +30,7 @@ export const datamatrix: ObjectTypeDefinition<DataMatrixProps> = {
return [
fieldPos(obj),
`^BXN,${p.dimension},${p.quality}`,
`^FD${p.content}^FS`,
fdField(p.content),
].join('');
},

Expand Down
9 changes: 6 additions & 3 deletions src/registry/ean13.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { ObjectTypeDefinition } from '../types/ObjectType';
import { useT } from '../lib/useT';
import { inputCls, labelCls } from '../components/Properties/styles';
import { fieldPos } from './zplHelpers';
import { fieldPos, fdField } from './zplHelpers';
import { commitHeightTransform } from './transformHelpers';
import { filterContent, type ContentSpec } from './contentSpec';

const ean13Spec: ContentSpec = { charset: '0-9', maxLength: 12 };

export interface Ean13Props {
content: string; // 12 digits — ZPL appends the check digit automatically
Expand Down Expand Up @@ -32,7 +35,7 @@ export const ean13: ObjectTypeDefinition<Ean13Props> = {
`^BY${p.moduleWidth}`,
fieldPos(obj),
`^BEN,${p.height},${interp},N`,
`^FD${p.content}^FS`,
fdField(p.content),
].filter(Boolean).join('');
},

Expand All @@ -48,7 +51,7 @@ export const ean13: ObjectTypeDefinition<Ean13Props> = {
value={p.content}
maxLength={12}
placeholder={t.registry.ean13.placeholder}
onChange={(e) => onChange({ content: e.target.value })}
onChange={(e) => onChange({ content: filterContent(e.target.value, ean13Spec) })}
/>
</div>

Expand Down
2 changes: 1 addition & 1 deletion src/registry/ean8.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ export const ean8 = createBarcode1D({
label: 'EAN-8',
icon: 'E8',
defaultContent: '1234567',
contentMaxLength: 7,
hasCheckDigit: false,
localeKey: 'ean8',
group: 'code-1d',
contentSpec: { charset: '0-9', maxLength: 7 },
zplCommand: (p) => {
const interp = p.printInterpretation ? 'Y' : 'N';
return `^B8N,${p.height},${interp},N`;
Expand Down
1 change: 1 addition & 0 deletions src/registry/gs1databar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const gs1databar = createBarcode1D({
hasCheckDigit: false,
localeKey: "gs1databar",
group: 'code-1d',
contentSpec: { charset: '0-9' },
// GS1 Databar Omnidirectional has a symbology-fixed height; Zebra/Labelary
// ignore the ^BR height parameter for this variant. Disabling resize and the
// height input keeps the designer honest about what affects the print.
Expand Down
1 change: 1 addition & 0 deletions src/registry/industrial2of5.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const industrial2of5 = createBarcode1D({
hasCheckDigit: false,
localeKey: "industrial2of5",
group: 'code-1d',
contentSpec: { charset: '0-9' },
zplCommand: (p) => {
const interp = p.printInterpretation ? "Y" : "N";
return `^BIN,${p.height},${interp},N`;
Expand Down
1 change: 1 addition & 0 deletions src/registry/interleaved2of5.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const interleaved2of5 = createBarcode1D({
hasCheckDigit: true,
localeKey: 'interleaved2of5',
group: 'code-1d',
contentSpec: { charset: '0-9' },
zplCommand: (p) => {
const interp = p.printInterpretation ? 'Y' : 'N';
const check = p.checkDigit ? 'Y' : 'N';
Expand Down
1 change: 1 addition & 0 deletions src/registry/logmars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const logmars = createBarcode1D({
hasCheckDigit: false,
localeKey: "logmars",
group: 'code-1d',
contentSpec: { charset: '0-9A-Za-z\\-. $/+%' },
zplCommand: (p) => {
const interp = p.printInterpretation ? "Y" : "N";
return `^BLN,${p.height},${interp}`;
Expand Down
4 changes: 2 additions & 2 deletions src/registry/micropdf417.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ObjectTypeDefinition } from "../types/ObjectType";
import { useT } from "../lib/useT";
import { inputCls, labelCls } from "../components/Properties/styles";
import { fieldPos } from "./zplHelpers";
import { fieldPos, fdField } from "./zplHelpers";
import { commitStacked2DTransform } from "./transformHelpers";

export interface MicroPdf417Props {
Expand Down Expand Up @@ -32,7 +32,7 @@ export const micropdf417: ObjectTypeDefinition<MicroPdf417Props> = {
`^BY${p.moduleWidth}`,
fieldPos(obj),
`^BFN,${p.rowHeight},${p.mode}`,
`^FD${p.content}^FS`,
fdField(p.content),
]
.filter(Boolean)
.join("");
Expand Down
1 change: 1 addition & 0 deletions src/registry/msi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const msi = createBarcode1D({
hasCheckDigit: true,
localeKey: "msi",
group: 'code-1d',
contentSpec: { charset: '0-9' },
// MSI standard specifies a 2:1 wide:narrow ratio, which bwip-js hardcodes
// internally. ZPL ^BY defaults to 3.0, so we must override to keep canvas
// and Labelary preview in sync.
Expand Down
4 changes: 2 additions & 2 deletions src/registry/pdf417.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ObjectTypeDefinition } from "../types/ObjectType";
import { useT } from "../lib/useT";
import { inputCls, labelCls } from "../components/Properties/styles";
import { fieldPos } from "./zplHelpers";
import { fieldPos, fdField } from "./zplHelpers";
import { commitStacked2DTransform } from "./transformHelpers";

export interface Pdf417Props {
Expand Down Expand Up @@ -33,7 +33,7 @@ export const pdf417: ObjectTypeDefinition<Pdf417Props> = {
`^BY${p.moduleWidth}`,
fieldPos(obj),
`^B7N,${p.rowHeight},${p.securityLevel},${p.columns},,,`,
`^FD${p.content}^FS`,
fdField(p.content),
]
.filter(Boolean)
.join("");
Expand Down
1 change: 1 addition & 0 deletions src/registry/planet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const planet = createBarcode1D({
hasCheckDigit: false,
localeKey: "planet",
group: 'code-postal',
contentSpec: { charset: '0-9' },
zplCommand: (p) => {
const interp = p.printInterpretation ? "Y" : "N";
return `^B5N,${p.height},${interp},N`;
Expand Down
1 change: 1 addition & 0 deletions src/registry/plessey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const plessey = createBarcode1D({
hasCheckDigit: true,
localeKey: "plessey",
group: 'code-1d',
contentSpec: { charset: '0-9A-Fa-f' },
// Plessey uses 2:1 wide:narrow ratio (same as MSI); override ZPL default of 3.0
byRatio: 2,
zplCommand: (p) => {
Expand Down
1 change: 1 addition & 0 deletions src/registry/postal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const postal = createBarcode1D({
hasCheckDigit: false,
localeKey: "postal",
group: 'code-postal',
contentSpec: { charset: '0-9' },
zplCommand: (p) => {
const interp = p.printInterpretation ? "Y" : "N";
// ^BZ{orientation},{height},{interp},{startStop}
Expand Down
Loading