Python工具箱系列(四十六)

发布时间 2023-11-24 10:50:34作者: 西安衍舆航天
PDF(Portable Document Format)是一种便携文档格式,它基于PostScripty这种脚本语言。
​​

PDF文档操作

PDF(Portable Document Format)是一种便携文档格式,它基于PostScripty这种脚本语言,它是第一个独立于设备的页面描述语言。pdf格式是与平台无关,独立于底层操作系统和渲染引擎。由于PDF文档遵循标准格式,相关生态建设完善,应用广泛,非常值得花费时间讨论使用python进行操作。


 

python中能够操作pdf格式文档的库有:
 

◆PyMuPDF

◆pikepdf

◆pdfplumber

◆ReportLab

◆borb

主攻方向稍有不同,大致分类是:
 

◆pikepdf专注对已经存在的PDF的操作,例如分割、合并、旋转等

◆pdfplumber专注PDF内容提取,例如文本(位置、字体及颜色等)和形状(矩形、直线、曲线),此外还能够解析表格

◆ReportLab专注PDF页面内容(文本、图、表等)的创建

◆PyMuPDF和borb同时支持读、写及PDF页面操作,功能最为全面

◆borb是较新的库,功能也较为完善,但经过笔者试用,对于中文的支持不好

PyMuPDF的基本功能全面,非常适应用于批处理PDF。使用python来操作PDF/WORD/EXCEL/PPT等类文档,应该有如下的期望:

◆进行创作是非常不称手的。为何不用Office提供的工具软件进行编辑创作呢。因此,不能够指望这类库以及python提供非常全面完善的能力

◆能够在原来文档的基础上,进行批量化的一些操作。例如,可以基于某个模板,批量得制作邀请函,只要简单得修改姓名即可。或者根据数据库中的内容,在模板的基础上,定期形成周报、月报类固定格式的文档

◆能够在原来文档的基础上,增加、删除以及调整图片、文字与页面布局

所以,下述代码均从已经存在的内容入手进行演示:
 

01.

图片处理

timport os
import fitz

print(fitz.__doc__)

def export2png(filename,pageno):
    """
    将pdf文件指定页(从0开始算)导出为PNG图片文件

    Args:
        filename (string): PDF文件名
        pageno (int): 页号(从0开始)
    """
    with fitz.open(filename) as doc:
        page = doc[pageno]
        pix = page.get_pixmap()  
        pix.save("page-%i.png" % pageno)

def export2svg(filename,pageno):
    """
    将pdf文件指定页(从0开始算)导出为SVG矢量图

    Args:
        filename (string): PDF文件名
        pageno (int): 页号(从0开始)
    """
    with fitz.open(filename) as doc:
        page = doc[pageno]
        svg = page.get_svg_image(matrix=fitz.Identity)
        output = open("page-%i.svg" % pageno,'w')
        output.write(svg)
        output.close()

def figs2pdf(filename,startdir):
    """
    将指定目录下的所有图片文件合并成为PDF文件

    Args:
        filename (string): 未来要生成的PDF文件名称
        startdir (string): 图片所在目录
    """
    doc = fitz.open()
    # 列出目录下所有文件
    imglist = os.listdir(startdir) 
    imglist.sort() 
    for _, f in enumerate(imglist):

        # 遍历并且打开每个图片文件
        img = fitz.open(os.path.join(startdir, f))

        # 计算图片大小
        rect = img[0].rect
        pdfbytes = img.convert_to_pdf()
        img.close()
        
        # 将图片文件贴合到PDF页上
        imgPDF = fitz.open("pdf", pdfbytes) 
        page = doc.new_page(width = rect.width,height = rect.height) 
        page.show_pdf_page(rect, imgPDF, 0)

    # 保存到目标PDF中
    doc.save(filename) 
    
def convert(inputfile,outputfile):
    """
    对图片格式进行转换

    Args:
        inputfile (string): 源图片
        outputfile (string): 目标图片
    """
    pix = fitz.Pixmap(inputfile)
    pix.save(outputfile)       

    
pdfname = r'd:\test\sed.pdf'

figs2pdf(r'd:\test\demo.pdf',r'D:\test\all\figures')

export2png(pdfname,10)
export2svg(pdfname,10)

convert(r"D:\test\all\serial1\000000.gif",r'd:\test\0.psd')
convert(r"D:\test\all\serial1\000000.jpg",r'd:\est\1.png')

上述代码演示了合并图片与分解PDF到图片,转换图片格式等功能。

•其中figs2pdf函数完成了从指定目录的多个图片合成一个PDF文件的常用功能。相对复杂一些。

•pymupdf也提供了逆向生成图片的方法。

•export2png将指定PDF文件的指定页转换成为PNG文件,效果比截图好

•export2svg将指定PDF文件的指定页转换成为svg文件,而SVG文件是矢量图、能够编辑,这个功能更是强大

•convert函数能够将多种图片格式进行转换。代码演示了转换成为psd与png格式。当然这些功能是pymupdf赠送的功能,不能够与pillow等专业库相比

下面的代码演示如何从pdf中提取文字,以及获得PDF文档中的各类链接。

import fitz


def showmeta(filename):
    """
    显示PDF的基本信息

    Args:
        filename (string): 要打开的PDF文件名称
    """
    print(fitz.__doc__)
    doc = fitz.open(filename)
    print(doc.page_count)
    print(doc.metadata)


def showlinks(filename):
    """
    显示PDF中的所有超链

    Args:
        filename (string): 要打开的PDF文件名称
    """
    with fitz.open(filename) as doc:
        for page in doc:
            for link in page.links():
                # 显示一个列表
                if link["kind"] == fitz.LINK_GOTO:
                    print("Jump to page", link["page"] + 1)
                elif link["kind"] in (fitz.LINK_GOTOR, fitz.LINK_LAUNCH):
                    print("Open or launch file", link["file"])
                elif link["kind"] == fitz.LINK_URI:
                    print("Open URI", link["uri"])


def showtext(filename, pageno, option='text'):
    """
    显示pdf中的文字

    Args:
        filename (string): 要打开的PDF文件名称
        pageno (int): 页号
        option (选项): 可能是'text','block'等

    Returns:
        list: 所有的文字列表
    """
    with fitz.open(filename) as doc:
        page = doc[pageno]
        return page.get_text(option)


def searchtext(filename, pageno, searchstr):
    """
    在pdf中搜寻文字

    Args:
        filename (string): 要打开的PDF文件名称
        pageno (int): 页号
        searchstr (string): 要搜索的内容

    Returns:
        list: 匹配文字所在的区域列表。
    """
    with fitz.open(filename) as doc:
        page = doc[pageno]
        areas = page.search_for(searchstr)
        return areas


pdfname = r'd:\test\阿里技术地图.pdf'

print(showtext(pdfname, 9))
areas = searchtext(pdfname, 1, '数据挖掘')
for rect in areas:
    print(rect)
showlinks(pdfname)

以上代码中:

•showtext需要传递相关的选项。pymupdf可以提取多种类型的文字。具体可以参考

•searchtext函数的返回与通常的预期不同,返回的是多个区域所形成的列表。主要用途是为了相关的工具软件显示用,例如可以高亮等。如果用这个函数只是期待返回具体的文字内容的话,不妨直接获得文字,然后再使用字符串的搜索功能。

•showlinks函数直接获得了所有页上所有的链接。大部分链接是内部的跳转,也有使用编辑软件加入的外部超链。文字内的包含的URL使用pymupdf是找不到的,必须自己写函数对字符串进行语议分析才能够获得,这个也是pymupdf不完善的地方。

除了上述对PDF本身的操作外,还有从PDF到DOCX文档的双向转换需要。由DOCX文档转PDF文档非常容易,直接在WORD中选择打印或者导出即可,没有什么可以讨论的。但是反过来稍微困难些,可以考虑以下方法:

•直接用WORD打开PDF,然后另存为DOCX文档。

•使用pdf2docx转化

•使用python-office转化

pdf2docx是一个轻量级的第三方库,对中文的支持不错,本身是基于pymupdf库发展起来的,它的安装过程如下。

pip install pdf2docx
# upgrade
pip install --upgrade pdf2docx

《阿里的技术地图》这个PDF可以从网上下载到,内容比较丰富,值得一看。如果想要转换成为WORD文档,示例代码如下。

from pdf2docx import Converter

inputfilename = r'd:\test\阿里技术地图.pdf'
outputfilename = r'd:\test\demo.docx'

cv = Converter(inputfilename)
# 将PDF全部内容转换成为DOCX
cv.convert(outputfilename)
cv.close()

cv = Converter(inputfilename)
# 将PDF部分页转换为DOCX
cv.convert(outputfilename,start=3,end=5)
cv.close()

cv = Converter(inputfilename)
# 将PDF个别页转换为DOCX
cv.convert(outputfilename,pages=[10,16,8])
cv.close()

这个库转换速度不是太快,还提供了多进程的功能,但实际使用时会有BUG出现,不建议使用。