0
点赞
收藏
分享

微信扫一扫

电子发票PDF文件批量处理 – (Python)

yellowone 2022-01-11 阅读 118
python

实现的功能:
发票代码&发票号码 重命名电子发票,

  • 电子发票二维码信息
    相关文章:发票上的二维码要素信息

  • 未识别的电子发票
    处理方法,详见文章:Python识别二维码获取电子发票基本信息

# -*- coding: utf-8 -*-

import io
import shutil
import sys
from pathlib import Path

import fitz
from PIL import Image
from PyPDF4.merger import PdfFileMerger
from pyzbar.pyzbar import decode


class E_fapiao:
    """E:electronic,电子发票"""

    def __init__(self):
        self.path = Path(sys.argv[0]).parent
        self.scr_pdf = []
        self.inputs = dict()
        self.to_do = []
        self.dst = set()
        self.dirs = ('未识别', '合并')

    def make_new_dir(self):
        """新建分类文件夹"""
        for _ in self.dirs:
            name = self.path.joinpath(_)
            if not name.exists():
                name.mkdir()

    def target_file(self):
        """PDF 文件列表"""
        suffix = '*.pdf'
        for entry in self.path.rglob(suffix):
            if entry.parent.stem in self.dirs:
                continue
            if entry.parent.stem.isdigit():
                continue
            else:
                self.scr_pdf.append(entry)

    def worker(self):
        while self.scr_pdf:
            kw = dict()
            # 获取文件
            pdf = self.scr_pdf.pop(0)
            # 打开文件
            doc = fitz.Document(pdf)  # Document 实例
            page = doc.load_page(0)  # Page 实例:第一页

            # Document 方法:页面的图像列表
            image_list = doc.get_page_images(0)
            if not image_list:
                # print(f'{doc.name}文件无图像列表')
                self.to_do.append(pdf)
                continue
            # Page 方法:页面的图像列表
            image_list = page.get_images()

            # 获取 xref
            # 多次的数据遍历得到规律:二维码图像为列表的第一个元素
            # 因区块链电子发票的二维码非列表第一个元素
            # 增加二维码属性判断条件
            for img in image_list:
                xref = img[0]
                pix = fitz.Pixmap(doc, xref)
                # 二维码为正方形:高与宽相等
                if pix.h == pix.w:
                    # 以 png 格式字节返回
                    png_byes = pix.tobytes()
                    # 载入图片数据
                    img = Image.open(io.BytesIO(png_byes))
                    # 解析二维码
                    result = ''
                    barcodes = decode(img)
                    for barcode in barcodes:
                        result += barcode.data.decode("utf-8")
                    # 存在获取到二维码信息,但是解析列表为空的情况
                    if not result:
                        # print(f'{doc.name}文件无法解析其二维码')
                        kw['data'] = 'empty'
                        break
                    else:
                        kw['data'] = result
                        break
            doc.close()  # 关闭文件
            self.inputs[str(pdf)] = kw

    def rename_file(self):
        for path, v in self.inputs.items():
            if v.get('data').startswith('http'):
                self.to_do.append(path)
                continue
            if v.get('data') == 'empty':
                self.to_do.append(path)
                continue
            if v.get('data'):
                data_list = v.get('data').split(',')
                # 发票代码
                code = data_list[2]
                # 发票号码
                number = data_list[3]
                # 日期
                date = data_list[5][:6]
                # 源文件路径
                src = Path(path)
                # 新建日期分类文件夹
                data_dir = src.parent.joinpath(date)
                self.dst.add(str(data_dir))  # 后续操作PDF的目录
                if not data_dir.exists():
                    data_dir.mkdir()
                # 新文件名称
                name = f'{code}&{number}.pdf'
                dst = data_dir.joinpath(name)
                # 移动文件
                shutil.move(src, dst)

        while self.to_do:
            item = Path(self.to_do.pop(0))
            files = item.name  # 文件名
            dirs = item.parent.stem  # 目录名
            fd = f'{dirs}_{files}'
            dst = self.path.joinpath('未识别', fd)
            shutil.move(item, dst)

    def add_bookmark(self):
        for i in self.dst:
            files = Path(i).glob('*.pdf')
            for file in files:
                name = file.name
                mark = name[13:].rstrip('.pdf')
                doc = fitz.Document(str(file))
                count = doc.page_count  # 页数
                toc = doc.get_toc()  # 大纲
                toc.append([1, mark, 1])
                doc.set_toc(toc)
                doc.save(str(file), incremental=True, pretty=True, encryption=0)

    def merger_pdf(self):
        key = 0
        for i in self.dst:
            key += 1
            pdf_merger = PdfFileMerger(strict=False)
            a = str(Path(i).parent.stem)
            for file in Path(i).glob('*.pdf'):
                pdf_merger.append(str(file))
            output = f'{self.path.joinpath("合并", a)}{key}.pdf'
            pdf_merger.write(output)
            pdf_merger.close()

    # ----------------------------
    # fitz-join 方法合并的PDF,无书签
    # ----------------------------
    """
    def merger_pdf(self):
        key = 0
        for i in self.dst:
            key += 1
            cmd = ['join', '-o']
            a = str(Path(i).parent.stem)
            target_pdf = f"{self.path.joinpath('合并', a)}{key}.pdf"
            cmd.append(target_pdf)
            for file in Path(i).glob('*.pdf'):
                pdf = f'{file},,N-1'
                cmd.append(pdf)
            saved_params = sys.argv[1:]
            sys.argv[1:] = cmd
            fitz_command()
            sys.argv[1:] = saved_params
    """

    def del_pages(self):
        for i in self.dst:
            files = Path(i).glob('*.pdf')
            for file in files:
                doc = fitz.Document(str(file))
                count = doc.page_count  # 页数
                if count > 1:
                    doc.delete_pages(range(1, count))
                doc.save(str(file), incremental=True, pretty=True, encryption=0)

    def run(self):
        self.make_new_dir()
        self.target_file()
        self.worker()
        self.rename_file()
        self.add_bookmark()
        self.merger_pdf()
        self.del_pages()


if __name__ == '__main__':
    app = E_fapiao()
    app.run()

如有可优化部分,欢迎指出改进。

举报

相关推荐

0 条评论