diff --git a/dist/LabelPrinter.exe b/dist/LabelPrinter.exe index a80fa6c..3323235 100644 Binary files a/dist/LabelPrinter.exe and b/dist/LabelPrinter.exe differ diff --git a/label_printer_gui.py b/label_printer_gui.py index a85a316..66e354a 100644 --- a/label_printer_gui.py +++ b/label_printer_gui.py @@ -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 diff --git a/print_label.py b/print_label.py index d5cfba7..a829fc4 100644 --- a/print_label.py +++ b/print_label.py @@ -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)}")