Fix printer detection, implement portable deployment with SumatraPDF

- Fixed network printer enumeration (PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS)
- Added printer name truncation to 20 chars with full name mapping
- Implemented silent PDF printing using SumatraPDF with landscape orientation
- Added auto-dismiss for success popup (3 seconds)
- Bundled SumatraPDF inside executable for portable single-file deployment
- Updated build script to embed SumatraPDF
- Added setup_sumatra.ps1 for downloading SumatraPDF portable
- Added DEPLOYMENT.md documentation
This commit is contained in:
2026-02-06 14:00:17 +02:00
parent b204ce38fc
commit f09c365384
5 changed files with 354 additions and 41 deletions

View File

@@ -3,6 +3,7 @@ import barcode
from barcode.writer import ImageWriter
import time
import os
import sys
import datetime
import platform
import subprocess
@@ -249,51 +250,139 @@ def print_to_printer(printer_name, file_path):
return True
elif SYSTEM == "Windows":
# Windows: Print directly without opening PDF viewer
# Windows: Print PDF silently without any viewer opening
try:
if WIN32_AVAILABLE:
import win32print
import win32api
if file_path.endswith('.pdf'):
# Use SumatraPDF command-line or direct raw printing
# Try printing via subprocess to avoid opening a PDF viewer window
# Try silent printing methods (no viewer opens)
import os
import winreg
# Method: Use win32print raw API to send to printer silently
try:
hprinter = win32print.OpenPrinter(printer_name)
printed = False
# Method 1: SumatraPDF (bundled inside exe or external)
sumatra_paths = []
# Get the directory where this script/exe is running
if getattr(sys, 'frozen', False):
# Running as compiled executable
# PyInstaller extracts bundled files to sys._MEIPASS temp folder
if hasattr(sys, '_MEIPASS'):
# Check bundled version first (inside the exe)
bundled_sumatra = os.path.join(sys._MEIPASS, 'SumatraPDF.exe')
sumatra_paths.append(bundled_sumatra)
# Also check app directory for external version
app_dir = os.path.dirname(sys.executable)
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF", "SumatraPDF.exe"))
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF.exe"))
else:
# Running as script - check local folders
app_dir = os.path.dirname(os.path.abspath(__file__))
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF", "SumatraPDF.exe"))
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF.exe"))
# Then check system installations
sumatra_paths.extend([
r"C:\Program Files\SumatraPDF\SumatraPDF.exe",
r"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe",
])
for sumatra_path in sumatra_paths:
if os.path.exists(sumatra_path):
try:
subprocess.run([
sumatra_path,
"-print-to",
printer_name,
file_path,
"-print-settings",
"fit,landscape",
"-silent",
"-exit-when-done"
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer via SumatraPDF: {printer_name}")
printed = True
break
except Exception as e:
print(f"SumatraPDF error: {e}")
# Method 2: Adobe Reader silent printing
if not printed:
adobe_path = None
for key_path in [
r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe",
r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Acrobat.exe"
]:
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path)
adobe_path, _ = winreg.QueryValueEx(key, "")
winreg.CloseKey(key)
break
except:
pass
if adobe_path and os.path.exists(adobe_path):
try:
subprocess.run([
adobe_path,
"/t", # Print and close
file_path,
printer_name
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer via Adobe Reader: {printer_name}")
printed = True
except:
pass
# Method 3: GhostScript (if installed)
if not printed:
gs_paths = [
r"C:\Program Files\gs\gs10.02.1\bin\gswin64c.exe",
r"C:\Program Files (x86)\gs\gs10.02.1\bin\gswin32c.exe",
]
# Try to find gswin in PATH
try:
# Start a print job
job_info = ("Label Print", None, "RAW")
hjob = win32print.StartDocPrinter(hprinter, 1, job_info)
win32print.StartPagePrinter(hprinter)
# Read PDF file and send to printer
with open(file_path, 'rb') as f:
pdf_data = f.read()
win32print.WritePrinter(hprinter, pdf_data)
win32print.EndPagePrinter(hprinter)
win32print.EndDocPrinter(hprinter)
print(f"Label sent to printer: {printer_name}")
finally:
win32print.ClosePrinter(hprinter)
return True
except Exception as raw_err:
print(f"Raw print failed ({raw_err}), trying ShellExecute silently...")
# Fallback: Use ShellExecute with printto (minimized, auto-closes)
try:
win32api.ShellExecute(
0, "printto", file_path,
f'"{printer_name}"', ".", 0
)
print(f"Label sent to printer: {printer_name}")
return True
except Exception as shell_err:
print(f"ShellExecute print failed: {shell_err}")
return True
gs_result = subprocess.run(['where', 'gswin64c'],
capture_output=True, text=True, check=False)
if gs_result.returncode == 0:
gs_paths.insert(0, gs_result.stdout.strip().split('\n')[0])
except:
pass
for gs_path in gs_paths:
if os.path.exists(gs_path):
try:
subprocess.run([
gs_path,
"-dNOPAUSE", "-dBATCH", "-dQUIET",
f"-sDEVICE=mswinpr2",
f"-sOutputFile=%printer%{printer_name}",
file_path
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
print(f"Label sent to printer via GhostScript: {printer_name}")
printed = True
break
except:
pass
if not printed:
# Fallback: Let user know and save PDF
print("=" * 60)
print("NOTICE: Silent PDF printing requires SumatraPDF")
print("SumatraPDF not found (should be bundled inside the app)")
print("If you built the app yourself, ensure SumatraPDF.exe is downloaded first.")
print("Run: setup_sumatra.ps1 before building")
print("=" * 60)
print(f"PDF saved to: {file_path}")
print("The PDF can be printed manually.")
return True
else:
# Non-PDF files: print silently with notepad
# Non-PDF files
subprocess.run(['notepad', '/p', file_path],
check=False,
creationflags=subprocess.CREATE_NO_WINDOW)