-
Notifications
You must be signed in to change notification settings - Fork 2
Platform Notes
Understanding platform-specific behavior helps you deploy @ssxv/node-printer successfully on Windows and Linux systems.
Windows Print Spooler manages all printing:
-
Service name:
spooler(Windows Service) - Start type: Usually automatic
- Dependencies: RPC services, Event Log
Common issues:
# Check spooler status
Get-Service spooler
# Restart spooler if needed
Restart-Service spooler
# View print queue
Get-WmiObject -Class Win32_PrintJobDriver types on Windows:
- Type 3: User-mode drivers (most common)
- Type 4: Kernel-mode drivers (older)
- Universal drivers: Windows 10+ preferred
Driver locations:
- System drivers:
%WINDIR%\System32\spool\drivers - Per-user drivers:
%USERPROFILE%\AppData\Local\Microsoft\Windows\Printer
Required permissions for printing:
- Print permission: Basic printing capability
- Manage printers: Install/configure printers
- Manage documents: Cancel other users' jobs
Service account considerations:
// When running as service account
const { printers } = require('@ssxv/node-printer');
try {
const allPrinters = await printers.list();
console.log('Available printers:', allPrinters.length);
} catch (error) {
if (error.code === 'ACCESS_DENIED') {
console.log('Service account lacks printer permissions');
// Solution: Grant "Log on as a service" + printer permissions
}
}Interactive vs service sessions:
- Session 0: Services run here (no user printers)
- Session 1+: User sessions (user-installed printers)
Headless Windows servers:
// Install printers system-wide, not per-user
// Use administrator account for printer installation
// Network printers work better than local printers
async function setupHeadlessPrinting() {
const printers = await printers.list();
// Filter for network printers (more reliable in headless environments)
const networkPrinters = printers.filter(p =>
p.type === 'network' || p.name.includes('\\\\')
);
if (networkPrinters.length === 0) {
console.warn('No network printers found - consider adding network printers');
}
return networkPrinters;
}Raw printing on Windows:
// Windows supports RAW datatype natively
const job = await jobs.printRaw({
printer: 'Zebra Printer',
data: data,
type: 'RAW' // Essential on Windows
});Windows job priorities:
// Set job priority (Windows-specific)
const job = await jobs.printFile({ printer: 'HP LaserJet', file: 'document.pdf' });
await jobs.setNative('HP LaserJet', job.id, {
priority: 'high' // 'low', 'normal', 'high'
});CUPS components:
- cupsd: Main CUPS daemon
- cupsd.conf: Main configuration file
- PPD files: Printer descriptions
- Filters: Data processing pipelines
CUPS directories:
- Configuration:
/etc/cups/ - Spool directory:
/var/spool/cups/ - Log files:
/var/log/cups/ - PPD files:
/etc/cups/ppd/
Basic CUPS setup:
# Install CUPS (Ubuntu/Debian)
sudo apt-get install cups cups-client
# Start CUPS service
sudo systemctl start cups
sudo systemctl enable cups
# Add user to lpadmin group (printer management)
sudo usermod -a -G lpadmin $USER
# Check CUPS status
systemctl status cupsCUPS web interface:
- URL:
http://localhost:631 - Requires admin user in
lpadmingroup - Useful for printer configuration
CUPS permission model:
# View current printer permissions
lpstat -p -d
# Grant user print permission
lpadmin -p PrinterName -u allow:username
# Grant group print permission
lpadmin -p PrinterName -u allow:@groupnameFile permissions for headless operation:
# CUPS runs as 'lp' user by default
# Ensure Node.js process can access CUPS socket
sudo usermod -a -G lp nodeuser
# Or run Node.js as lp user (less secure)
sudo -u lp node app.jsLocal printers:
// USB printers detected automatically
const localPrinters = await printers.list();
const usbPrinters = localPrinters.filter(p =>
p.uri?.startsWith('usb://')
);Network printers:
// Network printers (IPP, Socket, etc.)
const networkPrinters = await printers.list();
const ippPrinters = networkPrinters.filter(p =>
p.uri?.startsWith('ipp://') || p.uri?.startsWith('socket://')
);PPD options:
// Get CUPS-specific printer options
const capabilities = await printers.capabilities('HP_LaserJet');
console.log(capabilities.options); // PPD-defined options
// Examples of PPD options:
// - PageSize: A4, Letter, Legal
// - Duplex: None, DuplexNoTumble, DuplexTumble
// - ColorModel: Gray, RGB, CMYKCUPS job attributes:
// Set CUPS-specific job options
const job = await jobs.printFile({ printer: 'HP_LaserJet', file: 'document.pdf' });
await jobs.setNative('HP_LaserJet', job.id, {
'job-priority': 50, // 1-100
'job-hold-until': 'no-hold', // CUPS hold options
'media': 'iso_a4_210x297mm' // CUPS media sizes
});Windows printer names:
- Can contain spaces:
"HP LaserJet Pro M404n" - May include server:
"\\PRINTSERVER\HP_Laser" - Case-insensitive
Linux printer names:
- Usually no spaces:
HP_LaserJet_Pro_M404n - Case-sensitive
- Follow CUPS naming conventions
Cross-platform printer discovery:
async function findPrinter(searchName) {
const allPrinters = await printers.list();
// Exact match first
let printer = allPrinters.find(p => p.name === searchName);
if (printer) return printer;
// Case-insensitive match
printer = allPrinters.find(p =>
p.name.toLowerCase() === searchName.toLowerCase()
);
if (printer) return printer;
// Partial match (contains search term)
printer = allPrinters.find(p =>
p.name.toLowerCase().includes(searchName.toLowerCase())
);
return printer;
}Standardized error codes across platforms:
const { jobs, PrinterError } = require('@ssxv/node-printer');
try {
const job = await jobs.printFile({ printer: 'BadPrinter', file: 'document.pdf' });
} catch (error) {
if (error instanceof PrinterError) {
// Cross-platform error codes
switch (error.code) {
case 'PRINTER_NOT_FOUND':
// Same code on Windows and Linux
console.log('Printer does not exist');
break;
case 'ACCESS_DENIED':
// Different underlying causes, same code
console.log('Permission error');
break;
case 'PRINTER_OFFLINE':
// Normalized across platforms
console.log('Printer not available');
break;
}
// Platform-specific details still available
console.log('Platform details:', error.platformData);
}
}Challenges:
- No interactive desktop session
- Limited printer driver support
- Service account permissions
Solutions:
// Install printers system-wide
// Use network printers when possible
// Configure service to run as admin user
async function configureHeadlessWindows() {
// Prefer network printers
const printers = await printers.list();
const networkPrinters = printers.filter(p => p.type === 'network');
if (networkPrinters.length === 0) {
console.warn('Consider adding network printers for headless operation');
}
return networkPrinters;
}Windows service configuration:
<!-- App.config for Windows Service -->
<configuration>
<appSettings>
<add key="AllowInteractWithDesktop" value="false" />
<add key="ServiceAccount" value="LocalSystem" />
</appSettings>
</configuration>Challenges:
- No X11 display server
- Minimal package installations
- Container environments
Solutions:
# Minimal CUPS installation
sudo apt-get install --no-install-recommends cups cups-client
# Configure CUPS for headless operation
echo "WebInterface No" | sudo tee -a /etc/cups/cupsd.conf
# Enable network printer discovery
echo "Browsing On" | sudo tee -a /etc/cups/cupsd.conf
echo "BrowseLocalProtocols dnssd" | sudo tee -a /etc/cups/cupsd.confDocker considerations:
# Dockerfile for Node.js app with printing
FROM node:18-slim
# Install minimal CUPS
RUN apt-get update && apt-get install -y \
libcups2-dev \
cups-client \
&& rm -rf /var/lib/apt/lists/*
# Copy application
COPY . /app
WORKDIR /app
# Install dependencies and build
RUN npm install
# Run as non-root user
USER node
CMD ["node", "app.js"]Docker printing setup:
# docker-compose.yml
version: '3.8'
services:
node-printer-app:
build: .
volumes:
# Mount CUPS socket for printing access
- /var/run/cups/cups.sock:/var/run/cups/cups.sock
# Mount CUPS configuration (read-only)
- /etc/cups:/etc/cups:ro
environment:
- CUPS_SERVER=unix:///var/run/cups/cups.sockKubernetes printing:
# kubernetes-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: printer-app
spec:
template:
spec:
containers:
- name: app
image: node-printer-app
volumeMounts:
- name: cups-socket
mountPath: /var/run/cups/cups.sock
readOnly: false
- name: cups-config
mountPath: /etc/cups
readOnly: true
volumes:
- name: cups-socket
hostPath:
path: /var/run/cups/cups.sock
- name: cups-config
hostPath:
path: /etc/cupsUNC paths:
// Print to shared Windows printer
const job = await jobs.printFile({ printer: '\\\\PRINTSERVER\\HP_Laser', file: 'document.pdf' });Network printer discovery:
async function findNetworkPrinters() {
const allPrinters = await printers.list();
return allPrinters.filter(p =>
p.name.startsWith('\\\\') || // UNC path
p.type === 'network' // Network type
);
}IPP printers:
// Print to IPP network printer
const job = await jobs.printFile({ printer: 'ipp-printer', file: 'document.pdf' });
// IPP printer URIs: ipp://host:631/printers/nameCUPS printer sharing:
# Enable printer sharing in CUPS
sudo cupsctl --share-printers
sudo cupsctl --remote-any
# Allow remote connections
echo "Listen *:631" | sudo tee -a /etc/cups/cupsd.confPrint spooler issues:
# Clear print queue
Stop-Service spooler
Remove-Item C:\Windows\System32\spool\PRINTERS\* -Force
Start-Service spoolerDriver problems:
# List installed printer drivers
Get-PrinterDriver | Select-Object Name, PrinterEnvironment
# Remove problematic driver
Remove-PrinterDriver -Name "HP LaserJet Driver"CUPS issues:
# Check CUPS error logs
sudo tail -f /var/log/cups/error_log
# Test printer connectivity
lpstat -p printer-name -l
# Clear CUPS job queue
cancel -a printer-namePermission problems:
# Check CUPS socket permissions
ls -la /var/run/cups/cups.sock
# Add user to lp group
sudo usermod -a -G lp $USER// Enable detailed logging
process.env.NODE_PRINTER_DEBUG = 'true';
const { printers, jobs } = require('@ssxv/node-printer');
async function debugPrinting() {
try {
console.log('Platform:', process.platform);
// List all printers with details
const allPrinters = await printers.list();
console.log('Found printers:', JSON.stringify(allPrinters, null, 2));
// Test default printer
const defaultPrinter = await printers.default();
console.log('Default printer:', defaultPrinter);
// Test print job
const job = await jobs.printFile({ printer: defaultPrinter.name, file: 'test.txt' });
console.log('Test job submitted:', job.id);
// Monitor job
let attempts = 0;
while (attempts < 10) {
const status = await jobs.get(defaultPrinter.name, job.id);
console.log(`Job status (${attempts}):`, status);
if (status.state === 'completed' || status.state === 'error') {
break;
}
await new Promise(resolve => setTimeout(resolve, 1000));
attempts++;
}
} catch (error) {
console.error('Debug error:', {
message: error.message,
code: error.code,
platform: process.platform,
stack: error.stack
});
}
}
debugPrinting();class PrinterCache {
constructor(ttlMs = 30000) { // 30 second cache
this.cache = new Map();
this.ttl = ttlMs;
}
async getPrinters() {
const now = Date.now();
const cached = this.cache.get('printers');
if (cached && now - cached.timestamp < this.ttl) {
return cached.data;
}
const printers = await printers.list();
this.cache.set('printers', { data: printers, timestamp: now });
return printers;
}
}
const printerCache = new PrinterCache();// Batch job submission for better performance
async function submitBatchJobs(files, printer) {
const jobPromises = files.map(file =>
jobs.printFile({ printer, file }).catch(error => ({ error, file }))
);
return await Promise.all(jobPromises);
}// Validate printer access before printing
async function securePrint(filename, printerName, allowedPrinters) {
if (!allowedPrinters.includes(printerName)) {
throw new Error(`Printer '${printerName}' not in allowed list`);
}
// Additional security: verify file path
if (!filename.startsWith('/safe/print/directory/')) {
throw new Error('File not in allowed directory');
}
return await jobs.printFile({ printer: printerName, file: filename });
}// Log all print operations for security audit
const fs = require('fs').promises;
async function auditedPrint(filename, printer, userId) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
userId,
action: 'print',
filename: path.basename(filename),
printer,
fileSize: (await fs.stat(filename)).size
};
// Log the operation
await fs.appendFile('/var/log/printing-audit.log',
JSON.stringify(logEntry) + '\n'
);
// Perform the print
return await jobs.printFile({ printer, file: filename });
}- Home - Return to the main wiki page
- Printing Concepts - Core printing fundamentals
- printFile vs printRaw - Choose the right printing method
- Job Lifecycle and States - Understanding job management