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
28 changes: 26 additions & 2 deletions src/components/Canvas/hooks/useKonvaTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,17 @@ export function useKonvaTransformer({
: ObjectRegistry[singleType]?.heightLocked
? []
: BARCODE_1D_TYPES.has(singleType)
? ["top-center", "bottom-center"]
? [
"top-center",
"bottom-center",
// middle-left / middle-right drag the module-width axis.
// The bar count is fixed by content, so the resulting width
// is rounded to a valid ZPL ^BY moduleWidth (1..10) in
// commitBarcodeWidthHeightTransform on release; during the
// drag the bitmap stretches free-form for visual feedback.
"middle-left",
"middle-right",
]
: isUniformScale
? ["top-left", "top-right", "bottom-left", "bottom-right"]
: undefined;
Expand Down Expand Up @@ -307,9 +317,23 @@ export function useKonvaTransformer({
);
const renderedXDots = pxToDots(topLeft.x - objectsOffsetX, scale, dpmm);
const renderedYDots = pxToDots(topLeft.y - labelOffsetY, scale, dpmm);
// For FT-anchored 1D barcodes, model.y is the bar baseline — needs the
// post-resize bar height to convert from the bbox top back. Pipe the
// scaled height through the same snap() commitBarcodeWidthHeight-
// Transform uses so the baseline math sees the *committed* value and
// there's no 1-dot drift between the two pathways.
const newBarHeightDots =
obj.positionType === "FT" && BARCODE_1D_TYPES.has(obj.type)
? Math.max(1, snap(Math.round((obj.props as { height: number }).height * sy)))
: undefined;
// Invert per-type render offsets (e.g. QR's hardcoded +10 dot Y) so the
// stored model position matches what BarcodeObject.handleDragEnd produces.
const modelPos = modelPositionFromRenderedTopLeft(obj, renderedXDots, renderedYDots);
const modelPos = modelPositionFromRenderedTopLeft(
obj,
renderedXDots,
renderedYDots,
newBarHeightDots,
);
// Only apply snap when the resize actually moved the position
// (e.g. dragging the top-left handle). Anchored-corner drags must keep
// the original position so off-grid shapes don't snap as a side-effect.
Expand Down
19 changes: 16 additions & 3 deletions src/components/Canvas/transformPosition.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { LabelObject } from "../../registry";
import { BARCODE_1D_TYPES, type LabelObject } from "../../registry";
import { QR_FO_Y_OFFSET_DOTS } from "./bwipConstants";

/**
Expand All @@ -9,20 +9,33 @@ import { QR_FO_Y_OFFSET_DOTS } from "./bwipConstants";
* Currently handles:
* - QR (FO): subtracts the hardcoded +10 dot Y-offset that BarcodeObject adds
* to compensate for Zebra firmware artifact.
* - 1D barcodes (FT): adds the new bar height back so obj.y stays on the
* bar baseline that ^FT anchors at. Without the correction every resize
* commits the bar TOP as the new obj.y and the next render shifts the
* barcode up by another bar-height.
*
* Used by onTransformEnd to mirror the rendered→model conversion that
* BarcodeObject.handleDragEnd performs for drag.
*
* Note: Field-Typeset (FT) corrections for barcode resize are not yet
* implemented here; FT-mode barcode resize still has known position drift.
* Note: QR-FT correction is not yet implemented (the additional firmware
* 3-module offset would need a separate code path); FT resize on QR still
* drifts.
*/
export function modelPositionFromRenderedTopLeft(
obj: LabelObject,
renderedXDots: number,
renderedYDots: number,
newBarHeightDots?: number,
): { x: number; y: number } {
if (obj.type === "qrcode" && obj.positionType !== "FT") {
return { x: renderedXDots, y: renderedYDots - QR_FO_Y_OFFSET_DOTS };
}
if (
obj.positionType === "FT" &&
BARCODE_1D_TYPES.has(obj.type) &&
newBarHeightDots !== undefined
) {
return { x: renderedXDots, y: renderedYDots + newBarHeightDots };
}
return { x: renderedXDots, y: renderedYDots };
}
10 changes: 8 additions & 2 deletions src/registry/barcode1d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useT } from '../lib/useT';
import type { Translations } from '../locales';
import { inputCls, labelCls } from '../components/Properties/styles';
import { fieldPos, fdField } from './zplHelpers';
import { commitHeightTransform } from './transformHelpers';
import { commitBarcodeWidthHeightTransform } from './transformHelpers';
import { filterContent, type ContentSpec } from './contentSpec';
import { type ZplRotation } from './rotation';
import { RotationSelect } from '../components/Properties/RotationSelect';
Expand Down Expand Up @@ -71,7 +71,13 @@ export function createBarcode1D(config: Barcode1DConfig): ObjectTypeDefinition<B
heightLocked: config.heightLocked,
interpretationLocked: config.interpretationLocked,

commitTransform: config.heightLocked ? undefined : commitHeightTransform,
// Width-locked symbologies (currently just heightLocked = true ones like
// GS1 DataBar) keep undefined so the transformer is disabled entirely.
// Otherwise the bar height scales with sy and the module width scales
// with sx (clamped to [1, 10] in commitBarcodeWidthHeightTransform).
commitTransform: config.heightLocked
? undefined
: commitBarcodeWidthHeightTransform,

toZPL: (obj: LabelObjectBase & { props: Barcode1DProps }) => {
// Normalize printInterpretation for symbologies that have no HRI in ZPL
Expand Down
16 changes: 12 additions & 4 deletions src/registry/transformHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,23 @@ export function commitWidthHeightTransform<P extends WidthHeightProps>(
} as Partial<P>;
}

/** Shared commitTransform for 1D barcodes — only the bar height scales (width
* is determined by content + module width, not by the resize anchor). */
export function commitHeightTransform<P extends { height: number }>(
/**
* commitTransform for 1D barcodes. Vertical drag scales bar height (sy),
* horizontal drag scales the module width (sx) — the latter is clamped
* to the ZPL `^BY` range [1, 10]. During the drag the bitmap stretches
* free-form for visual feedback; this commit rounds the result to a
* valid integer moduleWidth on release.
*/
export function commitBarcodeWidthHeightTransform<
P extends { height: number; moduleWidth: number },
>(
obj: LabelObjectBase & { props: P },
ctx: TransformContext,
): Partial<P> {
const { sy, snap } = ctx;
const { sx, sy, snap } = ctx;
return {
height: Math.max(1, snap(Math.round(obj.props.height * sy))),
moduleWidth: clamp(1, 10, Math.round(obj.props.moduleWidth * sx)),
} as Partial<P>;
}

Expand Down