Remove dead code, DEVMODE overrides, and PNG fallback; use printer default settings

This commit is contained in:
2026-02-24 09:19:58 +02:00
parent 7a77199fcf
commit f7833ed4b9
5 changed files with 145 additions and 315 deletions

View File

@@ -23,12 +23,12 @@ if errorlevel 1 (
exit /b 1 exit /b 1
) )
echo [1/5] Checking Python installation... echo [1/6] Checking Python installation...
python --version python --version
echo. echo.
REM Upgrade pip REM Upgrade pip
echo [2/5] Upgrading pip, setuptools, and wheel... echo [2/6] Upgrading pip, setuptools, and wheel...
python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade pip setuptools wheel
if errorlevel 1 ( if errorlevel 1 (
echo ERROR: Failed to upgrade pip echo ERROR: Failed to upgrade pip

View File

@@ -1,20 +1,110 @@
from reportlab.lib.pagesizes import landscape """
from reportlab.lib.utils import ImageReader check_pdf_size.py verify that a PDF's page dimensions match 35 mm × 25 mm.
from reportlab.pdfgen import canvas
Usage:
python check_pdf_size.py [path/to/label.pdf]
If no path is given the script operates on every PDF found in pdf_backup/.
Exit code 0 = all OK, 1 = mismatch or error.
"""
import os import os
import sys
# Check the test PDF file # ── Target dimensions ────────────────────────────────────────────────────────
if os.path.exists('test_label.pdf'): TARGET_W_MM = 35.0 # width (landscape, wider side)
file_size = os.path.getsize('test_label.pdf') TARGET_H_MM = 25.0 # height
print(f'test_label.pdf exists ({file_size} bytes)') TOLERANCE_MM = 0.5 # ± 0.5 mm is acceptable rounding from PDF viewers
print(f'Expected: 35mm x 25mm landscape (99.2 x 70.9 points)')
print(f'') PT_PER_MM = 72.0 / 25.4 # 1 mm in points
print(f'Open test_label.pdf in a PDF viewer to verify:')
print(f' - Page size should be wider than tall')
print(f' - Content should be correctly oriented') def read_page_size_pt(pdf_path):
print(f'') """
print(f'In Adobe Reader: File > Properties > Description') Return (width_pt, height_pt) of the first page of *pdf_path*.
print(f' Page size should show: 3.5 x 2.5 cm or 1.38 x 0.98 in') Tries pypdf first, then pymupdf (fitz) as a fallback.
else: Raises RuntimeError if neither library is available.
print('test_label.pdf not found') """
# ── pypdf ────────────────────────────────────────────────────────────────
try:
from pypdf import PdfReader # type: ignore
reader = PdfReader(pdf_path)
page = reader.pages[0]
w = float(page.mediabox.width)
h = float(page.mediabox.height)
return w, h
except ImportError:
pass
# ── pymupdf (fitz) ───────────────────────────────────────────────────────
try:
import fitz # type: ignore
doc = fitz.open(pdf_path)
rect = doc[0].rect
return rect.width, rect.height
except ImportError:
pass
raise RuntimeError(
"Install pypdf or pymupdf:\n"
" pip install pypdf\n"
" pip install pymupdf"
)
def check_file(pdf_path):
"""Print a pass/fail line for one PDF. Returns True if dimensions match."""
if not os.path.exists(pdf_path):
print(f" MISS {pdf_path} (file not found)")
return False
try:
w_pt, h_pt = read_page_size_pt(pdf_path)
except Exception as e:
print(f" ERR {pdf_path} ({e})")
return False
w_mm = w_pt / PT_PER_MM
h_mm = h_pt / PT_PER_MM
w_ok = abs(w_mm - TARGET_W_MM) <= TOLERANCE_MM
h_ok = abs(h_mm - TARGET_H_MM) <= TOLERANCE_MM
ok = w_ok and h_ok
status = "PASS" if ok else "FAIL"
print(
f" {status} {os.path.basename(pdf_path)}"
f" {w_mm:.2f}×{h_mm:.2f} mm"
f" (target {TARGET_W_MM}×{TARGET_H_MM} mm ±{TOLERANCE_MM} mm)"
)
return ok
def main():
targets = sys.argv[1:]
if not targets:
backup_dir = os.path.join(os.path.dirname(__file__), "pdf_backup")
if os.path.isdir(backup_dir):
targets = [
os.path.join(backup_dir, f)
for f in sorted(os.listdir(backup_dir))
if f.lower().endswith(".pdf")
]
if not targets:
# fall back to test_label.pdf in cwd
targets = ["test_label.pdf"]
print(f"Checking {len(targets)} PDF(s)…")
results = [check_file(p) for p in targets]
total = len(results)
passed = sum(results)
failed = total - passed
print(f"\n {passed}/{total} passed" + (f", {failed} FAILED" if failed else ""))
sys.exit(0 if failed == 0 else 1)
if __name__ == "__main__":
main()

View File

@@ -990,7 +990,7 @@ printer = PDF
f'Printing {idx+1} of {count}...' f'Printing {idx+1} of {count}...'
), 0) ), 0)
success = print_label_standalone(label_text, printer, preview=0, use_pdf=True, svg_template=template_path) success = print_label_standalone(label_text, printer, preview=0, svg_template=template_path)
if not success: if not success:
all_success = False all_success = False

View File

@@ -1,6 +1,3 @@
from PIL import Image, ImageDraw, ImageFont
import barcode
from barcode.writer import ImageWriter
import time import time
import os import os
import sys import sys
@@ -89,119 +86,6 @@ def get_available_printers():
return ["PDF"] 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): def create_label_pdf(text, svg_template=None):
""" """
Create a high-quality PDF label with 3 rows: label + barcode for each field. 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) 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(): def find_ghostscript():
""" """
Find GhostScript executable. Search order: Find GhostScript executable. Search order:
@@ -403,10 +199,8 @@ def print_pdf_with_ghostscript(pdf_path, printer_name):
# -dTextAlphaBits=4 : anti-aliasing for text # -dTextAlphaBits=4 : anti-aliasing for text
# -dGraphicsAlphaBits=4 # -dGraphicsAlphaBits=4
# -dNOSAFER : allow file access needed for fonts # -dNOSAFER : allow file access needed for fonts
# -dDEVICEWIDTHPOINTS \ # The printer paper size and orientation are already configured
# -dDEVICEHEIGHTPOINTS : explicit label size (35mm x 25mm in pts) # in the driver do not override them here.
# -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
cmd = [ cmd = [
gs_path, gs_path,
"-dNOPAUSE", "-dNOPAUSE",
@@ -416,9 +210,6 @@ def print_pdf_with_ghostscript(pdf_path, printer_name):
"-r600", "-r600",
"-dTextAlphaBits=4", "-dTextAlphaBits=4",
"-dGraphicsAlphaBits=4", "-dGraphicsAlphaBits=4",
"-dDEVICEWIDTHPOINTS=99.21", # 35 mm
"-dDEVICEHEIGHTPOINTS=70.87", # 25 mm
"-dFIXEDMEDIA",
f"-sOutputFile=%printer%{printer_name}", f"-sOutputFile=%printer%{printer_name}",
pdf_path, pdf_path,
] ]
@@ -472,19 +263,10 @@ def print_to_printer(printer_name, file_path):
try: try:
if WIN32_AVAILABLE: if WIN32_AVAILABLE:
import win32print 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'): if file_path.endswith('.pdf'):
# Try silent printing methods (no viewer opens) # Try silent printing methods (no viewer opens)
import os import os
import winreg
printed = False printed = False
@@ -536,14 +318,9 @@ def print_to_printer(printer_name, file_path):
try: try:
print(f"Using SumatraPDF: {sumatra_path}") print(f"Using SumatraPDF: {sumatra_path}")
print(f"Sending to printer: {printer_name}") print(f"Sending to printer: {printer_name}")
# The printer DEVMODE has already been configured # "noscale" = print the PDF at its exact page size.
# above (Portrait, 35mm×25mm, high quality). # Do NOT add "landscape" the printer driver
# "noscale" tells SumatraPDF to send the PDF # already knows the orientation from its own settings.
# 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.
result = subprocess.run([ result = subprocess.run([
sumatra_path, sumatra_path,
"-print-to", "-print-to",
@@ -601,73 +378,41 @@ def print_to_printer(printer_name, file_path):
return True 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. Generate a PDF label, save it to pdf_backup/, and print it on the printer.
Always generates a PDF backup in pdf_backup and prints that PDF. The printer's own paper size and orientation settings are used as-is.
Args: Args:
value (str): The text to print on the label value (str): Label data in format "article;nr_art;serial;status"
printer (str): The name of the printer to use printer (str): Printer name (or "PDF" to skip physical printing)
preview (int): 0 = no preview, 1-3 = 3s preview, >3 = 5s preview preview (int): 0 = print immediately; 1-3 = 3 s delay; >3 = 5 s delay
use_pdf (bool): False to also generate a PNG if PDF generation fails svg_template (str): Path to SVG template (optional; auto-selected if None)
svg_template (str): Path to specific SVG template to use (optional)
Returns: 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 pdf_file = None
try: try:
# Debug output # Step 1 Generate and save PDF to pdf_backup/
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: try:
pdf_file = create_label_pdf(value, svg_template) pdf_file = create_label_pdf(value, svg_template)
if pdf_file and os.path.exists(pdf_file): if pdf_file and os.path.exists(pdf_file):
print(f"PDF label created: {pdf_file}") print(f"PDF label created: {pdf_file}")
print(f"PDF backup saved to: {pdf_file}")
else: else:
print("PDF generation returned no file path") print("PDF generation failed no output file")
return False
except Exception as pdf_err: except Exception as pdf_err:
print(f"PDF generation failed: {pdf_err}") print(f"PDF generation error: {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 return False
# Convert preview to int if it's a string # Step 2 Optional countdown before printing
if isinstance(preview, str): if isinstance(preview, str):
preview = int(preview) preview = int(preview)
if preview > 0: # Any value above 0 shows a preview message if preview > 0:
# Calculate preview duration in seconds preview_sec = 3 if 1 <= preview <= 3 else 5
if 1 <= preview <= 3: print(f"Printing in {preview_sec} seconds… (Ctrl+C to cancel)")
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: try:
for i in range(preview_sec, 0, -1): for i in range(preview_sec, 0, -1):
print(f" {i}...", end=" ", flush=True) 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: except KeyboardInterrupt:
print("\nCancelled by user") print("\nCancelled by user")
return False return False
# Print after preview # Step 3 Send to printer
print("Sending to printer...") print("Sending to printer...")
return print_to_printer(printer, temp_file) return print_to_printer(printer, pdf_file)
else:
print("Direct printing without preview...")
# Direct printing without preview (preview = 0)
return print_to_printer(printer, temp_file)
except Exception as e: except Exception as e:
print(f"Error printing label: {str(e)}") print(f"Error printing label: {str(e)}")
return False return False
finally: finally:
# This block always executes, ensuring cleanup
if pdf_file and os.path.exists(pdf_file): 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: 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 # Main code removed - import this module or run as part of the Kivy GUI application

View File

@@ -49,8 +49,8 @@ class PDFLabelGenerator:
""" """
self.label_width = label_width * cm self.label_width = label_width * cm
self.label_height = label_height * cm self.label_height = label_height * cm
# Force landscape: ensure width > height # label_width (3.5 cm) > label_height (2.5 cm) → page is already landscape
self.page_size = landscape((self.label_height, self.label_width)) if self.label_width > self.label_height else (self.label_width, self.label_height) self.page_size = (self.label_width, self.label_height)
self.dpi = dpi self.dpi = dpi
self.margin = 1 * mm # Minimal margin self.margin = 1 * mm # Minimal margin