Fix impression pellicule : la feuille -div2 doit etre envoyee a taille pleine

La PageSize CUPS w288h432-div2 declare la meme taille de page que w288h432
(confirme par le PPD Mitsubishi) : le K60 coupe la feuille pleine apres
impression, il ne duplique pas une image fournie a taille reduite. On envoyait
la bande seule (600x1800) sous cette PageSize, donc CUPS l'etirait pour
remplir la feuille 1200x1800 puis le K60 la coupait en 2 — d'ou la photo
etiree et coupee au lieu de 2 bandes identiques.

Ajoute aussi le miroir horizontal sur le preview live (selfie) et un flou de
chargement pendant le bref delai de remontee du miroir DSLR apres capture,
pour eviter l'impression d'image figee sur la photo precedente.
This commit is contained in:
2026-06-21 23:32:50 +02:00
parent a79762197d
commit 640715282c
4 changed files with 46 additions and 8 deletions

View File

@@ -15,8 +15,13 @@ FORMATS = {
"10x15": {"cups": "w288h432", "px": (1200, 1800)},
"15x15": {"cups": "w432h432", "px": (1800, 1800)},
"15x20": {"cups": "w432h576", "px": (1800, 2400)},
"10x15-2up": {"cups": "w288h432-div2", "px": (600, 1800)}, # 1 strip 5x15cm, printer duplique
"15x20-2up": {"cups": "w432h576-div2", "px": (900, 2400)}, # 1 strip 7.5x20cm, printer duplique
# "-div2" declare en CUPS la MEME taille de page que le format plein (le PPD le confirme :
# PaperDimension identique pour w288h432 et w288h432-div2). Le K60 ne duplique pas une
# image fournie a taille reduite : il imprime la feuille entiere telle qu'envoyee puis la
# coupe en 2 apres impression. "px" = taille d'une bande seule, "px_page" = feuille entiere
# a envoyer a lp (bande dupliquee cote a cote par _dupliquer_page).
"10x15-2up": {"cups": "w288h432-div2", "px": (600, 1800), "px_page": (1200, 1800)},
"15x20-2up": {"cups": "w432h576-div2", "px": (900, 2400), "px_page": (1800, 2400)},
}
ERREUR_FICHIER = "fichier_introuvable"
@@ -104,6 +109,22 @@ def _preparer_image(chemin: Path, largeur: int, hauteur: int,
return Path(tmp.name)
def _dupliquer_page(chemin: Path, taille_page: tuple[int, int]) -> Path:
"""Colle la bande 2 fois cote a cote pour remplir la feuille entiere.
Necessaire pour les formats "-div2" : le K60 coupe la feuille imprimee en 2
apres impression, il ne duplique pas une image fournie a taille reduite.
"""
from PIL import Image
img = Image.open(chemin)
page = Image.new("RGB", taille_page, (255, 255, 255))
page.paste(img, (0, 0))
page.paste(img, (img.width, 0))
tmp = tempfile.NamedTemporaryFile(suffix=".jpg", delete=False)
page.save(tmp.name, "JPEG", quality=95, dpi=(300, 300))
return Path(tmp.name)
def _statut(nom: str) -> str:
"""Retourne 'idle', 'printing', 'stopped' ou 'inconnue'."""
code, out, _ = _run(["lpstat", "-p", nom])
@@ -205,7 +226,13 @@ def imprimer(
return {"succes": False, "erreur": ERREUR_FICHIER, "message": "Fichier photo introuvable"}
chemin_print = _preparer_image(chemin_photo, largeur, hauteur, fmt_base, cadre, orientation)
tmp_cree = chemin_print != chemin_photo
tmp_a_supprimer = [chemin_print] if chemin_print != chemin_photo else []
px_page = fmt.get("px_page")
if px_page:
chemin_page = _dupliquer_page(chemin_print, px_page)
tmp_a_supprimer.append(chemin_page)
chemin_print = chemin_page
if _statut(imprimante) == "stopped":
log.warning(f"Imprimante {imprimante} arrêtée — réactivation")
@@ -261,5 +288,5 @@ def imprimer(
"message": "Impression échouée après 3 tentatives",
}
finally:
if tmp_cree:
chemin_print.unlink(missing_ok=True)
for tmp in tmp_a_supprimer:
tmp.unlink(missing_ok=True)

View File

@@ -325,6 +325,11 @@ html, body {
width: 100%;
height: 100%;
object-fit: contain;
transform: scaleX(-1); /* miroir selfie : ce que voit l'utilisateur en se posant */
}
.preview-live img.preview-chargement {
filter: blur(8px) brightness(0.6);
}
.cadre-guide-recadrage {

View File

@@ -7,7 +7,7 @@
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta name="google" content="notranslate">
<meta http-equiv="Content-Language" content="fr">
<link rel="stylesheet" href="/css/style.css?v=3">
<link rel="stylesheet" href="/css/style.css?v=4">
<link rel="stylesheet" href="/css/themes.css">
</head>
<body>
@@ -739,7 +739,7 @@
<script src="/js/websocket.js?v=3"></script>
<script src="/js/app.js?v=4"></script>
<script src="/js/camera.js?v=8"></script>
<script src="/js/camera.js?v=9"></script>
<script src="/js/effects.js?v=3"></script>
<script src="/js/gallery.js?v=3"></script>
<script src="/js/share.js?v=5"></script>

View File

@@ -48,6 +48,9 @@ function lancerPreview() {
if (previewActif) return;
previewActif = true;
afficherErreurPreview(false);
// Flou le temps que la 1ere frame fraiche arrive (le miroir DSLR doit remonter
// apres la capture precedente) : evite l'impression d'image figee sur l'ancienne photo.
document.getElementById('img-preview')?.classList.add('preview-chargement');
wsEnvoyer({ type: 'preview_start' });
resetPreviewTimeout();
}
@@ -72,7 +75,10 @@ wsOnMessage('preview', (msg) => {
if (!previewActif) return;
const img = document.getElementById('img-preview');
if (img) {
img.onload = positionnerGuideRecadrage;
img.onload = () => {
img.classList.remove('preview-chargement');
positionnerGuideRecadrage();
};
img.src = msg.image;
}
afficherErreurPreview(false);