使用win32print打印PDF仅发声无输出,求排查与替代方案
Win32print打印PDF无输出及ShellExecute报错问题排查与替代方案
问题描述
我正在使用win32print API向打印机发送文档,相关代码如下:
import win32print import win32api import os import time def list_printers(): printers = win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL |win32print.PRINTER_ENUM_CONNECTIONS) printer_list=[printer[2] for printer in printers] return printer_list def choose_printer(printers): print("Available Printers:") for i, printer in enumerate(printers): print(f"{i + 1}. {printer}") while True: try: choice = int(input("Select a printer by number: ")) if 1 <= choice <= len(printers): return printers[choice - 1] else: print("Invalid choice. Please select a valid printer number.") except ValueError: print("Invalid input. Please enter a number.") def print_pdf(pdf_path, printer_name): if not os.path.exists(pdf_path): raise FileNotFoundError(f"No such file: '{pdf_path}'") print(f"Printing '{pdf_path}' to '{printer_name}'") printer_handle = win32print.OpenPrinter(printer_name) job_id = win32print.StartDocPrinter(printer_handle, 1, ("PDF Document",None,"RAW")) try: win32print.StartPagePrinter(printer_handle) with open(pdf_path, 'rb') as pdf_file: pdf_content = pdf_file.read() # print(pdf_content) # Write the PDF content to the printer # time.sleep(4) bytesPrinted=win32print.WritePrinter(printer_handle, pdf_content) print(bytesPrinted) win32print.EndPagePrinter(printer_handle) finally: win32print.EndDocPrinter(printer_handle) win32print.ClosePrinter(printer_handle) if __name__ == "__main__": pdf_file_path = "C:/Users/DELL/Downloads/test.pdf" printers = list_printers() if not printers: print("No printers found.") else: selected_printer=choose_printer(printers) print_pdf(pdf_file_path,selected_printer)
我还尝试了该方法:win32api.ShellExecute(0, "printto",pdf_file_path,f'"{printername}"', ".", 0),但出现错误:device attached to the system is not functioning。第一种方法运行无报错,但打印机仅发出声音却没有打印输出,请问我哪里操作有误?有没有其他替代方法可以使用?
问题原因与修复
1. Win32print直接发送PDF内容的问题
用RAW模式直接把PDF字节流发给打印机,绝大多数普通打印机不支持直接解析PDF格式——打印机只识别自家打印语言(如PCL、PostScript)或通用ESC/POS指令。打印机发声是因为接收到数据但无法解析,直接丢弃了。
修复方向:
- 若打印机支持PostScript,可将PDF转成PostScript后发送(需结合GhostScript工具);
- 通用方案是先将PDF转成打印机可识别的位图/增强元文件(EMF),再发送打印。
2. ShellExecute "printto"报错的问题
printto参数要求打印机名称为原始设备名/共享名,不能加多余引号或格式错误。此外该方法依赖系统默认PDF阅读器,若阅读器异常(如Acrobat Reader故障)也会触发设备错误。
修复方向:
- 确保打印机名称是
list_printers()返回的原始字符串,不要手动添加引号; - 检查系统默认PDF阅读器能否手动正常打印该PDF,异常则修复或更换阅读器。
替代方案
方案1:PyMuPDF转图片打印
PyMuPDF可将PDF每页转成图片,再通过win32print输出,兼容性极强:
import fitz import win32print import win32ui import win32con def print_pdf_as_image(pdf_path, printer_name): doc = fitz.open(pdf_path) printer_handle = win32print.OpenPrinter(printer_name) try: printer_info = win32print.GetPrinter(printer_handle, 2) dc = win32ui.CreateDC() dc.CreatePrinterDC(printer_name) dc.StartDoc("PDF Print Job") for page_num in range(len(doc)): page = doc.load_page(page_num) pix = page.get_pixmap() # 创建位图并绘制 bmp = win32ui.CreateBitmap() bmp.CreateCompatibleBitmap(dc, pix.width, pix.height) hdc = win32ui.CreateDCFromHandle(dc.GetSafeHdc()) hdc.SelectObject(bmp) hdc.DrawBitmap((0,0), bmp, (0,0,pix.width,pix.height)) # 发送到打印机 dc.StartPage() dc.StretchBlt((0,0), printer_info['pDevMode'].dmPaperWidth, printer_info['pDevMode'].dmPaperHeight, hdc, (0,0), pix.width, pix.height, win32con.SRCCOPY) dc.EndPage() dc.EndDoc() finally: win32print.ClosePrinter(printer_handle)
方案2:ShellExecute普通打印命令
放弃printto,直接用print命令让系统默认阅读器处理打印,稳定性更高:
import win32api def print_with_shell(pdf_path): # 0为后台执行,1为前台显示阅读器窗口 win32api.ShellExecute(0, "print", pdf_path, None, ".", 0)
该方法使用系统默认打印机;若需指定打印机,确保printto参数格式正确(无需额外引号):
win32api.ShellExecute(0, "printto", pdf_path, printer_name, ".", 0)
方案3:GhostScript转PostScript打印
若打印机支持PostScript,可先用GhostScript将PDF转成PS,再用RAW模式发送:
import subprocess import win32print def pdf_to_ps(pdf_path, ps_path): # 需提前安装GhostScript,并将gswin64c.exe加入系统环境变量 subprocess.run(["gswin64c", "-sDEVICE=ps2write", "-o", ps_path, pdf_path], check=True) def print_ps(ps_path, printer_name): printer_handle = win32print.OpenPrinter(printer_name) job_id = win32print.StartDocPrinter(printer_handle, 1, ("PS Document", None, "RAW")) try: win32print.StartPagePrinter(printer_handle) with open(ps_path, 'rb') as f: win32print.WritePrinter(printer_handle, f.read()) win32print.EndPagePrinter(printer_handle) finally: win32print.EndDocPrinter(printer_handle) win32print.ClosePrinter(printer_handle)
内容的提问来源于stack exchange,提问作者dhrummil
相关产品推荐
相关产品推荐

