python --生成pdf/插入图片;reportlab/fitz

Python3.8

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

PyMuPDF==1.22.5
reportlab==4.2.2

直接插入图片

from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from PIL import Image

# 创建一个 PDF 文档
c = canvas.Canvas("1.pdf", pagesize=letter)
page_width, page_height = letter

# 图片路径
image_path = r"C:\Users\Yi\Desktop\88.jpg"
image = Image.open(image_path)
img_width, img_height = image.size
img_width, img_height = img_width * 0.1, img_height * 0.1
image.close()


# 设置图片的位置(从左上角计算)
x = 0  # x 坐标
y = 0  # y 坐标(从左上角计算)
c.drawImage(image_path, x, page_height - y - img_height, width=img_width, height=img_height)

# 保存 PDF 文档
c.save()

创建pdf与插入图片

import fitz  # PyMuPDF
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import SimpleDocTemplate, Paragraph, Table, TableStyle
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics

class CreateBaoXiuDan(object):
    '''生成保修单'''

    def __init__(self, pdf_name):
        pdfmetrics.registerFont(TTFont('SimHei', 'SimHei.ttf'))  # 注册中文字体
        self.pdf = SimpleDocTemplate(pdf_name, pagesize=letter)
        # self.pdf = SimpleDocTemplate(pdf_name, pagesize=letter, topMargin=0, bottomMargin=0)  页面顶部边距和底部边距

    def table1(self, data: list):
        '''顶部表格  ■  □'''
        data = [
            ['客户姓名', '', '客户电话', '', '客户电话', ''],
            ['安装地址', '', '', '', '购买场所', ''],
            ['供暖方式', '□集中 ■独立', '管路连接方式', '□串联 ■并联 □混联', '', ''],
            ['墙体情况', '□混凝土 ■砖墙\n□石膏板 □其他', '进回水管材质', '□铝塑 □镀锌 □PPR\n□PB  □PERT  □其他', '', ''],
            ['工作类型', '□安装    □换线   □改造', '', '', '', ''],
        ]

        style = TableStyle([          # 定义表格样式
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),  # 水平居中对齐  LETF左  RIGHT右 CENTER居中
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),  # 垂直居中对齐  BOTTOM下  TOP上 MIDDLE居中
            ('FONT', (0, 0), (-1, -1), 'SimHei'),  # 字体
            ('BACKGROUND', (0, 0), (-1, 0), '#FFFFFF'),  # 单元格背景色
            ('GRID', (0, 0), (-1, -1), 1, 'black'),  # 加网格线 0,0开始 (-1, -1) 表示到最后一个单元格(负值表示最后的单元格) 1线条宽度
            # ('GRID', (0, 0), (-1, -1), 0, colors.transparent)  不要边框
            ('SPAN', (1, 1), (3, 1)),  # 合并第二行的第2、3、4列 (起始列, 起始行), (结束列, 结束行))
            ('SPAN', (3, 2), (5, 2)),
            ('SPAN', (3, 3), (5, 3)),
            ('SPAN', (1, 4), (5, 4)),
        ])
        table = Table(data, colWidths=[80, 80, 80, 80, 80, 80])  # 创建表格  元素为列的数量
        table.setStyle(style)
        return table

    def table2(self, content: list):
        '''表格2'''
        data = [
            ['订货清单', ],
            ['房间', '型号', '组数']
        ]
        data.extend(content)
        style = TableStyle([  # 定义表格样式
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),  # 水平居中对齐
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),  # 垂直居中对齐
            ('FONT', (0, 0), (-1, -1), 'SimHei'),  # 字体
            ('BACKGROUND', (0, 0), (-1, 0), '#FFFFFF'),  # 单元格背景色
            ('GRID', (0, 0), (-1, -1), 1, 'black'),  # 加网格线 0,0开始 (-1, -1) 表示到最后一个单元格(负值表示最后的单元格) 1线条宽度
            ('SPAN', (0, 0), (2, 0)),
        ])
        table = Table(data, colWidths=[160, 160, 160])  # 创建表格  元素为列的数量
        table.setStyle(style)
        return table

    def table3(self, content: list):
        '''表格3'''
        data = [
            ['改管材料清单', ],
            ['项目', '单价', '数量', '合计']
        ]
        data.extend(content)
        dj, sl, hj = [],[],[]
        for i in content:
            dj.append(float(i[1]))
            sl.append(int(i[2]))
            hj.append(float(i[3]))

        data.append(['总计', f'{sum(dj):.2f}', sum(sl), f'{sum(hj):.2f}'])
        style = TableStyle([  # 定义表格样式
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),  # 水平居中对齐
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),  # 垂直居中对齐
            ('FONT', (0, 0), (-1, -1), 'SimHei'),  # 字体
            ('BACKGROUND', (0, 0), (-1, 0), '#FFFFFF'),  # 单元格背景色
            ('GRID', (0, 0), (-1, -1), 1, 'black'),  # 加网格线 0,0开始 (-1, -1) 表示到最后一个单元格(负值表示最后的单元格) 1线条宽度
            ('SPAN', (0, 0), (3, 0)),
        ])
        table = Table(data, colWidths=[120, 120, 120, 120])  # 创建表格  元素为列的数量
        table.setStyle(style)
        return table

    def table4(self):
        '''表格4'''
        data = [
            ['完工状态', ],
            ['压力测试', '□已做       □未做'],
            ['阀门状态', '□关闭       □开启'],
            ['放气螺栓', '□已拧紧      □未拧紧'],
            ['所有接口', '□无漏水      □漏水'],
            ['位置正确', '□是         □否'],
            ['清理现场', '□是         □否']
        ]

        style = TableStyle([  # 定义表格样式
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),  # 水平居中对齐
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),  # 垂直居中对齐
            ('FONT', (0, 0), (-1, -1), 'SimHei'),  # 字体
            ('BACKGROUND', (0, 0), (-1, 0), '#FFFFFF'),  # 单元格背景色
            ('GRID', (0, 0), (-1, -1), 1, 'black'),  # 加网格线 0,0开始 (-1, -1) 表示到最后一个单元格(负值表示最后的单元格) 1线条宽度
            ('SPAN', (0, 0), (1, 0)),
        ])
        table = Table(data, colWidths=[240, 240])  # 创建表格  元素为列的数量
        table.setStyle(style)
        return table

    def table5(self):
        '''表格5'''
        data = [
            ['\n用户意见\n', ''],
        ]

        style = TableStyle([  # 定义表格样式
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),  # 水平居中对齐
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),  # 垂直居中对齐
            ('FONT', (0, 0), (-1, -1), 'SimHei'),  # 字体
            ('BACKGROUND', (0, 0), (-1, 0), '#FFFFFF'),  # 单元格背景色
            ('GRID', (0, 0), (-1, -1), 1, 'black'),  # 加网格线 0,0开始 (-1, -1) 表示到最后一个单元格(负值表示最后的单元格) 1线条宽度
        ])
        table = Table(data, colWidths=[80, 400])  # 创建表格  元素为列的数量
        table.setStyle(style)
        return table


    def qianzi(self):
        '''签字区'''
        text_style = ParagraphStyle(
            name='TitleStyle',
            fontName='SimHei',
            fontSize=10,
            alignment=0,
            # spaceAfter=5,
            fontWeight='Bold'  # 设置加粗
        )
        nbsp = ' '
        text = [
            Paragraph('签字区: (签字前请阅读背后合同说明,未签字或未付安装费用本协议不生效)', text_style),
            Paragraph(f'改造网点:{nbsp * 35}安装网点:', text_style),
            Paragraph(f'改造时间:{nbsp * 35}安装网点24小时电话:', text_style),
            Paragraph(f'用户签字:{nbsp * 35}施工人员签字:', text_style),
        ]
        return text

    def insert_img(self, yonghu_img: str, shigongyuan_img: str, input_pdf: str, output_pdf: str):
        '''插入图片'''
        # fitz.Rect(123, 250, 200, 1003)  # 左上角x,y  和右下角的x,y   右下角必须大于左上角
        pdf_document = fitz.open(input_pdf)
        page = pdf_document[0]  # 选择要插入图片的页面(这里选择第一页)
        page.insert_image(fitz.Rect(142, 265, 219, 990), filename=shigongyuan_img)  # 施工员签字
        page.insert_image(fitz.Rect(402, 250, 479, 990), filename=yonghu_img)  # 用户签字
        page.insert_image(fitz.Rect(123, 560, 200, 640), filename=r'D:\code\nuantong\media\default\gz.png')  # 公章

        page_number = pdf_document.page_count  # 创建一个新的页面
        pdf_document.insert_page(page_number)  # 在末尾插入新页面
        new_page = pdf_document[page_number]  # 获取新插入的页面
        new_page.insert_image(fitz.Rect(0, 50, 580, 700), filename=r'C:\Users\Yi\Desktop\23.jpg')

        pdf_document.save(output_pdf)  # 保存修改后的 PDF 文件
        pdf_document.close()


    def save(self, title):
        '''保存'''
        title_style = ParagraphStyle(
            name='TitleStyle',
            fontName='SimHei',
            fontSize=16,
            alignment=1,  # 0 - 左对齐  1 - 中间对齐  2 - 右对齐
            spaceAfter=12,
            fontWeight='Bold'  # 设置加粗
        )
        title = Paragraph(title, title_style)
        elements = [title,
                    self.table1([]),
                    self.table2([('11', '22', '33'), ('we', 're', 'gf')]),
                    self.table3([('1', '1', '1', '1'), ('2', '2', '2', '2')]),
                    self.table4(),
                    self.table5()
                    ]
        elements.extend(self.qianzi())
        self.pdf.build(elements)



if __name__ == '__main__':
    a = CreateBaoXiuDan('1.pdf')
    a.save('散热器安装保修协议')
    a.insert_img(r'C:\Users\Yi\Desktop\88.png', r'D:\code\nuantong\backstage\1.pdf', r'D:\code\nuantong\backstage\2.pdf')

边框虚线效果

from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle

创建文档

doc = SimpleDocTemplate(“example.pdf”)
data = [[‘标题1’, ‘标题2’], [‘内容1’, ‘内容2’]]

定义表格样式

style = TableStyle([
    ('ALIGN', (0, 0), (-1, -1), 'RIGHT'),  # 水平右对齐
    ('VALIGN', (0, 0), (-1, -1), 'BOTTOM'), # 垂直底部对齐
    ('FONT', (0, 0), (-1, -1), 'SimHei'),   # 字体
    ('FONTSIZE', (0, 0), (-1, -1), 12), # 字体大小
    ('BACKGROUND', (0, 0), (-1, 0), colors.white),  # 单元格背景色
    ('GRID', (0, 0), (-1, -1), 1, colors.black),  # 实线边框
    ('LINEABOVE', (0, 0), (-1, 0), 1, colors.black),  # 上边框
    ('LINEBELOW', (0, 0), (-1, 0), 1, colors.black),  # 下边框
])

# 添加虚线效果
style.add('LINEBELOW', (0, 0), (-1, -1), 1, colors.black)  # 在这里更改为虚线

# 创建表格并应用样式
table = Table(data)
table.setStyle(style)

# 添加表格到文档并生成PDF
doc.build([table])

食材案例

import os, sys, django
import fitz
from django.db.models import Sum
from reportlab.lib.pagesizes import A4, letter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen import canvas


# 将django项目根目录加入环境变量
parent_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(parent_path)
# 引入django配置文件
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "shicaigongying.settings")
# 启动django
django.setup()
from backstage.utils import create_random_file_name
from backstage.models import Order, OrderInfo, Gongzhang, User
from shicaigongying.settings import MEDIA_ROOT, FontPath, SERVER_URL
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Image, Paragraph
import os


class Createfapiaopdf(object):
    '''生成pdf'''

    def __init__(self, order):
        self.cache_pdf = os.path.join(MEDIA_ROOT, 'default/.cache.pdf') # 临时缓存pdf
        self.pdf_name = create_random_file_name('.pdf') # 生成pdf的名称
        self.pdf_path = os.path.join(MEDIA_ROOT, self.pdf_name) # 保存pdf的路径
        self.pdf_url = f'{SERVER_URL}/media/{self.pdf_name}' # 访问pdf的url

        pdfmetrics.registerFont(TTFont('SimHei', FontPath))  # 注册中文字体
        self.pdf = SimpleDocTemplate(self.cache_pdf,
                                     pagesize=letter,
                                     leftMargin=8,  # 左边距 36pt ≈ 0.5英寸
                                     rightMargin=8,
                                     topMargin=36,
                                     bottomMargin=8)
        self.order = order
        self.user = User.objects.get(user_id=order.user_id)
        self.gongzhang = Gongzhang.objects.filter(zhuangtai=1).first()
        self.jieyu = Order.objects.filter(zhuangtai=4).aggregate(res=Sum('zongjin_e')).get('res')
        self.jieyu = f'{self.jieyu:.2f}' if self.jieyu else '0.00'

        self.tongji_table = None

    def create_biaotou(self):
        '''生成表头'''
        gongsiming_style = ParagraphStyle(
            name='TitleStyle',
            fontName='SimHei',
            fontSize=20,
            alignment=0,
            spaceAfter=11,
            fontWeight='Bold'  # 设置加粗
        )

        dierhang_style = ParagraphStyle(
            name='TitleStyle',
            fontName='SimHei',
            fontSize=13,
            alignment=0,
            spaceAfter=5,
            fontWeight='Bold'  # 设置加粗
        )

        fapiao_style = ParagraphStyle(
            name='TitleStyle',
            fontName='SimHei',
            fontSize=20,
            alignment=1,
            spaceAfter=11,
            fontWeight='Bold'  # 设置加粗
        )

        text_style = ParagraphStyle(
            name='TitleStyle',
            fontName='SimHei',
            fontSize=13,
            alignment=0,
            spaceAfter=5,
        )
        nbsp = ' '
        text = [
            Paragraph(f'{nbsp * 6}萬利龍有限公司', gongsiming_style),
            Paragraph(f'{nbsp * 9}Profit Dragon Inc. Limited', dierhang_style),
            Paragraph(f'{nbsp * 9}香港葵湧永業街14-20號華榮工業大廈4樓4A室', text_style),
            Paragraph(f'<u>{nbsp * 9}TEL:26665357 / 36151094 {nbsp * 41} FAX: 3003 3061</u>', text_style),
            Paragraph(f'發票INVOICE', fapiao_style),
        ]

        return text

    def kehuxinxi(self):
        '''客户信息'''
        # 定义样式
        left_style = ParagraphStyle(
            name='LeftStyle',
            fontName='SimHei',
            fontSize=12,
            alignment=0  # 左对齐
        )

        right_style = ParagraphStyle(
            name='RightStyle',
            fontName='SimHei',
            fontSize=12,
            alignment=2  # 右对齐
        )

        data = [
            [Paragraph(f'客戶名稱:{order.kehumingcheng}', left_style), Paragraph(f'到貨日期: {order.daohuoriqi}&nbsp;', right_style)],
            [Paragraph(f'客戶編號:{order.kehubianhao}', left_style), Paragraph(f'發票號碼: KL632B03001', right_style)]
        ]
        table = Table(data, colWidths=[300, 300])  # 创建表格  元素为列的数量
        return table

    def create_shangpin(self):
        '''添加商品信息'''
        style = TableStyle([  # 定义表格样式
            ('BACKGROUND', (0, 0), (-1, 0), '#D3D3D3'),# (起始列, 起始行) (结束列, 结束行)
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),  # 水平居中对齐
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),  # 垂直居中对齐
            ('FONT', (0, 0), (-1, -1), 'SimHei'),  # 字体
            ('GRID', (0, 0), (-1, -1), 1, 'black'),  # 加网格线 0,0开始 (-1, -1) 表示到最后一个单元格(负值表示最后的单元格) 1线条宽度
        ])
        data = [
            ['序號', '名稱', '規格', '單價HK$', '單位', '數量', '金額HK$'],
        ]
        queryset = OrderInfo.objects.filter(order_id=order.order_id)
        for index, value in enumerate(queryset, start=1):
            data.append([index,
                         value.shangpingmingcheng,
                         value.beizhu,
                         f'{value.danjia:.2f}',
                         value.danwei,
                         value.shuliang,
                         f'{value.jin_e:.2f}' if value.ctypes == 1 else f'-{value.jin_e:.2f}',
            ])
        table = Table(data, colWidths=[50, 70, 170, 70, 70, 70, 90])  # 创建表格  元素为列的数量
        table.setStyle(style)
        return table

    def tongji(self):
        '''统计信息'''
        style = TableStyle([  # 定义表格样式
            ('ALIGN', (4, 0), (4, -1), 'RIGHT'),  # 第5列所有行 水平居中
            ('VALIGN', (4, 0), (4, -1), 'MIDDLE'),  # 第5列所有行 垂直居中

            ('ALIGN', (5, 0), (5, -1), 'LEFT'),  # 第6列所有行 水平居中
            ('VALIGN', (5, 0), (5, -1), 'MIDDLE'),  # 第6列所有行 垂直居中

            ('ALIGN', (6, 0), (6, -1), 'RIGHT'),  # 第7列所有行 水平居中
            ('VALIGN', (6, 0), (6, -1), 'MIDDLE'),  # 第7列所有行 垂直居中

            ('FONT', (0, 0), (-1, -1), 'SimHei'),  # 字体

            ('LINEBELOW', (4, 2), (-1, 2), 1, colors.black), # 第三行加底边框 (起始列, 行索引), (结束列, 行索引), 线宽, 颜色)
        ])

        data = [
            ['', '', '', '', '總金額:', 'HKD', f'{self.order.zongjin_e:.2f}'], # {:,.2f} 带千分隔符
            ['', '', '', '', '折扣:', 'HKD', f'{self.user.zhekou:.2f}'],
            ['', '', '', '', '運輸費:', 'HKD', f'{self.user.yunfei:.2f}'],
            ['', '', '', '', '結餘:', 'HKD', self.jieyu],
        ]
        # if self.gongzhang:  # 直接给表格中添加图片 表格会随着图片的尺寸变宽高
        #     img = Image(os.path.join(MEDIA_ROOT, os.path.split(self.gongzhang.img)[1]))
        #     img.drawWidth = 38
        #     img.drawHeight = 38
        #     data[0][3] = img

        self.tongji_table = Table(data, colWidths=[50, 70, 170, 70, 70, 70, 90])  # 创建表格  元素为列的数量
        self.tongji_table.setStyle(style)
        return self.tongji_table


    def fukuanfangshi(self):
        '''付款方式'''
        text_style = ParagraphStyle(
            name='TitleStyle',
            fontName='SimHei',
            fontSize=13,
            alignment=0,
            spaceAfter=5,
            fontWeight='Bold'  # 设置加粗
        )
        nbsp = '&nbsp;'
        text = [
            Paragraph(f'《付款方式》', text_style),
            Paragraph(f'BANK ACCOUNT: 收支票或現金存入帳戶', text_style),
            Paragraph(f'BENEFICIARY NAME 戶名: PROFIT DRAGON INC LIMITED', text_style),
            Paragraph(f'A/C NO 賬號: 012-588-2-0235787', text_style),
            Paragraph(f'BANK NAME 銀行: BANK OF CHINA(HONG KONG)LIMITED 香港中國銀行', text_style),
            Paragraph(f'FPS 轉數快: 105408397', text_style),
        ]
        return text


    def insert_img(self):
        '''插入图片'''
        # fitz.Rect(123, 250, 200, 1003)  # 左上角x,y  和右下角的x,y   右下角必须大于左上角
        pdf_document = fitz.open(self.cache_pdf)
        first_page = pdf_document[0]  # 选择要插入图片的页面(这里选择第一页)
        first_page.insert_image(fitz.Rect(10, 39, 70, 120), filename=os.path.join(MEDIA_ROOT, 'default/logo.png'))

        for page_num in range(len(pdf_document)):  # 提取文字的实际坐标
            page = pdf_document[page_num]
            words = page.get_text('words')
            text = () # 提取总金额的总坐标
            for i, v in enumerate(words):
                if v[4] == '總' and words[i + 1][4] == '金' and words[i + 2][4] == '額':
                    text = v
                    print(f'公章提取坐标:【{text}】')
                    break

            x1, y1, x2, y2 = text[:4]
            x1 = x1 - 50
            x2 = x2 - 38
            y2 = y2 + 38
            page.insert_image(fitz.Rect(x1, y1, x2, y2),
                              filename=os.path.join(MEDIA_ROOT, os.path.split(self.gongzhang.img)[1]))
        pdf_document.save(self.pdf_path)  # 保存修改后的 PDF 文件
        pdf_document.close()
        return self.pdf_url




    def start(self):
        elements = []
        elements.extend(self.create_biaotou())
        elements.append(self.kehuxinxi())
        elements.append(self.create_shangpin())
        elements.append(self.tongji())
        elements.extend(self.fukuanfangshi())
        self.pdf.build(elements)


order = Order.objects.filter(order_id=2).first()
a = Createfapiaopdf(order)
a.start()
url = a.insert_img()
print(a.pdf_url)

在这里插入图片描述

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像风一样的男人@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值