fpdf简介

fpdf

fpdfgithub地址 fpdf 是从 PHP 的 FPDF 移植而来的,与其他 PDF 库相比,fpdf 简单、小巧、用途广泛,功能先进,易于学习、扩展和维护。

特征

  • Python 2.5 到 3.4 支持
  • Unicode (UTF-8) TrueType 字体子集嵌入
  • 条码 I2of5 和 code39,二维码即将推出…
  • PNG、GIF 和 JPG 支持(包括透明度和 Alpha 通道)
  • 带有视觉设计师和基本 html2pdf 的模板
  • 异常支持、其他小修复、改进和 PEP8 代码清

Tips: 暂时未整理 fpdf 写入中文操作

安装

pip install fpdf

pypdf2

pypdf2github地址 , 能够拆分、合并、裁剪和转换 PDF 文件的页面。它还可以为 PDF 文件添加自定义数据、查看选项和密码。PyPDF2 也可以从 PDF 中检索文本和元数据。

安装

pip install PyPDF2

使用

fpdf

纯文字

# 最简单引用
from fpdf import FPDF

def hello_world():
    pdf = FPDF(orientation="P", unit="mm", format="A4")  # 竖版,A4,mm单位
    pdf.add_page()
    pdf.set_font(family="Arial", style="B", size=16)
    pdf.cell(w=40, h=10, txt="hello world pdf")
    pdf.text(x=60, y=140, txt="Some text.")
    pdf.output(name="hello_world_pdf.pdf", dest="F")

图片,页眉页脚

# 添加图片,页眉,页脚等

# 继承FPDF 重写 header 页眉,footer页脚方法
class PDF(FPDF):
    def header(self):
        # Logo
        self.image(name='img.png', x=10, y=10, w=5)
        self.set_draw_color(0, 80, 180)
        self.set_fill_color(230, 230, 0)
        self.set_text_color(220, 50, 50)
        # 设置页眉字体
        self.set_font('Arial', '', 12)
        # 设置页眉前空字符
        # self.cell(100)
        # 页眉标题文字
        self.cell(0, 10, 'Title', 0, 0, 'C')  # 居中
        # self.cell(30, 10, 'Title', 1, 0, 'L')  # 左对齐
        # self.cell(30, 10, 'Title', 1, 0, 'R')  # 右对齐
        # 换行
        self.ln(10)

    def footer(self):
        # 距离底部1.5cm
        self.set_y(-15)
        # 页脚字体
        self.set_font('Arial', 'I', 8)
        # 页数  当前页/总页数
        self.cell(0, 10, str(self.page_no()) + " / {nb}", 0, 0, 'C')

    # 添加副标题
    def chapter_title(self, num, label):
        # Arial 12
        self.set_font('Arial', '', 12)
        # Background color
        self.set_fill_color(200, 220, 255)
        # Title
        self.cell(0, 6, 'Chapter %d : %s' % (num, label), 0, 1, 'L', 1)
        # Line break
        self.ln(4)

    # 添加文件内容
    def chapter_body(self, name):
        # Read text file
        with open(name, 'rb') as fh:
            txt = fh.read().decode('latin-1')
        # Times 12
        self.set_font('Times', '', 12)
        # Output justified text
        self.multi_cell(0, 5, txt)
        # Line break
        self.ln()
        # 设置链接
        self.set_link(link=name, y=-1, page=-1)
        # 斜体I,标记结束
        self.set_font('', 'I')
        self.cell(0, 5, '(end of excerpt)')

    # 组合副标题和文件内容
    def print_chapter(self, num, title, name):
        self.add_page()
        self.chapter_title(num, title)
        self.chapter_body(name)


if __name__ == '__main__':
    pdf = PDF()
    pdf.alias_nb_pages()  # 定义总页数可以使用{nb}来获取
    pdf.set_font('Times', '', 12)
    pdf.set_title("page title")
    pdf.set_author('yiwanliejiu')
    pdf.print_chapter(1, 'first chapter', 'a.txt')
    pdf.print_chapter(2, 'second chapter', 'b.txt')
    pdf.output('hello_world.pdf', 'F')

添加表格,二维码,注释

import csv

from fpdf import FPDF

"""
生成表格的三种样式
"""

class PDF(FPDF):
    def basic_table(self, headings, rows):
        # 表头
        for heading in headings:
            self.cell(30, 7, heading, 1, align="C")
        self.ln()  # 换行
        # 数据
        for row in rows:
            for col in row:
                self.cell(40, 6, col, 1, align="L")
            self.ln()

    def improved_table(self, headings, rows, col_widths=(42, 39, 35, 40)):
        for col_width, heading in zip(col_widths, headings):
            self.cell(col_width, 7, heading, border=1, align="C")
        self.ln()
        for row in rows:
            self.cell(col_widths[0], 6, row[0], border="LR")  # border="LR" 边框线设置为左右,那么上下为空,做到合并单元格效果
            self.cell(col_widths[1], 6, row[1], border="LR")
            self.cell(col_widths[2], 6, row[2], border="LR", align="R")
            self.cell(col_widths[3], 6, row[3], border="LR", align="R")
            self.ln()
        # 底部闭合线:
        self.cell(sum(col_widths), 0, "", border="T")

    def colored_table(self, headings, rows, col_widths=(42, 39, 35, 42)):
        # Colors, line width and bold font:
        self.set_fill_color(255, 100, 0)  # 背景色
        self.set_text_color(255)  # 文字颜色
        self.set_draw_color(255, 0, 0)  # 边框线颜色
        self.set_line_width(0.3)  # 边框线宽度
        self.set_font(style="B")
        for col_width, heading in zip(col_widths, headings):
            self.cell(col_width, 7, heading, border=1, align="C", fill=True)
        self.ln()
        # Color and font restoration:
        self.set_fill_color(224, 235, 255)
        self.set_text_color(0)
        self.set_font()
        fill = False  # 最初不绘制单元格背景
        for row in rows:
            self.cell(col_widths[0], 6, row[0], border="LR", align="L", fill=fill)
            self.cell(col_widths[1], 6, row[1], border="LR", align="L", fill=fill)
            self.cell(col_widths[2], 6, row[2], border="LR", align="R", fill=fill)
            self.cell(col_widths[3], 6, row[3], border="LR", align="R", fill=fill)
            self.ln()
            fill = not fill  # 将配置取反。这样可以实现隔行绘制背景的效果
        # 底部闭合线
        self.cell(sum(col_widths), 0, "", "T")


# 处理文件数据
def load_data_from_csv(csv_filepath):
    headings, rows = [], []
    with open(csv_filepath, encoding="utf8") as csv_file:
        for row in csv.reader(csv_file, delimiter=","):
            if not headings:  # extracting column names from first row:
                headings = row
            else:
                rows.append(row)
    return headings, rows


col_names, data = load_data_from_csv("a.txt")
pdf = PDF()
pdf.set_font("helvetica", size=14)
pdf.add_page()
pdf.basic_table(col_names, data)
pdf.add_page()
pdf.improved_table(col_names, data)
pdf.add_page()
pdf.colored_table(col_names, data)
# 条形码
pdf.add_page()
pdf.code39("*fpdf2*", x=30, y=50, w=4, h=20)
# 二维码
import qrcode
pdf.add_page()
img = qrcode.make("fpdf2")
pdf.image(img.get_image(), x=50, y=50)
# 文本注释
pdf.add_page()
pdf.set_font("Helvetica", size=24)
pdf.text(x=60, y=140, txt="Some text.")
pdf.text_annotation(
    x=100,
    y=130,
    text="This is a text annotation.",
)
# 强调背景色,hover注释
pdf.add_page()
# pdf.add_font(family="youyuan", style="", fname="./SIMYOU.TTF", uni=False)  # 添加一个本地字体
pdf.set_font("Helvetica", size=24)
with pdf.add_highlight("lingt ", title="title", color=(120, 230, 0)):
    pdf.text(50, 50, "first")
    pdf.set_y(50)
    pdf.multi_cell(w=30, txt="second")
pdf.cell(w=60, txt="Not highlighted", border=1)

pdf.output("tuto5.pdf")

添加链接

from fpdf import FPDF, HTMLMixin


class MyFPDF(FPDF, HTMLMixin):
    pass


pdf = MyFPDF()

# First page:
pdf.add_page()
pdf.set_font("helvetica", size=20)
pdf.write(5, "To find out what's new in self tutorial, click ")
pdf.set_font(style="U")
# 添加本文件内部链接
link = pdf.add_link()
# 添加链接引用位置
pdf.write(5, "click here jump", link)
pdf.set_font()


# Second page:
pdf.add_page()
# 设置链接目标地址
pdf.set_link(link)
# 给图片添加 url链接
pdf.image(
    "img.png", 10, 10, 50, 0, "", "https://pyfpdf.github.io/fpdf2/"
)


pdf.set_left_margin(60)
pdf.set_font_size(18)
# 在html格式数据内指定URL链接地址
pdf.write_html(
    """You can print text mixing different styles using HTML tags: <b>bold</b>, <i>italic</i>,
<u>underlined</u>, or <b><i><u>all at once</u></i></b>!
<br><br>You can also insert links on text, such as <a href="https://pyfpdf.github.io/fpdf2/" target="blank">https://pyfpdf.github.io/fpdf2/</a>,
or on an image: the logo is clickable!"""
)
pdf.output("tuto6.pdf")

目录大纲

from fpdf import FPDF, HTMLMixin, HTML2FPDF

class CustomHTML2FPDF(HTML2FPDF):
    def render_toc(self, pdf, outline):
        pdf.cell(txt='Table of contents:', new_x="LMARGIN", new_y="NEXT")
        for section in outline:
            pdf.cell(txt=f'* {section.name} (page {section.page_number})', new_x="LMARGIN", new_y="NEXT")


class PDF(FPDF, HTMLMixin):
    HTML2FPDF_CLASS = CustomHTML2FPDF


pdf = PDF()
pdf.add_page()
pdf.write_html("""<toc></toc>
    <h1>Level 1</h1>
    <h2>Level 2</h2>
    <h3>Level 3</h3>
    <h4>Level 4</h4>
    <h5>Level 5</h5>
    <h6>Level 6</h6>
    <p>paragraph<p>""")
pdf.output("html_toc.pdf")

fpdf2

# fpdf 生成水印
import fpdf
pdf = fpdf.FPDF()
# pdf.set_text_color(245, 245, 245)
pdf.set_text_color(250, 250, 250)  # 设置文字颜色
pdf.set_font("Arial", size=20)
pdf.add_page()
pdf.text(80, 130, "yiwanliejiu")  # 水印文案
pdf.output("waterMark.pdf")
# PyPDF2
from PyPDF2 import PdfFileReader, PdfFileWriter, PdfFileMerger

password = "123456"

reader = PdfFileReader("pypdf2_meta.pdf")
writer = PdfFileWriter()
merger = PdfFileMerger()


# 解密
if reader.isEncrypted:
    reader.decrypt(password)


def reader_demo():
    # 读
    print(reader.numPages)  # 获取总页数
    print(reader.getNumPages())  # 获取总页数
    info = reader.getDocumentInfo()  # 获取文档信息元数据
    print("author ", info.author)
    print("creator ", info.creator)
    print("producer ", info.producer)
    print("title ", info.title)
    print("subject ", info.subject)

    page0 = reader.getPage(0)
    print(page0.extractText())  # 读取文本内容


def wtiter_demo():
    # 写
    for pageNum in range(reader.numPages):
        page = reader.getPage(pageNum)
        writer.addPage(page)

    # 写入元数据
    writer.addMetadata({
        "/Author": "yiwanliejiu",
        "/Producer": "PyPDF2 writer",
        "/Title": "ahaha"
    })

    # 添加密码
    # writer.encrypt(password)

    with open("pypdf2_meta.pdf", "wb") as f:
        writer.write(f)


def merger_demo():
    # 逐个添加合并pdf文件
    # for pdf in ["tuto2.pdf", "tuto4.pdf"]:
    #     merger.append(pdf)
    # merger.write("merger_demo.pdf")

    # 指定合并的pdf具体页数,位置
    input1 = open("tuto2.pdf", "rb")
    input2 = open("tuto4.pdf", "rb")
    input3 = open("tuto5.pdf", "rb")

    merger.append(fileobj=input1, pages=(0, 2))  # 指定合并的页码 pages=[)
    merger.merge(position=1, fileobj=input2, pages=(0, 1))  # 指定新页面插入的位置和页码
    merger.append(input3)  # 在最后位置插入全部页码

    output = open("merger_more.pdf", "wb")
    merger.write(output)
    merger.close()
    output.close()

# 水印旋转
def water():
    watermark = PdfFileReader("water.pdf")
    reader = PdfFileReader("none.pdf")
    writer = PdfFileWriter()
    page = reader.getPage(0)
    page.mergeRotatedPage(watermark.getPage(0), 20)  # 添加水印图片,旋转一定角度
    writer.addPage(page)
    with open("water2.pdf", "wb") as fp:
        writer.write(fp)


def waterMark():
    watermark = PdfFileReader("water.pdf")
    reader = PdfFileReader("tuto2.pdf")
    writer = PdfFileWriter()
    page = reader.getPage(0)
    # page.mergePage(watermark.getPage(0))  # 添加水印图片
    # page.mergeRotatedPage(waterpage, 20)  # 添加水印图片,旋转一定角度
    # writer.addPage(page)
    for page in reader.pages:
        page.mergePage(watermark.getPage(0))  # 不知道为什么只能是水印在上面
        writer.addPage(page)
    with open("watermark.pdf", "wb") as fp:
        writer.write(fp)


def add_attachment_note():
    writer = PdfFileWriter()
    writer.addBlankPage(width=2000, height=2000)
    writer.addAttachment(fname="桌面壁纸马.jpg", fdata="这里是注释".encode("utf-8"))
    with open("attach.pdf", "wb") as f:
        writer.write(f)

def compress():
    # 压缩文件
    reader = PdfFileReader("waterMark.pdf")
    writer = PdfFileWriter()
    for page in reader.pages:
        page.compressContentStreams()  # 压缩
        writer.addPage(page)
    with open("waterMarkCompress.pdf", "wb") as f:
        writer.write(f)