使用Python中的reportlab库生成PDF文件

1 安装

pip install reportlab

2 应用场景

  • 通过网页动态生成PDF文档
  • 大量的报告和数据发布
  • 用XML一步生成PDF
    官网案例
  • 3 PLATYPUS

    Platypus是“Page Layout and Typography Using Scripts”,是使用脚本的页面布局和印刷术的缩写,这是一个高层次页面布局库,它可以让你通过编程创造复杂的文档,并且毫不费力。

    Platypus设计的目的是尽可能地将高层布局设计与文档内容分离,比如,段落使用段落格式构造,页面使用页面模板,这样做是有好处的,在仅仅修改几行代码的情况下,包含数百个页面的数百个文档就能够被构造成不同的风格。

  • Platypus从上到下,可以被看成具备多个层次。
  • DocTemplates:文档最外层的容器
  • PageTemplates:各种页面布局的规格
  • Frames:包含流动的文本和图形的文档区域规范
  • Flowables:能够被“流入文档”的文本、图形和段落等。
  • 3.1 start demo

    import reportlab
    from reportlab.platypus import SimpleDocTemplate, Paragraph
    from reportlab.lib.styles import getSampleStyleSheet
    
    # 调用模板,创建指定名称的PDF文档
    doc = SimpleDocTemplate("start_demo.pdf")
    # 获得模板表格
    styles = getSampleStyleSheet()
    # 指定模板
    style = styles['Normal']
    # 初始化内容
    story =[]
    # 将段落添加到内容中
    story.append(Paragraph("this is a demo pdf!",style))
    # 将内容输出到PDF中
    doc.build(story)
    

    在Python文件的同目录下,生成一个文件名为”start_demo.pdf“的文件:

    doc = SimpleDocTemplate("start_demo.pdf") 调用了 BaseDocTemplate模板
    
    BaseDocTemplate 基本思想很简单。
    1) 该文档有一个与之相关的数据列表,这些数据应该来自流动性。我们会特殊类,如PageBreak、FrameBreak,用于执行强制页面结束等操作。
    2) 文档有一个或多个页面模板。
    3) 每个页面模板都有一个或多个框架。
    4) document类提供了处理故事事件的基本方法,以及将故事流放入框架的一些合理方法。
    5) 文档实例可以覆盖基本处理程序例程。
    这个类的大多数方法不是由用户直接调用的,但在一些高级用法中,它们可能需要通过子类化来重写。
    异常:必须调用doctemplate.build(…)才能获得最合理的用途,因为它使用页面模板生成文档。
    每个文档模板在初始化时将恰好一个文档构建到filename参数指定的文件中。
    初始化的可能关键字参数:
    -pageTemplates:模板列表。必须是非空的。指定给模板的名称用于引用它们,因此使用的两个模板不应具有相同的名称。例如,在奇数页和偶数页上,您可能需要一个标题页模板、一个章节首页模板、一章首页模板和两个章节内部模板。如果省略了此参数,则在生成文档之前,应使用addPageTemplates方法至少提供一个pageTemplate。
    -pageSize:reportlab/lib/pagesizes.pu中的2元组或大小常量。由SimpleDocTemplate子类使用,该子类不接受的列表
    pageTemplates,但为您制作一个;使用pageTemplates时忽略。
    -showBoundary:如果设置,则在框架边界周围绘制一个框。
    -左边距:
    -右侧边距:
    -顶部边距:
    -底部边距:以点为单位的边距大小(默认为1英寸)。这些页边距可能会被pageTemplates覆盖。它们主要是SimpleDocumentTemplate子类感兴趣的。
    -allowSpliting:如果设置了流动性(如段落),则可以在框架或页面之间进行拆分(默认值:1)
    -title:文档的内部标题(不会自动显示在任何页面上)
    -author:文档的内部作者(不会自动显示在任何页面上)
    
    初始化参数:
          'pagesize':defaultPageSize,
          'pageTemplates':[],
          'showBoundary':0,
          'leftMargin':inch,
          'rightMargin':inch,
          'topMargin':inch,
          'bottomMargin':inch,
          'allowSplitting':1,
          'title':None,
          'author':None,
          'subject':None,
          'creator':None,
          'producer':None,
          'keywords':[],
          'invariant':None,
          'pageCompression':None,
          '_pageBreakQuick':1,
          'rotation':0,
          '_debug':0,
          'encrypt': None,
          'cropMarks': None,
          'enforceColorSpace': None,
          'displayDocTitle': None,
          'lang': None,
          'initialFontName': None,
          'initialFontSize': None,
          'initialLeading': None,
          'cropBox': None,
          'artBox': None,
          'trimBox': None,
          'bleedBox': None,
          'keepTogetherClass': KeepTogether,
          'hideToolbar': None,
          'hideMenubar': None,
          'hideWindowUI': None,
          'fitWindow': None,
          'centerWindow': None,
          'nonFullScreenPageMode': None,
          'direction': None,
          'viewArea': None,
          'viewClip': None,
          'printArea': None,
          'printClip': None,
          'printScaling': None,
          'duplex': None,
    

    4 段落

    段落是一种重要的Flowables,它可以格式化任意的文本。

    段落中主要包括两种信息:文本和格式。Paragraph(text, style)

  • text参数提供了段落的文本,末尾和换行处的空白都会被删除。
  • style参数用于设置段落的格式,这里段落的格式是指参数的集合,包括字体大小、行间距、首行缩进等参数。我们可以调用如下语句来获得默认段落格式。
  • from reportlab.lib.styles import ParagraphStyle
    

    4.1 ParagraphStyle 参数

    class ParagraphStyle(PropertySet):
        defaults = {
            'fontName':_baseFontName,
            'fontSize':10,
            'leading':12,
            'leftIndent':0,
            'rightIndent':0,
            'firstLineIndent':0,
            'alignment':TA_LEFT,
            'spaceBefore':0,
            'spaceAfter':0,
            'bulletFontName':_baseFontName,
            'bulletFontSize':10,
            'bulletIndent':0,
            #'bulletColor':black,
            'textColor': black,
            'backColor':None,
            'wordWrap':None,        #None means do nothing special
                                    #CJK use Chinese Line breaking
                                    #LTR RTL use left to right / right to left
                                    #with support from pyfribi2 if available
            'borderWidth': 0,
            'borderPadding': 0,
            'borderColor': None,
            'borderRadius': None,
            'allowWidows': 1,
            'allowOrphans': 0,
            'textTransform':None,   #uppercase lowercase (captitalize not yet) or None or absent
            'endDots':None,         #dots on the last line of left/right justified paras
                                    #string or object with text and optional fontName, fontSize, textColor & backColor
                                    #dy
            'splitLongWords':1,     #make best efforts to split long words
            'underlineWidth': _baseUnderlineWidth,  #underline width default
            'bulletAnchor': 'start',    #where the bullet is anchored ie start, middle, end or numeric
            'justifyLastLine': 0,   #n allow justification on the last line for more than n words 0 means don't bother
            'justifyBreaks': 0,     #justify lines broken with <br/>
            'spaceShrinkage': _spaceShrinkage,  #allow shrinkage of percentage of space to fit on line
            'strikeWidth': _baseStrikeWidth,    #stroke width default
            'underlineOffset': _baseUnderlineOffset,    #fraction of fontsize to offset underlines
            'underlineGap': _baseUnderlineGap,      #gap for double/triple underline
            'strikeOffset': _baseStrikeOffset,  #fraction of fontsize to offset strikethrough
            'strikeGap': _baseStrikeGap,        #gap for double/triple strike
            'linkUnderline': _platypus_link_underline,
            'underlineColor':   None,
            'strikeColor': None,
            'hyphenationLang': _hyphenationLang,
            #'hyphenationMinWordLength': _hyphenationMinWordLength,
            'embeddedHyphenation': _embeddedHyphenation,
            'uriWasteReduce': _uriWasteReduce,
            }
    
    

    几个重要的参数说明:

  • fontName:字体名称
  • fontSize:字体大小
  • leading:行间距
  • leftIndent:左缩进
  • rightIndent:右缩进
  • firstLineIndent:首行缩进
  • alignment:对齐方式
  • spaceBefore:段前间隙
  • spaceAfter:段后间隙
  • bulletFontName:列表名称
  • bulletFontSize:列表字体大小
  • bulletIndent:列表缩进
  • textColor:字体颜色
  • backColor:背景色
  • borderWidth:边框粗细
  • borderPadding:边框间距
  • borderColor:边框颜色
  • ParagraphStyle是段落的默认格式,也就是在调用Paragraph(text, style)语句时,如果不传入style参数,默认的段落格式。

    还有其他方式获得ReportLab提供的段落格式:

    from reportlab.lib.styles import getSampleStyleSheet
    stylesheet=getSampleStyleSheet()
    normalStyle = stylesheet['Normal']
    

    这里获得系统提供的Normal格式,其实Normal格式与ParagraphStyle是一模一样的,除了Normal格式,还可以获得其他的格式:

  • Normal
  • BodyText
  • Italic
  • Title
  • Heading1
  • Heading2
  • Heading3
  • Heading4
  • Heading5
  • Heading6
  • Bullet
  • Definition
  • Code
  • UnorderedList
  • OrderedList
  • def getSampleStyleSheet():
        """Returns a stylesheet object"""
        stylesheet = StyleSheet1()
    
        stylesheet.add(ParagraphStyle(name='Normal',
                                      fontName=_baseFontName,
                                      fontSize=10,
                                      leading=12)
                       )
    
        stylesheet.add(ParagraphStyle(name='BodyText',
                                      parent=stylesheet['Normal'],
                                      spaceBefore=6)
                       )
        stylesheet.add(ParagraphStyle(name='Italic',
                                      parent=stylesheet['BodyText'],
                                      fontName = _baseFontNameI)
                       )
    
        stylesheet.add(ParagraphStyle(name='Heading1',
                                      parent=stylesheet['Normal'],
                                      fontName = _baseFontNameB,
                                      fontSize=18,
                                      leading=22,
                                      spaceAfter=6),
                       alias='h1')
    
        stylesheet.add(ParagraphStyle(name='Title',
                                      parent=stylesheet['Normal'],
                                      fontName = _baseFontNameB,
                                      fontSize=18,
                                      leading=22,
                                      alignment=TA_CENTER,
                                      spaceAfter=6),
                       alias='title')
    
        stylesheet.add(ParagraphStyle(name='Heading2',
                                      parent=stylesheet['Normal'],
                                      fontName = _baseFontNameB,
                                      fontSize=14,
                                      leading=18,
                                      spaceBefore=12,
                                      spaceAfter=6),
                       alias='h2')
    
        stylesheet.add(ParagraphStyle(name='Heading3',
                                      parent=stylesheet['Normal'],
                                      fontName = _baseFontNameBI,
                                      fontSize=12,
                                      leading=14,
                                      spaceBefore=12,
                                      spaceAfter=6),
                       alias='h3')
    
        stylesheet.add(ParagraphStyle(name='Heading4',
                                      parent=stylesheet['Normal'],
                                      fontName = _baseFontNameBI,
                                      fontSize=10,
                                      leading=12,
                                      spaceBefore=10,
                                      spaceAfter=4),
                       alias='h4')
    
        stylesheet.add(ParagraphStyle(name='Heading5',
                                      parent=stylesheet['Normal'],
                                      fontName = _baseFontNameB,
                                      fontSize=9,
                                      leading=10.8,
                                      spaceBefore=8,
                                      spaceAfter=4),
                       alias='h5')
    
        stylesheet.add(ParagraphStyle(name='Heading6',
                                      parent=stylesheet['Normal'],
                                      fontName = _baseFontNameB,
                                      fontSize=7,
                                      leading=8.4,
                                      spaceBefore=6,
                                      spaceAfter=2),
                       alias='h6')
    
        stylesheet.add(ParagraphStyle(name='Bullet',
                                      parent=stylesheet['Normal'],
                                      firstLineIndent=0,
                                      spaceBefore=3),
                       alias='bu')
    
        stylesheet.add(ParagraphStyle(name='Definition',
                                      parent=stylesheet['Normal'],
                                      firstLineIndent=0,
                                      leftIndent=36,
                                      bulletIndent=0,
                                      spaceBefore=6,
                                      bulletFontName=_baseFontNameBI),
                       alias='df')
    
        stylesheet.add(ParagraphStyle(name='Code',
                                      parent=stylesheet['Normal'],
                                      fontName='Courier',
                                      fontSize=8,
                                      leading=8.8,
                                      firstLineIndent=0,
                                      leftIndent=36,
                                      hyphenationLang=''))
    
        stylesheet.add(ListStyle(name='UnorderedList',
                                    parent=None,
                                    leftIndent=18,
                                    rightIndent=0,
                                    bulletAlign='left',
                                    bulletType='1',
                                    bulletColor=black,
                                    bulletFontName='Helvetica',
                                    bulletFontSize=12,
                                    bulletOffsetY=0,
                                    bulletDedent='auto',
                                    bulletDir='ltr',
                                    bulletFormat=None,
                                    #start='circle square blackstar sparkle disc diamond'.split(),
                                    start=None,
                                ),
                       alias='ul')
    
        stylesheet.add(ListStyle(name='OrderedList',
                                    parent=None,
                                    leftIndent=18,
                                    rightIndent=0,
                                    bulletAlign='left',
                                    bulletType='1',
                                    bulletColor=black,
                                    bulletFontName='Helvetica',
                                    bulletFontSize=12,
                                    bulletOffsetY=0,
                                    bulletDedent='auto',
                                    bulletDir='ltr',
                                    bulletFormat=None,
                                    #start='1 a A i I'.split(),
                                    start=None,
                                ),
                       alias='ol')
        return stylesheet
    
    

    假如想要获得Title格式,我们只需要按照如下格式调用即可:

    from reportlab.lib.styles import getSampleStyleSheet
    stylesheet=getSampleStyleSheet()
    titleStyle = stylesheet['Title']
    

    从Title的源代码可知:Title的效果是:字体18号;行间距22;对齐方式:居中;段落后间距:6。

    5 表格

    表格是Flowable的派生类,是一种简单文本表格机制。表格可以保存所有能被转换为字符串或Flowerable是所有事物。
    如果我们不提供行高,它们可以根据数据自动计算出行高。
    如果需要,它们可以跨页分割,你可以指定跨页分割后,需要重复的行数。
    表格风格和表格数据是分离的,因此你可以声明一系列的风格,然后将它们用于一大堆报告。
    表格使用如下代码进行创建:

    Table(data, colWidths=None, rowHeights=None, style=None, splitByRow=1,
    repeatRows=0, repeatCols=0, rowSplitRange=None, spaceBefore=None,
    spaceAfter=None)
    

    几个关键参数:

  • data:数据参数是一系列的表格值,每个表格值能够被转换为字符串或者Flowable实例。data值的第一行是data[0],第i行j列表格值是data[i] [j]。
  • colWidths:是一系列值,这些值代表每列的宽度。如果传递的是None,则对应列宽需要被自动计算。
  • rowHeights:是一系列值,这些值代表每行的高度。如果传递的是None,则对应的行高需要被自动计算。
  • style:表格被创建时的初始样式值。
  • splitByRow:布尔值,当指定值为1时,允许跨页分割表格,当指定指为0时,不允许跨页分割表格。
  • repeatRows:指定跨页分行时,需要重复的行数。
  • repeatCols:暂时没用。
  • spaceBefore:指定表格前的行数。
  • spaceAfter:指定表格后的行数。
  • from reportlab.platypus import SimpleDocTemplate, Table
    from reportlab.lib.styles import getSampleStyleSheet
    
    # 调用模板,创建指定名称的PDF文档
    doc = SimpleDocTemplate("my_pdf_01.pdf")
    # 获得模板表格
    styles = getSampleStyleSheet()
    # 指定模板
    style = styles['Normal']
    # 初始化内容
    story =[]
    
    # 初始化表格内容
    data= [['00', '01', '02', '03', '04'],
           ['10', '11', '12', '13', '14'],
           ['20', '21', '22', '23', '24'],
           ['30', '31', '32', '33', '34']]
    
    # 根据内容创建表格
    t = Table(data)
    # 将表格添加到内容中
    story.append(t)
    # 将内容输出到PDF中
    doc.build(story)
    

    5.1 表格格式

    指定表格格式有两种方式,一种是在调用创建表格接口时,传入style参数,一种是在创建完表格后,调用如下接口:

    Table.setStyle(tblStyle)
    

    5.1.1 直接传入style参数

    from reportlab.platypus import SimpleDocTemplate, Table
    from reportlab.lib import colors
    from reportlab.lib.styles import getSampleStyleSheet
    
    # 调用模板,创建指定名称的PDF文档,输入参数为路径文件名
    doc = SimpleDocTemplate("../datas/wws_tables.pdf")
    # 获得模板表格
    styles = getSampleStyleSheet()
    # 指定模板
    style = styles['Normal']
    # 初始化内容
    story = []
    
    # 初始化表格内容
    data = [['00', '01', '02', '03', '04'],
           ['10', '11', '12', '13', '14'],
           ['20', '21', '22', '23', '24'],
           ['30', '31', '32', '33', '34']]
    
    # 根据内容创建表格,同时添加格式
    t = Table(data,style=[
                         ('GRID', (0, 0), (-1, -1), 1, colors.grey),
                         ('GRID', (1, 1), (-2, -2), 1, colors.green),
                         ('BOX', (0, 0), (1, -1), 2, colors.red),
                         ('BACKGROUND', (0, 0), (0, 1), colors.pink),
                         ('BACKGROUND', (1, 1), (1, 2), colors.lavender),
                         ('BACKGROUND', (2, 2), (2, 3), colors.orange),
                        ]
            )
    
    # 将表格添加到内容中
    story.append(t)
    # 将内容输出到PDF中
    doc.build(story)
    

    5.1.2 调用setStyle

    from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
    from reportlab.lib import colors
    from reportlab.lib.styles import getSampleStyleSheet
    
    # 调用模板,创建指定名称的PDF文档
    doc = SimpleDocTemplate("../datas/wws_tables.pdf")
    # 获得模板表格
    styles = getSampleStyleSheet()
    # 指定模板
    style = styles['Normal']
    # 初始化内容
    story = []
    
    # 初始化表格内容
    data = [['00', '01', '02', '03', '04'],
           ['10', '11', '12', '13', '14'],
           ['20', '21', '22', '23', '24'],
           ['30', '31', '32', '33', '34']]
    
    # 根据内容创建表格
    t = Table(data)
    
    # 然后调用setStyle添加格式
    t.setStyle(TableStyle([
                             ('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
                             ('BOX', (0, 0), (-1, -1), 2, colors.black),
                             ('LINEBELOW', (0, 0), (-1, 0), 2, colors.yellow),
                             ('LINEAFTER', (0, 0), (0, -1), 2, colors.blue),
                             ('ALIGN', (1, 1), (-1, -1), 'RIGHT')
                            ])
                )
    
    # 将表格添加到内容中
    story.append(t)
    # 将内容输出到PDF中
    doc.build(story)
    

  • 表格的位置索引方式与列表的索引方式一致
  • 左上角第一个是data[0] [0]
  • 第二行第一个是data[1] [0]
  • 最后一行第一个位置是data[-1] [0],依次类推。
  • 重点表格格式化命令:

  • FONTNAME:字体名称
  • FONTSIZE:字体大小
  • LEADING:行间距
  • TEXTCOLOR:字体颜色
  • ALIGNMENT:水平对齐方式(可选值:“LEFT”,”RIGHT“,”CENTER“)
  • LEFTPADDING:左边填充
  • RIGHTPADDING:右边填充
  • BOTTOMPADDING:底部填充
  • TOPPADDING:顶部填充
  • BACKGROUND:背景色
  • VALIGN:垂直对齐方式(可选值:“TOP”,“MIDDLE”,“BOTTOM”)
  • GRID:表格颜色,被指定的行列中的所有子行和子列都被设置成相应颜色
  • INNERGRID:表格颜色,仅仅修改指定的子行和子列的相应颜色(不包括边框)
  • BOX:边框颜色,被指定的边框的颜色
  • LINEBELOW:指定块底部的行颜色
  • LINEAFTER:指定块右边的行颜色。
  • 6 图片

    在调用接口时,支持默认的jpeg格式。接口如下:

    Image(filename, width=None, height=None)
    
  • filename:指定文件名
  • width:指定图片的宽度
  • height:指定图片的高度
  • 如果宽度和高度有一个没有被指定,则参考原来的图片像素。

    from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
    from reportlab.lib import colors
    from reportlab.lib.styles import getSampleStyleSheet
    
    from reportlab.platypus import SimpleDocTemplate, Image
    
    from reportlab.lib.styles import getSampleStyleSheet
    
    # 调用模板,创建指定名称的PDF文档
    doc = SimpleDocTemplate("../datas/wws_tables.pdf")
    # 获得模板表格
    styles = getSampleStyleSheet()
    # 指定模板
    style = styles['Normal']
    # 初始化内容
    story = []
    
    # 初始化表格内容
    data = [['00', '01', '02', '03', '04'],
           ['10', '11', '12', '13', '14'],
           ['20', '21', '22', '23', '24'],
           ['30', '31', '32', '33', '34']]
    
    # 根据内容创建表格, 表格后空两行,不然图片连一块了,难看
    t = Table(data, spaceAfter=10)
    
    # 然后调用setStyle添加格式
    t.setStyle(TableStyle([
                             ('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
                             ('BOX', (0, 0), (-1, -1), 2, colors.black),
                             ('LINEBELOW', (0, 0), (-1, 0), 2, colors.yellow),
                             ('LINEAFTER', (0, 0), (0, -1), 2, colors.blue),
                             ('ALIGN', (1, 1), (-1, -1), 'RIGHT')
                            ])
                )
    
    # 将表格添加到内容中
    story.append(t)
    # 添加图片
    IMG = Image("C:\\Users\\admin\\Documents\\index\\PS图片\\20230926-144332.jpg", width=120, height=160)
    story.append(IMG)
    
    # 将内容输出到PDF中
    doc.build(story)
    


    参考

    python 生成pdf
    reportlab 官网案例
    reportlab 官方文档
    文档官方demo
    flask_reportlab
    adding-links-to-pdf
    Python的reportlab制作pdf时,如何让插入的图片适应pdf的大小
    reportlab 中文问题

    作者:桂花很香,旭很美

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用Python中的reportlab库生成PDF文件

    发表回复