updated to silent print

This commit is contained in:
2025-09-24 21:42:22 +03:00
parent b49a22832d
commit 198563aaba
26 changed files with 693 additions and 1520 deletions

View File

@@ -0,0 +1,74 @@
# Windows Print Service Network Configuration Guide
## Current Issue
Your Flask server (Linux) cannot connect to Windows Print Service because they're on different machines.
## Solution Options
### Option 1: Same Machine Setup
If both Flask and Windows service are on the same machine:
```python
WINDOWS_PRINT_SERVICE_URL = "http://localhost:8765"
```
### Option 2: Different Machines (Recommended)
If Flask server (Linux) and Windows service are on different machines:
1. **Find Windows Machine IP Address:**
```cmd
# On Windows machine, run:
ipconfig
# Look for IPv4 Address, e.g., 192.168.1.100
```
2. **Update Configuration:**
```python
# In /app/print_config.py, change:
WINDOWS_PRINT_SERVICE_URL = "http://192.168.1.100:8765"
```
3. **Test Connection:**
```bash
# From Linux machine, test:
curl http://192.168.1.100:8765/health
```
### Option 3: Windows Firewall Configuration
Ensure Windows allows incoming connections on port 8765:
1. **Windows Firewall Settings:**
- Control Panel → System and Security → Windows Defender Firewall
- Advanced Settings → Inbound Rules → New Rule
- Port → TCP → 8765 → Allow the connection
2. **Test from Linux:**
```bash
telnet [WINDOWS_IP] 8765
```
### Option 4: Port Forwarding/Tunneling
If direct connection isn't possible, set up SSH tunnel or port forwarding.
## Quick Fix for Testing
1. **Get Windows IP Address**
2. **Update print_config.py with the IP**
3. **Restart Flask server**
4. **Test printing**
## Current Status
- ✅ Windows Print Service: Running (localhost:8765/health works)
- ❌ Network Connection: Flask can't reach Windows service
- 🔧 Fix Needed: Update WINDOWS_PRINT_SERVICE_URL with correct IP
## Example Commands
```bash
# 1. Find Windows IP (run on Windows):
# ipconfig | findstr IPv4
# 2. Test connection (run on Linux):
# curl http://[WINDOWS_IP]:8765/health
# 3. Update config and restart Flask
```

View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""
PDF Label Dimension Analysis Tool
Analyzes the generated PDF to verify exact dimensions and content positioning
"""
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'app'))
from app.pdf_generator import LabelPDFGenerator, mm_to_points
from reportlab.lib.units import mm
def analyze_pdf_dimensions():
"""Analyze the PDF dimensions in detail"""
print("=== PDF Label Dimension Analysis ===\n")
generator = LabelPDFGenerator()
# Label dimensions
print("1. LABEL PHYSICAL DIMENSIONS:")
print(f" Width: {generator.label_width/mm:.1f}mm ({generator.label_width:.2f} points)")
print(f" Height: {generator.label_height/mm:.1f}mm ({generator.label_height:.2f} points)")
print(f" ✓ Should be exactly 80.0mm × 110.0mm")
# Content area
print(f"\n2. CONTENT AREA DIMENSIONS:")
print(f" Content Width: {generator.content_width/mm:.1f}mm")
print(f" Content Height: {generator.content_height/mm:.1f}mm")
print(f" Left margin: {generator.content_x/mm:.1f}mm")
print(f" Bottom margin: {generator.content_y/mm:.1f}mm")
print(f" Right margin: {(generator.label_width - generator.content_x - generator.content_width)/mm:.1f}mm")
print(f" Top margin: {(generator.label_height - generator.content_y - generator.content_height)/mm:.1f}mm")
# Row analysis
print(f"\n3. ROW LAYOUT:")
total_rows = 9
standard_rows = 8 # 7 standard + 1 double
double_rows = 1
standard_row_height_mm = generator.row_height/mm
double_row_height_mm = generator.double_row_height/mm
total_content_used = (standard_rows * standard_row_height_mm + double_rows * double_row_height_mm)
expected_content_height = generator.content_height/mm
print(f" Standard row height: {standard_row_height_mm:.1f}mm")
print(f" Double row height: {double_row_height_mm:.1f}mm")
print(f" Total rows: {total_rows} (8 standard + 1 double)")
print(f" Content height used: {total_content_used:.1f}mm")
print(f" Content area available: {expected_content_height:.1f}mm")
print(f" Remaining space: {expected_content_height - total_content_used:.1f}mm")
# Barcode area
print(f"\n4. BARCODE AREA:")
barcode_bottom_margin = generator.content_y/mm
print(f" Bottom barcode space: {barcode_bottom_margin:.1f}mm (should be ~12-15mm)")
top_margin = (generator.label_height - generator.content_y - generator.content_height)/mm
print(f" Top margin space: {top_margin:.1f}mm")
# Check for potential issues
print(f"\n5. POTENTIAL ISSUES ANALYSIS:")
# Check if content is too high
if total_content_used > expected_content_height:
print(f" ⚠️ ISSUE: Content ({total_content_used:.1f}mm) exceeds available space ({expected_content_height:.1f}mm)")
else:
print(f" ✓ Content fits within available space")
# Check margins
if barcode_bottom_margin < 10:
print(f" ⚠️ WARNING: Bottom margin ({barcode_bottom_margin:.1f}mm) may be too small for barcode")
else:
print(f" ✓ Bottom margin adequate for barcode")
if top_margin < 3:
print(f" ⚠️ WARNING: Top margin ({top_margin:.1f}mm) very small")
else:
print(f" ✓ Top margin adequate")
# Aspect ratio check
aspect_ratio = generator.label_height / generator.label_width
expected_ratio = 110.0 / 80.0 # 1.375
print(f"\n6. ASPECT RATIO CHECK:")
print(f" Current ratio: {aspect_ratio:.3f}")
print(f" Expected ratio: {expected_ratio:.3f} (110/80)")
if abs(aspect_ratio - expected_ratio) < 0.001:
print(f" ✓ Aspect ratio is correct")
else:
print(f" ⚠️ Aspect ratio mismatch!")
# Browser display notes
print(f"\n7. BROWSER DISPLAY CONSIDERATIONS:")
print(f" • Browser zoom affects apparent size")
print(f" • PDF viewer scaling can change appearance")
print(f" • Monitor DPI affects screen dimensions")
print(f" • Print preview scaling may alter appearance")
print(f"\n 🔧 SOLUTION: Always verify with physical printout at 100% scale")
return {
'width_mm': generator.label_width/mm,
'height_mm': generator.label_height/mm,
'content_height_mm': expected_content_height,
'content_used_mm': total_content_used,
'aspect_ratio': aspect_ratio,
'expected_ratio': expected_ratio
}
if __name__ == "__main__":
results = analyze_pdf_dimensions()
print(f"\n=== SUMMARY ===")
if (abs(results['width_mm'] - 80.0) < 0.1 and
abs(results['height_mm'] - 110.0) < 0.1 and
abs(results['aspect_ratio'] - results['expected_ratio']) < 0.001):
print("✅ PDF dimensions are CORRECT: 80mm × 110mm")
print("If labels appear too large, check:")
print(" • Browser zoom level (should be 100%)")
print(" • PDF viewer scaling settings")
print(" • Print scaling (should be 'Actual size' or 100%)")
else:
print("❌ PDF dimensions need adjustment")

View File

@@ -0,0 +1,16 @@
# Windows Print Service Configuration
# Configure the Windows Print Service connection for cross-platform printing
# Default Windows Print Service URL
# Change this to the IP address of your Windows machine if running on different systems
# Examples:
# Same machine: http://localhost:8765
# Different machine: http://192.168.1.100:8765
# Domain name: http://printserver.company.local:8765
WINDOWS_PRINT_SERVICE_URL = "http://192.168.1.XXX:8765" # ← REPLACE XXX WITH YOUR WINDOWS IP
# Print service connection timeout (seconds)
PRINT_SERVICE_TIMEOUT = 30
# Enable print service connection (set to False to always use PDF fallback)
PRINT_SERVICE_ENABLED = True

View File

@@ -1616,145 +1616,13 @@ def import_locations_csv():
from app.warehouse import import_locations_csv_handler
return import_locations_csv_handler()
@bp.route('/print_labels_silent/<int:order_id>', methods=['POST'])
def print_labels_silent(order_id):
"""Generate PDF and send directly to Windows Print Service - bypasses CORS"""
print(f"DEBUG: print_labels_silent called for order_id: {order_id}")
if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']:
return jsonify({'error': 'Access denied'}), 403
try:
import requests
import tempfile
import os
from .pdf_generator import generate_order_labels_pdf, update_order_printed_status
from .print_module import get_db_connection
# Get printer name from request
data = request.get_json() or {}
printer_name = data.get('printer_name', 'default')
print(f"DEBUG: Using printer: {printer_name}")
# Get order data from database
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("""
SELECT id, comanda_productie, cod_articol, descr_com_prod, cantitate,
data_livrare, dimensiune, com_achiz_client, nr_linie_com_client, customer_name,
customer_article_number, open_for_order, line_number,
printed_labels, created_at, updated_at
FROM order_for_labels
WHERE id = %s
""", (order_id,))
order_data = cursor.fetchone()
cursor.close()
conn.close()
if not order_data:
return jsonify({'error': 'Order not found'}), 404
# Convert to dictionary for easier access
columns = ['id', 'comanda_productie', 'cod_articol', 'descr_com_prod', 'cantitate',
'data_livrare', 'dimensiune', 'com_achiz_client', 'nr_linie_com_client',
'customer_name', 'customer_article_number', 'open_for_order', 'line_number',
'printed_labels', 'created_at', 'updated_at']
order_dict = dict(zip(columns, order_data))
print(f"DEBUG: Order data: {order_dict}")
# Generate PDF content in memory
pdf_content = generate_order_labels_pdf(order_dict)
# Save PDF to a location accessible by Windows service
pdf_dir = r"C:\temp\quality_labels"
if not os.path.exists(pdf_dir):
os.makedirs(pdf_dir, exist_ok=True)
pdf_filename = f"order_{order_id}_{order_dict['comanda_productie']}.pdf"
pdf_path = os.path.join(pdf_dir, pdf_filename)
# Write PDF to file
with open(pdf_path, 'wb') as pdf_file:
pdf_file.write(pdf_content)
print(f"DEBUG: Created PDF file: {pdf_path}")
try:
# Send to Windows Print Service with local file path
print_service_url = 'http://localhost:8765'
# Create the print request with local file path
print_data = {
'pdf_path': pdf_path,
'printer_name': printer_name,
'copies': 1,
'silent': True,
'order_id': order_id,
'quantity': order_dict['cantitate']
}
print(f"DEBUG: Sending print request to {print_service_url}/print/silent")
# Send request to Windows service
response = requests.post(
f'{print_service_url}/print/silent',
json=print_data,
headers={'Content-Type': 'application/json'},
timeout=30
)
print(f"DEBUG: Print service response: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"DEBUG: Print result: {result}")
if result.get('success'):
# Update order status
update_order_printed_status(order_id)
return jsonify({
'success': True,
'message': 'Labels printed successfully',
'printer': printer_name,
'order': order_dict['comanda_productie'],
'quantity': order_dict['cantitate']
})
else:
return jsonify({
'success': False,
'error': result.get('error', 'Print service returned error')
}), 500
else:
return jsonify({
'success': False,
'error': f'Print service returned status {response.status_code}'
}), 500
except requests.exceptions.RequestException as e:
print(f"DEBUG: Print service connection error: {e}")
# Fallback: Return the PDF for manual printing
return jsonify({
'success': False,
'error': f'Windows Print Service not available: {str(e)}',
'fallback': 'pdf_download',
'pdf_path': pdf_path
}), 503
finally:
# Clean up PDF file
try:
os.unlink(pdf_path)
print(f"DEBUG: Cleaned up PDF file: {pdf_path}")
except Exception as e:
print(f"DEBUG: Error cleaning up PDF file: {e}")
except Exception as e:
print(f"DEBUG: Error in print_labels_silent: {e}")
import traceback
traceback.print_exc()
return jsonify({'error': str(e)}), 500
# NOTE for frontend/extension developers:
# To print labels, call the Chrome extension and pass the PDF URL:
# /generate_labels_pdf/<order_id>
# The extension should POST to http://localhost:8765/print/silent with:
# {
# "pdf_url": "https://your-linux-server/generate_labels_pdf/15",
# "printer_name": "default",
# "copies": 1
# }

View File

@@ -8,6 +8,17 @@
overflow: hidden;
}
/* Inserted custom CSS from user */
.card.scan-table-card table.print-module-table.scan-table thead th {
border-bottom: 2e6 !important;
background-color: #f8f9fa !important;
padding: 0.25rem 0.4rem !important;
text-align: left !important;
font-weight: 600 !important;
font-size: 10px !important;
line-height: 1.2 !important;
}
/* Enhanced table styling to match view_orders.html with higher specificity */
.card.scan-table-card table.print-module-table.scan-table {
width: 100% !important;
@@ -849,10 +860,17 @@ async function updatePrintedStatus(orderId) {
}
// PDF generation handler
// Helper to get extension ID injected by content script
function getInjectedExtensionId() {
const el = document.getElementById('chrome-extension-id');
if (el) return el.getAttribute('data-extension-id');
return null;
}
function addPDFGenerationHandler() {
const printButton = document.getElementById('print-label-btn');
if (!printButton) return;
if (printButton) {
printButton.addEventListener('click', async function(e) {
e.preventDefault();
@@ -866,7 +884,6 @@ function addPDFGenerationHandler() {
const orderId = selectedRow.dataset.orderId;
const prodOrder = selectedRow.querySelector('td:nth-child(2)').textContent.trim();
const quantity = selectedRow.querySelector('td:nth-child(5)').textContent.trim();
if (!orderId) {
alert('Error: Could not determine order ID.');
return;
@@ -879,47 +896,65 @@ function addPDFGenerationHandler() {
printButton.disabled = true;
try {
let success = false;
console.log(`🖨️ Print operation started - Service available: ${printServiceAvailable}`);
// Try Windows service first if available
if (printServiceAvailable) {
console.log('🚀 Attempting silent print via Windows service...');
// Step 1: Generate PDF and get its URL
const pdfResponse = await fetch(`/generate_labels_pdf/${orderId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
if (!pdfResponse.ok) throw new Error('Failed to generate PDF');
// Try to get the PDF URL from the response (assume server returns a URL or we can construct it)
// If not, fallback to download
let pdfUrl = '';
try {
success = await printLabelsWithService(orderId, prodOrder, quantity);
console.log(`✅ Windows service print result: ${success}`);
} catch (serviceError) {
console.error('❌ Windows service failed:', serviceError);
console.warn('🔄 Falling back to PDF download mode');
printServiceAvailable = false; // Mark as unavailable for this session
updatePrintButtonForService(false);
const data = await pdfResponse.json();
pdfUrl = data.pdf_url || '';
} catch {
// If not JSON, fallback to constructing the URL
pdfUrl = `/static/generated_labels/labels_${prodOrder}_${quantity}pcs.pdf`;
}
// Step 2: Prepare print job for Chrome extension
const selectedPrinter = getSelectedPrinter();
const printJob = {
pdfUrl: window.location.origin + pdfUrl,
printer: selectedPrinter,
orderId: orderId,
prodOrder: prodOrder,
quantity: quantity
};
// Step 3: Get extension ID from injected DOM
const extensionId = getInjectedExtensionId();
// Step 4: Send message to Chrome extension
if (window.chrome && window.chrome.runtime && window.chrome.runtime.sendMessage && extensionId) {
window.chrome.runtime.sendMessage(
extensionId,
{ action: 'print_pdf', ...printJob },
function(response) {
if (response && response.success) {
alert('✅ Labels sent to printer!\nOrder: ' + prodOrder + '\nQuantity: ' + quantity + '\nPrinter: ' + selectedPrinter);
updatePrintedStatus(orderId);
} else {
console.log('📄 Service not available, using PDF download mode');
alert('❌ Failed to print via extension. PDF will be downloaded.');
downloadPDFLabels(orderId, prodOrder, quantity);
}
// Fallback to PDF download if service failed or unavailable
if (!success) {
console.log('📥 Generating PDF for download...');
success = await downloadPDFLabels(orderId, prodOrder, quantity);
console.log(`📄 PDF download result: ${success}`);
}
);
} else {
// Fallback: Download PDF
alert(' Chrome extension not detected or extension ID not injected. PDF will be downloaded.');
await downloadPDFLabels(orderId, prodOrder, quantity);
}
} catch (error) {
console.error('Print operation failed:', error);
alert(`❌ Print operation failed: ${error.message}\n\nPlease check:\n• Windows Print Service is running\n• Chrome extension is installed\n• Network connectivity\n• PDF generation permissions`);
alert('❌ Print operation failed: ' + error.message);
} finally {
// Reset button state
printButton.innerHTML = originalText;
printButton.style.background = originalColor;
printButton.disabled = false;
// Recheck service availability for next operation
setTimeout(checkPrintServiceAvailability, 2000);
}
});
}
}
</script>
{% endblock %}

View File

@@ -6,3 +6,4 @@ flask-sqlalchemy
pyodbc
mariadb
reportlab
requests

View File

@@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
Windows Print Service Connection Tester
Tests connectivity between Flask server and Windows Print Service
"""
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'app'))
import requests
import time
from app.print_config import WINDOWS_PRINT_SERVICE_URL, PRINT_SERVICE_TIMEOUT
def test_print_service_connection():
"""Test connection to Windows Print Service"""
print("=== Windows Print Service Connection Test ===\n")
print(f"🔍 Testing connection to: {WINDOWS_PRINT_SERVICE_URL}")
print(f"⏱️ Timeout: {PRINT_SERVICE_TIMEOUT} seconds")
print(f"🌐 From: Flask server (Linux)")
print(f"🎯 To: Windows Print Service")
print()
# Test 1: Health check
print("1⃣ Testing /health endpoint...")
try:
start_time = time.time()
response = requests.get(
f"{WINDOWS_PRINT_SERVICE_URL}/health",
timeout=PRINT_SERVICE_TIMEOUT
)
elapsed = time.time() - start_time
if response.status_code == 200:
data = response.json()
print(f" ✅ SUCCESS (Status: {response.status_code}, Time: {elapsed:.2f}s)")
print(f" 📋 Service: {data.get('service', 'Unknown')}")
print(f" 🖥️ Platform: {data.get('platform', 'Unknown')}")
print(f" 📅 Timestamp: {data.get('timestamp', 'Unknown')}")
health_ok = True
else:
print(f" ❌ FAILED (Status: {response.status_code})")
print(f" 📝 Response: {response.text}")
health_ok = False
except requests.exceptions.ConnectionError as e:
print(f" ❌ CONNECTION REFUSED")
print(f" 🔧 Error: {str(e)}")
print(f" 💡 This usually means:")
print(f" • Windows service is not running")
print(f" • Wrong IP address in configuration")
print(f" • Firewall blocking connection")
print(f" • Network routing issue")
health_ok = False
except Exception as e:
print(f" ❌ ERROR: {str(e)}")
health_ok = False
print()
# Test 2: Printers endpoint (only if health check passed)
if health_ok:
print("2⃣ Testing /printers endpoint...")
try:
response = requests.get(
f"{WINDOWS_PRINT_SERVICE_URL}/printers",
timeout=PRINT_SERVICE_TIMEOUT
)
if response.status_code == 200:
data = response.json()
printers = data.get('printers', [])
print(f" ✅ SUCCESS - Found {len(printers)} printer(s)")
for i, printer in enumerate(printers[:3]): # Show first 3
name = printer.get('name', 'Unknown') if isinstance(printer, dict) else printer
print(f" 🖨️ {i+1}. {name}")
if len(printers) > 3:
print(f" ... and {len(printers)-3} more")
else:
print(f" ❌ FAILED (Status: {response.status_code})")
except Exception as e:
print(f" ❌ ERROR: {str(e)}")
print()
# Test 3: Network diagnostics
print("3⃣ Network Diagnostics...")
# Parse URL to get host and port
try:
from urllib.parse import urlparse
parsed = urlparse(WINDOWS_PRINT_SERVICE_URL)
host = parsed.hostname
port = parsed.port or 8765
print(f" 🌐 Target Host: {host}")
print(f" 🔌 Target Port: {port}")
# Test DNS resolution
try:
import socket
ip = socket.gethostbyname(host)
print(f" 🔍 DNS Resolution: {host}{ip}")
dns_ok = True
except Exception as e:
print(f" ❌ DNS Resolution failed: {e}")
dns_ok = False
# Test raw TCP connection
if dns_ok:
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
result = sock.connect_ex((host, port))
sock.close()
if result == 0:
print(f" ✅ TCP Connection: Port {port} is open")
else:
print(f" ❌ TCP Connection: Port {port} is closed or filtered")
print(f" 💡 Check Windows Firewall and service status")
except Exception as e:
print(f" ❌ TCP Test error: {e}")
except Exception as e:
print(f" ❌ URL parsing error: {e}")
print()
# Final recommendations
print("🔧 TROUBLESHOOTING RECOMMENDATIONS:")
print()
if not health_ok:
print("❌ CONNECTION FAILED - Try these steps:")
print("1. Verify Windows Print Service is running:")
print(" • Windows: sc query QualityLabelPrinting")
print(" • Or: Get-Service QualityLabelPrinting")
print()
print("2. Check Windows machine IP address:")
print(" • Windows: ipconfig | findstr IPv4")
print(" • Update WINDOWS_PRINT_SERVICE_URL in print_config.py")
print()
print("3. Test firewall:")
print(" • Windows: Allow port 8765 in Windows Firewall")
print(" • Test: telnet [WINDOWS_IP] 8765")
print()
print("4. If same machine, verify service binding:")
print(" • Service should bind to 0.0.0.0:8765 (not 127.0.0.1)")
else:
print("✅ CONNECTION SUCCESS - Print service should work!")
print("• Service is reachable and responding")
print("• Ready for label printing")
print("• Try printing labels from the web interface")
print(f"\n📝 Current Configuration:")
print(f" Service URL: {WINDOWS_PRINT_SERVICE_URL}")
print(f" Timeout: {PRINT_SERVICE_TIMEOUT}s")
return health_ok
if __name__ == "__main__":
test_print_service_connection()

View File

@@ -0,0 +1,18 @@
#!/bin/bash
# Cleanup script for Quality Label Printing Service folder
# Run this from the windows_print_service directory (in Git Bash, WSL, or Linux)
# Remove Python-based service and management
rm -f print_service.py
rm -f service_manager.py
# Remove extra documentation (keep only README.md)
rm -f INSTALLATION_GUIDE.md NATIVE_SOLUTION_SUMMARY.md QUICK_SETUP.md QUICK_SETUP_NATIVE.md
# Optionally remove test_service.ps1 if not needed
# rm -f test_service.ps1
# Done
ls -l
echo "Cleanup complete. Only PowerShell service, Chrome extension, and README remain."

View File

@@ -1,361 +0,0 @@
# Quality Recticel Windows Print Service - Installation Guide
## 📋 Overview
The Quality Recticel Windows Print Service enables **silent PDF printing** directly from the web application through a Chrome extension. This system eliminates the need for manual PDF downloads and provides seamless label printing functionality.
## 🏗️ System Architecture
```
Web Application (print_module.html)
Windows Print Service (localhost:8765)
Chrome Extension (Native Messaging)
Windows Print System
```
## 📦 Package Contents
```
windows_print_service/
├── print_service.py # Main Windows service (Flask API)
├── service_manager.py # Service installation & management
├── install_service.bat # Automated installation script
├── chrome_extension/ # Chrome extension files
│ ├── manifest.json # Extension configuration
│ ├── background.js # Service worker
│ ├── content.js # Page integration
│ ├── popup.html # Extension UI
│ ├── popup.js # Extension logic
│ └── icons/ # Extension icons
└── INSTALLATION_GUIDE.md # This documentation
```
## 🔧 Prerequisites
### System Requirements
- **Operating System**: Windows 10/11 (64-bit)
- **Python**: Python 3.8 or higher
- **Browser**: Google Chrome (latest version)
- **Privileges**: Administrator access required for installation
### Python Dependencies
The following packages will be installed automatically:
- `flask` - Web service framework
- `flask-cors` - Cross-origin resource sharing
- `requests` - HTTP client library
- `pywin32` - Windows service integration
## 🚀 Installation Process
### Step 1: Download and Extract Files
1. Download the `windows_print_service` folder to your system
2. Extract to a permanent location (e.g., `C:\QualityRecticel\PrintService\`)
3. **Do not move or delete this folder after installation**
### Step 2: Install Windows Service
#### Method A: Automated Installation (Recommended)
1. **Right-click** on `install_service.bat`
2. Select **"Run as administrator"**
3. Click **"Yes"** when Windows UAC prompt appears
4. Wait for installation to complete
#### Method B: Manual Installation
If the automated script fails, follow these steps:
```bash
# Open Command Prompt as Administrator
cd C:\path\to\windows_print_service
# Install Python dependencies
pip install flask flask-cors requests pywin32
# Install Windows service
python service_manager.py install
# Add firewall exception
netsh advfirewall firewall add rule name="Quality Recticel Print Service" dir=in action=allow protocol=TCP localport=8765
# Create Chrome extension registry entry
reg add "HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.qualityrecticel.printservice" /ve /d "%cd%\chrome_extension\manifest.json" /f
```
### Step 3: Install Chrome Extension
1. Open **Google Chrome**
2. Navigate to `chrome://extensions/`
3. Enable **"Developer mode"** (toggle in top-right corner)
4. Click **"Load unpacked"**
5. Select the `chrome_extension` folder
6. Verify the extension appears with a printer icon
### Step 4: Verify Installation
#### Check Windows Service Status
1. Press `Win + R`, type `services.msc`, press Enter
2. Look for **"Quality Recticel Print Service"**
3. Status should show **"Running"**
4. Startup type should be **"Automatic"**
#### Test API Endpoints
Open a web browser and visit:
- **Health Check**: `http://localhost:8765/health`
- **Printer List**: `http://localhost:8765/printers`
Expected response for health check:
```json
{
"status": "healthy",
"service": "Quality Recticel Print Service",
"version": "1.0",
"timestamp": "2025-09-21T10:30:00"
}
```
#### Test Chrome Extension
1. Click the extension icon in Chrome toolbar
2. Verify it shows "Service Status: Connected ✅"
3. Check that printers are listed
4. Try the "Test Print" button
## 🔄 Web Application Integration
The web application automatically detects the Windows service and adapts the user interface:
### Service Available (Green Button)
- Button text: **"🖨️ Print Labels (Silent)"**
- Functionality: Direct printing to default printer
- User experience: Click → Labels print immediately
### Service Unavailable (Blue Button)
- Button text: **"📄 Generate PDF"**
- Functionality: PDF download for manual printing
- User experience: Click → PDF downloads to browser
### Detection Logic
```javascript
// Automatic service detection on page load
const response = await fetch('http://localhost:8765/health');
if (response.ok) {
// Service available - enable silent printing
} else {
// Service unavailable - fallback to PDF download
}
```
## 🛠️ Configuration
### Service Configuration
The service runs with the following default settings:
| Setting | Value | Description |
|---------|-------|-------------|
| **Port** | 8765 | Local API port |
| **Host** | localhost | Service binding |
| **Startup** | Automatic | Starts with Windows |
| **Printer** | Default | Uses system default printer |
| **Copies** | 1 | Default print copies |
### Chrome Extension Permissions
The extension requires these permissions:
- `printing` - Access to printer functionality
- `nativeMessaging` - Communication with Windows service
- `activeTab` - Access to current webpage
- `storage` - Save extension settings
## 🔍 Troubleshooting
### Common Issues
#### 1. Service Not Starting
**Symptoms**: API not accessible at localhost:8765
**Solutions**:
```bash
# Check service status
python -c "from service_manager import service_status; service_status()"
# Restart service manually
python service_manager.py restart
# Check Windows Event Viewer for service errors
```
#### 2. Chrome Extension Not Working
**Symptoms**: Extension shows "Service Status: Disconnected ❌"
**Solutions**:
- Verify Windows service is running
- Check firewall settings (port 8765 must be open)
- Reload the Chrome extension
- Restart Chrome browser
#### 3. Firewall Blocking Connection
**Symptoms**: Service runs but web page can't connect
**Solutions**:
```bash
# Add firewall rule manually
netsh advfirewall firewall add rule name="Quality Recticel Print Service" dir=in action=allow protocol=TCP localport=8765
# Or disable Windows Firewall temporarily to test
```
#### 4. Permission Denied Errors
**Symptoms**: Installation fails with permission errors
**Solutions**:
- Ensure running as Administrator
- Check Windows UAC settings
- Verify Python installation permissions
#### 5. Print Jobs Not Processing
**Symptoms**: API accepts requests but nothing prints
**Solutions**:
- Check default printer configuration
- Verify printer drivers are installed
- Test manual printing from other applications
- Check Windows Print Spooler service
### Log Files
Check these locations for troubleshooting:
| Component | Log Location |
|-----------|--------------|
| **Windows Service** | `print_service.log` (same folder as service) |
| **Chrome Extension** | Chrome DevTools → Extensions → Background page |
| **Windows Event Log** | Event Viewer → Windows Logs → System |
### Diagnostic Commands
```bash
# Check service status
python service_manager.py status
# Test API manually
curl http://localhost:8765/health
# List available printers
curl http://localhost:8765/printers
# Check Windows service
sc query QualityRecticelPrintService
# Check listening ports
netstat -an | findstr :8765
```
## 🔄 Maintenance
### Updating the Service
1. Stop the current service:
```bash
python service_manager.py stop
```
2. Replace service files with new versions
3. Restart the service:
```bash
python service_manager.py start
```
### Uninstalling
#### Remove Chrome Extension
1. Go to `chrome://extensions/`
2. Find "Quality Recticel Print Service"
3. Click "Remove"
#### Remove Windows Service
```bash
# Run as Administrator
python service_manager.py uninstall
```
#### Remove Firewall Rule
```bash
netsh advfirewall firewall delete rule name="Quality Recticel Print Service"
```
## 📞 Support Information
### API Endpoints Reference
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/health` | GET | Service health check |
| `/printers` | GET | List available printers |
| `/print/pdf` | POST | Print PDF from URL |
| `/print/silent` | POST | Silent print with metadata |
### Request Examples
**Silent Print Request**:
```json
POST /print/silent
{
"pdf_url": "http://localhost:5000/generate_labels_pdf/123",
"printer_name": "default",
"copies": 1,
"silent": true,
"order_id": "123",
"quantity": "10"
}
```
**Expected Response**:
```json
{
"success": true,
"message": "Print job sent successfully",
"job_id": "print_20250921_103000",
"printer": "HP LaserJet Pro",
"timestamp": "2025-09-21T10:30:00"
}
```
## 📚 Technical Details
### Service Architecture
- **Framework**: Flask (Python)
- **Service Type**: Windows Service (pywin32)
- **Communication**: HTTP REST API + Native Messaging
- **Security**: Localhost binding only (127.0.0.1:8765)
### Chrome Extension Architecture
- **Manifest Version**: 3
- **Service Worker**: Handles background print requests
- **Content Script**: Integrates with Quality Recticel web pages
- **Native Messaging**: Communicates with Windows service
### Security Considerations
- Service only accepts local connections (localhost)
- No external network access required
- Chrome extension runs in sandboxed environment
- Windows service runs with system privileges (required for printing)
---
## 📋 Quick Start Checklist
- [ ] Download `windows_print_service` folder
- [ ] Right-click `install_service.bat` → "Run as administrator"
- [ ] Install Chrome extension from `chrome_extension` folder
- [ ] Verify service at `http://localhost:8765/health`
- [ ] Test printing from Quality Recticel web application
**Installation Time**: ~5 minutes
**User Training Required**: Minimal (automatic detection and fallback)
**Maintenance**: Zero (auto-starts with Windows)
For additional support, check the log files and diagnostic commands listed above.

View File

@@ -1,167 +0,0 @@
# Native Windows Print Service - Implementation Summary
## 🎯 Solution Overview
Successfully replaced the Python Flask-based Windows print service with a **native PowerShell implementation** that eliminates all external dependencies while maintaining full functionality.
## 📁 Files Created/Modified
### Core Service Files
-`print_service.ps1` - Complete PowerShell HTTP server with REST API
-`install_native_service.bat` - Native Windows service installer
-`uninstall_service.bat` - Service removal script
### Documentation Updated
-`README.md` - Comprehensive native solution documentation
-`QUICK_SETUP_NATIVE.md` - Fast setup guide for native solution
-`routes.py` - Updated ZIP package to prioritize native files
### Web Integration Updated
-`print_module.html` - Replaced PDF info card with printer dropdown
- ✅ Service detection and printer enumeration integration
- ✅ Enhanced error handling for native service endpoints
## 🚀 Key Advantages
| Feature | Python Flask | Native PowerShell |
|---------|-------------|------------------|
| **Dependencies** | Python + Flask + Requests + pip packages | PowerShell only (built-in) |
| **Installation Time** | 5-10 minutes | 1-2 minutes |
| **Startup Time** | 10-15 seconds | 2-3 seconds |
| **Memory Usage** | 50-100MB | 10-20MB |
| **Disk Space** | 200-500MB | 5-10MB |
| **Security** | External packages | Microsoft-signed only |
| **Enterprise Ready** | Requires IT approval | Uses trusted components |
| **Troubleshooting** | Python debugging | Native Windows tools |
## 🔧 Technical Implementation
### PowerShell HTTP Server
```powershell
# Uses .NET HttpListener for native HTTP serving
$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add("http://localhost:8765/")
```
### Printer Integration
```powershell
# Native WMI integration for printer enumeration
Get-WmiObject -Class Win32_Printer | Where-Object { $_.Local -eq $true }
```
### PDF Printing
```powershell
# Native file download and print via Windows shell
$webClient = New-Object System.Net.WebClient
Start-Process -FilePath $pdfFile -Verb Print
```
## 📡 API Endpoints (Maintained Compatibility)
All original endpoints preserved:
- `GET /health` - Service health check
- `GET /printers` - List available printers
- `POST /print/pdf` - Print PDF documents
- `POST /print/silent` - Silent PDF printing
## 🔄 Migration Path
### For Existing Users
1. Run `uninstall_service.bat` to remove Python service
2. Run `install_native_service.bat` to install native service
3. No Chrome extension changes needed - same API endpoints
### For New Deployments
1. Download updated service package (includes native solution)
2. Run `install_native_service.bat` as Administrator
3. Install Chrome extension as before
4. Everything works identically to Python version
## 🛡️ Security & Compliance
### Enterprise Benefits
- **No Third-Party Code**: Uses only Microsoft PowerShell and .NET
- **Reduced Attack Surface**: Fewer dependencies = fewer vulnerabilities
- **Audit Friendly**: All code visible and editable
- **Corporate Compliance**: Easier approval through IT security
### Security Features
- Localhost-only binding (127.0.0.1)
- CORS headers for browser security
- Automatic temporary file cleanup
- Service-level isolation
## 📊 Performance Metrics
### Service Startup
- Python Flask: ~12 seconds (package imports, Flask initialization)
- Native PowerShell: ~2 seconds (PowerShell load only)
### Memory Footprint
- Python Flask: ~60MB (Python runtime + packages)
- Native PowerShell: ~15MB (PowerShell host + .NET objects)
### HTTP Response Time
- Both solutions: <50ms for API endpoints (no significant difference)
## 🎉 Deployment Advantages
### IT Department Benefits
1. **Single Installer**: One .bat file installs everything
2. **No Prerequisites**: Works on any Windows machine
3. **Easy Troubleshooting**: Native Windows service tools
4. **Clean Uninstall**: Complete removal with uninstall script
5. **Standard Logging**: Uses Windows event system integration
6. **Group Policy Compatible**: Can be deployed via GPO
### End User Benefits
1. **Faster Installation**: 3 minutes vs 10+ minutes
2. **Better Reliability**: Fewer moving parts to break
3. **Lower Resource Usage**: Less CPU and RAM consumption
4. **Familiar Tools**: Standard Windows service management
## 🔧 Maintenance & Support
### Service Management
```batch
# All standard Windows service commands work
sc start QualityRecticelPrintService
sc stop QualityRecticelPrintService
sc query QualityRecticelPrintService
```
### Log Files
- **Location**: `C:\Program Files\QualityRecticel\PrintService\print_service.log`
- **Format**: Timestamp + message (human readable)
- **Rotation**: Automatic (PowerShell handles)
### Configuration
- **Port**: Editable in print_service.ps1 (default 8765)
- **Logging**: Configurable verbosity levels
- **Service Account**: Standard Windows service account options
## ✅ Success Metrics
### Installation Success Rate
- Target: 95%+ first-time success
- Native solution eliminates dependency conflicts
### Performance Improvement
- 80% faster startup time
- 70% less memory usage
- 95% smaller disk footprint
### Support Ticket Reduction
- Fewer dependency-related issues
- Easier troubleshooting with native tools
- Better compatibility across Windows versions
## 🚀 Next Steps
1. **User Testing**: Deploy to pilot group for validation
2. **Documentation Updates**: Ensure all guides reflect native solution
3. **Package Distribution**: Update download system with native version
4. **Migration Support**: Help existing users transition from Python version
5. **Training Materials**: Create guides for IT support teams
The native PowerShell solution provides all the functionality of the Python version while being significantly more enterprise-friendly, faster, and easier to deploy and maintain.

View File

@@ -1,69 +0,0 @@
# 🚀 Quality Recticel Print Service - Quick Setup
## 📦 What You Get
- **Silent PDF Printing** - No more manual downloads!
- **Automatic Detection** - Smart fallback when service unavailable
- **Zero Configuration** - Works out of the box
## ⚡ 2-Minute Installation
### Step 1: Install Windows Service
1. **Right-click** `install_service.bat`
2. Select **"Run as administrator"**
3. Click **"Yes"** and wait for completion
### Step 2: Install Chrome Extension
1. Open Chrome → `chrome://extensions/`
2. Enable **"Developer mode"**
3. Click **"Load unpacked"** → Select `chrome_extension` folder
### Step 3: Verify Installation
- Visit: `http://localhost:8765/health`
- Should see: `{"status": "healthy"}`
## 🎯 How It Works
| Service Status | Button Appearance | What Happens |
|---------------|-------------------|--------------|
| **Running** ✅ | 🖨️ **Print Labels (Silent)** (Green) | Direct printing |
| **Not Running** ❌ | 📄 **Generate PDF** (Blue) | PDF download |
## ⚠️ Troubleshooting
| Problem | Solution |
|---------|----------|
| **Service won't start** | Run `install_service.bat` as Administrator |
| **Chrome extension not working** | Reload extension in `chrome://extensions/` |
| **Can't connect to localhost:8765** | Check Windows Firewall (port 8765) |
| **Nothing prints** | Verify default printer is set up |
## 🔧 Management Commands
```bash
# Check service status
python service_manager.py status
# Restart service
python service_manager.py restart
# Uninstall service
python service_manager.py uninstall
```
## 📍 Important Notes
-**Auto-starts** with Windows - no manual intervention needed
- 🔒 **Local only** - service only accessible from same computer
- 🖨️ **Uses default printer** - configure your default printer in Windows
- 💾 **Don't move files** after installation - keep folder in same location
## 🆘 Quick Support
**Service API**: `http://localhost:8765`
**Health Check**: `http://localhost:8765/health`
**Printer List**: `http://localhost:8765/printers`
**Log File**: `print_service.log` (same folder as installation)
---
*Installation takes ~5 minutes • Zero maintenance required • Works with existing Quality Recticel web application*

View File

@@ -1,187 +0,0 @@
# Quality Recticel Print Service - Quick Setup Guide (Native)
Get the Windows print service running in under 3 minutes - **Zero Dependencies!**
## What You Need
✅ Windows 10/11 or Windows Server
✅ Administrator access
✅ Chrome browser
✅ Downloaded service package
## Installation Steps
### 1. Install Native Windows Service (1 minute)
```batch
# Extract service package to any folder
# Right-click install_native_service.bat
# Select "Run as administrator"
install_native_service.bat
```
**Expected Output:**
```
✅ Administrator privileges confirmed
✅ Service directory created
✅ Service files copied successfully
✅ Windows service created successfully
✅ Service started successfully
🌐 Service running on http://localhost:8765
```
### 2. Install Chrome Extension (1 minute)
```
1. Open Chrome → More Tools → Extensions
2. Enable "Developer mode" (top right)
3. Click "Load unpacked"
4. Select the chrome_extension folder
5. Extension installed ✅
```
### 3. Test Everything (30 seconds)
**Test Service:**
- Open browser: http://localhost:8765/health
- Should show: `{"status": "healthy", "service": "Quality Recticel Print Service", "platform": "Windows PowerShell"}`
**Test Printing:**
- Go to Quality Recticel web app
- Open a label for printing
- Click print - should print silently ✅
## Native Advantages
🚀 **No Python Required** - Pure PowerShell implementation
**Instant Install** - No package downloads or dependencies
🛡️ **Enterprise Ready** - Uses only Microsoft components
📦 **Tiny Footprint** - Minimal disk and memory usage
🔧 **Easy Deployment** - Single installer, works everywhere
## Troubleshooting
### ❌ Service Won't Install
```batch
# Check if already installed
sc query QualityRecticelPrintService
# If exists, uninstall first
uninstall_service.bat
# Try installation again
install_native_service.bat
```
### ❌ Service Won't Start
```powershell
# Check PowerShell execution policy
Get-ExecutionPolicy
# Should be RemoteSigned or Unrestricted
# Set if needed (as Administrator)
Set-ExecutionPolicy RemoteSigned -Force
# Check the logs
Get-Content "C:\Program Files\QualityRecticel\PrintService\print_service.log" -Tail 20
# Manual start
sc start QualityRecticelPrintService
```
### ❌ Port Already in Use
```cmd
# Check what's using port 8765
netstat -ano | findstr :8765
# Kill process if needed (find PID from above)
taskkill /PID <process_id> /F
```
### ❌ Chrome Extension Issues
1. Check extension is enabled in chrome://extensions/
2. Test service URL directly: http://localhost:8765/health
3. Check browser console (F12) for errors
4. Verify CORS headers are working
### ❌ Printing Doesn't Work
```bash
# Test printer enumeration
curl http://localhost:8765/printers
# Check Windows print spooler
sc query spooler
# Restart print spooler if needed
sc stop spooler && sc start spooler
```
## Quick Commands
**Service Management:**
```batch
sc start QualityRecticelPrintService # Start
sc stop QualityRecticelPrintService # Stop
sc query QualityRecticelPrintService # Status
sc qc QualityRecticelPrintService # Configuration
```
**Test Endpoints:**
```bash
curl http://localhost:8765/health # Health check
curl http://localhost:8765/printers # List printers
```
**Uninstall:**
```batch
uninstall_service.bat # Complete removal
```
**Log Files:**
```
C:\Program Files\QualityRecticel\PrintService\print_service.log
```
## Advanced Configuration
**Custom Port:**
Edit `print_service.ps1` and change:
```powershell
param([int]$Port = 8765) # Change to desired port
```
**Service Account:**
```batch
# Run service as specific user (optional)
sc config QualityRecticelPrintService obj="domain\username" password="password"
```
## Performance Notes
- **Startup**: ~2 seconds (vs 10+ seconds for Python)
- **Memory**: ~15MB RAM (vs 50MB+ for Python/Flask)
- **CPU**: Minimal background usage
- **Dependencies**: Zero external packages
## Need Help?
1. **Check logs first** - PowerShell errors are detailed
2. **Test step by step** - service → health → printers → printing
3. **Verify PowerShell policy** - execution policy must allow scripts
4. **Contact IT support** with specific error messages
## Success Checklist
- [ ] Installer ran without errors
- [ ] Service shows "RUNNING" status: `sc query QualityRecticelPrintService`
- [ ] Health endpoint responds: http://localhost:8765/health
- [ ] Printers endpoint lists devices: http://localhost:8765/printers
- [ ] Chrome extension loaded and enabled
- [ ] Web app can print labels silently
---
**Success**: Native service running + Extension loaded + Silent printing works
📞 **Support**: Contact Quality Recticel IT with log details if issues persist
**Advantages over Python version:**
🚀 No external dependencies | ⚡ Faster startup | 🛡️ Better security | 📦 Smaller footprint

View File

@@ -3,17 +3,25 @@
* Injects print service functionality into web pages
*/
// Only inject on Quality Label Printing domains or localhost
const allowedDomains = [
'localhost',
'127.0.0.1'
];
const currentDomain = window.location.hostname;
if (!allowedDomains.includes(currentDomain)) {
console.log('Quality Label Printing Service: Not injecting on', currentDomain);
// return; // Commented out for development - remove in production
// Inject extension ID into the page as a hidden DOM element for detection
function injectExtensionId() {
try {
const existing = document.getElementById('chrome-extension-id');
if (existing) return; // Already injected
const el = document.createElement('div');
el.id = 'chrome-extension-id';
el.setAttribute('data-extension-id', chrome.runtime.id);
el.style.display = 'none';
document.documentElement.appendChild(el);
// Optionally, also set a global variable
window.CHROME_EXTENSION_ID = chrome.runtime.id;
console.log('Injected extension ID:', chrome.runtime.id);
} catch (e) {
console.warn('Failed to inject extension ID:', e);
}
}
injectExtensionId();
console.log('Quality Label Printing Service: Content script loaded');

View File

@@ -0,0 +1,7 @@
# Placeholder for icon files - in production, add actual PNG icons:
# - icon16.png (16x16 pixels)
# - icon48.png (48x48 pixels)
# - icon128.png (128x128 pixels)
# For now, create simple text-based icons using SVG converted to PNG
# These should be replaced with proper icons later

View File

@@ -0,0 +1,27 @@
# Simple text-based icons for the Chrome extension
# These are placeholder files - replace with actual PNG icons for production
# Create simple SVG icons that can be converted to PNG
ICON_16_SVG = '''
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" fill="#007bff"/>
<text x="8" y="12" font-family="Arial" font-size="10" fill="white" text-anchor="middle">🖨</text>
</svg>
'''
ICON_48_SVG = '''
<svg width="48" height="48" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" fill="#007bff" rx="8"/>
<text x="24" y="32" font-family="Arial" font-size="24" fill="white" text-anchor="middle">🖨️</text>
</svg>
'''
ICON_128_SVG = '''
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg">
<rect width="128" height="128" fill="#007bff" rx="16"/>
<text x="64" y="84" font-family="Arial" font-size="48" fill="white" text-anchor="middle">🖨️</text>
</svg>
'''
# For now, create simple text placeholders
# In production, convert these SVGs to PNG files

View File

@@ -0,0 +1,122 @@
#!/usr/bin/env python3
"""
Generate PNG icons for Chrome extension
Creates simple colored squares with printer icons
"""
try:
from PIL import Image, ImageDraw, ImageFont
PIL_AVAILABLE = True
except ImportError:
PIL_AVAILABLE = False
import os
def create_simple_icon(size, filename):
"""Create a simple colored square icon"""
if PIL_AVAILABLE:
# Create image with PIL
img = Image.new('RGBA', (size, size), (0, 123, 255, 255)) # Blue background
draw = ImageDraw.Draw(img)
# Add a white border
border_width = max(1, size // 16)
draw.rectangle([0, 0, size-1, size-1], outline=(255, 255, 255, 255), width=border_width)
# Add text (P for Print)
try:
font_size = size // 2
font = ImageFont.load_default()
text = "P"
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
x = (size - text_width) // 2
y = (size - text_height) // 2
draw.text((x, y), text, fill=(255, 255, 255, 255), font=font)
except:
pass
img.save(filename, 'PNG')
print(f"Created {filename} ({size}x{size})")
else:
# Create a minimal PNG file without PIL
create_minimal_png(size, filename)
def create_minimal_png(size, filename):
"""Create a minimal PNG file without PIL"""
# This creates a very basic PNG file
# Blue square with minimal PNG structure
import struct
import zlib
# PNG signature
png_signature = b'\x89PNG\r\n\x1a\n'
# IHDR chunk
width = height = size
bit_depth = 8
color_type = 2 # RGB
compression = 0
filter_method = 0
interlace = 0
ihdr_data = struct.pack('>IIBBBBB', width, height, bit_depth, color_type, compression, filter_method, interlace)
ihdr_crc = zlib.crc32(b'IHDR' + ihdr_data) & 0xffffffff
ihdr_chunk = struct.pack('>I', len(ihdr_data)) + b'IHDR' + ihdr_data + struct.pack('>I', ihdr_crc)
# IDAT chunk (blue pixels)
pixels = []
for y in range(height):
row = [0] # Filter byte
for x in range(width):
# Blue color RGB(0, 123, 255)
row.extend([0, 123, 255])
pixels.extend(row)
pixel_data = bytes(pixels)
compressed_data = zlib.compress(pixel_data)
idat_crc = zlib.crc32(b'IDAT' + compressed_data) & 0xffffffff
idat_chunk = struct.pack('>I', len(compressed_data)) + b'IDAT' + compressed_data + struct.pack('>I', idat_crc)
# IEND chunk
iend_crc = zlib.crc32(b'IEND') & 0xffffffff
iend_chunk = struct.pack('>I', 0) + b'IEND' + struct.pack('>I', iend_crc)
# Write PNG file
with open(filename, 'wb') as f:
f.write(png_signature)
f.write(ihdr_chunk)
f.write(idat_chunk)
f.write(iend_chunk)
print(f"Created minimal {filename} ({size}x{size})")
def main():
# Create icons directory if it doesn't exist
icons_dir = "/home/ske087/quality_recticel/windows_print_service/chrome_extension/icons"
print("Creating Chrome extension icons...")
print(f"PIL available: {PIL_AVAILABLE}")
# Create required icon sizes
sizes = [16, 32, 48, 128]
for size in sizes:
filename = os.path.join(icons_dir, f"icon{size}.png")
create_simple_icon(size, filename)
print("✅ All icons created successfully!")
print("\nIcons created:")
for size in sizes:
filename = f"icon{size}.png"
filepath = os.path.join(icons_dir, filename)
if os.path.exists(filepath):
file_size = os.path.getsize(filepath)
print(f"{filename} ({size}x{size}px, {file_size} bytes)")
else:
print(f"{filename} - FAILED")
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

View File

@@ -0,0 +1,4 @@
# Placeholder for 128x128 icon
# This is a text file placeholder
# Replace with actual icon128.png file
🖨️

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

View File

@@ -0,0 +1,4 @@
# Placeholder for 16x16 icon
# This is a text file placeholder
# Replace with actual icon16.png file
🖨️

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

View File

@@ -0,0 +1,4 @@
# Placeholder for 48x48 icon
# This is a text file placeholder
# Replace with actual icon48.png file
🖨️

View File

@@ -1,374 +0,0 @@
"""
Quality Recticel Windows Print Service
=====================================
A local Windows service that provides a REST API for silent printing
through Chrome extension integration.
Features:
- Local HTTP API server (localhost:8765)
- Chrome extension native messaging
- Silent PDF printing
- Windows service management
- Security and error handling
Installation:
1. Run install_service.bat as Administrator
2. Install Chrome extension
3. Configure web application to use localhost:8765
Author: Quality Recticel Development Team
Version: 1.0.0
"""
import sys
import os
import json
import logging
import threading
from datetime import datetime
from pathlib import Path
# Add current directory to path for imports
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from flask import Flask, request, jsonify, send_file
from flask_cors import CORS
import requests
import subprocess
import tempfile
import uuid
import time
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('print_service.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
class WindowsPrintService:
"""Main Windows Print Service class"""
def __init__(self, port=8765):
self.port = port
self.app = Flask(__name__)
CORS(self.app) # Enable CORS for web page communication
self.setup_routes()
self.chrome_extension_id = None
self.service_status = "starting"
def setup_routes(self):
"""Set up Flask routes for the API"""
@self.app.route('/health', methods=['GET'])
def health_check():
"""Health check endpoint"""
return jsonify({
'status': 'healthy',
'service': 'Quality Recticel Print Service',
'version': '1.0.0',
'timestamp': datetime.now().isoformat(),
'chrome_extension_connected': self.is_chrome_extension_available()
})
@self.app.route('/print/pdf', methods=['POST'])
def print_pdf():
"""Print PDF endpoint"""
try:
data = request.get_json()
# Validate required fields
required_fields = ['pdf_url', 'printer_name']
for field in required_fields:
if field not in data:
return jsonify({
'error': f'Missing required field: {field}',
'success': False
}), 400
# Execute print job
result = self.execute_print_job(data)
if result['success']:
return jsonify(result), 200
else:
return jsonify(result), 500
except Exception as e:
logger.error(f"Print PDF error: {e}")
return jsonify({
'error': str(e),
'success': False
}), 500
@self.app.route('/print/silent', methods=['POST'])
def silent_print():
"""Silent print endpoint using Chrome extension"""
try:
data = request.get_json()
# Validate required fields
if 'pdf_data' not in data and 'pdf_url' not in data:
return jsonify({
'error': 'Either pdf_data or pdf_url is required',
'success': False
}), 400
# Send to Chrome extension for silent printing
result = self.send_to_chrome_extension(data)
if result['success']:
return jsonify(result), 200
else:
return jsonify(result), 500
except Exception as e:
logger.error(f"Silent print error: {e}")
return jsonify({
'error': str(e),
'success': False
}), 500
@self.app.route('/printers', methods=['GET'])
def get_printers():
"""Get available printers"""
try:
printers = self.get_available_printers()
return jsonify({
'printers': printers,
'success': True
})
except Exception as e:
logger.error(f"Get printers error: {e}")
return jsonify({
'error': str(e),
'success': False
}), 500
@self.app.route('/extension/status', methods=['GET'])
def extension_status():
"""Check Chrome extension status"""
return jsonify({
'extension_available': self.is_chrome_extension_available(),
'success': True
})
def execute_print_job(self, print_data):
"""Execute a print job"""
try:
pdf_url = print_data.get('pdf_url')
printer_name = print_data.get('printer_name', 'default')
copies = print_data.get('copies', 1)
logger.info(f"Executing print job: {pdf_url} -> {printer_name}")
# Download PDF if URL provided
if pdf_url:
pdf_content = self.download_pdf(pdf_url)
else:
pdf_content = print_data.get('pdf_data')
if not pdf_content:
return {
'success': False,
'error': 'No PDF content available'
}
# Save PDF to temporary file
temp_pdf = self.save_temp_pdf(pdf_content)
# Print using system command
print_result = self.print_pdf_file(temp_pdf, printer_name, copies)
# Cleanup
os.unlink(temp_pdf)
return {
'success': print_result,
'message': 'Print job completed' if print_result else 'Print job failed',
'job_id': str(uuid.uuid4())
}
except Exception as e:
logger.error(f"Execute print job error: {e}")
return {
'success': False,
'error': str(e)
}
def send_to_chrome_extension(self, print_data):
"""Send print command to Chrome extension"""
try:
# Prepare message for Chrome extension
message = {
'action': 'silent_print',
'data': print_data,
'timestamp': datetime.now().isoformat(),
'job_id': str(uuid.uuid4())
}
# Try to communicate with Chrome extension via native messaging
result = self.send_native_message(message)
if result:
return {
'success': True,
'message': 'Print command sent to Chrome extension',
'job_id': message['job_id']
}
else:
# Fallback to direct printing
logger.warning("Chrome extension not available, falling back to direct printing")
return self.execute_print_job(print_data)
except Exception as e:
logger.error(f"Send to Chrome extension error: {e}")
return {
'success': False,
'error': str(e)
}
def send_native_message(self, message):
"""Send native message to Chrome extension"""
try:
# This would be implemented based on Chrome's native messaging protocol
# For now, we'll simulate the communication
# In a real implementation, this would:
# 1. Find Chrome extension by ID
# 2. Send message via stdin/stdout pipe
# 3. Wait for response
logger.info(f"Sending native message to Chrome extension: {message}")
# Simulate successful communication
return True
except Exception as e:
logger.error(f"Native messaging error: {e}")
return False
def download_pdf(self, url):
"""Download PDF from URL"""
try:
response = requests.get(url, timeout=30)
response.raise_for_status()
return response.content
except Exception as e:
logger.error(f"PDF download error: {e}")
raise
def save_temp_pdf(self, pdf_content):
"""Save PDF content to temporary file"""
temp_file = tempfile.mktemp(suffix='.pdf')
with open(temp_file, 'wb') as f:
if isinstance(pdf_content, str):
# Base64 encoded content
import base64
pdf_content = base64.b64decode(pdf_content)
f.write(pdf_content)
return temp_file
def print_pdf_file(self, pdf_path, printer_name, copies=1):
"""Print PDF file using system command"""
try:
# Windows printing command
if printer_name == 'default':
cmd = f'powershell -Command "Start-Process -FilePath \\"{pdf_path}\\" -ArgumentList \\"/p\\" -Wait"'
else:
cmd = f'powershell -Command "Start-Process -FilePath \\"{pdf_path}\\" -ArgumentList \\"/p /h /{printer_name}\\" -Wait"'
logger.info(f"Executing print command: {cmd}")
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode == 0:
logger.info("Print command executed successfully")
return True
else:
logger.error(f"Print command failed: {result.stderr}")
return False
except Exception as e:
logger.error(f"Print PDF file error: {e}")
return False
def get_available_printers(self):
"""Get list of available printers"""
try:
# Windows command to get printers
cmd = 'powershell -Command "Get-Printer | Select-Object Name, DriverName, PortName | ConvertTo-Json"'
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode == 0:
printers_data = json.loads(result.stdout)
# Ensure it's a list
if isinstance(printers_data, dict):
printers_data = [printers_data]
printers = []
for printer in printers_data:
printers.append({
'name': printer.get('Name', ''),
'driver': printer.get('DriverName', ''),
'port': printer.get('PortName', ''),
'is_default': False # Could be enhanced to detect default printer
})
return printers
else:
logger.error(f"Failed to get printers: {result.stderr}")
return []
except Exception as e:
logger.error(f"Get available printers error: {e}")
return []
def is_chrome_extension_available(self):
"""Check if Chrome extension is available"""
# This would check for Chrome extension via native messaging
# For now, we'll return a simulated status
return True
def run_service(self):
"""Run the Flask service"""
try:
self.service_status = "running"
logger.info(f"Starting Quality Recticel Print Service on port {self.port}")
self.app.run(
host='localhost',
port=self.port,
debug=False,
threaded=True
)
except Exception as e:
logger.error(f"Service run error: {e}")
self.service_status = "error"
finally:
self.service_status = "stopped"
def main():
"""Main entry point"""
print("Quality Recticel Windows Print Service")
print("=====================================")
service = WindowsPrintService()
try:
service.run_service()
except KeyboardInterrupt:
logger.info("Service stopped by user")
except Exception as e:
logger.error(f"Service error: {e}")
if __name__ == "__main__":
main()

View File

@@ -1,143 +0,0 @@
"""
Windows Service Installation and Management
==========================================
This module handles Windows service installation, configuration, and management
for the Quality Recticel Print Service.
"""
import os
import sys
import time
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
from pathlib import Path
class QualityRecticelPrintService(win32serviceutil.ServiceFramework):
"""Windows Service wrapper for the print service"""
_svc_name_ = "QualityRecticelPrintService"
_svc_display_name_ = "Quality Recticel Print Service"
_svc_description_ = "Local API service for silent PDF printing via Chrome extension"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.is_alive = True
def SvcStop(self):
"""Stop the service"""
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
self.is_alive = False
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, '')
)
def SvcDoRun(self):
"""Run the service"""
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, '')
)
# Import and run the print service
try:
from print_service import WindowsPrintService
service = WindowsPrintService(port=8765)
# Run service in a separate thread
import threading
service_thread = threading.Thread(target=service.run_service)
service_thread.daemon = True
service_thread.start()
# Wait for stop event
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
except Exception as e:
servicemanager.LogErrorMsg(f"Service error: {e}")
def install_service():
"""Install the Windows service"""
try:
# Install the service with automatic startup
win32serviceutil.InstallService(
QualityRecticelPrintService._svc_reg_class_,
QualityRecticelPrintService._svc_name_,
QualityRecticelPrintService._svc_display_name_,
description=QualityRecticelPrintService._svc_description_,
startType=win32service.SERVICE_AUTO_START # Auto-start on system boot
)
print(f"✅ Service '{QualityRecticelPrintService._svc_display_name_}' installed successfully")
print(f"🔄 Service configured for AUTOMATIC startup on system restart")
# Start the service
win32serviceutil.StartService(QualityRecticelPrintService._svc_name_)
print(f"✅ Service started successfully")
return True
except Exception as e:
print(f"❌ Service installation failed: {e}")
return False
def uninstall_service():
"""Uninstall the Windows service"""
try:
# Stop the service first
try:
win32serviceutil.StopService(QualityRecticelPrintService._svc_name_)
print(f"✅ Service stopped")
except:
pass # Service might not be running
# Remove the service
win32serviceutil.RemoveService(QualityRecticelPrintService._svc_name_)
print(f"✅ Service '{QualityRecticelPrintService._svc_display_name_}' uninstalled successfully")
return True
except Exception as e:
print(f"❌ Service uninstallation failed: {e}")
return False
def service_status():
"""Get service status"""
try:
status = win32serviceutil.QueryServiceStatus(QualityRecticelPrintService._svc_name_)
status_names = {
win32service.SERVICE_STOPPED: "Stopped",
win32service.SERVICE_START_PENDING: "Start Pending",
win32service.SERVICE_STOP_PENDING: "Stop Pending",
win32service.SERVICE_RUNNING: "Running",
win32service.SERVICE_CONTINUE_PENDING: "Continue Pending",
win32service.SERVICE_PAUSE_PENDING: "Pause Pending",
win32service.SERVICE_PAUSED: "Paused"
}
current_status = status_names.get(status[1], "Unknown")
print(f"Service Status: {current_status}")
return status[1]
except Exception as e:
print(f"❌ Failed to get service status: {e}")
return None
if __name__ == '__main__':
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(QualityRecticelPrintService)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(QualityRecticelPrintService)