用python压缩pdf文件

由于某些不可避免的原因,需要在某些网站上传一些.pdf文件,那些奇葩的网站对文件大小做了限制,所以需要对较大的.pdf文件进行压缩处理,而一般免费的阅读器都不能随意的压缩(要么需要你交很高的会员费),好在这些任务python都可以很好的处理。

本文编写了相关的python脚本,有需要的可以参考。

一些说明

本文的内容参考了其他博客(如此处),但对其中脚本进行了一些修改。

我使用的操作系统是MacOS,如果是其他系统,可能需要你自己动手进行一些修改。

安装PyMuPDF

首先我们安装PyMuPDF包,如下:

pip install pymupdf=1.20.2

上述命令指定了版本,是为了避免因为版本不同导致的问题。

压缩脚本

编写的脚本如下:

import os
import shutil
import fitz
from tqdm import tqdm


def pdf2pic(file_root, filename: str, out_name: str = None, zoom=50):
"""
将pdf逐页转化为png图片
:param file_root: 文件根目录
:param filename: 文件名,不带后缀
:param out_name: 输出文件名,默认为 filename+'_new',用以存储pdf转化生成的 'png' 文件
:param zoom: 缩小的百分比比例
"""
if '.pdf' in filename:
filename = filename.split('.pdf')[0]
file = f"{file_root}/{filename}.pdf".replace('//', '/')
if not os.path.exists(file):
raise FileExistsError(f"File {file} not exists")
out_name = out_name or f"{filename}_new"
out_file = f"{file_root}/{out_name}".replace('//', '/') # 文件夹用以存储pdf转的图片
doc_f = fitz.Document(file)
if os.path.exists(out_file):
shutil.rmtree(out_file)
os.mkdir(out_file)
tq_t = tqdm(range(doc_f.page_count))
for pg in tq_t:
tq_t.set_description(f"Pdf to png. Page {pg}...")
page = doc_f.load_page(pg)
zoom = int(zoom)
lurl = f"{out_file}/{pg}.png"
trans = fitz.Matrix(zoom / 100.0, zoom / 100.0)
pm = page.get_pixmap(matrix=trans, alpha=False)
pm.save(lurl)
doc_f.close()


def pic2pdf(file_root, pic_filename, pdf_name: str = None):
"""
将图片合并成pdf
:param file_root: 文件存储的根目录
:param pic_filename: 图片保存的目录名
:param pdf_name: 转出的pdf名称,若不指定则采用pic_filename加上后缀'.pdf'
"""
pic_path = f"{file_root}/{pic_filename}".replace('//', '/')
pdf_name = pdf_name or pic_filename
pdf_path = f"{file_root}/{pdf_name}.pdf".replace('//', '/')
if not os.path.exists(pic_path):
raise FileExistsError(f"pic_path {pic_path} not exists.")
if os.path.exists(pdf_path):
os.remove(pdf_path)
pics = os.listdir(pic_path)
pics = [file for file in pics if '.png' in file]
doc = fitz.Document()
tq_t = tqdm(range(len(pics)))
for pg in tq_t:
tq_t.set_description(f"Pic to pdf, processing {pg}...")
img = f'{pic_path}/{pg}.png'
imgdoc = fitz.Document(img)
pdfbytes = imgdoc.convert_to_pdf()
imgpdf = fitz.Document(stream=pdfbytes, filetype='pdf')
doc.insert_pdf(imgpdf)
doc.save(pdf_path)
doc.close()
shutil.rmtree(pic_path)


def pdf_zoom(file_path, out_name=None, zoom=50):
"""
将pdf缩小
:param file_path: 待转换pdf文件的绝对路径
:param out_name: 输出的pdf名称(不带后缀)
:param zoom: 缩小的百分比
"""
file_root, filename = os.path.split(file_path)
pdf2pic(file_root=file_root, filename=filename, out_name=out_name, zoom=zoom)
pic_filename = out_name or f"{filename.split('.pdf')[0]}_new"
pic2pdf(file_root=file_root, pic_filename=pic_filename, pdf_name=out_name)
print(f"Finished to reduce size of {filename}, the new file is {pic_filename}.pdf")


if __name__ == "__main__":
pdf_zoom(file_path='/path/to/file/xxx.pdf', zoom=55)

上述脚本中还用到了tqdm包,用来显示进度条;fitzpymupdf包下的内容。

执行的时候:

  • 将最后一行的file_path参数的赋值改成自己需要转换文件的路径(需要绝对路径)即可。

  • 缩小的百分比通过zoom参数来控制调节,其可以取值范围为\([0, 100]\)

  • 先生成一个临时的图片文件夹(需要转换的.pdf文件根目录下),在完成转化后、该图片文件夹会被自动删除。

最后输出的清晰度会降低。这是因为没有做任何优化,仅仅是通过降低清晰度来缩小文件大小的。