Remove dead code, DEVMODE overrides, and PNG fallback; use printer default settings
This commit is contained in:
326
print_label.py
326
print_label.py
@@ -1,6 +1,3 @@
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import barcode
|
||||
from barcode.writer import ImageWriter
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
@@ -89,119 +86,6 @@ def get_available_printers():
|
||||
return ["PDF"]
|
||||
|
||||
|
||||
def create_label_image(text):
|
||||
"""
|
||||
Create a label image with 3 rows: label + barcode for each field.
|
||||
|
||||
Args:
|
||||
text (str): Combined text in format "SAP|CANTITATE|LOT" or single value
|
||||
|
||||
Returns:
|
||||
PIL.Image: The generated label image
|
||||
"""
|
||||
# Parse the text input
|
||||
parts = text.split('|') if '|' in text else [text, '', '']
|
||||
sap_nr = parts[0].strip() if len(parts) > 0 else ''
|
||||
cantitate = parts[1].strip() if len(parts) > 1 else ''
|
||||
lot_number = parts[2].strip() if len(parts) > 2 else ''
|
||||
|
||||
# Label dimensions (narrower, 3 rows)
|
||||
label_width = 800 # 8 cm
|
||||
label_height = 600 # 6 cm
|
||||
|
||||
# Create canvas
|
||||
label_img = Image.new('RGB', (label_width, label_height), 'white')
|
||||
draw = ImageDraw.Draw(label_img)
|
||||
|
||||
# Row setup - 3 equal rows
|
||||
row_height = label_height // 3
|
||||
left_margin = 15
|
||||
row_spacing = 3
|
||||
|
||||
# Fonts
|
||||
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
|
||||
try:
|
||||
label_font = ImageFont.truetype(font_path, 16)
|
||||
value_font = ImageFont.truetype(font_path, 14)
|
||||
except IOError:
|
||||
label_font = ImageFont.load_default()
|
||||
value_font = ImageFont.load_default()
|
||||
|
||||
# Data for 3 rows
|
||||
rows_data = [
|
||||
("SAP-Nr", sap_nr),
|
||||
("Cantitate", cantitate),
|
||||
("Lot Nr", lot_number),
|
||||
]
|
||||
|
||||
# Generate barcodes first
|
||||
CODE128 = barcode.get_barcode_class('code128')
|
||||
writer_options = {
|
||||
"write_text": False,
|
||||
"module_width": 0.4,
|
||||
"module_height": 8,
|
||||
"quiet_zone": 2,
|
||||
"font_size": 0
|
||||
}
|
||||
|
||||
barcode_images = []
|
||||
for _, value in rows_data:
|
||||
if value:
|
||||
try:
|
||||
code = CODE128(value[:25], writer=ImageWriter())
|
||||
filename = code.save('temp_barcode', options=writer_options)
|
||||
barcode_img = Image.open(filename)
|
||||
barcode_images.append(barcode_img)
|
||||
except:
|
||||
barcode_images.append(None)
|
||||
else:
|
||||
barcode_images.append(None)
|
||||
|
||||
# Draw each row with label and barcode
|
||||
for idx, ((label_name, value), barcode_img) in enumerate(zip(rows_data, barcode_images)):
|
||||
row_y = idx * row_height
|
||||
|
||||
# Draw label name
|
||||
draw.text(
|
||||
(left_margin, row_y + 3),
|
||||
label_name,
|
||||
fill='black',
|
||||
font=label_font
|
||||
)
|
||||
|
||||
# Draw barcode if available
|
||||
if barcode_img:
|
||||
# Resize barcode to fit in row width
|
||||
barcode_width = label_width - left_margin - 10
|
||||
barcode_height = row_height - 25
|
||||
# Use high-quality resampling for crisp barcodes
|
||||
try:
|
||||
# Try newer Pillow API first
|
||||
from PIL.Image import Resampling
|
||||
barcode_resized = barcode_img.resize((barcode_width, barcode_height), Resampling.LANCZOS)
|
||||
except (ImportError, AttributeError):
|
||||
# Fallback for older Pillow versions
|
||||
barcode_resized = barcode_img.resize((barcode_width, barcode_height), Image.LANCZOS)
|
||||
label_img.paste(barcode_resized, (left_margin, row_y + 20))
|
||||
else:
|
||||
# Fallback: show value as text
|
||||
draw.text(
|
||||
(left_margin, row_y + 25),
|
||||
value if value else "(empty)",
|
||||
fill='black',
|
||||
font=value_font
|
||||
)
|
||||
|
||||
# Clean up temporary barcode files
|
||||
try:
|
||||
if os.path.exists('temp_barcode.png'):
|
||||
os.remove('temp_barcode.png')
|
||||
except:
|
||||
pass
|
||||
|
||||
return label_img
|
||||
|
||||
|
||||
def create_label_pdf(text, svg_template=None):
|
||||
"""
|
||||
Create a high-quality PDF label with 3 rows: label + barcode for each field.
|
||||
@@ -259,94 +143,6 @@ def create_label_pdf(text, svg_template=None):
|
||||
return generator.create_label_pdf(article, nr_art, serial, pdf_filename, image_path, selected_template)
|
||||
|
||||
|
||||
def configure_printer_quality(printer_name, width_mm=35, height_mm=25):
|
||||
"""
|
||||
Configure printer for high quality label printing (Windows only).
|
||||
Sets paper size, orientation, and QUALITY settings.
|
||||
|
||||
Args:
|
||||
printer_name (str): Name of the printer
|
||||
width_mm (int): Label width in millimeters (default 35)
|
||||
height_mm (int): Label height in millimeters (default 25)
|
||||
|
||||
Returns:
|
||||
bool: True if successful
|
||||
"""
|
||||
if SYSTEM != "Windows" or not WIN32_AVAILABLE:
|
||||
return False
|
||||
|
||||
try:
|
||||
import win32print
|
||||
import pywintypes
|
||||
|
||||
hprinter = win32print.OpenPrinter(printer_name)
|
||||
|
||||
try:
|
||||
# Get current printer properties
|
||||
props = win32print.GetPrinter(hprinter, 2)
|
||||
devmode = props.get("pDevMode")
|
||||
|
||||
if devmode is None:
|
||||
print("Could not get printer DEVMODE")
|
||||
return False
|
||||
|
||||
# CRITICAL: Set print quality to HIGHEST
|
||||
# This prevents dotted/pixelated text
|
||||
try:
|
||||
devmode.PrintQuality = 600 # 600 DPI (high quality)
|
||||
except:
|
||||
try:
|
||||
devmode.PrintQuality = 4 # DMRES_HIGH
|
||||
except:
|
||||
pass
|
||||
|
||||
# Set custom paper size
|
||||
try:
|
||||
devmode.PaperSize = 256 # DMPAPER_USER (custom size)
|
||||
devmode.PaperLength = height_mm * 10 # Height in 0.1mm units
|
||||
devmode.PaperWidth = width_mm * 10 # Width in 0.1mm units
|
||||
except:
|
||||
pass
|
||||
|
||||
# Set orientation to PORTRAIT (1 = no rotation).
|
||||
# For a 35mm × 25mm thermal label, Portrait means "print across the
|
||||
# 35mm print-head width without rotating". Landscape (2) would
|
||||
# rotate the output 90° CCW, which is exactly the reported
|
||||
# "rotated-left" symptom – so we must NOT use Landscape here.
|
||||
try:
|
||||
devmode.Orientation = 1 # Portrait = no rotation
|
||||
except:
|
||||
pass
|
||||
|
||||
# Set additional quality settings
|
||||
try:
|
||||
devmode.Color = 1 # Monochrome for labels
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
devmode.TTOption = 2 # DMTT_BITMAP - print TrueType as graphics (sharper)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Apply settings
|
||||
try:
|
||||
props["pDevMode"] = devmode
|
||||
win32print.SetPrinter(hprinter, 2, props, 0)
|
||||
print(f"Printer configured: {width_mm}x{height_mm}mm @ HIGH QUALITY")
|
||||
return True
|
||||
except Exception as set_err:
|
||||
print(f"Could not apply printer settings: {set_err}")
|
||||
return False
|
||||
|
||||
finally:
|
||||
win32print.ClosePrinter(hprinter)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Could not configure printer quality: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def find_ghostscript():
|
||||
"""
|
||||
Find GhostScript executable. Search order:
|
||||
@@ -403,10 +199,8 @@ def print_pdf_with_ghostscript(pdf_path, printer_name):
|
||||
# -dTextAlphaBits=4 : anti-aliasing for text
|
||||
# -dGraphicsAlphaBits=4
|
||||
# -dNOSAFER : allow file access needed for fonts
|
||||
# -dDEVICEWIDTHPOINTS \
|
||||
# -dDEVICEHEIGHTPOINTS : explicit label size (35mm x 25mm in pts)
|
||||
# -dFIXEDMEDIA : do not auto-scale/rotate to a different size
|
||||
# 35mm = 35/25.4*72 ≈ 99.21 pt, 25mm = 25/25.4*72 ≈ 70.87 pt
|
||||
# The printer paper size and orientation are already configured
|
||||
# in the driver – do not override them here.
|
||||
cmd = [
|
||||
gs_path,
|
||||
"-dNOPAUSE",
|
||||
@@ -416,9 +210,6 @@ def print_pdf_with_ghostscript(pdf_path, printer_name):
|
||||
"-r600",
|
||||
"-dTextAlphaBits=4",
|
||||
"-dGraphicsAlphaBits=4",
|
||||
"-dDEVICEWIDTHPOINTS=99.21", # 35 mm
|
||||
"-dDEVICEHEIGHTPOINTS=70.87", # 25 mm
|
||||
"-dFIXEDMEDIA",
|
||||
f"-sOutputFile=%printer%{printer_name}",
|
||||
pdf_path,
|
||||
]
|
||||
@@ -472,19 +263,10 @@ def print_to_printer(printer_name, file_path):
|
||||
try:
|
||||
if WIN32_AVAILABLE:
|
||||
import win32print
|
||||
import win32api
|
||||
|
||||
# Configure printer DEVMODE BEFORE sending any job.
|
||||
# This is critical: it sets Portrait orientation (no rotation)
|
||||
# and maximum print quality so the 35mm×25mm PDF maps
|
||||
# directly to the physical label without being auto-rotated
|
||||
# by the driver (which caused the 90° "rotated left" symptom).
|
||||
configure_printer_quality(printer_name)
|
||||
|
||||
if file_path.endswith('.pdf'):
|
||||
# Try silent printing methods (no viewer opens)
|
||||
import os
|
||||
import winreg
|
||||
|
||||
printed = False
|
||||
|
||||
@@ -536,14 +318,9 @@ def print_to_printer(printer_name, file_path):
|
||||
try:
|
||||
print(f"Using SumatraPDF: {sumatra_path}")
|
||||
print(f"Sending to printer: {printer_name}")
|
||||
# The printer DEVMODE has already been configured
|
||||
# above (Portrait, 35mm×25mm, high quality).
|
||||
# "noscale" tells SumatraPDF to send the PDF
|
||||
# at its exact size without any shrink/fit.
|
||||
# Do NOT add "landscape" here: the DEVMODE
|
||||
# Portrait setting already matches the label
|
||||
# orientation; adding landscape would tell the
|
||||
# driver to rotate 90° again and undo the fix.
|
||||
# "noscale" = print the PDF at its exact page size.
|
||||
# Do NOT add "landscape" – the printer driver
|
||||
# already knows the orientation from its own settings.
|
||||
result = subprocess.run([
|
||||
sumatra_path,
|
||||
"-print-to",
|
||||
@@ -601,73 +378,41 @@ def print_to_printer(printer_name, file_path):
|
||||
return True
|
||||
|
||||
|
||||
def print_label_standalone(value, printer, preview=0, use_pdf=True, svg_template=None):
|
||||
def print_label_standalone(value, printer, preview=0, svg_template=None):
|
||||
"""
|
||||
Print a label with the specified text on the specified printer.
|
||||
Always generates a PDF backup in pdf_backup and prints that PDF.
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
value (str): The text to print on the label
|
||||
printer (str): The name of the printer to use
|
||||
preview (int): 0 = no preview, 1-3 = 3s preview, >3 = 5s preview
|
||||
use_pdf (bool): False to also generate a PNG if PDF generation fails
|
||||
svg_template (str): Path to specific SVG template to use (optional)
|
||||
|
||||
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)
|
||||
|
||||
Returns:
|
||||
bool: True if printing was successful, False otherwise
|
||||
bool: True if sending to printer succeeded, False otherwise
|
||||
"""
|
||||
# Track generated files
|
||||
file_created = False
|
||||
temp_file = None
|
||||
pdf_file = None
|
||||
|
||||
try:
|
||||
# Debug output
|
||||
print(f"Preview value: {preview}")
|
||||
print(f"Preview type: {type(preview)}")
|
||||
print(f"Using format: {'PDF' if use_pdf else 'PNG'}")
|
||||
|
||||
# Always generate a PDF backup and print that PDF for verification
|
||||
# Step 1 – Generate and save PDF to pdf_backup/
|
||||
try:
|
||||
pdf_file = create_label_pdf(value, svg_template)
|
||||
if pdf_file and os.path.exists(pdf_file):
|
||||
print(f"PDF label created: {pdf_file}")
|
||||
print(f"PDF backup saved to: {pdf_file}")
|
||||
else:
|
||||
print("PDF generation returned no file path")
|
||||
print("PDF generation failed – no output file")
|
||||
return False
|
||||
except Exception as pdf_err:
|
||||
print(f"PDF generation failed: {pdf_err}")
|
||||
|
||||
# Optionally also create the label image (PNG)
|
||||
if not pdf_file or not os.path.exists(pdf_file):
|
||||
if not use_pdf:
|
||||
label_img = create_label_image(value)
|
||||
temp_file = 'final_label.png'
|
||||
label_img.save(temp_file)
|
||||
print(f"PNG label created: {temp_file}")
|
||||
else:
|
||||
temp_file = pdf_file
|
||||
|
||||
file_created = True
|
||||
|
||||
if not temp_file or not os.path.exists(temp_file):
|
||||
print("No label file created for printing")
|
||||
print(f"PDF generation error: {pdf_err}")
|
||||
return False
|
||||
|
||||
# Convert preview to int if it's a string
|
||||
|
||||
# Step 2 – Optional countdown before printing
|
||||
if isinstance(preview, str):
|
||||
preview = int(preview)
|
||||
|
||||
if preview > 0: # Any value above 0 shows a preview message
|
||||
# Calculate preview duration in seconds
|
||||
if 1 <= preview <= 3:
|
||||
preview_sec = 3 # 3 seconds
|
||||
else: # preview > 3
|
||||
preview_sec = 5 # 5 seconds
|
||||
|
||||
print(f"Printing in {preview_sec} seconds... (Press Ctrl+C to cancel)")
|
||||
|
||||
# Simple countdown timer using time.sleep
|
||||
|
||||
if preview > 0:
|
||||
preview_sec = 3 if 1 <= preview <= 3 else 5
|
||||
print(f"Printing in {preview_sec} seconds… (Ctrl+C to cancel)")
|
||||
try:
|
||||
for i in range(preview_sec, 0, -1):
|
||||
print(f" {i}...", end=" ", flush=True)
|
||||
@@ -676,25 +421,20 @@ def print_label_standalone(value, printer, preview=0, use_pdf=True, svg_template
|
||||
except KeyboardInterrupt:
|
||||
print("\nCancelled by user")
|
||||
return False
|
||||
|
||||
# Print after preview
|
||||
print("Sending to printer...")
|
||||
return print_to_printer(printer, temp_file)
|
||||
else:
|
||||
print("Direct printing without preview...")
|
||||
# Direct printing without preview (preview = 0)
|
||||
return print_to_printer(printer, temp_file)
|
||||
|
||||
|
||||
# Step 3 – Send to printer
|
||||
print("Sending to printer...")
|
||||
return print_to_printer(printer, pdf_file)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error printing label: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
finally:
|
||||
# This block always executes, ensuring cleanup
|
||||
if pdf_file and os.path.exists(pdf_file):
|
||||
print("Cleanup complete - PDF backup saved to pdf_backup folder")
|
||||
print("Cleanup complete – PDF backup saved to pdf_backup/")
|
||||
else:
|
||||
print("Cleanup complete - label file retained for reference")
|
||||
print("Cleanup complete")
|
||||
|
||||
|
||||
# Main code removed - import this module or run as part of the Kivy GUI application
|
||||
|
||||
Reference in New Issue
Block a user