实现的功能:
以 发票代码&发票号码 重命名电子发票,
-
电子发票二维码信息
相关文章:发票上的二维码要素信息 -
未识别的电子发票
处理方法,详见文章: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()
如有可优化部分,欢迎指出改进。