赞
踩
在办公场景中,不可避免地会遇到大型PDF文件的情况。其一是某些PDF文件由图片扫描而来,本身占据空间确实较大;其二是这种PDF文件一般页码较多,且大概率为书籍扫描而来。这就导致在某些文件占用空间大小有限制或者传输速率有限的情况下对这类大型PDF文件操作会比较困难,故而引出了PDF压缩的需求。
先放源码,源码如下,若有调参需要自行根据注释修改即可;
若不想使用脚本,而想通过python程序执行,自行在程序内写入路径即可:
#!/usr/bin/python3 # Author: Zeed # 11/09/2023 import os import sys import fitz import argparse from io import BytesIO from PIL import Image from tqdm import tqdm def pdf_compress(input_path, output_folder_path, _dpi=200, _type="png", method=0): """ 本方法适用于纯图片型(包含文字型图片)的PDF文档压缩,可复制型的文字类的PDF文档不建议使用本方法 :param input_path: 文件名全路径 :param _dpi: 转化后图片的像素(范围72-600),默认150,想要清晰点,可以设置成高一点,这个参数直接影响PDF文件大小 测试: 纯图片PDF文件(即单个页面就是一个图片,内容不可复制) 300dpi,压缩率约为30-50%,即原来大小的30-50%,基本无损,看不出来压缩后导致的分辨率差异 200dpi,压缩率约为20-30%,轻微有损 150dpi,压缩率约为5-10%,有损,但是基本不影响图片形文字的阅读 :param _type: 保存格式,默认为png,其他:JPEG, PNM, PGM, PPM, PBM, PAM, PSD, PS :param method: int,图像压缩方法,只支持下面3个选项,默认值是0 0 : `MEDIANCUT` (median cut) 1 : `MAXCOVERAGE` (maximum coverage) 2 : `FASTOCTREE` (fast octree) :return: """ merges = [] _file = None with fitz.open(input_path) as doc: for i, page in tqdm( enumerate(doc.pages(), start=0), desc=f"{input_path} 压缩处理中,请耐心等待" ): img = page.get_pixmap(dpi=_dpi) # 将PDF页面转化为图片 img_bytes = img.pil_tobytes(format=_type) # 将图片转为为bytes对象 image = Image.open(BytesIO(img_bytes)) # 将bytes对象转为PIL格式的图片对象 if i == 0: _file = image # 取第一张图片用于创建PDF文档的首页 pix: Image.Image = image.quantize(colors=256, method=method).convert( "RGB" ) # 单张图片压缩处理 merges.append(pix) # 组装pdf pdf_name = os.path.split(input_path)[-1] # 提取单纯的文件名称,带pdf后缀,但是没有绝对路径 output_path = os.path.join(output_folder_path, pdf_name.rsplit(".")[-2]) _file.save( f"{output_path}_by_{_dpi}dpi.pdf", "pdf", # 用PIL自带的功能保存为PDF格式文件 save_all=True, append_images=merges[1:], ) print(f"{input_path} was compressed into {output_path}_by_{_dpi}dpi.pdf!") def pdf_folder_compress( input_path, output_folder_path, _dpi=200, _type="png", method=0 ): item_paths = os.listdir(input_path) # 提取所有文件信息 item_paths = [ item_path for item_path in item_paths if item_path.split(".")[-1].lower() == "pdf" ] # 找出所有的PDF文件 for item_path in item_paths: # 对每个pdf操作 item_input_path = os.path.join(input_path, item_path) # 这里一定要注意路径的拼接 pdf_compress(item_input_path, output_folder_path, _dpi=200) def main(): """ 根据传入的是单pdf文件还是pdf文件夹选用不同的压缩方法 """ output_folder_path = "output_pdf" # 判断输出文件夹是否存在 if os.path.exists(output_folder_path): # 输出文件夹存在 print("output_pdf folder already existed.") else: # 输出文件夹不存在 os.mkdir(output_folder_path) print("output_pdf folder has been created.") # 创建接受参数的argparse类 parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( "-f", "--file", help="Relative or absolute path of the input PDF file name" ) parser.add_argument("-r", "--route", help="Relative or absolute path PDF folder") args = parser.parse_args() if not args.file and not args.route: print("错误:请输入pdf的文件名或pdf所在文件夹") sys.exit(1) if args.file and args.route: print("错误:要么压缩单一文件,要么批量压缩") if args.file: pdf_compress(args.file, output_folder_path) if args.route: pdf_folder_compress(args.route, output_folder_path) if __name__ == "__main__": main()
先在环境下装好两个库
pip install PyMuPDF
pip install tqdm
不懂虚拟环境的同学还是直接根据源程序修改吧,我放在最后了。会用普通终端的也可以试一试命令行调用
# 进入虚拟环境
conda activate {env}
# 进入脚本所在文件夹,或者直接绝对路径调用
cd C:\{script}
# 压缩单个PDF
python pdf_compress.py -f {file_name}
# 批量压缩PDF
python pdf_compress.py -r {folder_path}
def pdf_compress(input_path, output_folder_path, **_dpi=200**, _type="png", method=0):
代码如下↓
#!/usr/bin/python3 # Author: Zeed # 11/09/2023 import os import sys import fitz import argparse from io import BytesIO from PIL import Image from tqdm import tqdm def pdf_compress(input_path, output_folder_path, _dpi=200, _type="png", method=0): ''' 本方法适用于纯图片型(包含文字型图片)的PDF文档压缩,可复制型的文字类的PDF文档不建议使用本方法 :param input_path: 文件名全路径 :param _dpi: 转化后图片的像素(范围72-600),默认150,想要清晰点,可以设置成高一点,这个参数直接影响PDF文件大小 测试: 纯图片PDF文件(即单个页面就是一个图片,内容不可复制) 300dpi,压缩率约为30-50%,即原来大小的30-50%,基本无损,看不出来压缩后导致的分辨率差异 200dpi,压缩率约为20-30%,轻微有损 150dpi,压缩率约为5-10%,有损,但是基本不影响图片形文字的阅读 :param _type: 保存格式,默认为png,其他:JPEG, PNM, PGM, PPM, PBM, PAM, PSD, PS :param method: int,图像压缩方法,只支持下面3个选项,默认值是0 0 : `MEDIANCUT` (median cut) 1 : `MAXCOVERAGE` (maximum coverage) 2 : `FASTOCTREE` (fast octree) :return: ''' merges = [] _file = None with fitz.open(input_path) as doc: for i, page in tqdm(enumerate(doc.pages(), start=0) , desc=f"{input_path} 压缩处理中,请耐心等待"): img = page.get_pixmap(dpi=_dpi) # 将PDF页面转化为图片 img_bytes = img.pil_tobytes(format=_type) # 将图片转为为bytes对象 image = Image.open(BytesIO(img_bytes)) # 将bytes对象转为PIL格式的图片对象 if i == 0: _file = image # 取第一张图片用于创建PDF文档的首页 pix: Image.Image = image.quantize(colors=256, method=method).convert('RGB') # 单张图片压缩处理 merges.append(pix) # 组装pdf pdf_name = os.path.split(input_path)[-1] # 提取单纯的文件名称,带pdf后缀,但是没有绝对路径 output_path = os.path.join(output_folder_path,pdf_name.rsplit('.')[-2]) _file.save(f"{output_path}_by_{_dpi}dpi.pdf", "pdf", # 用PIL自带的功能保存为PDF格式文件 save_all=True, append_images=merges[1:]) print(f"{input_path} was compressed into {output_path}_by_{_dpi}dpi.pdf!") def pdf_folder_compress(input_path, output_folder_path, _dpi=200, _type="png", method=0): item_paths = os.listdir(input_path) # 提取所有文件信息 item_paths = [item_path for item_path in item_paths if item_path.split('.')[-1].lower() == 'pdf'] # 找出所有的PDF文件 for item_path in item_paths: # 对每个pdf操作 item_input_path = os.path.join(input_path,item_path) # 这里一定要注意路径的拼接 pdf_compress(item_input_path, output_folder_path, _dpi=200) def main(): """ 根据传入的是单pdf文件还是pdf文件夹选用不同的压缩方法 """ input_path = 'input_path' # 改这里 output_folder_path = 'output_pdf' # 判断输出文件夹是否存在 if os.path.exists(output_folder_path): # 输出文件夹存在 print("output_pdf folder already existed.") else: # 输出文件夹不存在,创建一个文件夹 os.mkdir(output_folder_path) print("output_pdf folder has been created.") if input_path.rsplit('.')[-1]== 'pdf': # 判断是否是单个pdf文件 pdf_compress(input_path, output_folder_path) else: pdf_folder_compress(input_path, output_folder_path) if __name__ == "__main__": main()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。