feat(skills): gb-cart-flasher - stampa del foglio etichette su CUPS/IPP (vassoio, tipo supporto lucido, qualita' alta, scala 100%)

This commit is contained in:
2026-09-25 17:55:32 +02:00
parent f7fa38fc69
commit f9b1042a93
3 changed files with 369 additions and 7 deletions
+39 -7
View File
@@ -3,13 +3,16 @@ name: gb-cart-flasher
description: >
Legge e scrive ROM e salvataggi su cartucce Game Boy, Game Boy Color e Game Boy
Advance con un programmatore di cartucce USB (Joey Jr, GBxCart RW, GBFlash, Game Bub,
cloni/repro) pilotato dalla CLI di FlashGBX. Usare quando l'utente chiede di
identificare il lettore di cartucce collegato, fare un test di connessione, leggere
le informazioni della cartuccia, fare il backup di ROM o salvataggi, scrivere/flashare
una ROM su una cartuccia riscrivibile, ripristinare un backup, o diagnosticare errori
di lettura/scrittura ("No devices found", dump incoerenti, scrittura fallita). NON
usare per emulatori (PyBoy, mGBA), per compilare ROM (GBDK/SDCC, vedere la skill di
progetto), per cartucce di altre console, né per organizzare file ROM su disco.
cloni/repro) pilotato dalla CLI di FlashGBX, e stampa il foglio etichette della
cartuccia su stampanti CUPS/IPP. Usare quando l'utente chiede di identificare il lettore
di cartucce collegato, fare un test di connessione, leggere le informazioni della
cartuccia, fare il backup di ROM o salvataggi, scrivere/flashare una ROM su una
cartuccia riscrivibile, ripristinare un backup, stampare o ristampare l'etichetta della
cartuccia scegliendo vassoio di alimentazione, supporto lucido/fotografico, qualità e
scala reale 100% (o diagnosticare stampa in bianco e nero, vassoio sbagliato, job fermo
per coperchio/supporto aperto). NON usare per emulatori (PyBoy, mGBA), per compilare ROM
(GBDK/SDCC, vedere la skill di progetto), per cartucce di altre console, per gestire
file ROM su disco, né per stampe generiche non legate alla cartuccia.
---
# Programmatore di cartucce Game Boy (FlashGBX + lettore USB)
@@ -104,6 +107,30 @@ cmp /tmp/verify.gb /percorso/rom.gb && echo IDENTICI
4. Registrare l'evidenza in memoria condivisa (qmem: progetto della skill o `host-<host>`)
con path dei file e hash — non duplicare qui la procedura.
## Stampa dell'etichetta della cartuccia
Procedura completa in `references/stampa-etichetta.md`; script pronto
`scripts/print-label-pwg.sh` (default: solo `Validate-Job`, stampa con `--print`).
Punti che decidono l'esito:
1. **Interrogare prima**: `lpoptions -p <coda> -l` (opzioni del driver) e
`ipptool -tv ipp://<ip>:631/ipp/print get-printer-attributes.test` (capacità del device)
danno liste diverse: il device può molto più della coda.
2. **Colore**: molte code hanno `print-color-mode-default = monochrome` → servono
`-o print-color-mode=color` (+ `CNGrayscale=False`, `ColorModel=rgb`), altrimenti
l'etichetta esce in bianco e nero.
3. **Vassoio**: se il PPD del driver non espone alcuna opzione di sorgente carta (tipico
dei driver Canon), dalla coda il vassoio non si sceglie: il driver lo deduce dal tipo
supporto (fotografico ⇒ posteriore/superiore). Per imporlo serve la stampa diretta IPP
con `media-col { media-source=rear media-type=photographic }` e un raster PWG a 600 dpi.
4. **Qualità**: la coda può offrire solo `normal` mentre il device accetta `high` (5).
5. **Validare l'ordine**: `Validate-Job` non consuma carta e segnala gli attributi non
supportati (es. `print-scaling`): toglierli prima di `Print-Job`.
6. **Esito dal device**: `get-jobs.test` + `printer-state-reasons`. Un job
`processing-stopped` con `code=coverOpen` non è un fallimento: chiuso il supporto del
vassoio posteriore la stampa riparte da sola.
7. **Scala reale**: raster esattamente A4 (4961×7016 px a 600 dpi) su media A4, stampa in
scala 100% (mai "adatta alla pagina"); verificare la misura finale (etichetta 42×37 mm,
15 mm da sinistra e 20 mm dall'alto).
## Troubleshooting (dettaglio in `references/cartucce-profili-errori.md`)
- *"No devices found"* con GBxCart RW/GBFlash su Linux: il modulo `brltty` cattura i
CH340/341 → rimuoverlo/blacklistarlo.
@@ -127,5 +154,10 @@ cmp /tmp/verify.gb /percorso/rom.gb && echo IDENTICI
DMG/AGB, conflitti driver.
- `references/cartucce-profili-errori.md` — mask ROM vs repro/flashcart, categorie di
cartucce scrivibili, tensione/erase, batteryless, catalogo errori.
- `references/stampa-etichetta.md` — stampa del foglio etichette su CUPS/IPP: trappole
colore/vassoio/qualità, raster PWG a 600 dpi, Validate-Job, diagnosi job fermo.
- `scripts/gb-cart.sh` — wrapper deterministico (detect/info/backup/flash con backup
obbligatorio e confronto hash finale).
- `scripts/print-label-pwg.sh` — stampa IPP di un PDF come PWG raster A4 600 dpi con
vassoio/tipo supporto/qualità/colore espliciti (Validate-Job di default, `--print` per
stampare).
@@ -0,0 +1,132 @@
# Stampa dell'etichetta della cartuccia (CUPS / IPP)
Procedura per stampare il foglio A4 con l'etichetta della cartuccia Game Boy
(42×37 mm, eventualmente ripetuta su un'unica riga) e per controllare i parametri
fisici che contano davvero: **vassoio di alimentazione, tipo supporto lucido/fotografico,
qualità, colore e scala reale**.
## 0. Regola d'oro: interrogare prima, stampare poi
Prima di qualunque stampa su supporto costoso (vinile adesivo lucido) verificare
le capacità reali della coda e del device — non assumerle:
```bash
lpstat -p -d # code e predefinita
lpstat -v # device URI (dnssd/ipp/usb)
lpoptions -p <coda> -l # opzioni esposte dal PPD (quelle del DRIVER)
lpoptions -p <coda> # default correnti del job (qui si vedono le trappole)
ipptool -tv ipp://localhost:631/printers/<coda> get-printer-attributes.test # attributi della coda CUPS
ipptool -tv ipp://<IP-stampante>:631/ipp/print get-printer-attributes.test # attributi del DEVICE
```
Le due liste di attributi **non coincidono**: la coda è limitata da quello che il PPD
del driver espone, il device via IPP può supportare molto di più (qualità alta, sorgenti
carta, scaling). Da qui le tre trappole seguenti.
## 1. Trappola colore: default monocromatico
Alcune code (es. driver Canon con PPD cnijfilter2) hanno
`print-color-mode-default = monochrome`: senza override il foglio esce in bianco e nero.
Passare **sempre**:
```
-o print-color-mode=color # attributo IPP
-o CNGrayscale=False -o ColorModel=rgb # opzioni PPD del driver Canon
```
## 2. Trappola vassoio: il PPD del driver può non avere la sorgente carta
Verificare con `grep -iE "inputslot|papersource|tray|source" <PPD>`:
se il PPD **non contiene alcuna opzione di vassoio** (caso dei driver Canon per
TS700 series: solo MediaType, CNGrayscale, PageSize, PageRegion, Duplex, Resolution,
ColorModel), allora **dalla coda la sorgente non è selezionabile**: il driver sceglie il
vassoio in base al *media type* (supporto fotografico ⇒ vassoio posteriore/superiore,
carta normale ⇒ cassetto principale), e `-o media-source=rear` resta un attributo di job
che non arriva al driver (il trasporto verso il device è il datastream Canon, non IPP).
Per **forzare** il vassoio serve la stampa diretta IPP al device (§4), dove
`media-col { media-source=… }` è supportato davvero.
## 3. Trappola qualità: la coda può essere più povera del device
Esempio reale: la coda esponeva `print-quality = normal` (unico valore), mentre il
device via IPP dichiarava `print-quality-supported = draft, normal, high`.
Con i driver PPD-CUPS la "qualità alta" si ottiene comunque scegliendo un **media type
fotografico** (più passaggi del carrello, inchiostro più denso); via IPP si può chiedere
direttamente `print-quality=5` (high).
## 4. Stampa diretta IPP con controllo completo (vassoio, qualità, colore)
Serve un formato che il device accetti: verificare `document-format-supported`
(tipicamente `image/pwg-raster`, `image/urf`, `image/jpeg` — **spesso non il PDF**).
Il percorso verificato è **PWG raster a 600 dpi** generato con i filtri di sistema:
```bash
# PPD minimo "full page" (raster A4 intero, non solo area stampabile)
# *ImageableArea A4/A4: "0 0 595.276 841.89"
# *cupsFilter: "application/vnd.cups-raster 0 pdftoraster"
# *cupsFilter: "image/pwg-raster 0 rastertopwg"
PPD=/tmp/pwg-generic.ppd pdftoraster 1 "$USER" "etichetta" 1 \
"media=A4 PageSize=A4 Resolution=600dpi ColorModel=rgb print-color-mode=color" \
< foglio.pdf > job.ras
rastertopwg 1 "$USER" "etichetta" 1 "media=A4 Resolution=600dpi" < job.ras > job.pwg
# atteso: ~4961x7016 px a 600 dpi (A4), 24 bpp, ~9 MB con RLE
```
Attributi del job (file di test `ipptool`):
```
ATTR collection media-col {
MEMBER collection media-size { MEMBER integer x-dimension 21000
MEMBER integer y-dimension 29700 }
MEMBER integer media-left-margin 340 # margini default A4 non-borderless
MEMBER integer media-right-margin 340 # (0 = borderless: evitarlo se non serve)
MEMBER integer media-top-margin 500
MEMBER integer media-bottom-margin 500
MEMBER keyword media-source rear # auto | main | rear | disc
MEMBER keyword media-type photographic # stationery | photographic | envelope | disc
}
ATTR enum print-quality 5 # 3=draft, 4=normal, 5=high
ATTR keyword print-color-mode color
ATTR resolution printer-resolution 600dpi
ATTR keyword sides one-sided
```
**Prima `Validate-Job`, poi `Print-Job`**: `Validate-Job` non stampa e dice se il device
accetta gli attributi. Un valore non supportato viene segnalato come
`successful-ok-ignored-or-substituted-attributes` con `status-message`
(es. *"Unsupported print-scaling"*): togliere quell'attributo e ripetere la validazione
fino a `successful-ok`. Con `ipp-attribute-fidelity=true` un attributo non supportato
farebbe **fallire** il job: non attivarlo.
```bash
ipptool -tv ipp://<IP>:631/ipp/print validate.test # non stampa
ipptool -tv -d filename=job.pwg ipp://<IP>:631/ipp/print print.test
```
Scala reale: se il device non supporta `print-scaling`, con un raster **esattamente**
della misura del supporto (A4 = A4) non c'è riscalatura. Verificare comunque la misura
fisica del risultato (etichetta 42×37 mm, a 15 mm da sinistra e 20 mm dall'alto).
## 5. Stampa via coda (quando il vassoio non va imposto)
Comando di riferimento per supporto lucido fotografico, colore, scala 100%, fronte singolo:
```bash
lp -d <coda> file.pdf -t "<titolo>" \
-o media=iso_a4_210x297mm -o PageSize=A4 \
-o media-type=<media-type> -o MediaType=<media-type PPD> \
-o print-color-mode=color -o CNGrayscale=False -o ColorModel=rgb \
-o print-scaling=none -o fit-to-page=Off -o natural-scaling=100 \
-o sides=one-sided -o Duplex=None
```
`print-scaling` è l'attributo corretto in cups-filters 2.x (`none` = nessuna scalatura);
`natural-scaling` conta solo per input raster, non per i PDF.
## 6. Esito e trappole fisiche
- Controllare esito e allerte **del device**, non solo della coda:
`ipptool … get-jobs.test` (job-state) e `printer-state-reasons` / `printer-alert-description`.
- **`cover-open-error` / `code=coverOpen`**: se il supporto/blocco del foglio del vassoio
posteriore è alzato, il job resta in `processing-stopped` con motivo `job-stopped` e non
parte nulla. Chiuso il supporto, il job **riparte da solo** (non serve reinviarlo).
Verificare prima di dichiarare un fallimento: la stampa può essere solo in attesa.
- `spool-area-full-report` compare come allerta non critica quando un job è fermo e si
azzera da solo a stampa conclusa.
- Contatti/carta: se il job non parte, controllare `media-ready` e il livello inchiostro
(`marker-levels`).
## 7. Riferimenti verificati
I valori concreti della macchina (nome coda, IP del device, PPD, modello, esiti dei job)
stanno in qmem, project `host-<hostname>` — non duplicarli qui. In questo repo il foglio
di stampa si genera con `scripts/legacy/cartridge/generate_label_strip_pdf.py`.
+198
View File
@@ -0,0 +1,198 @@
#!/usr/bin/env bash
# print-label-pwg.sh — stampa un PDF (es. foglio etichette cartuccia) su una stampante
# IPP generando un PWG raster A4 a 600 dpi, con controllo di vassoio, tipo supporto,
# qualità e colore che il driver CUPS/PPD spesso non espone.
#
# Uso: print-label-pwg.sh FILE.pdf ipp://IP:631/ipp/print [opzioni]
#
# Opzioni:
# --source auto|main|rear|disc vassoio (default: rear)
# --media-type stationery|photographic|envelope|disc (default: photographic)
# --quality draft|normal|high (default: high)
# --color color|monochrome (default: color)
# --job-name NOME (default: nome del file)
# --print invia davvero il job (default: solo Validate-Job)
# --keep DIR conserva PWG/PPD/test in DIR
# -h|--help
#
# Variabili: PDFTORASTER/RASTERTOPWG per sovrascrivere i filtri,
# IPPTOOL per l'eseguibile ipptool.
#
# Il default è NON stampare: si esegue solo Validate-Job (non consuma carta) e si
# riporta l'esito; aggiungere --print per inviare il job.
set -euo pipefail
PDFTORASTER="${PDFTORASTER:-/usr/lib/cups/filter/pdftoraster}"
RASTERTOPWG="${RASTERTOPWG:-/usr/lib/cups/filter/rastertopwg}"
IPPTOOL="${IPPTOOL:-ipptool}"
DPI=600
die() { printf 'errore: %s\n' "$*" >&2; exit 1; }
info() { printf '>> %s\n' "$*"; }
usage() { awk 'NR==1{next} /^#/{sub(/^# ?/,""); print; next} {exit}' "$0"; }
[[ $# -ge 2 ]] || { usage; exit 1; }
case "${1:-}" in -h|--help) usage; exit 0;; esac
PDF="$1"; URI="$2"; shift 2
SOURCE="rear"; MEDIA_TYPE="photographic"; QUALITY="high"; COLOR="color"
DO_PRINT=0; KEEP_DIR=""; JOB_NAME=""
while [[ $# -gt 0 ]]; do
case "$1" in
--source) SOURCE="$2"; shift 2 ;;
--media-type) MEDIA_TYPE="$2"; shift 2 ;;
--quality) QUALITY="$2"; shift 2 ;;
--color) COLOR="$2"; shift 2 ;;
--job-name) JOB_NAME="$2"; shift 2 ;;
--print) DO_PRINT=1; shift ;;
--keep) KEEP_DIR="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) die "opzione sconosciuta: $1" ;;
esac
done
[ -f "$PDF" ] || die "PDF non trovato: $PDF"
[ -x "$PDFTORASTER" ] || die "filtro pdftoraster non trovato ($PDFTORASTER)"
[ -x "$RASTERTOPWG" ] || die "filtro rastertopwg non trovato ($RASTERTOPWG)"
command -v "$IPPTOOL" >/dev/null 2>&1 || die "ipptool non trovato nel PATH"
case "$QUALITY" in draft) PQ=3 ;; normal) PQ=4 ;; high) PQ=5 ;; *) die "--quality: draft|normal|high" ;; esac
[[ -n "$JOB_NAME" ]] || JOB_NAME="$(basename "${PDF%.*}")"
if [[ -n "$KEEP_DIR" ]]; then
mkdir -p "$KEEP_DIR"; WORK="$KEEP_DIR"
else
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
fi
# ---------------------------------------------------------------- PPD generico
# Raster A4 "full page" (area stampabile = pagina intera) a 600 dpi, RGB.
cat > "$WORK/generic-pwg.ppd" <<'PPD'
*PPD-Adobe: "4.3"
*FormatVersion: "4.3"
*FileVersion: "1.0"
*LanguageVersion: English
*LanguageEncoding: ISOLatin1
*PCFileName: "generic-pwg.ppd"
*Manufacturer: "Generic"
*ModelName: "Generic PWG Raster (full page)"
*NickName: "Generic PWG Raster (full page)"
*ShortNickName: "Generic PWG Raster"
*Product: "(Generic IPP Everywhere raster)"
*DefaultResolution: 600dpi
*OpenUI *Resolution/Output Resolution: PickOne
*DefaultResolution: 600dpi
*Resolution 600dpi/600 DPI: "<</HWResolution[600 600]>>setpagedevice"
*CloseUI: *Resolution
*DefaultPageSize: A4
*DefaultPageRegion: A4
*DefaultImageableArea: A4
*DefaultPaperDimension: A4
*OpenUI *PageSize/Page Size: PickOne
*DefaultPageSize: A4
*PageSize A4/A4: "<</PageSize[595.276 841.89]>>setpagedevice"
*CloseUI: *PageSize
*OpenUI *PageRegion/Page Region: PickOne
*DefaultPageRegion: A4
*PageRegion A4/A4: "<</PageSize[595.276 841.89]>>setpagedevice"
*CloseUI: *PageRegion
*ImageableArea A4/A4: "0 0 595.276 841.89"
*PaperDimension A4/A4: "595.276 841.89"
*OpenUI *ColorModel/Color Model: PickOne
*DefaultColorModel: rgb
*ColorModel rgb/RGB: "<</cupsColorSpace 1/cupsBitsPerColor 8/cupsColorOrder 0>>setpagedevice"
*CloseUI: *ColorModel
*cupsFilter: "application/vnd.cups-raster 0 pdftoraster"
*cupsFilter: "image/pwg-raster 0 rastertopwg"
PPD
# ---------------------------------------------------------------- PDF -> PWG raster
info "PDF -> CUPS raster (600 dpi, A4 intero)"
PPD="$WORK/generic-pwg.ppd" "$PDFTORASTER" 1 "${USER:-user}" "$JOB_NAME" 1 \
"media=A4 PageSize=A4 Resolution=600dpi ColorModel=rgb print-color-mode=$COLOR" \
< "$PDF" > "$WORK/job.ras" 2> "$WORK/pdftoraster.log" \
|| { tail -5 "$WORK/pdftoraster.log" >&2; die "pdftoraster fallito"; }
info "CUPS raster -> PWG raster"
"$RASTERTOPWG" 1 "${USER:-user}" "$JOB_NAME" 1 "media=A4 Resolution=600dpi" \
< "$WORK/job.ras" > "$WORK/job.pwg" 2> "$WORK/rastertopwg.log" \
|| { tail -5 "$WORK/rastertopwg.log" >&2; die "rastertopwg fallito"; }
head -c 4096 "$WORK/job.pwg" | grep -q "PwgRaster" || die "output non è un PWG raster"
info "PWG raster: $(stat -c%s "$WORK/job.pwg") byte (A4 600 dpi = 4961x7016 px attesi)"
# ---------------------------------------------------------------- job IPP
attr_block() {
cat <<EOF
GROUP job-attributes-tag
ATTR collection media-col {
MEMBER collection media-size {
MEMBER integer x-dimension 21000
MEMBER integer y-dimension 29700
}
MEMBER integer media-left-margin 340
MEMBER integer media-right-margin 340
MEMBER integer media-top-margin 500
MEMBER integer media-bottom-margin 500
MEMBER keyword media-source $SOURCE
MEMBER keyword media-type $MEDIA_TYPE
}
ATTR enum print-quality $PQ
ATTR keyword print-color-mode $COLOR
ATTR resolution printer-resolution 600dpi
ATTR keyword sides one-sided
EOF
}
{
printf '{\n\tNAME "Validate-Job %s"\n' "$JOB_NAME"
printf '\tOPERATION Validate-Job\n'
cat <<EOF
GROUP operation-attributes-tag
ATTR charset attributes-charset utf-8
ATTR language attributes-natural-language en
ATTR uri printer-uri \$uri
ATTR name requesting-user-name \$user
ATTR name job-name $JOB_NAME
ATTR mimetype document-format image/pwg-raster
EOF
attr_block
printf '\tSTATUS successful-ok\n}\n'
} > "$WORK/validate.test"
{
printf '{\n\tNAME "Print-Job %s"\n' "$JOB_NAME"
printf '\tOPERATION Print-Job\n'
cat <<EOF
GROUP operation-attributes-tag
ATTR charset attributes-charset utf-8
ATTR language attributes-natural-language en
ATTR uri printer-uri \$uri
ATTR name requesting-user-name \$user
ATTR name job-name $JOB_NAME
ATTR mimetype document-format image/pwg-raster
EOF
attr_block
printf '\tFILE $filename\n\tSTATUS successful-ok\n}\n'
} > "$WORK/print.test"
info "attributi: vassoio=$SOURCE supporto=$MEDIA_TYPE qualità=$QUALITY(pq$PQ) colore=$COLOR"
info "Validate-Job su $URI (non stampa)"
if "$IPPTOOL" -tv "$URI" "$WORK/validate.test" 2>&1 | tee "$WORK/validate.out" | tail -5 | grep -q "\[PASS\]"; then
info "Validate-Job: OK"
else
sed -n 's/.*status-message (textWithoutLanguage) = //p' "$WORK/validate.out" | head -3 || true
die "Validate-Job non passato (vedi $WORK/validate.out)"
fi
if [[ "$DO_PRINT" -eq 1 ]]; then
info "invio Print-Job"
"$IPPTOOL" -tv -d filename="$WORK/job.pwg" "$URI" "$WORK/print.test" 2>&1 | tail -8
info "job inviato: controllare esito con 'ipptool -tv $URI get-jobs.test'"
info "se il job resta in processing-stopped: verificare printer-state-reasons (es. cover aperto)"
else
info "nessuna stampa inviata (aggiungi --print per stampare davvero)"
fi