codesoft指定打印机打印_巧用win32print来控制windows系统打印机并推送打印任务

  小爬最近接到的一个需求是:将windows系统下的打印任务批量有序传输给网络打印机,实现批量有序打印。

9b16156d95788545d19f13d58732a3d2.png

    用户先从公司的OA(B/S模式)系统下 打印指定内容的表单以及表单中的附件内容。这个问题可以这样分解:

1、抓包,得到OA对应的任务接口,然后利用python requests模拟post请求,获取所有的表单的URL并进行必要的去重处理;

2、打印OA表单的过程,需要浏览器在前台,这个时候可以结合selenium的driver.get(url)方法,打开每一个表单,同时解析网页内容,拿到所有附件的相关信息(名称、后缀、下载地址),利用requests再度保存这些附件至本地;

3、打开表单后,利用 win32api.keybd_event,模拟键盘快捷键“Ctrl + Shift + P”调出系统的打印窗口;

4、选中“PDF打印机”,需要电脑中有“Microsoft Print to Pdf”或者“Foxit Reader PDF Printer”等;

5、利用pywin32中的相关方法,驱动打印过程,将每个OA表单(网页)打印成PDF文件并格式化命名&存储,与前面的附件内容存储到同一个文件夹;

6、附件文件和OA生成的PDF文件均格式化存储,用OA单号作为文件名的一部分,将两者关联起来;

7、将本地对应文件夹的所有内容有序推送给打印机,指定打印机为某一台网络打印机。同时要确保打印过程中,不乱序;

针对步骤3,可以自定义函数来实现:

#键盘按下
def key_down(keyname):
    win32api.keybd_event(vk_code[keyname],0,0,0)

#键盘抬起
def key_up(key_name):
    win32api.keybd_event(vk_code[key_name],0,win32con.KEYEVENTF_KEYUP,0)

#按键组合操作
def simulate_three_key(firstkey,sencondkey,lastkey):
    key_down(firstkey)
    key_down(sencondkey)
    key_down(lastkey)
    key_up(lastkey)
    key_up(sencondkey)
    key_up(firstkey)
#按键组合操作
def simulate_two_key(firstkey,sencondkey):
    key_down(firstkey)
    key_down(sencondkey)
    key_up(sencondkey)
    key_up(firstkey)

    然后利用 simulate_three_key('ctrl',"shift",'p') 即可呼出系统的默认打印窗口:

b9653ccb54ce8effc747778b87658be9.png

  那么步骤4,也就是上图的打印窗口,如何选中某一个打印机呢?直接利用win32gui.SendMessage

    来选中某个打印机是非常困难的。一种可行的方法是,利用pywin32下的win32print模块,也就是本文的重点。

比如,用下面的代码可以遍历并获取到当前计算机的所有打印机信息:

for it in win32print.EnumPrinters(6):
    print(it[1])

    我们甚至可以知道某台打印机的当前状态,假定某台打印机名为printerName,则可以这样获取打印机状态:

hPrinter = win32print.OpenPrinter (printerName)
dic = hex(win32print.GetPrinter(hPrinter,2)['Status'])
if dic[-2]=="8":
    print("The printer is offline.")
if dic[-5]=="4":
   print("The printer is out of toner.")
elif dic[-5]=="2":
   print("The printer is low on toner.")

Printer status name/value

Description

PRINTER_STATUS_BUSY

0x00000200

The printer is busy.

PRINTER_STATUS_DOOR_OPEN

0x00400000

The printer door is open.

PRINTER_STATUS_ERROR

0x00000002

The printer is in an error state.

PRINTER_STATUS_INITIALIZING

0x00008000

The printer is initializing.

PRINTER_STATUS_IO_ACTIVE

0x00000100

The printer is in an active input or output state.

PRINTER_STATUS_MANUAL_FEED

0x00000020

The printer is in a manual feed state.

PRINTER_STATUS_NOT_AVAILABLE

0x00001000

The printer is not available for printing.

PRINTER_STATUS_NO_TONER

0x00040000

The printer is out of toner.

PRINTER_STATUS_OFFLINE

0x00000080

The printer is offline.

PRINTER_STATUS_OUTPUT_BIN_FULL

0x00000800

The printer's output bin is full.

PRINTER_STATUS_OUT_OF_MEMORY

0x00200000

The printer has run out of memory.

PRINTER_STATUS_PAGE_PUNT

0x00080000

The printer cannot print the current page.

PRINTER_STATUS_PAPER_JAM

0x00000008

Paper is stuck in the printer.

PRINTER_STATUS_PAPER_OUT

0x00000010

The printer is out of paper.

PRINTER_STATUS_PAPER_PROBLEM

0x00000040

The printer has an unspecified paper problem.

PRINTER_STATUS_PAUSED

0x00000001

The printer is paused.

PRINTER_STATUS_PENDING_DELETION

0x00000004

The printer is being deleted as a result of a client's call to RpcDeletePrinter. No new jobs can be submitted on existing printer objects for that printer.

PRINTER_STATUS_POWER_SAVE

0x01000000

The printer is in power-save mode.<182>

PRINTER_STATUS_PRINTING

0x00000400

The printer is printing.

PRINTER_STATUS_PROCESSING

0x00004000

The printer is processing a print job.

PRINTER_STATUS_SERVER_OFFLINE

0x02000000

The printer is offline.<183>

PRINTER_STATUS_SERVER_UNKNOWN

0x00800000

The printer status is unknown.<184>

PRINTER_STATUS_TONER_LOW

0x00020000

The printer is low on toner.

PRINTER_STATUS_USER_INTERVENTION

0x00100000

The printer has an error that requires the user to do something.

PRINTER_STATUS_WAITING

0x00002000

The printer is waiting.

PRINTER_STATUS_WARMING_UP

0x00010000

The printer is warming up.

        更多的打印机接口信息,可查询微软的开发文档:https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/1625e9d9-29e4-48f4-b83d-3bd0fdaea787?redirectedfrom=MSDN

我们也可以得到当前默认的打印机,设置默认打印机:

currentPrinter=win32print.GetDefaultPrinterW()
win32print.SetDefaultPrinterW(printer)

9e7beb447ea1b1aa4af5f772842de0e6.png

        我们利用上面两个函数,可以先得到系统当前的打印机,用变量存储后,再设置默认打印机至 PDF打印机,待执行完所有任务后,再设置默认打印机为用户一开始的默认打印机,整个过程用户不需要更多的干预;

  重点说步骤7:我们需要以OA表单+附件的形式,逐一给打印机分配任务,且不能乱序:

如果附件是图片性质,我们可以结合Pillow库来处理,示例代码如下:

import win32print
import win32ui
from PIL import Image, ImageWin


# Constants for GetDeviceCaps
#
#
# HORZRES / VERTRES = printable area
#
HORZRES = 8
VERTRES = 10
#
# LOGPIXELS = dots per inch
#
LOGPIXELSX = 88
LOGPIXELSY = 90
#
# PHYSICALWIDTH/HEIGHT = total area
#
PHYSICALWIDTH = 110
PHYSICALHEIGHT = 111
#
# PHYSICALOFFSETX/Y = left / top margin
#
PHYSICALOFFSETX = 112
PHYSICALOFFSETY = 113

def print_image(file_name):
 
    printer_name = win32print.GetDefaultPrinterW() # 获得默认打印机
    
    #
    # You can only write a Device-independent bitmap
    # directly to a Windows device context; therefore
    # we need (for ease) to use the Python Imaging
    # Library to manipulate the image.
    #
    # Create a device context from a named printer
    # and assess the printable size of the paper.
    #
    hDC = win32ui.CreateDC ()
    hDC.CreatePrinterDC (printer_name)
    printable_area = hDC.GetDeviceCaps (HORZRES), hDC.GetDeviceCaps (VERTRES)
    printer_size = hDC.GetDeviceCaps (PHYSICALWIDTH), hDC.GetDeviceCaps (PHYSICALHEIGHT)
    printer_margins = hDC.GetDeviceCaps (PHYSICALOFFSETX), hDC.GetDeviceCaps (PHYSICALOFFSETY)
    
    #
    # Open the image, rotate it if it's wider than
    # it is high, and work out how much to multiply
    # each pixel by to get it as big as possible on
    # the page without distorting.
    #
    bmp = Image.open (file_name)
    # bmp = bmp.rotate (90)
    # bmp.save("test1.png")
    if bmp.size[0] > bmp.size[1]:
        # bmp = bmp.rotate (90)
        bmp=bmp.transpose(Image.ROTATE_90)
    
    ratios = [1.0 * printable_area[0] / bmp.size[0], 1.0 * printable_area[1] / bmp.size[1]]
    scale = min (ratios)*0.85 #这个0.85的系数是不希望图片被打印太大,缺少margin,不方便文档的装订
    file_name=file_name.split("\\")[-1] #这一步是为了提取fullpath中的filename部分
    
    #
    # Start the print job, and draw the bitmap to
    # the printer device at the scaled size.
    #
    hDC.StartDoc (file_name)
    hDC.StartPage ()
    
    dib = ImageWin.Dib (bmp)
    scaled_width, scaled_height = [int (scale * i) for i in bmp.size]
    x1 = int ((printer_size[0] - scaled_width) / 2)
    y1 = int ((printer_size[1] - scaled_height) / 2)
    x2 = x1 + scaled_width
    y2 = y1 + scaled_height
    dib.draw (hDC.GetHandleOutput (), (x1, y1, x2, y2))
    
    hDC.EndPage ()
    hDC.EndDoc ()
    hDC.DeleteDC ()

        需要强调的是,如果我们对图片进行后台旋转90度时,一定要用transpose(Image.ROTATE_90),不要使用 rotate (90),否则打印的图片很有可能显示不完整,且有黑边;

具体的transpose用法见Pillow官网文档:

b216430c73447c9489b12a1e08331089.png

    如果我们要打印的任务是PDF或者其他office类型的文档,可以利用win32api.ShellExecute方法,示例如下:

def printer_loading(filename):
    # open (filename, "r")
    currentPrinter=win32print.GetDefaultPrinterW()
    win32api.ShellExecute (0,"print",filename,'/d:"%s"' % currentPrinter,".",0)

  该方法有一个缺陷,win32api.ShellExecute 会在指令发出后,立即返回值,而不是等打印任务真正传输到打印机后再返回。这就意味着,附件中的图片用win32ui的方法走后台已经传输给打印机,而PDF等其他文件可能还没及时发送给打印机,造成打印任务乱序。

        可行的解决方法是,利用win32print.EnumJobs,定时获取打印机当前的任务队列,确保队列中出现刚推送的任务后,再来推送下一个打印任务。示例如下:

handle = win32print.OpenPrinter(printer_name).handle
tasks=win32print.EnumJobs(handle,0, -1, 1)
for task in tasks:
    taskName=task["pDocument"]

        由于打印任务是动态增减的,每次得到的tasks可能都不同,且由于打印机可能有很多人共同使用,不能保证某个用户的某次打印任务一定会出现在打印队列的最上方。所以要尽可能拿到所有的任务;

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        至此,这个项目中的难点都逐一有了解决方案,希望小爬以上的思路,对喜欢自动化的你,能有所借鉴~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/506304.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

crmeb pc端模板下载_PC端人人影视下载速度如何提高

首先下载最新版本的人人影视我的是这个&#xff0c;右下角版本号1022然后在下载设置那里把连接数调高一点大致就这样我的就是调了一下然后下载速度高了许多&#xff0c;刚开始只有1M左右的速度&#xff0c;现在4&#xff0c;5M每秒

android 编辑自定义可编辑表格,smart 框架 列表 可编辑表格

可编辑表格常用属性colModel: [{label: "主键ID", name: "hellop1",hidden:true},{label: "列明", name: "hellop2",align: "center", editable: true, edittype: text, editrules: { required: true } }editable&#xff1…

dos虚拟机如何全屏显示_实用工具 | 虚拟机软件VirtualBox详细使用介绍

前言&#xff1a;搞自动化控制的工程师&#xff0c;要擅于利用工具和软件&#xff0c;其中&#xff0c;虚拟机就是很好的工具&#xff0c;安装操作系统以后&#xff0c;就相当于多了一台电脑&#xff0c;可以用来测试网络&#xff0c;测试软件&#xff0c;或者直接用来调试设备…

axure原型案例_Axure RP9原型案例:制作一个可以滑动的菜单

摘要&#xff1a;在PC端的产品中我们会常常见到滑动式菜单&#xff0c;当鼠标移入菜单上方&#xff0c;向下或向右自动滑动弹出子菜单&#xff0c;当页面信息层级较多或功能较多时&#xff0c;在产品设计时经常会用到这种滑动式菜单。今天就和大家分享如下制作滑动式菜单的交互…

一会404一会500_没网络就是404?这锅可不能乱背!

在那个房价未突破天际&#xff0c;一台计算机还可以占着几间房的年代。数据库被存放在一个神秘的房间中。如果无法找到请求者所需要的文件&#xff0c;用户将会得到file not found的信息。而这件房间的门牌就是404。404https://www.zhihu.com/video/1168484640850579456当然以上…

android8 呼吸灯,红米note8pro呼吸灯颜色如何设置?

红米note8pro支持呼吸灯功能&#xff0c;当有未读通知的时候&#xff0c;指示灯就会闪烁提醒。此外&#xff0c;我们还可以根据自己的喜好设置呼吸灯的颜色&#xff0c;下面为大家带来详细的设置教程。红米note8pro怎么设置呼吸灯颜色1、首先&#xff0c;进入手机桌面&#xff…

java string 返回匹配正则的字符串的起始位置_【Python】正则表达式

概述正则表达式是一个特殊的字符序列&#xff0c;它能帮助你方便的检查一个字符串是否与某种模式匹配。 Python 自1.5版本起增加了re 模块&#xff0c;它提供 Perl 风格的正则表达式模式。re 模块使 Python 语言拥有全部的正则表达式功能。 compile 函数根据一个模式字符串和可…

html九图拼图游戏代码,HTML5拼图游戏

拼图游戏介绍 拼图游戏将一幅图片分割成若干拼块并将它们随机打乱顺序。当将所有拼块都放回原位置时&#xff0c;就完成了拼图(游戏结束)。 在“游戏”中&#xff0c;单击滑块选择游戏难易&#xff0c;“容易”为3行3列拼图游戏&#xff0c;中间为一个4行4列拼图游戏&#xff0…

access驱动程序_Linux驱动程序学习二 (续) scull 源码在内核5.4.0上的编译调试

《LINUX设备驱动程序》第三章提供了源码scull,但是由于我用的是5.4.0内核,书中的是2.6.10内核,内核发生了很大的变化,因此编译scull源码花费了不少时间,下面是编译调试记录。&#xff08;这个编译调试记录应该是目前网络上适应内核版本最高的&#xff0c;所以也希望给近期加入《…

android评论嵌套,android 嵌套的listview示例(可参照实现朋友圈评论)

android 嵌套的listview示例(可参考实现朋友圈评论) 最近在项目中用到listview中再嵌套一个listview&#xff0c;两层也有监听&#xff0c;都没有问题。其实&#xff0c;主要解决里面那一层的listview的高度计算就可以&#xff0c;外面那一层listview自动计算。加上里面那层展开…

捷达vs7测试_捷达VS5话题:防撞钢梁,溃缩梁。第200311期

//封面图&#xff0c;捷达VS5&#xff0c;自中&#xff0c;最近看到网上有些观点有点儿带偏&#xff0c;然后咱们技术群今天也讨论了一下&#xff0c;大家也来听听咱们爱折腾的车主们是怎么看防撞梁的事情的。事情起因是因为大家看到一些网上的拆车视频&#xff0c;说捷达VS5前…

html5 原生拖拽,原生JS实现拖拽效果

这篇文章主要为大家详细介绍了原生JS实现拖拽效果&#xff0c;文中示例代码介绍的非常详细&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下本文实例为大家分享了JS实现拖拽效果的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下想要让整个元…

ov5640帧率配置_逃离塔科夫怎么提升帧率 帧率优化建议_单机游戏_游戏攻略

逃离塔科夫有着非常真实的游玩与画面表现&#xff0c;所以这类的多人游戏比较吃配置&#xff0c;那么帧率上不去会十分影响游戏体验&#xff0c;下面请看由“SIIYAM”带来的逃离塔科夫帧率优化建议&#xff0c;一起来看看吧。帧率优化建议&#xff1a;这游戏对于cpu资源分配和内…

js 带笔锋 签字版_年轻人的第一支签字笔? ——米家签字笔评测

emm感觉笔者能咕到自己都怀疑人生惹QAQ…对于小米而言&#xff0c;可能他家中性笔做的还真没手机那么好。但对于劝退这件事&#xff0c;理由其实是很复杂的。但既然决定了要来写这样一点东西&#xff0c;那我也自然要把我知道的和能想到的&#xff0c;略述一二。我们先来看看小…

itools 不支持缩略图下载_PS插件缩略图3.8.0.96安装教程

插件下载[名称]&#xff1a;PS插件『缩略图补丁3.8.0.96』[大小]&#xff1a;1.4 MB [语言]&#xff1a;简体中文 [安装环境]&#xff1a;Win7/Win8/Win10[支持版本]&#xff1a;PS CS6—CC2019[32/64位下载链接]&#xff1a;https://pan.baidu.com/s/1AlOlWzMZfYgdJSlZpbQsmw…

z370支持pcie信号拆分吗_定了!AMD B550主板确认将支持PCIE4.0,多项能力接近X570

近日&#xff0c;华擎B550AM Gaming主板照片和文档泄露&#xff0c;Micro-ATX板型、具备4内存插槽&#xff0c;支持PCIE4.0显卡/固态硬盘。B550芯片组本身无法拆分PCIE4.0信道&#xff0c;所以华擎的这张B550主板在搭配第三代锐龙时只有第一条PCIE插槽(通常安装独立显卡)和M.2固…

mac 修改conda镜像 condarc_win10 修改anaconda源

通过 conda config 命令生成配置文件&#xff0c;这里&#xff0c;我们使用清华的镜像&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/&#xff0c;首先&#xff0c;打开CMD&#xff0c;执行命令&#xff1a;conda config --add channels https://mirro…

倒计时css和js html代码,手把手教你利用CSS和JS创建一个倒数计时器

倒计时功能&#xff0c;在很多地方都会用到&#xff0c;我们平时都习惯去用一些插件来应用&#xff0c;会减少不少的工作量&#xff0c;并且效果也能达到预期。我今天并不是想分享什么倒计时插件&#xff0c;而是自己写一个简单的倒数计时器&#xff0c;有兴趣的同学可以往下看…

手机端使用ghelper_Anki手机端使用指南(一)

【本篇会对如何使用手机端anki进行详解】有小伙伴询问在应用商店搜索anki找不到名字叫“anki”的软件&#xff0c;这里解释一下&#xff0c;在手机端的名字和电脑端的名字不太一样。安卓对应的名字叫做AnkiDroidIOS对应的名字叫做Ankimobile不过其实是一个软件&#xff0c;同步…

三甲医院his系统源码_三甲医院科研管理系统是什么,科研成果包括哪些

对于三甲医院来说&#xff0c;做科研管理系统必不可少的是数据收集&#xff0c;有一个方便的数据收集管理软件能记科研效率提高很多&#xff0c;那就是三甲医院科研管理系统&#xff0c;首先&#xff0c;我们先了解一下三甲医院科研管理系统是什么&#xff0c;科研成果包括哪些…