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 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-09-04 11:35:20 +10:00
parent d1b2d7ec64
commit 50157933fa

View File

@ -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),