Fix blank label between copies: send all copies in single GDI print job (StartDoc/N pages/EndDoc) instead of N separate jobs - thermal printers eject blank label between rapid separate jobs

This commit is contained in:
2026-02-25 16:20:45 +02:00
parent be1b494527
commit 71ccdb7b96
3 changed files with 33 additions and 34 deletions

BIN
dist/LabelPrinter.exe vendored

Binary file not shown.

View File

@@ -982,23 +982,13 @@ printer = PDF
success = False
all_success = True
try:
# Print multiple copies if count > 1
for i in range(count):
if count > 1:
Clock.schedule_once(lambda dt, idx=i: popup.content.children[0].text.replace(
f'Processing {count} label(s)...',
f'Printing {idx+1} of {count}...'
), 0)
success = print_label_standalone(label_text, printer, preview=0, svg_template=template_path)
if not success:
all_success = False
break
# Small delay between prints for multiple copies
if i < count - 1:
time.sleep(0.5)
# Send all copies in ONE print job to prevent blank labels
# being ejected between separate jobs on thermal printers.
success = print_label_standalone(
label_text, printer, preview=0,
svg_template=template_path, copies=count
)
all_success = success
# Get the PDF filename that was created
# Files are saved to pdf_backup/ with timestamp

View File

@@ -158,15 +158,18 @@ def create_label_pdf(text, svg_template=None):
return generator.create_label_pdf(article, nr_art, serial, pdf_filename, image_path, selected_template)
def _print_pdf_windows(file_path, printer_name):
def _print_pdf_windows(file_path, printer_name, copies=1):
"""
Print a PDF on Windows using pymupdf (fitz) to render and win32print/GDI to send.
All copies are sent as multiple pages within ONE print job so the printer
never sees a gap (which causes blank labels on thermal printers).
No external application is launched. Falls back to SumatraPDF if pymupdf
is unavailable.
Returns True on success, False on failure.
"""
_write_print_log(f"_print_pdf_windows called: printer={printer_name!r} file={file_path!r}")
copies = max(1, int(copies))
_write_print_log(f"_print_pdf_windows called: printer={printer_name!r} copies={copies} file={file_path!r}")
# ── Method 1: pure-Python GDI (pymupdf + win32print) ─────────────────
try:
@@ -207,16 +210,18 @@ def _print_pdf_windows(file_path, printer_name):
f"rendered={pix.width}x{pix.height}px dest={dest_w}x{dest_h}px"
)
# Draw at exact physical size NOT stretched to the driver's paper area
# Draw at exact physical size NOT stretched to the driver's paper area.
# All copies go into ONE print job to prevent blank labels between jobs.
hdc.StartDoc(os.path.basename(file_path))
hdc.StartPage()
dib = ImageWin.Dib(img)
dib.draw(hdc.GetHandleOutput(), (0, 0, dest_w, dest_h))
hdc.EndPage()
for copy_idx in range(copies):
hdc.StartPage()
dib.draw(hdc.GetHandleOutput(), (0, 0, dest_w, dest_h))
hdc.EndPage()
hdc.EndDoc()
hdc.DeleteDC()
_write_print_log(f"GDI print SUCCESS → {printer_name}")
_write_print_log(f"GDI print SUCCESS → {printer_name} ({copies} cop{'y' if copies==1 else 'ies'} in 1 job)")
return True
except ImportError as ie:
@@ -291,15 +296,16 @@ def _print_pdf_windows(file_path, printer_name):
return False
def print_to_printer(printer_name, file_path):
def print_to_printer(printer_name, file_path, copies=1):
"""
Print file to printer (cross-platform).
Uses SumatraPDF for silent printing on Windows.
Uses pymupdf+GDI for silent printing on Windows with all copies in one job.
Args:
printer_name (str): Name of printer or "PDF" for PDF output
file_path (str): Path to file to print
copies (int): Number of copies to print in a single print job
Returns:
bool: True if successful
"""
@@ -320,7 +326,7 @@ def print_to_printer(printer_name, file_path):
# Windows: Print PDF using Python GDI (pymupdf + win32print).
# No external viewer is launched at any point.
if file_path.endswith('.pdf'):
return _print_pdf_windows(file_path, printer_name)
return _print_pdf_windows(file_path, printer_name, copies=copies)
else:
subprocess.run(['notepad', '/p', file_path],
check=False,
@@ -344,20 +350,23 @@ def print_to_printer(printer_name, file_path):
return True
def print_label_standalone(value, printer, preview=0, svg_template=None):
def print_label_standalone(value, printer, preview=0, svg_template=None, copies=1):
"""
Generate a PDF label, save it to pdf_backup/, and print it on the printer.
The printer's own paper size and orientation settings are used as-is.
All copies are sent in a single print job to avoid blank labels on thermal
printers that eject a label between separate jobs.
Args:
value (str): Label data in format "article;nr_art;serial;status"
printer (str): Printer name (or "PDF" to skip physical printing)
preview (int): 0 = print immediately; 1-3 = 3 s delay; >3 = 5 s delay
svg_template (str): Path to SVG template (optional; auto-selected if None)
copies (int): Number of copies to print in one job (default 1)
Returns:
bool: True if sending to printer succeeded, False otherwise
"""
copies = max(1, int(copies))
pdf_file = None
try:
# Step 1 Generate and save PDF to pdf_backup/
@@ -388,9 +397,9 @@ def print_label_standalone(value, printer, preview=0, svg_template=None):
print("\nCancelled by user")
return False
# Step 3 Send to printer
print("Sending to printer...")
return print_to_printer(printer, pdf_file)
# Step 3 Send to printer (all copies in one job)
print(f"Sending to printer ({copies} cop{'y' if copies==1 else 'ies'})...")
return print_to_printer(printer, pdf_file, copies=copies)
except Exception as e:
print(f"Error printing label: {str(e)}")