功能描述
老大最近一直需要将一个好几兆的pdf文件压缩到2mb甚至1mb以内。通常的做法可能会是打印pdf文件,然后用打印机设置生产很小的pdf文件,或者就是截图保存等等。但都有点太傻瓜了,这时候还是python大法好,网上摘取了一部分别人的代码,稍作修改,就变成了一个能用的小工具。在此感谢各位大佬的资源。目前差不多能用,功能如下:
- 从PDF中提取图片
- 将图片进行质量和尺寸大小的压缩
- 生成新的PDF文件
源代码
1.1 安装必要的库
先安装库 fitz,再安装库pymupdf,地址:https://github.com/pymupdf/PyMuPDF/
pip install fitz
pip install pymupdf
1.2 源代码
第一个pdf2pic从pdf中提取jpg文件的部分引用了别人的代码
以下两行doc.引用的注意了,不然会报错
lenXREF = doc.xref_length()
text = doc.xref_object(i) # 定义对象字符串
另外加入了重新调整过大的照片尺寸,和保存照片的质量,这里有个变量comp_ratio
im = im.resize((1376, y_s), Image.ANTIALIAS)
im.save(pic_path_d, quality=comp_ratio)
import fitz
import re
import os
from PIL import Image
from tkinter import filedialog
def pdf2pic(path, pic_path, comp_ratio):
checkXO = r"/Type(?= */XObject)" # 使用正则表达式来查找图片
checkIM = r"/Subtype(?= */Image)"
doc = fitz.open(path) # 打开pdf文件
imgcount = 0 # 图片计数
lenXREF = doc.xref_length() # 获取对象数量长度
# 打印PDF的信息
print("文件名:{}, 页数: {}, 对象: {}".format(path, len(doc), lenXREF - 1))
# 遍历每一个对象
for i in range(1, lenXREF):
text = doc.xref_object(i) # 定义对象字符串
isXObject = re.search(checkXO, text) # 使用正则表达式查看是否是对象
isImage = re.search(checkIM, text) # 使用正则表达式查看是否是图片
if not isXObject or not isImage: # 如果不是对象也不是图片,则continue
continue
imgcount += 1
pix = fitz.Pixmap(doc, i) # 生成图像对象
new_name = "pic{}.jpg".format(imgcount) # 生成图片的名称
print(new_name)
if pix.n < 5: # 如果pix.n<5,可以直接存为PNG
pic_path_d = os.path.join(pic_path, new_name)
pix.writeImage(os.path.join(pic_path, new_name))
im = Image.open(pic_path_d)
x, y = im.size
if x > 1376:
y_s = int(y * 1376 / x)
im = im.resize((1376, y_s), Image.ANTIALIAS)
im.save(pic_path_d, quality=comp_ratio)
else: # 否则先转换CMYK
pix0 = fitz.Pixmap(fitz.csRGB, pix)
pix0.writeImage(os.path.join(pic_path, new_name))
pix0 = None
pix = None # 释放资源
print("提取了{}张图片".format(imgcount))
os.startfile(pic_path)
下面这个rea是用来将文件夹内的照片重新组合为pdf文件
def rea(path, pdf_name):
file_list = os.listdir(path)
pic_name = []
im_list = []
for x in file_list:
if "jpg" in x or 'png' in x or 'jpeg' in x:
pic_name.append(x)
pic_name.sort()
new_pic = []
for x in pic_name:
if "jpg" in x:
new_pic.append(x)
for x in pic_name:
if "png" in x:
new_pic.append(x)
print("hec", new_pic)
im1 = Image.open(os.path.join(path, new_pic[0]))
new_pic.pop(0)
for i in new_pic:
img = Image.open(os.path.join(path, i))
# im_list.append(Image.open(i))
if img.mode == "RGBA":
img = img.convert('RGB')
im_list.append(img)
else:
im_list.append(img)
im1.save(pdf_name, "PDF", resolution=100.0, save_all=True, append_images=im_list)
print("输出文件名称:", pdf_name)
def pdf_out():
print('功能完善中')
主程序中随意加了一些判断,如压缩等级1、2、3等。
if __name__ == '__main__':
print("Hello world!请先输入压缩等级1~3,然后在弹出的对话框中选择需要压缩的文件")
comp_level = input("压缩等级(1=高画质,2=中画质,3=低画质):(输入数字并按回车键)")
ratio = 10
if comp_level == "1":
ratio = 20
elif comp_level == "2":
ratio = 10
elif comp_level == "3":
ratio = 5
'''打开选择文件夹对话框'''
filepath = filedialog.askopenfilename() # 获得选择好的文件
print('选择的PDF地址:', filepath)
if os.path.exists("./pdf_output"):
pass
else:
os.mkdir("./pdf_output")
pic_path = str(os.getcwd()) + "\pdf_output"
print('提取图片的输出地址:', pic_path )
pdf2pic(filepath, pic_path, comp_ratio=ratio)
pdf_name = 'Compressed.pdf'
if ".pdf" in pdf_name:
rea(pic_path, pdf_name=pdf_name)
else:
rea(pic_path, pdf_name="{}.pdf".format(pdf_name))
print("压缩完成,请关闭窗口。若压缩等级不合适,请先删除图片和文件并重新打开程序。")