You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Python打印宏在部分HP激光打印机上静默取消任务的排查求助

Python打印宏在部分HP激光打印机上静默取消任务的排查求助

大家好,我最近开发了一个Python打印宏,核心功能是把信头粘贴到PDF文档上,再发送到打印机完成打印。这个工具在大部分公司部署后都运行正常,但在部分HP激光打印机上遇到了棘手的问题:打印任务能正常出现在打印机队列中,但很快就会被静默取消,完全没有输出。

已排查的信息:

  • 最初怀疑是老型号打印机内存不足,所以把PDF转位图的DPI从300降到了150,但问题依然存在
  • 测试环境(正常):HP LaserJet M608
  • 出问题的机型:HP LaserJet 600 M601
  • 同时该宏也会向HP LaserJet 600 M603发送任务(暂时未反馈问题)
  • 程序通过PyInstaller打包为可执行文件分发
  • 使用的依赖库:sysosjsonwin32printwin32uiwin32conwin32guifitzshutillogginguuidPILpypdf

关键代码片段:

1. PDF转位图的函数

def convert_to_bitmap(pdf_path, input_name):
    print_queue = []
    # cycle through pages in doc for bitmap conversions
    doc = fitz.open(pdf_path)
    i = 0
    logger.info("Converting PDF file pages to bitmaps")
    for page in doc:
        pixmap = page.get_pixmap(dpi=150)  # convert page to pixel map
        img = Image.frombytes("RGB", [pixmap.width, pixmap.height], pixmap.samples)  # converts pixel map to PIL image
        # creates appropriate path and saves bit map
        uuid4 = uuid.uuid4()
        bitmap_name = f"bitmap({i})-" + str(uuid4) + ".bmp"
        bitmap_path = get_resource_path(f"temp\\{bitmap_name}")
        try:
            img.save(bitmap_path)
        except PermissionError as e:
            logger.error(f"User lacks permission to save bitmap to {bitmap_path}:\n{e} \nExiting program")
            input_path = get_resource_path(os.path.join("temp\\", input_name))
            clean_up(print_queue, input_path, pdf_path)
            sys.exit(1)
        except FileNotFoundError as e:
            logger.error(f"{bitmap_path} is an invalid directory:\n{e} \nExiting program")
            input_path = get_resource_path(os.path.join("temp\\", input_name))
            clean_up(print_queue, input_path, pdf_path)
            sys.exit(1)
        except ValueError as e:
            logger.error(f"Unsupported file format or specifier:\n{e} \nExiting program")
            input_path = get_resource_path(os.path.join("temp\\", input_name))
            clean_up(print_queue, input_path, pdf_path)
            sys.exit(1)
        except OSError as e:
            logger.error(f"OS error encountered:\n{e} \nExiting program")
            input_path = get_resource_path(os.path.join("temp\\", input_name))
            clean_up(print_queue, input_path, pdf_path)
            sys.exit(1)
        except Exception as e:
            logger.error(f"Unexpected error encountered:\n{e} \nExiting program")
            input_path = get_resource_path(os.path.join("temp\\", input_name))
            clean_up(print_queue, input_path, pdf_path)
            sys.exit(1)
        print_queue.append(bitmap_path)  # queues up bitmap for printing
        i = i + 1
        logger.info(f"Page {i + 1}: converted to bitmap")
    logger.info("All PDF file pages converted to bitmaps")
    return print_queue

2. 发送打印任务的函数(关键部分)

def print_pdf(printer, tray, print_queue):
    # cycle through printers to see if passed printer is valid
    printers = win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS)
    valid_printer = False
    for p in printers:
        if (p[2] == printer):
            valid_printer = True
            logger.info(f"Printer: {printer} found")
    if (not(valid_printer)):
        # printer not found setting to default
        logger.warning(f"Printer: {printer} not found")
        printer = win32print.GetDefaultPrinter()
        logger.info(f"Printing from default: {printer}")

    # creates handler, device context, memory device context, and starts print job
    printer_handler = win32print.OpenPrinter(printer)
    # acceses printer settings
    devmode = win32print.GetPrinter(printer_handler, 2)["pDevMode"]
    if tray is not None:
        # sets tray if tray >= 0
        if tray >= 0:
            devmode.DefaultSource = tray
            logger.info(f"Tray set to: {tray}")
        else:
            logger.warning(f"Tray set to default: {devmode.DefaultSource}")
    win32print.DocumentProperties(0, printer_handler, printer, devmode, devmode,
                                  win32con.DM_IN_BUFFER | win32con.DM_OUT_BUFFER)

    dc = win32ui.CreateDC()
    dc.CreatePrinterDC(printer)
    memory_dc = dc.CreateCompatibleDC()
    dc.StartDoc("Letterhead on Message")

    # gets page dimensions
    page_width = dc.GetDeviceCaps(win32con.HORZRES)
    page_height = dc.GetDeviceCaps(win32con.VERTRES)

    # cycles bitmaps for each page
    for bm in print_queue:
        dc.StartPage()
        # load bitmap object
        bitmap_handle = win32gui.LoadImage(0, bm, win32con.IMAGE_BITMAP, 0, 0, win32con.LR_LOADFROMFILE)
        bitmap = win32ui.CreateBitmapFromHandle(bitmap_handle)
        old_bitmap = memory_dc.SelectObject(bitmap)
        
        # 原代码此处有截断,以下为现有完整部分
        bitmap_info = bitmap.GetInfo()
        # 后续绘制逻辑(原代码未完全提供)
        # ...

想请教大家的问题:

  • 有没有遇到过HP LaserJet 600 M601这类老机型和Python打印库的兼容性问题?
  • PDF转位图的过程中,有没有什么细节(比如位图格式、颜色模式)会导致老打印机无法识别?
  • 使用win32print系列库配置打印参数时,针对这类机型有没有特殊的设置项需要注意?
  • 有没有可能是PyInstaller打包后的exe在某些系统上存在权限或依赖缺失的问题?

内容来源于stack exchange

火山引擎 最新活动