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:
BIN
dist/LabelPrinter.exe
vendored
BIN
dist/LabelPrinter.exe
vendored
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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)}")
|
||||
|
||||
Reference in New Issue
Block a user