问题描述
最近遇到一个需求,帮财务打印报销单。大致工作内容是把财务那边的报销单拿过来,在平台上把报销单的pdf下载下来,然后打印出来再装订到对应的报销文件上。
问题出在打印和最后的匹配上,非常的花时间。因为打印出来的文件是无序的,即使你已经在文件夹里对PDF按时间排序了,打印出来的最终顺序也取决于发送到打印机的快慢。
就算一个一个文件选择,手动去右键打印,出来的顺序也很可能不是你想要的顺序。
如果打印可以按顺序,那我最终的工作就会很节省时间,所以想要解决的问题是:
如何让打印机根据顺序一个个的打印所选文件
用win32包执行打印任务
首先,要用python模拟打印工作,这里我们用到win32print,**它来自pypiwin32这个包,我们通过以下语句安装
pip install pypiwin32
以下是进行一个打印任务的简单示例
# 获取默认打印机名称,例如"3009"
print_name = win32print.GetDefaultPrinter()
# 获取打印机的句柄,或者说进程
handle = win32print.OpenPrinter(print_name)
# 获取打印机当前状态
status = win32print.GetPrinter(handle, 2)['Status']
print("当前状态", status)
# 获取当前打印机任务
tasks = win32print.EnumJobs(handle, 0, -1, 1)
# 执行打印,注意第三个参数是字符串,如果是完整文件路径请转换成字符串
win32api.ShellExecute(0, "print", "FT_CN000445148884.pdf", '/d:"%s"' % print_name, ".", 0)
选择文件夹,读取所有pdf文件
这里用到了tk库来实现文件夹选择,用os自带的方法应该也可以
def get_files():
# 创建并隐藏TK窗口
root = Tk()
root.withdraw()
root.attributes('-topmost', True)
folder = askdirectory(title="请选择PDF文件所在的文件夹")
print("所选路径是:", folder)
source_src = Path(folder)
files = source_src.glob("*.pdf")
return files
运行效果如下:
读取文件后进行打印
核心程序如下,获取到文件列表后,循环调用以下程序即可:
def print_file(self, file):
file = str(file)
win32api.ShellExecute(0, "print", file, '/d:"%s"' % self.print_name, ".", 0)
time.sleep(2)
tasks = win32print.EnumJobs(self.handle, 0, -1, 1)
if tasks:
print("打印阻塞,请稍等")
while tasks:
time.sleep(1)
tasks = win32print.EnumJobs(self.handle, 0, -1, 1)
完整程序如下:
from PDF_kayotin_main import get_files
import win32print
import win32api
import time
class PrintPDF:
def __init__(self, desc=False):
self.files = get_files()
self.print_name = win32print.GetDefaultPrinter()
self.handle = win32print.OpenPrinter(self.print_name)
if self.print_name == "":
print("请设置默认打印机")
return
self.sort_file_by_time(desc)
print(f"打印完毕,本次共打印{len(self.files)}个文件")
def sort_file_by_time(self, desc):
file_dict = dict()
for file in self.files:
file_dict[file] = file.stat().st_mtime
res = sorted(file_dict.keys(), key=file_dict.get, reverse=desc)
print("开始打印------")
for r in res:
print("正在打印:", r)
self.print_file(r)
def print_file(self, file):
file = str(file)
win32api.ShellExecute(0, "print", file, '/d:"%s"' % self.print_name, ".", 0)
time.sleep(2)
tasks = win32print.EnumJobs(self.handle, 0, -1, 1)
if tasks:
print("打印阻塞,请稍等")
while tasks:
time.sleep(1)
tasks = win32print.EnumJobs(self.handle, 0, -1, 1)
if __name__ == '__main__':
my_pdf_printer = PrintPDF()
get_files方法请见上文
运行效果如下:
另一种方法:合并PDF
事实上还有另外一种简单粗暴的方法,就是把多个PDF合并成一个,这样打印的时候,必然就是按顺序的了。
PDF页数确认
我这里的打印机设置的是双面打印,所以单数页的pdf文件,需要check一下,给它加一页空白页,函数如下所示
def check_files(self, size=2):
"""检查文件,如果是奇数页,就补足一页空白页"""
for file in self.files:
reader = PyPDF2.PdfReader(file)
if len(reader.pages) % size:
writer = PyPDF2.PdfWriter()
for page in reader.pages:
writer.add_page(page)
writer.add_blank_page()
with open(file, mode='wb') as new_file:
writer.write(new_file)
合并
合并没啥好说的,用了PyPDF2来进行合并
def merger_files(self):
"""合并到一个pdf中"""
file_join = PyPDF2.PdfMerger()
for file in self.files:
file_join.append(file)
self.counts += 1
file_join.write("已合并.pdf")
print("pdf文件合并已完成---")
print(f"本次共合并{self.counts}个pdf文件^ ^")
参考链接
https://blog.csdn.net/zimkeavin/article/details/125872023
完整代码:https://github.com/h-kayotin/PDF_kayotin/tree/master/auto_print