Compare commits
3 Commits
1cf4482914
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 58082ed171 | |||
| f09c365384 | |||
| b204ce38fc |
88
DEPLOYMENT.md
Normal file
88
DEPLOYMENT.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Label Printer - Portable Deployment Guide
|
||||||
|
|
||||||
|
## Deployment Structure
|
||||||
|
|
||||||
|
The app is now **fully self-contained** with SumatraPDF embedded inside:
|
||||||
|
|
||||||
|
```
|
||||||
|
LabelPrinter/
|
||||||
|
├── LabelPrinter.exe # Main application (includes SumatraPDF inside)
|
||||||
|
├── pdf_backup/ # Auto-created: PDF backups
|
||||||
|
└── logs/ # Auto-created: Print logs
|
||||||
|
```
|
||||||
|
|
||||||
|
**No visible folders!** SumatraPDF is bundled inside LabelPrinter.exe and extracted to a temporary location at runtime.
|
||||||
|
|
||||||
|
## Setup Instructions
|
||||||
|
|
||||||
|
### 1. Download SumatraPDF (For Building Only)
|
||||||
|
|
||||||
|
**This step is only needed when building the app.** SumatraPDF will be embedded inside the executable.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# PowerShell command to download SumatraPDF
|
||||||
|
powershell -ExecutionPolicy Bypass -File setup_sumatra.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
This downloads SumatraPDF portable (~5 MB) to the `SumatraPDF` folder.
|
||||||
|
|
||||||
|
### 2. Build the Application
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\build_windows.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The build script will:
|
||||||
|
- Check for SumatraPDF
|
||||||
|
- Bundle it inside the executable
|
||||||
|
- Create `dist\LabelPrinter.exe` (~80 MB including all dependencies)
|
||||||
|
|
||||||
|
### 3. Deploy
|
||||||
|
|
||||||
|
**Simply copy `LabelPrinter.exe` to any Windows machine!**
|
||||||
|
|
||||||
|
```
|
||||||
|
📁 Deployment (any folder)
|
||||||
|
└── LabelPrinter.exe ← Just this one file!
|
||||||
|
```
|
||||||
|
|
||||||
|
- No installation needed
|
||||||
|
- No additional files or folders
|
||||||
|
- Double-click to run
|
||||||
|
- Works on any Windows 10/11 machine
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ✅ **Single Executable** - Everything bundled in one .exe file (~80 MB)
|
||||||
|
- ✅ **Fully Portable** - No installation needed, no external dependencies
|
||||||
|
- ✅ **Silent Printing** - No PDF viewer windows pop up
|
||||||
|
- ✅ **Network Printers** - Supports printers from print servers (e.g. `\\server\printer`)
|
||||||
|
- ✅ **PDF Backup** - All labels saved to `pdf_backup/` folder
|
||||||
|
- ✅ **Print Logging** - CSV logs in `logs/` folder
|
||||||
|
- ✅ **SumatraPDF Hidden** - Embedded inside, not visible to users
|
||||||
|
|
||||||
|
## Printer Name Display
|
||||||
|
|
||||||
|
Network printer names (e.g. `\\filesibiusb05\ZDesigner_ZQ630`) are automatically shortened to 20 characters in the dropdown for better display. The full printer name is used for actual printing.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Printing Not Working
|
||||||
|
|
||||||
|
1. **Check Printer Connection**: Verify printer is online and accessible
|
||||||
|
2. **Check PDF Backup**: Labels are always saved to `pdf_backup/` folder even if printing fails
|
||||||
|
3. **Check Logs**: View print logs in `logs/` folder for error messages
|
||||||
|
4. **Rebuild App**: If you built the app yourself, ensure `setup_sumatra.ps1` was run first to download SumatraPDF before building
|
||||||
|
|
||||||
|
### Network Printers Not Showing
|
||||||
|
|
||||||
|
- Network printers must be installed/connected on the machine before running the app
|
||||||
|
- For print server printers like `\\filesibiusb05\printer`, ensure the share is accessible
|
||||||
|
- Run as administrator if printer enumeration fails
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- First run may be slower (Kivy initialization)
|
||||||
|
- PDF backups are auto-deleted after 5 days
|
||||||
|
- Log files are auto-deleted after 5 days
|
||||||
|
- Supports Python 3.10-3.13 (Python 3.14+ may have issues with Kivy)
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
a = Analysis(
|
a = Analysis(
|
||||||
['label_printer_gui.py'],
|
['label_printer_gui.py'],
|
||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[],
|
binaries=[('SumatraPDF\\SumatraPDF.exe', '.')],
|
||||||
datas=[],
|
datas=[],
|
||||||
hiddenimports=['kivy', 'PIL', 'barcode', 'reportlab', 'print_label', 'print_label_pdf'],
|
hiddenimports=['kivy', 'PIL', 'barcode', 'reportlab', 'print_label', 'print_label_pdf'],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
|
|||||||
BIN
SumatraPDF/SumatraPDF.exe
Normal file
BIN
SumatraPDF/SumatraPDF.exe
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -47,13 +47,39 @@ if ($LASTEXITCODE -ne 0) {
|
|||||||
}
|
}
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
Write-Host "[4/5] Cleaning old build artifacts..." -ForegroundColor Cyan
|
Write-Host "[4/6] Checking for SumatraPDF..." -ForegroundColor Cyan
|
||||||
|
$sumatraPath = "SumatraPDF\SumatraPDF.exe"
|
||||||
|
if (-not (Test-Path $sumatraPath)) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "WARNING: SumatraPDF not found!" -ForegroundColor Yellow
|
||||||
|
Write-Host "SumatraPDF is required for silent PDF printing." -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Run the setup script first:" -ForegroundColor Yellow
|
||||||
|
Write-Host " powershell -ExecutionPolicy Bypass -File setup_sumatra.ps1" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
$response = Read-Host "Continue building without SumatraPDF? (y/n)"
|
||||||
|
if ($response -ne "y") {
|
||||||
|
Write-Host "Build cancelled."
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Building without SumatraPDF (PDF printing will not work)..." -ForegroundColor Yellow
|
||||||
|
$addBinaryArg = @()
|
||||||
|
} else {
|
||||||
|
Write-Host "Found: $sumatraPath" -ForegroundColor Green
|
||||||
|
# Add SumatraPDF as bundled binary (will be embedded inside the exe)
|
||||||
|
$addBinaryArg = @("--add-binary", "$sumatraPath;.")
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "[5/6] Cleaning old build artifacts..." -ForegroundColor Cyan
|
||||||
if (Test-Path "dist") { Remove-Item -Recurse -Force "dist" }
|
if (Test-Path "dist") { Remove-Item -Recurse -Force "dist" }
|
||||||
if (Test-Path "build") { Remove-Item -Recurse -Force "build" }
|
if (Test-Path "build") { Remove-Item -Recurse -Force "build" }
|
||||||
Remove-Item -Force "*.spec" -ErrorAction SilentlyContinue
|
Remove-Item -Force "*.spec" -ErrorAction SilentlyContinue
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
Write-Host "[5/5] Building executable with PyInstaller..." -ForegroundColor Cyan
|
Write-Host "[6/6] Building executable with PyInstaller..." -ForegroundColor Cyan
|
||||||
Write-Host "This may take 5-15 minutes, please wait..."
|
Write-Host "This may take 5-15 minutes, please wait..."
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
@@ -73,6 +99,11 @@ $pyinstallerArgs = @(
|
|||||||
"-y"
|
"-y"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add SumatraPDF binary if available (bundles inside the exe)
|
||||||
|
if ($addBinaryArg) {
|
||||||
|
$pyinstallerArgs += $addBinaryArg
|
||||||
|
}
|
||||||
|
|
||||||
pyinstaller @pyinstallerArgs
|
pyinstaller @pyinstallerArgs
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|||||||
BIN
dist/LabelPrinter.exe
vendored
Normal file
BIN
dist/LabelPrinter.exe
vendored
Normal file
Binary file not shown.
@@ -38,15 +38,42 @@ class LabelPrinterApp(App):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.available_printers = self.get_available_printers()
|
# Build printer display names and mapping to full names
|
||||||
|
full_printers = get_available_printers()
|
||||||
|
self.printer_display_map = {} # display_name -> full_name
|
||||||
|
self.available_printers = []
|
||||||
|
for full_name in full_printers:
|
||||||
|
display_name = self._shorten_printer_name(full_name)
|
||||||
|
# Ensure unique display names
|
||||||
|
if display_name in self.printer_display_map:
|
||||||
|
display_name = full_name[:20]
|
||||||
|
self.printer_display_map[display_name] = full_name
|
||||||
|
self.available_printers.append(display_name)
|
||||||
# Clean old PDF backup files on startup
|
# Clean old PDF backup files on startup
|
||||||
self.cleanup_old_pdfs()
|
self.cleanup_old_pdfs()
|
||||||
# Clean old log files on startup
|
# Clean old log files on startup
|
||||||
self.cleanup_old_logs()
|
self.cleanup_old_logs()
|
||||||
|
|
||||||
def get_available_printers(self):
|
def _shorten_printer_name(self, name, max_len=20):
|
||||||
"""Get list of available printers (cross-platform)"""
|
"""Shorten printer name for display (max 20 chars).
|
||||||
return get_available_printers()
|
For network printers like \\\\server\\printer, show just the printer part."""
|
||||||
|
if name.startswith('\\\\'):
|
||||||
|
# Network printer: \\server\printer -> extract printer name
|
||||||
|
parts = name.strip('\\').split('\\')
|
||||||
|
if len(parts) >= 2:
|
||||||
|
short = parts[-1] # Just the printer name
|
||||||
|
else:
|
||||||
|
short = name
|
||||||
|
else:
|
||||||
|
short = name
|
||||||
|
# Truncate to max_len
|
||||||
|
if len(short) > max_len:
|
||||||
|
short = short[:max_len]
|
||||||
|
return short
|
||||||
|
|
||||||
|
def _get_full_printer_name(self, display_name):
|
||||||
|
"""Resolve display name back to full printer name for printing."""
|
||||||
|
return self.printer_display_map.get(display_name, display_name)
|
||||||
|
|
||||||
def cleanup_old_pdfs(self, days=5):
|
def cleanup_old_pdfs(self, days=5):
|
||||||
"""
|
"""
|
||||||
@@ -278,7 +305,8 @@ class LabelPrinterApp(App):
|
|||||||
values=self.available_printers,
|
values=self.available_printers,
|
||||||
size_hint_y=None,
|
size_hint_y=None,
|
||||||
height=45,
|
height=45,
|
||||||
font_size='12sp'
|
font_size='12sp',
|
||||||
|
sync_height=True,
|
||||||
)
|
)
|
||||||
self.printer_spinner = printer_spinner
|
self.printer_spinner = printer_spinner
|
||||||
form_layout.add_widget(printer_spinner)
|
form_layout.add_widget(printer_spinner)
|
||||||
@@ -320,7 +348,8 @@ class LabelPrinterApp(App):
|
|||||||
sap_nr = self.sap_input.text.strip()
|
sap_nr = self.sap_input.text.strip()
|
||||||
quantity = self.qty_input.text.strip()
|
quantity = self.qty_input.text.strip()
|
||||||
cable_id = self.cable_id_input.text.strip()
|
cable_id = self.cable_id_input.text.strip()
|
||||||
printer = self.printer_spinner.text
|
# Resolve display name to full printer name
|
||||||
|
printer = self._get_full_printer_name(self.printer_spinner.text)
|
||||||
|
|
||||||
# Validate input
|
# Validate input
|
||||||
if not sap_nr and not quantity and not cable_id:
|
if not sap_nr and not quantity and not cable_id:
|
||||||
@@ -364,7 +393,7 @@ class LabelPrinterApp(App):
|
|||||||
|
|
||||||
# Use Clock.schedule_once to update UI from main thread
|
# Use Clock.schedule_once to update UI from main thread
|
||||||
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
|
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
|
||||||
Clock.schedule_once(lambda dt: self.show_popup("Success", "Label printed successfully!"), 0.1)
|
Clock.schedule_once(lambda dt: self.show_popup("Success", "Label printed successfully!", auto_dismiss=True), 0.1)
|
||||||
# Clear inputs after successful print (but keep printer selection)
|
# Clear inputs after successful print (but keep printer selection)
|
||||||
Clock.schedule_once(lambda dt: self.clear_inputs(), 0.2)
|
Clock.schedule_once(lambda dt: self.clear_inputs(), 0.2)
|
||||||
else:
|
else:
|
||||||
@@ -391,8 +420,14 @@ class LabelPrinterApp(App):
|
|||||||
self.cable_id_input.text = ''
|
self.cable_id_input.text = ''
|
||||||
# Printer selection is NOT cleared - it persists until user changes it
|
# Printer selection is NOT cleared - it persists until user changes it
|
||||||
|
|
||||||
def show_popup(self, title, message):
|
def show_popup(self, title, message, auto_dismiss=False):
|
||||||
"""Show a popup message"""
|
"""Show a popup message
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title (str): Popup title
|
||||||
|
message (str): Popup message
|
||||||
|
auto_dismiss (bool): If True, popup will auto-dismiss after 3 seconds
|
||||||
|
"""
|
||||||
popup = Popup(
|
popup = Popup(
|
||||||
title=title,
|
title=title,
|
||||||
content=BoxLayout(
|
content=BoxLayout(
|
||||||
@@ -410,6 +445,10 @@ class LabelPrinterApp(App):
|
|||||||
popup.content.add_widget(close_button)
|
popup.content.add_widget(close_button)
|
||||||
|
|
||||||
popup.open()
|
popup.open()
|
||||||
|
|
||||||
|
# Auto-dismiss after 3 seconds if requested
|
||||||
|
if auto_dismiss:
|
||||||
|
Clock.schedule_once(lambda dt: popup.dismiss(), 3)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
155
print_label.py
155
print_label.py
@@ -3,6 +3,7 @@ import barcode
|
|||||||
from barcode.writer import ImageWriter
|
from barcode.writer import ImageWriter
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import datetime
|
import datetime
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -249,38 +250,146 @@ def print_to_printer(printer_name, file_path):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
elif SYSTEM == "Windows":
|
elif SYSTEM == "Windows":
|
||||||
# Windows: Use win32print API for reliable printing (supports UNC paths)
|
# Windows: Print PDF silently without any viewer opening
|
||||||
try:
|
try:
|
||||||
if WIN32_AVAILABLE:
|
if WIN32_AVAILABLE:
|
||||||
import win32print
|
import win32print
|
||||||
import win32api
|
import win32api
|
||||||
|
|
||||||
# Set the target printer as default temporarily, then print
|
if file_path.endswith('.pdf'):
|
||||||
# This approach works reliably with both local and UNC printer paths
|
# Try silent printing methods (no viewer opens)
|
||||||
try:
|
import os
|
||||||
old_default = win32print.GetDefaultPrinter()
|
import winreg
|
||||||
except:
|
|
||||||
old_default = None
|
printed = False
|
||||||
|
|
||||||
try:
|
# Method 1: SumatraPDF (bundled inside exe or external)
|
||||||
win32print.SetDefaultPrinter(printer_name)
|
sumatra_paths = []
|
||||||
win32api.ShellExecute(0, "print", file_path, None, ".", 0)
|
|
||||||
print(f"Label sent to printer: {printer_name}")
|
# Get the directory where this script/exe is running
|
||||||
finally:
|
if getattr(sys, 'frozen', False):
|
||||||
# Restore original default printer
|
# Running as compiled executable
|
||||||
if old_default:
|
# 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:
|
try:
|
||||||
win32print.SetDefaultPrinter(old_default)
|
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:
|
except:
|
||||||
pass
|
pass
|
||||||
return True
|
|
||||||
else:
|
for gs_path in gs_paths:
|
||||||
# Fallback: Open with default printer
|
if os.path.exists(gs_path):
|
||||||
if file_path.endswith('.pdf'):
|
try:
|
||||||
os.startfile(file_path, "print")
|
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:
|
else:
|
||||||
subprocess.run(['notepad', '/p', file_path], check=False)
|
# Non-PDF files
|
||||||
print(f"Label sent to default printer")
|
subprocess.run(['notepad', '/p', file_path],
|
||||||
|
check=False,
|
||||||
|
creationflags=subprocess.CREATE_NO_WINDOW)
|
||||||
|
print(f"Label sent to printer: {printer_name}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("win32print not available, PDF saved as backup only")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Windows print error: {e}")
|
print(f"Windows print error: {e}")
|
||||||
|
|||||||
95
setup_sumatra.ps1
Normal file
95
setup_sumatra.ps1
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Download and Setup SumatraPDF Portable for Label Printer
|
||||||
|
# This script downloads SumatraPDF portable and sets up the deployment structure
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host " Label Printer - SumatraPDF Setup"
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Create SumatraPDF folder if it doesn't exist
|
||||||
|
$sumatraFolder = "SumatraPDF"
|
||||||
|
if (-not (Test-Path $sumatraFolder)) {
|
||||||
|
New-Item -ItemType Directory -Path $sumatraFolder -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if SumatraPDF.exe already exists
|
||||||
|
$sumatraExe = Join-Path $sumatraFolder "SumatraPDF.exe"
|
||||||
|
if (Test-Path $sumatraExe) {
|
||||||
|
Write-Host "[OK] SumatraPDF.exe already exists at: $sumatraExe" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[1/3] Downloading SumatraPDF portable (64-bit, ~5 MB)..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# SumatraPDF download URL (latest stable version)
|
||||||
|
$url = "https://www.sumatrapdfreader.org/dl/rel/3.5.2/SumatraPDF-3.5.2-64.zip"
|
||||||
|
$zipFile = "SumatraPDF-temp.zip"
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Download with progress
|
||||||
|
$progressPreference = 'SilentlyContinue'
|
||||||
|
Invoke-WebRequest -Uri $url -OutFile $zipFile -ErrorAction Stop
|
||||||
|
Write-Host "[OK] Download complete" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to download SumatraPDF" -ForegroundColor Red
|
||||||
|
Write-Host "Error: $_" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Please download manually from:" -ForegroundColor Yellow
|
||||||
|
Write-Host " https://www.sumatrapdfreader.org/download-free-pdf-viewer" -ForegroundColor Yellow
|
||||||
|
Write-Host " Extract SumatraPDF.exe to the 'SumatraPDF' folder" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[2/3] Extracting..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Extract ZIP file
|
||||||
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
|
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipFile, $sumatraFolder)
|
||||||
|
Write-Host "[OK] Extraction complete" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to extract ZIP file" -ForegroundColor Red
|
||||||
|
Write-Host "Error: $_" -ForegroundColor Red
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[3/3] Cleaning up..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Remove temporary ZIP file
|
||||||
|
if (Test-Path $zipFile) {
|
||||||
|
Remove-Item $zipFile -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[OK] Cleanup complete" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
if (Test-Path $sumatraExe) {
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host " SETUP SUCCESSFUL!" -ForegroundColor Green
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "SumatraPDF portable is now installed at:" -ForegroundColor Green
|
||||||
|
Write-Host " $sumatraExe" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "The Label Printer app will now be able to print PDFs silently." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
} else {
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host " SETUP INCOMPLETE" -ForegroundColor Yellow
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Could not find SumatraPDF.exe after extraction." -ForegroundColor Yellow
|
||||||
|
Write-Host "Please check the SumatraPDF folder and ensure SumatraPDF.exe is present." -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
Reference in New Issue
Block a user