Fix Dymo label geometry: rotation and 300x600 dpi halving
Two bugs found on the bench, both from wrong assumptions about the LW450: 1. Orientation. I assumed the 57mm head meant the label feeds landscape. It doesn't -- the Dymo takes the SAME 24mm narrow-edge-first stock as the Urovo, so the landscape design needs the identical 90-degree rotation. Rendering 656 dots onto a ~288-dot label also silently clipped the price and QR off the right-hand edge. Now rotates by default, overridable per request via "rotate", and the daemon rejects an image wider than the head instead of clipping it. 2. Half-length labels. print_image() sent ESC i (300x600 Barcode/Graphics mode), where the head stays 300 dpi across but each raster line becomes 1/600" in the travel direction -- so a 650-line label printed at 27.5mm instead of 55mm, with the across-head dimension correct. Default is now ESC h (300x300), matching our square 300 dpi bitmap. graphics_mode=True remains available and now stretches the image 2x vertically so physical size stays correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2727545ac7
commit
d254d16278
@ -152,8 +152,17 @@ def do_encode(body):
|
||||
def do_print_paper(body):
|
||||
"""Print the paper label on the Dymo LabelWriter 450 (raw USB, no CUPS driver)."""
|
||||
prof = label.profile(body.get('size'), dpmm=label.DPMM_DYMO,
|
||||
art_mm=body.get('art_mm'), margin_mm=body.get('margin'))
|
||||
img = label.render(body, prof, rotate=False) # 57 mm head: no rotation needed
|
||||
web_mm=body.get('w'), pitch_mm=body.get('h'),
|
||||
margin_mm=body.get('margin'))
|
||||
# The Dymo takes the SAME 24 mm stock as the Urovo, fed narrow-edge-first, so the
|
||||
# landscape design has to be rotated 90 degrees here too. (The 57 mm head is wider
|
||||
# than the label; that does NOT mean the label feeds landscape.) Overridable per
|
||||
# request via "rotate" in case a wider Dymo roll is loaded.
|
||||
rotate = body.get('rotate', True)
|
||||
img = label.render(body, prof, rotate=rotate)
|
||||
if img.width > dymo_mod.HEAD_BYTES * 8:
|
||||
return {'ok': False, 'error': 'label is %d dots wide; Dymo head is %d'
|
||||
% (img.width, dymo_mod.HEAD_BYTES * 8)}
|
||||
with LOCK, dymo_mod.Dymo() as d:
|
||||
before = d.status()
|
||||
if before is not None and not before & 0x01:
|
||||
|
||||
12
mac/dymo.py
12
mac/dymo.py
@ -131,13 +131,23 @@ class Dymo(object):
|
||||
def form_feed(self):
|
||||
self.write(b'\x1bE')
|
||||
|
||||
def print_image(self, img, density='normal', graphics_mode=True, feed=True):
|
||||
def print_image(self, img, density='normal', graphics_mode=False, feed=True):
|
||||
"""Send a 1-bit PIL image, already oriented for the head (width = across head).
|
||||
|
||||
The bitmap is assumed to be 300 dpi in BOTH axes, so the default is `ESC h`
|
||||
(300x300 Text mode). `ESC i` selects 300x600 Barcode/Graphics mode, where the
|
||||
head is still 300 dpi across but each raster line becomes 1/600" in the travel
|
||||
direction -- printing the label at HALF length unless the image is doubled up.
|
||||
So when graphics_mode is on we stretch the image 2x vertically to keep the
|
||||
physical size right.
|
||||
|
||||
Returns the number of raster lines sent.
|
||||
"""
|
||||
if img.mode != '1':
|
||||
img = img.convert('1')
|
||||
if graphics_mode:
|
||||
from PIL import Image as _I
|
||||
img = img.resize((img.width, img.height * 2), _I.NEAREST)
|
||||
if img.width > HEAD_BYTES * 8:
|
||||
raise DymoError('image is %d dots wide; head is %d'
|
||||
% (img.width, HEAD_BYTES * 8))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user