docs(mac): patched CUPS PPD for the DYMO 450's 24x55mm price stock
macOS auto-adds a USB LabelWriter 450 with the GENERIC CUPS driver (dymo.ppd, ModelName "DYMO Label Printer"), which has 10 fixed sizes and no CustomPageSize block — so a custom paper size made in the page-setup dialog can never reach the printer. CUPS falls back to w81h252 (Address, 28.6x88.9mm) and the 54x24mm design prints rotated 90 degrees and shrunk. Cost a bench session to diagnose; the extension's labels.js was never at fault. Adds the stock in both feed orientations (pg55x25 / pg55x25L — John's price roll is the landscape-feed one) plus patch_ppd.py to regenerate. Lives here because mac/dymo.py drives the same printer. Must be re-applied after any macOS update or after re-adding the printer, so the file and the lpadmin invocation are both written down in the README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e792e8315e
commit
3fa41ddbf7
44
README.md
44
README.md
@ -45,6 +45,50 @@ is the **only** thing that needs to change if it must match a different (Mac/Cha
|
||||
nothing else depends on the byte order. Confirm by reading an existing tag: put a
|
||||
Chafon-written record at the antenna and `GET /read-tag`.
|
||||
|
||||
## DYMO LabelWriter 450 on macOS — the PPD you will need again
|
||||
|
||||
Not the daemon, but it lives here because `mac/dymo.py` drives the same printer.
|
||||
|
||||
macOS auto-adds a USB LabelWriter 450 with the **generic** CUPS driver
|
||||
(`*PCFileName: "dymo.ppd"`, `*ModelName: "DYMO Label Printer"`). That PPD ships 10 fixed
|
||||
label sizes and **no** `CustomPageSize`/`VariablePaperSize` block — so a custom paper size
|
||||
made in the macOS page-setup dialog can never reach the printer. CUPS silently falls back
|
||||
to `w81h252` (Address, 28.6 × 88.9 mm), and a 54×24 mm label design comes out **rotated 90°
|
||||
and shrunk**, with a big blank area. Nothing is wrong with the extension's `labels.js`.
|
||||
|
||||
`mac/dymo-24x55.ppd` is the generic PPD patched to add the shop's 24×55 mm price stock, in
|
||||
both feed orientations, so the right one can be picked in the print dialog:
|
||||
|
||||
| PageSize | pt | mm | roll |
|
||||
|---|---|---|---|
|
||||
| `pg55x25` | 72 × 155 | 25.4 × 54.7 | ~25 mm wide, label runs 55 mm along the feed |
|
||||
| `pg55x25L` | 155 × 72 | 54.7 × 25.4 | ~55 mm wide, label runs 25 mm along the feed |
|
||||
|
||||
**John's price roll is the landscape-feed kind → use `pg55x25L`.**
|
||||
|
||||
```sh
|
||||
sudo lpadmin -p <queue> -P mac/dymo-24x55.ppd # apply (queue on JING5: make_it_soso)
|
||||
sudo lpadmin -p <queue> -m drv:///sample.drv/dymo.ppd # revert to stock
|
||||
```
|
||||
|
||||
**Re-apply after any macOS update or after re-adding the printer** — both revert the queue to
|
||||
the generic driver and the symptom comes straight back.
|
||||
|
||||
Regenerate from the **pristine** driver PPD — once applied, the queue's own PPD is already
|
||||
patched and `patch_ppd.py` will refuse it:
|
||||
|
||||
```sh
|
||||
ppdc -d /tmp/ppdout /usr/share/cups/drv/sample.drv
|
||||
python3 mac/patch_ppd.py mac/dymo-24x55.ppd /tmp/ppdout/dymo.ppd # byte-identical, PASSes cupstestppd
|
||||
```
|
||||
|
||||
Two traps it handles: margins are set to 2 pt sides / 4 pt ends instead of the driver's
|
||||
stock 14.9 pt, which would eat 10.5 mm of a 55 mm label; and every one of the 31 languages
|
||||
in `*cupsLanguages` needs a translation string per new choice or `cupstestppd` FAILs.
|
||||
|
||||
Dead end for next time: `LabelPrinter-*.pkg` in `~/Documents` is **not** the DYMO driver —
|
||||
it's the generic bundle for the PM-246S/Wagtail printers.
|
||||
|
||||
## Notes / open items
|
||||
- Default label geometry is `65×37 mm`, continuous (`GAP 0`). Adjust to your actual RFID
|
||||
roll via the `w`/`h`/`gap` fields, or change the `DEF_*` constants.
|
||||
|
||||
1048
mac/dymo-24x55.ppd
Normal file
1048
mac/dymo-24x55.ppd
Normal file
File diff suppressed because it is too large
Load Diff
100
mac/patch_ppd.py
Normal file
100
mac/patch_ppd.py
Normal file
@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Add John's 24x55mm label stock to the generic CUPS DYMO PPD.
|
||||
|
||||
The macOS-supplied generic dymo.ppd (ModelName "DYMO Label Printer") ships 10 fixed
|
||||
label sizes and NO CustomPageSize block -- so a "custom paper size" made in the macOS
|
||||
page-setup dialog can never reach the printer, and CUPS silently falls back to
|
||||
w81h252 (Address, 28.6 x 88.9 mm). That is why the label prints rotated and shrunk.
|
||||
|
||||
We add BOTH orientations so the correct one can be picked in the print dialog:
|
||||
pg55x25 72 x 155 pt = 25.4 x 54.7 mm -- roll ~25 mm wide, label runs 55 mm along the feed
|
||||
pg55x25L 155 x 72 pt = 54.7 x 25.4 mm -- roll ~55 mm wide, label runs 25 mm along the feed
|
||||
|
||||
Margins: 2 pt sides, 4 pt ends (~0.7 / 1.4 mm) -- tighter than the driver's stock
|
||||
14.9 pt end margin, which would eat 10.5 mm of a 55 mm label.
|
||||
|
||||
Regenerate from the PRISTINE driver PPD, not from the live queue -- once you have applied
|
||||
this, /etc/cups/ppd/<queue>.ppd IS the patched file and the script will refuse it:
|
||||
|
||||
ppdc -d /tmp/ppdout /usr/share/cups/drv/sample.drv
|
||||
python3 patch_ppd.py dymo-24x55.ppd /tmp/ppdout/dymo.ppd
|
||||
|
||||
(Verified: that route reproduces the committed dymo-24x55.ppd byte-for-byte and PASSes
|
||||
cupstestppd.) Source defaults to JING5's queue for a first run on a stock install.
|
||||
"""
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
DST = sys.argv[1] if len(sys.argv) > 1 else 'dymo-24x55.ppd'
|
||||
SRC = sys.argv[2] if len(sys.argv) > 2 else '/etc/cups/ppd/make_it_soso.ppd'
|
||||
|
||||
# name, width_pt, height_pt, human label
|
||||
NEW = [
|
||||
('pg55x25', 72, 155, '24x55mm Price Label (portrait feed)'),
|
||||
('pg55x25L', 155, 72, '24x55mm Price Label (landscape feed)'),
|
||||
]
|
||||
|
||||
shutil.copyfile(SRC, DST)
|
||||
with open(DST, encoding='utf-8', errors='surrogateescape') as f:
|
||||
ppd = f.read()
|
||||
|
||||
if 'pg55x25' in ppd:
|
||||
sys.exit('already patched')
|
||||
|
||||
|
||||
def area(w, h):
|
||||
"""left bottom right top -- 2pt sides, 4pt ends"""
|
||||
return f'2 4 {w - 2} {h - 4}'
|
||||
|
||||
|
||||
# 1. *PageSize entries -- insert after the last existing *PageSize line
|
||||
def insert_after_last(text, prefix, lines):
|
||||
idx = [m.start() for m in re.finditer(rf'^\*{prefix} ', text, re.M)]
|
||||
end = text.index('\n', idx[-1]) + 1
|
||||
return text[:end] + ''.join(lines) + text[end:]
|
||||
|
||||
|
||||
ppd = insert_after_last(ppd, 'PageSize', [
|
||||
f'*PageSize {n}/{lab}: "<</PageSize[{w} {h}]/ImagingBBox null>>setpagedevice"\n'
|
||||
for n, w, h, lab in NEW])
|
||||
|
||||
ppd = insert_after_last(ppd, 'PageRegion', [
|
||||
f'*PageRegion {n}/{lab}: "<</PageSize[{w} {h}]/ImagingBBox null>>setpagedevice"\n'
|
||||
for n, w, h, lab in NEW])
|
||||
|
||||
ppd = insert_after_last(ppd, 'ImageableArea', [
|
||||
f'*ImageableArea {n}/{lab}: "{area(w, h)}"\n'
|
||||
for n, w, h, lab in NEW])
|
||||
|
||||
ppd = insert_after_last(ppd, 'PaperDimension', [
|
||||
f'*PaperDimension {n}/{lab}: "{w} {h}"\n'
|
||||
for n, w, h, lab in NEW])
|
||||
|
||||
# 1b. per-language translation strings. The PPD declares 31 languages in *cupsLanguages
|
||||
# and cupstestppd FAILs if a PageSize choice is missing any of them.
|
||||
langs = re.search(r'^\*cupsLanguages:\s*"([^"]+)"', ppd, re.M).group(1).split()
|
||||
for lg in langs:
|
||||
if lg == 'en':
|
||||
continue
|
||||
for opt in ('PageSize', 'PageRegion'):
|
||||
pat = rf'^\*{lg}\.{opt} \S+.*$'
|
||||
hits = [m for m in re.finditer(pat, ppd, re.M)]
|
||||
if not hits:
|
||||
continue
|
||||
end = hits[-1].end() + 1
|
||||
add = ''.join(f'*{lg}.{opt} {n}/{lab}: ""\n' for n, w, h, lab in NEW)
|
||||
ppd = ppd[:end] + add + ppd[end:]
|
||||
|
||||
# 2. make the portrait one the default so a plain "print" already lands right
|
||||
ppd = ppd.replace('*DefaultPageSize: w81h252', '*DefaultPageSize: pg55x25')
|
||||
ppd = ppd.replace('*DefaultPageRegion: w81h252', '*DefaultPageRegion: pg55x25')
|
||||
ppd = ppd.replace('*DefaultImageableArea: w81h252', '*DefaultImageableArea: pg55x25')
|
||||
ppd = ppd.replace('*DefaultPaperDimension: w81h252', '*DefaultPaperDimension: pg55x25')
|
||||
|
||||
with open(DST, 'w', encoding='utf-8', errors='surrogateescape') as f:
|
||||
f.write(ppd)
|
||||
|
||||
print(f'wrote {DST}')
|
||||
for n, w, h, lab in NEW:
|
||||
print(f' {n:9s} {w:3d} x {h:3d} pt = {w/72*25.4:5.1f} x {h/72*25.4:5.1f} mm {lab}')
|
||||
Loading…
Reference in New Issue
Block a user