From 50157933fa809f79a08b83b4a67cc56f3078d1b6 Mon Sep 17 00:00:00 2001 From: type-two Date: Fri, 4 Sep 2026 11:35:20 +1000 Subject: [PATCH] fix(label): rotation is a profile property; rfid65 feeds landscape render() always rotated the landscape design 90 degrees -- right for the 24 mm narrow stock, which feeds narrow-edge-first, and wrong for the 65 x 35 UHF roll, whose 65 mm is the WEB. On that roll the rotated design ran along a 35 mm label: printed sideways and clipped after ~35 mm, seen on the bench. rotate now defaults from the profile (rfid65: False) and only an explicit argument overrides it, so the Dymo path (which passes rotate itself) is untouched. When a label-printer profile is unrotated the canvas is still the media -- web x pitch, 65 x 35 -- with the 64 x 34 design centred, not an art-sized bitmap; that art-sized case is kept only for the Dymo, whose head is wider than its label. Offline render verified 520 x 280 px = 65.0 x 35.0 mm, upright. Co-Authored-By: Claude Opus 5 --- mac/label.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mac/label.py b/mac/label.py index e58799d..c54562a 100644 --- a/mac/label.py +++ b/mac/label.py @@ -50,7 +50,7 @@ PROFILES = { # nearly two labels per feed and looked exactly like a chip that would not stop at the # antenna. gap=0 on purpose: the inlay fills most of the label and blinds the optical gap # sensor, so the printer positions by pitch. Proven with a real print-test on this roll. - 'rfid65': dict(web_mm=65, pitch_mm=35, art_mm=(64, 34), gap_mm=0, margin_mm=1.5), + 'rfid65': dict(web_mm=65, pitch_mm=35, art_mm=(64, 34), gap_mm=0, margin_mm=1.5, rotate=False), } DEFAULT_PROFILE = 'large' @@ -184,7 +184,7 @@ def render_art(fields, prof): return img -def render(fields, prof=None, rotate=True): +def render(fields, prof=None, rotate=None): """Full media-sized bitmap, design centred and oriented for the print head. rotate=True (Urovo) the 24 mm web feeds narrow-edge-first, so the landscape @@ -195,12 +195,24 @@ def render(fields, prof=None, rotate=True): prof = prof or profile() dpmm = prof.get('dpmm', DPMM) art = render_art(fields, prof) + # A profile may say it feeds landscape already. The 65 x 35 UHF roll does: 65 is the WEB, + # so the 64 x 34 design sits across it unrotated. Rotating it (the default, right for the + # 24 mm narrow stock) ran the design ALONG a 35 mm label -- printed sideways and clipped + # after ~35 mm. Seen on the bench. Only an explicit rotate=... argument overrides it. + if rotate is None: + rotate = prof.get('rotate', True) if rotate: # ROTATE_270 == 90 degrees clockwise, matching TSPL's rotation sense and the # orientation confirmed on the bench. art = art.transpose(Image.ROTATE_270) cw, ch = mm(prof['web_mm'], dpmm), mm(prof['pitch_mm'], dpmm) + elif 'rotate' in prof: + # Unrotated ON A LABEL PRINTER: the canvas is still the media, web x pitch, so the + # printer gets a full-label bitmap with the design centred -- not an art-sized one. + cw, ch = mm(prof['web_mm'], dpmm), mm(prof['pitch_mm'], dpmm) else: + # Unrotated for the Dymo path (rotate=False passed explicitly): the head is wider than + # the label, so the canvas is the design itself. Unchanged. cw, ch = mm(prof['art_mm'][0], dpmm), mm(prof['art_mm'][1], dpmm) canvas = Image.new('1', (cw + (-cw % 8), ch), 1) canvas.paste(art, (max(0, (canvas.width - art.width) // 2),