Files
adaptronic_label-printer/print_label.py
2026-02-12 22:43:26 +02:00

442 lines
16 KiB
Python

from PIL import Image, ImageDraw, ImageFont
import barcode
from barcode.writer import ImageWriter
import time
import os
import datetime
import platform
import subprocess
from print_label_pdf import PDFLabelGenerator
# Cross-platform printer support
try:
import cups
CUPS_AVAILABLE = True
except ImportError:
CUPS_AVAILABLE = False
try:
import win32api
import win32print
WIN32_AVAILABLE = True
except ImportError:
WIN32_AVAILABLE = False
SYSTEM = platform.system() # 'Linux', 'Windows', 'Darwin'
def get_available_printers():
"""
Get list of available printers (cross-platform).
Includes both local and network printers on Windows.
Returns:
list: List of available printer names, with "PDF" as fallback
"""
try:
if SYSTEM == "Linux" and CUPS_AVAILABLE:
# Linux: Use CUPS
conn = cups.Connection()
printers = conn.getPrinters()
return list(printers.keys()) if printers else ["PDF"]
elif SYSTEM == "Windows":
# Windows: Get local + connected printers (includes print server connections)
try:
printers = []
# PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS captures:
# - Locally installed printers
# - Printers connected from a print server (e.g. \\server\printer)
try:
flags = win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS
for printer_info in win32print.EnumPrinters(flags):
printer_name = printer_info[2]
if printer_name and printer_name not in printers:
printers.append(printer_name)
except Exception as e:
print(f"Error enumerating printers: {e}")
# Add PDF as fallback option
if "PDF" not in printers:
printers.append("PDF")
return printers if printers else ["PDF"]
except Exception as e:
print(f"Error getting Windows printers: {e}")
return ["PDF"]
elif SYSTEM == "Darwin":
# macOS: Use lpstat command
try:
result = subprocess.run(["lpstat", "-p", "-d"],
capture_output=True, text=True)
printers = []
for line in result.stdout.split('\n'):
if line.startswith('printer'):
printer_name = line.split()[1]
printers.append(printer_name)
return printers if printers else ["PDF"]
except:
return ["PDF"]
else:
return ["PDF"]
except Exception as e:
print(f"Error getting printers: {e}")
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
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):
"""
Create a high-quality PDF label with 3 rows: label + barcode for each field.
PDFs are saved to the pdf_backup folder.
Args:
text (str): Combined text in format "article;nr_art;serial" or single value
Returns:
str: Path to the generated PDF file
"""
# Parse the text input - using semicolon separator
parts = text.split(';') if ';' in text else [text, '', '']
article = parts[0].strip() if len(parts) > 0 else ''
nr_art = parts[1].strip() if len(parts) > 1 else ''
serial = parts[2].strip() if len(parts) > 2 else ''
# Create PDF using high-quality generator
generator = PDFLabelGenerator()
# Ensure pdf_backup folder exists
pdf_backup_dir = 'pdf_backup'
os.makedirs(pdf_backup_dir, exist_ok=True)
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
pdf_filename = os.path.join(pdf_backup_dir, f"final_label_{timestamp}.pdf")
# Check for default SVG template
svg_template = None
default_svg = os.path.join('conf', 'label_template.svg')
if os.path.exists(default_svg):
svg_template = default_svg
# Check for default image path
image_path = os.path.join('conf', 'accepted.png')
return generator.create_label_pdf(article, nr_art, serial, pdf_filename, image_path, svg_template)
def print_to_printer(printer_name, file_path):
"""
Print file to printer (cross-platform).
Args:
printer_name (str): Name of printer or "PDF" for PDF output
file_path (str): Path to file to print
Returns:
bool: True if successful
"""
try:
if printer_name == "PDF":
# PDF output - file is already saved
print(f"PDF output: {file_path}")
return True
elif SYSTEM == "Linux" and CUPS_AVAILABLE:
# Linux: Use CUPS
conn = cups.Connection()
conn.printFile(printer_name, file_path, "Label Print", {})
print(f"Label sent to printer: {printer_name}")
return True
elif SYSTEM == "Windows":
# Windows: Print PDF using various methods
try:
if file_path.endswith('.pdf'):
# Method 1: Try SumatraPDF for best silent printing
sumatra_paths = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf', 'SumatraPDF.exe'),
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf', 'SumatraPDF-portable.exe'),
r"C:\Program Files\SumatraPDF\SumatraPDF.exe",
r"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe",
os.path.expandvars(r"%LOCALAPPDATA%\SumatraPDF\SumatraPDF.exe"),
]
sumatra_found = False
for sumatra_path in sumatra_paths:
if os.path.exists(sumatra_path):
sumatra_found = True
try:
# Use SumatraPDF silent printing with high quality settings
# noscale = print at actual size without scaling
# landscape = force landscape orientation for 35x25mm labels
# Note: SumatraPDF uses printer driver's quality settings
subprocess.run([
sumatra_path,
'-print-to', printer_name,
'-silent',
'-print-settings', 'noscale,landscape',
file_path
], check=True, creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer via SumatraPDF: {printer_name}")
return True
except Exception as sumatra_err:
print(f"SumatraPDF print failed: {sumatra_err}")
break
# Method 2: Use ShellExecute with printto (requires default PDF viewer)
if not sumatra_found or WIN32_AVAILABLE:
try:
import win32api
win32api.ShellExecute(
0, "printto", file_path,
f'"{printer_name}"', ".", 0
)
print(f"Label sent to printer via ShellExecute: {printer_name}")
return True
except Exception as shell_err:
print(f"ShellExecute print failed: {shell_err}")
# Method 3: Open PDF with default viewer (last resort)
try:
os.startfile(file_path, "print")
print(f"Opened PDF for printing: {file_path}")
print("Please close the PDF viewer after printing.")
return True
except Exception as startfile_err:
print(f"Could not open PDF: {startfile_err}")
print("PDF saved as backup only")
return True
else:
# Non-PDF files: print silently with notepad
subprocess.run(['notepad', '/p', file_path],
check=False,
creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer: {printer_name}")
return True
except Exception as e:
print(f"Windows print error: {e}")
print("PDF backup saved as fallback")
return True
elif SYSTEM == "Darwin":
# macOS: Use lp command
subprocess.run(["lp", "-d", printer_name, file_path], check=True)
print(f"Label sent to printer: {printer_name}")
return True
else:
print(f"Unsupported system: {SYSTEM}")
return False
except Exception as e:
print(f"Printer error: {str(e)}")
print("Label already saved to file as fallback...")
print(f"Label file: {file_path}")
return True
def print_label_standalone(value, printer, preview=0, use_pdf=True):
"""
Print a label with the specified text on the specified printer.
Always generates a PDF backup in pdf_backup and prints that PDF.
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
Returns:
bool: True if printing was successful, 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
try:
pdf_file = create_label_pdf(value)
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")
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")
return False
# Convert preview to int if it's a string
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
try:
for i in range(preview_sec, 0, -1):
print(f" {i}...", end=" ", flush=True)
time.sleep(1)
print("\nPrinting now...")
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)
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")
else:
print("Cleanup complete - label file retained for reference")
# Main code removed - import this module or run as part of the Kivy GUI application