Python调用WPS进行文档转换PDF及PDF转图片

文档转PDF

这里是利用WPS进行转换,要先安装WPS。

安装依赖

1
pip install pypiwin32

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/python
# -*- coding: UTF-8 -*-

import os
import win32com.client


def ConvertByWps(sourceFile, targetFile):
if not os.path.exists(sourceFile):
print(sourceFile + "不存在,无法继续!")
return False
typemap = {
'doc': 'word',
'docx': 'word',
'ppt': 'ppt',
'pptx': 'ppt',
'xls': 'excel',
'xlsx': 'excel',
}
name_arr = sourceFile.split(".")
suffix = name_arr[len(name_arr) - 1]
wpstype = typemap.get(suffix)

if (wpstype is None):
return False

os.system('taskkill /im wps.exe')
# 如果文件存在就删除
if os.path.exists(targetFile):
os.remove(targetFile)
if wpstype == 'word':
ConvertDocToPdf(sourceFile, targetFile)
elif wpstype == 'ppt':
ConvertPptToPdf(sourceFile, targetFile)
elif wpstype == 'excel':
ConvertXlsToPdf(sourceFile, targetFile)
if os.path.exists(targetFile):
return True
else:
return False


# 转换 Word文件档到pdf
def ConvertDocToPdf(src, dst):
wps = win32com.client.Dispatch("Kwps.Application")
wps.Visible = False
doc = wps.Documents.Open(src)
doc.ExportAsFixedFormat(dst, 17)
doc.Close()
wps.Quit()


# 转换 PPT文件档到pdf
def ConvertPptToPdf(src, dst):
wps = win32com.client.Dispatch("Kwpp.Application")
wps.Visible = False
ppt = wps.Presentations.Open(src)
ppt.SaveAs(dst, 32)
ppt.Close()
wps.Quit()


# 转换 XLS文件档到pdf
def ConvertXlsToPdf(src, dst):
wps = win32com.client.Dispatch("Ket.Application")
excel = wps.Workbooks.Open(src)
excel.ExportAsFixedFormat(0, dst)
excel.Close()
wps.Quit()


if __name__ == '__main__':
# 当前目录
d = os.path.dirname(__file__)
abspath = os.path.abspath(d)

# 测试用例
src = abspath + r"/Doc/test.docx"
dst = abspath + r"/Doc/test.pdf"
r = ConvertByWps(src, dst)
print(r)

PDF转图片

方式1

fitz

1
2
pip install fitz
pip install PyMuPDF

顺序转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import fitz
import os
import time


# 将PDF转化为图片
# pdfPath pdf文件的路径
# imgPath 图像要保存的文件夹
# zoom_x x方向的缩放系数
# zoom_y y方向的缩放系数
# rotation_angle 旋转角度
def pdf_image(pdfPath, imgPath, zoom_x, zoom_y, rotation_angle):
path_arr = pdfPath.split(os.sep)
path_arr.reverse()
filename = ""
if (len(path_arr) > 0):
filename = path_arr[0].split(".")[0]
start_time = time.time()
pdf = fitz.open(pdfPath)
cpu_time = 0
io_time = 0
# 逐页读取PDF
filename_arr = []
for pg in range(0, pdf.pageCount):
time0 = time.time()
page = pdf[pg]
# 设置缩放和旋转系数
trans = fitz.Matrix(zoom_x, zoom_y).prerotate(rotation_angle)
pm = page.get_pixmap(matrix=trans, alpha=False)
# 开始写图像
filename_all = f'{imgPath}{os.sep}{filename}_{str(pg)}.png'
time1 = time.time()
cpu_time += time1 - time0
pm.save(filename_all)
time2 = time.time()
io_time += time2 - time1
filename_arr.append(filename_all)
pdf.close()
end_time = time.time()
print(f"时间差:{end_time - start_time}")
print(f"cpu_time:{cpu_time}")
print(f"io_time:{io_time}")
print(len(filename_arr))


pdf_image(
r"D:\Tools\DocTest\145页.pdf",
r"D:\Tools\DocTest\pic",
2,
2,
0
)

结果

1
2
3
4
时间差:16.93852996826172
cpu_time:7.023632049560547
io_time:9.910914421081543
145

多线程

方式1我切换为多线程依旧作用不大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import fitz
import os
import time
from concurrent import futures
import threading


# 将PDF转化为图片
# pdfPath pdf文件的路径
# imgPath 图像要保存的文件夹
# zoom_x x方向的缩放系数
# zoom_y y方向的缩放系数
# rotation_angle 旋转角度
def pdf_image(pdfPath, imgPath, zoom_x, zoom_y, rotation_angle):
path_arr = pdfPath.split(os.sep)
path_arr.reverse()
filename = ""
if(len(path_arr) > 0):
filename = path_arr[0].split(".")[0]
start_time = time.perf_counter()
pdf = fitz.open(pdfPath)

# 逐页读取PDF
filename_arr = []
pages = []

def savePage(pageobj):
#print(threading.current_thread().name)
page = pageobj["page"]
pg = pageobj["pg"]
# 设置缩放和旋转系数
trans = fitz.Matrix(zoom_x, zoom_y).prerotate(rotation_angle)
pm = page.get_pixmap(matrix=trans, alpha=False)
# 开始写图像
filename_all = f'{imgPath}{os.sep}{filename}_{str(pg)}.png'
pm.save(filename_all)
filename_arr.append(filename_all)

for pg in range(0, pdf.pageCount):
page = pdf[pg]
pages.append({
"page": page,
"pg": pg
})

print(len(pages))

with futures.ThreadPoolExecutor(max_workers=8) as executor:
for future in executor.map(savePage, pages):
pass
pdf.close()
end_time = time.perf_counter()
print(f"时间差:{end_time-start_time}")
print(len(filename_arr))


pdf_image(
r"D:\Tools\DocTest\145页.pdf",
r"D:\Tools\DocTest\pic",
2,
2,
0
)

转换时间没有减少反而增多了,用时17s。

原因在于Python的多线程是伪多线程,同时只能有一个线程运行。

多进程

方式1改为多进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import fitz
import os
import time
from concurrent import futures


def savePage(pageobj):
pdfPath = pageobj["pdfPath"]
pg = pageobj["pg"]
filename_all = pageobj["filename_all"]
zoom_x = pageobj["zoom_x"]
zoom_y = pageobj["zoom_y"]
rotation_angle = pageobj["rotation_angle"]

pdf = fitz.open(pdfPath)
page = pdf[pg]
# 设置缩放和旋转系数
trans = fitz.Matrix(zoom_x, zoom_y).prerotate(rotation_angle)
pm = page.get_pixmap(matrix=trans, alpha=False)
# 开始写图像
pm.save(filename_all)


# 将PDF转化为图片
# pdfPath pdf文件的路径
# imgPath 图像要保存的文件夹
# zoom_x x方向的缩放系数
# zoom_y y方向的缩放系数
# rotation_angle 旋转角度
def pdf_image(pdfPath, imgPath, zoom_x, zoom_y, rotation_angle):
path_arr = pdfPath.split(os.sep)
path_arr.reverse()
filename = ""
if (len(path_arr) > 0):
filename = path_arr[0].split(".")[0]
start_time = time.perf_counter()
pdf = fitz.open(pdfPath)
path_list = []
# 逐页读取PDF
page_model_list = []
for pg in range(0, pdf.pageCount):
filename_all = f'{imgPath}{os.sep}{filename}_{str(pg)}.png'
page_model_list.append({
"pdfPath": pdfPath,
"pg": pg,
"filename_all": filename_all,
"zoom_x": zoom_x,
"zoom_y": zoom_y,
"rotation_angle": rotation_angle
})
path_list.append(filename_all)

with futures.ProcessPoolExecutor(max_workers=8) as executor:
for future in executor.map(savePage, page_model_list):
pass
pdf.close()
end_time = time.perf_counter()
print(f"时间差:{end_time - start_time}")
print(path_list)


if __name__ == "__main__":
pdf_image(
r"D:\Tools\DocTest\145页.pdf",
r"D:\Tools\DocTest\pic",
2,
2,
0
)

这样转换时间大幅缩短,用时不到4s。

方式2

安装poppler

https://blog.alivate.com.au/poppler-windows/

添加bin对应目录到环境变量

注意

如果调用方法传入poppler_path参数,则不用设置环境变量。

https://github.com/Belval/pdf2image

安装依赖

1
pip install pdf2image

转换代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from pdf2image import convert_from_path, convert_from_bytes
from pdf2image.exceptions import (
PDFInfoNotInstalledError,
PDFPageCountError,
PDFSyntaxError
)
import time

try:
start_time = time.perf_counter()
images = convert_from_path(
r"D:\Tools\DocTest\145页.pdf",
output_folder=r"D:\Tools\DocTest\pic",
poppler_path=r"D:\Tools\poppler-0.67.0\bin",
size=(1024, None),
thread_count=4,
timeout=60,
fmt='jpeg'
)
end_time = time.perf_counter()
print(f"时间差:{end_time-start_time}")
print(images[0])
except PDFInfoNotInstalledError:
print("未安装poppler")
except PDFPageCountError:
print("页面异常")
except PDFSyntaxError:
print("PDF页面异常")
except:
print("其它异常")

转换时间对比

PDF页数 是否带水印 方式1(s) 方式2(s) 方式2-4线程(s)
1 0.0596313 0.1325932 0.1315048
2 0.2342085 0.2125386 0.1481465
145 23.7634431 16.4102064 5.3497324
448 否/纯文字 20.5313859 30.8443124 8.4962063

结论

  • 转换速度方式2在页面少的时候时间稍长,但是一旦页码多的时候方式2优势就比较明显。
  • 两种都能正常转换带水印的文档。
  • 如果文档都是纯文字GBK的时候,方式1转换正常,而方式2乱码。