学Python,用Python自动创建PDF文档,实现办公自动化

我们经常将DOC/DOCX、PPT文档另存或者转换为PDF文档。但是这个转换过程不可控,结果不一定能够达到我们的版式需求,因此本节介绍如何使用库从零开始制作PDF文档。

8.2.1 用ReportLab库创建PDF文档

ReportLab是一个用于创建PDF文档的Python库,其功能非常强大,安装方法也非常简单,直接用pip命令安装即可。

1.创建简单的PDF文档

下面我们看一下8.1节示例中的PDF文档是如何自动创建的。

首先从reportlab包的pdfgen目录下导入canvas模块。

>>> from reportlab.pdfgen import canvas

canvas是画布的意思,制作PDF文档好比在空白的画布上作画。

canvas模块有个Canvas类,是创建PDF文档的入口。通过help函数可以查询它的用法。

>>> help(canvas.Canvas)
...
def__init__(self,filename,pagesize=None,bottomup=1,pageCompression=None,invariant=None,verbosity=0, \  
|encrypt=None,cropMarks=None,pdfVersion=None,enforceColorSpace=None,initialFontName=None, \  
initialFontSize=None,initialLeading=None,cropBox=None,artBox=None,trimBox=None,bleedBox=None,lang=None,):
...

初始化方法可以传入的值很多,必须传入的是待创建的PDF文档的文件名(filename)。

>>> c=canvas.Canvas('H:\示例\第8章\HelloWorld.pdf')
>>> c
<reportlab.pdfgen.canvas.Canvas object at 0x00000000025674E0>

方法返回的是
reportlab.pdfgen.canvas.Canvas类的一个实例对象,赋值给变量c,后面用c指代该实例对象。

用dir函数查看对象的属性和方法,主要包括:absolutePosition、acroForm、addLiteral、addOutlineEntry、addPageLabel、addPostScriptCommand、arc、beginForm、beginPath、beginText、bezier、bookmarkHorizontal、
bookmarkHorizontalAbsolute、bookmarkPage、bottomup、circle、clipPath、cross、delCatalogEntry、delViewerPreference、doForm、drawAlignedString、drawBoundary、drawCentredString、drawImage、drawInlineImage、drawPath、drawRightString、drawString、drawText、ellipse、endForm、freeTextAnnotation、getAvailableFonts、getCatalogEntry、getCurrentPageContent、getPageNumber、getViewerPreference、getpdfdata、grid、hasForm、highlightAnnotation、imageCaching、init_graphics_state、inkAnnotation、inkAnnotation0、line、linearGradient、lines、linkAbsolute、linkRect、linkURL、listLoadedFonts0、pageHasData、pop_state_stack、push_state_stack、radialGradient、rect、resetTransforms、restoreState、rotate、roundRect、save、saveState、scale、setArtBox、setAuthor、setBleedBox、setCatalogEntry、setCreator、setCropBox、setDash、setDateFormatter、setEncrypt、setFillAlpha、setFillColor、setFillColorCMYK、setFillColorRGB、setFillGray、setFillOverprint、setFont、setFontSize、setKeywords、setLineCap、setLineJoin、setLineWidth、setMiterLimit、setOutlineNames0、setOverprintMask、setPageCallBack、setPageCompression、setPageDuration、setPageRotation、setPageSize、setPageTransition、setProducer、setStrokeAlpha、setStrokeColor、setStrokeColorCMYK、setStrokeColorRGB、setStrokeGray、setStrokeOverprint、setSubject、setTitle、setTrimBox、setViewerPreference、shade、showFullScreen0、showOutline、showPage、skew、state_stack、stringWidth、textAnnotation、textAnnotation0、transform、translate、wedge。

通过这些方法,我们可以在画布上绘制复杂的PDF文档。

使用setPageSize方法设置页面大小。

>>> c.setPageSize((1200,800))

页面大小也可以在初始化Canvas对象的时候,通过代入.pagesize进行设置。

使用setFont方法设置字体。

>>> c.setFont('Helvetica',200)

设置后,我们可以看到属性值发生了变化。

>>> c._pagesize, c._fontname,c._fontsize
((1200, 800), 'Helvetica', 200)

使用drawString方法在画布上书写,参数包括起点坐标和文本内容。PDF文档中的每个元素都和位置相关,所以绘制元素时必须指定坐标。画布上的每个点都可以用坐标(x,y)表示,原点(0,0)在左下角,向右移动增加x值,向上移动增加y值。

>>> c.drawString(50, 400, 'Hello,World!')

画布画完后,使用showPage方法关闭当前页并翻页,继续绘制下一页。

>>> c.showPage()

本例只有一页,直接保存文件,结束任务。

>>> c.save()

用PDF阅读器或者文本编辑器打开PDF文档,可以看到和8.1.1节的PDF文档是一样的。

我们可以解析PDF文档。

>>> import re
>>> from reportlab.lib.utils import import_zlib as z_pdf
>>> from reportlab.lib.rl_accel import asciiBase85Decode as abd_pdf
>>> pdf=open('H:\示例\第8章\HelloWorld.pdf', 'rb').read()
>>> stream=re.compile(b'.*?FlateDecode.*?stream(.*?)endstream', re.S)
>>> [z_pdf().decompress(abd_pdf(s.strip(b'\r\n'))) for s in re.findall(stream,pdf)]
 [b'1 0 0 1 0 0 cm  BT /F1 12 Tf 14.4 TL ET\nBT /F1 200 Tf 240 TL ET\nBT 1 0 0 1 50 400 Tm (Hello,World!) Tj T* ET\n \n']

作画之前还可以设置画笔的状态,例如颜色、线条的宽度(_lineWidth)、写字用的字体(_fontname、_fontsize)等。前面我们设置了英文字体,由于reportlab包不带中文字体,需要通过官方渠道下载字体文件(下面用到微软雅黑msyh.ttf),放到reportlab安装包下面的font文件夹中,如图8-5所示。

要注意的是,字体使用之前还需要注册。

>>> from reportlab.pdfbase.ttfonts import TTFont
>>> from reportlab.pdfbase import pdfmetrics
>>> pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))

图8-5

对页面大小的修改在翻页以后仍然有效,但是字体的设置只在本页有效。

>>> c.setPageSize((1200,800))
>>> c.setFont('Helvetica',200)
>>> c.showPage()
>>> c._pagesize, c._fontname,c._fontsize
((1200, 800), 'Helvetica', 12)

也就是说,每次翻页,字体都恢复到最初状态。最初的字体状态是由类实例化时传入的数值控制的。

>>> c._initialFontName,c._initialFontSize
('Helvetica', 12)

类实例化时调用了init_graphics_state方法,初始化了画笔状态,包括字体、颜色、字符间距、线条宽度等。showPage方法调用了_startPage方法,后者又调用了init_graphics_state方法,最终将字体恢复到最初状态(_initialFontName、_initialFontSize)。

如果我们需要在同一页面多次设置画笔状态,可以使用saveState和restoreState方法保存和还原画笔状态。

下面以字体设置为例。

>>> c.setFont('Courier',100)
>>> c.saveState()
>>> c._pagesize, c._fontname,c._fontsize
((1200, 800), 'Courier', 100)
>>> c.setFont('Helvetica',300)

使用restoreState方法可以将画笔恢复到上次使用saveState方法保存的状态。

>>> c.restoreState()
>>> c._pagesize, c._fontname,c._fontsize
((1200, 800), 'Courier', 100)

案例:制作精美的封面

下面我们多次设置画笔状态,书写汉字,并绘制线条和图形。

   from reportlab.pdfgen import canvas
   from reportlab.lib.pagesizes import landscape, letter
   from reportlab.pdfbase.ttfonts import TTFont
   from reportlab.pdfbase import pdfmetrics
   from reportlab.lib.colors import pink, black, red, blue, green
⓿ pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
   c=canvas.Canvas(r'H:\示例\第8章\report.pdf')
❶ c.setPageSize((1200,800))
   c.drawImage(r'H:\示例\第8章\background.png',0,500,1200,300)
   c.drawImage(r'H:\示例\第8章\logo.png',0,800-72,190,72)
❷ c.setFont('微软雅黑',50)
   c.drawCentredString(600, 400,'2020年汽车金融专题研究报告')
   c.setFont('微软雅黑',30)
   c.drawCentredString(600, 300, '南山研究院 分析师 金融哥')
   c.setFont('微软雅黑',20)
   c.drawString(50, 120, '因 / 为 / 专 / 注 / 所 / 以 / 专 / 业')
   c.setFont('微软雅黑',30)
   c.drawRightString(1150, 120, '2020年3月')
❸ c.setLineWidth(10)
   c.line(0, 100,1200 ,100 )
   c.setFont('微软雅黑',15)
   c.drawString(50, 80, '本产品保密并受到版权法保护')
   c.drawRightString(1150, 80, 'Confidential and Protected by Copyright Laws')
❹ c.setFillColor(red)
   c.rect(800, 500, 1200, 20, stroke=0, fill=1)
❺ c.setFillGray(0.75)
   c.setFillAlpha(0.3)
   c.rect(0, 500, 800, 20, stroke=0, fill=1)
   c.showPage()
   c.save()

语句⓿注册中文字体微软雅黑;语句❶设置画布大小;语句❷设置书写要用到的字体;语句❸设置画笔线条宽度;语句❹设置图形填充色;语句❺设置矩形的灰度。还用了drawImage方法添加图片,用rect方法绘制矩形,图片和矩形的参数均要指定起始坐标、宽度和高度,另外图片还要指定文件路径。打开生成的PDF文档,效果如图8-6所示。

图8-6

如果一个PDF文档有多页,每页都有固定的元素,每页都重复绘制的话,代码量就比较大,因此可以将固定部分的制作代码放入循环。

使用Canvas类的doForm、beginForm、endForm方法也可以达到同样的效果。

   from reportlab.pdfgen import canvas
   from reportlab.pdfbase.ttfonts import TTFont
   from reportlab.pdfbase import pdfmetrics
   pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
   c=canvas.Canvas(r'H:\示例\第8章\mydoc_form.pdf')
   c.setPageSize((1200,800))
⓿ c.beginForm('LOGO')
   c.drawImage(r'H:\示例\第8章\logo.png',0,800-72,190,72)
❶ c.endForm()
   list=['2020年汽车金融专题研究报告','2020年消费金融专题研究报告',         
   '2020年融资租赁专题研究报告','2020年汽车销售专题研究报告']
   for item in list:
❷     c.doForm('LOGO')
       c.setFont('微软雅黑',80)
       c.drawCentredString(600, 400,item)
       c.showPage()
   c.save()

语句⓿创建form,并将其命名为LOGO;语句❶结束并保持form;语句⓿和❶之间的代码绘制封面的固定内容,通过循环和语句❷,完成文字的书写。打开生成的PDF文档,效果如图8-7所示。

图8-7

在以上例子中,我们用drawString、line、rect方法可以书写不同类型的内容。但是这种“画图”的方式非常低端,始终离不开坐标,如果我们要写入一大段文字,则需要计算每一行能放多少字,并不断调整坐标。由于所有的文字都是图画点,也就没有“自动换行”的功能。

pdfgen目录里面的模块还有很多,都只能进行比较底层的操作。如果要制作更复杂的内容,就要用到页面布局(platypus)。

2.添加段落、表格与图表

要想提升效率,就要减少重复劳动,多用模板和样式。在reportlab包中,platypus目录里的模块就是用来实现各种样式、版式的。platypus是“Page Layout and Typography Using Scripts”的缩写,它致力于把文档的样式和内容分开,段落、表格都直接套用相应的格式,页面也可以套用页面模版。

platypus包括几个层面:文档模板(DocTemplate)、页面模板(PageTemplate)、页面框架(Frame)、页面元素(flowables)。一个文档可以有多个页面模板,一个页面可以有多个框架,一个框架里可以放很多元素。

flowables,即可流动的元素,这是一个形象的比喻。最常见的页面元素就是段落,同样一段文字,随着框架大小的变化,可以被拆分来适应框架,每行字符不固定,其占据的行数也会发生变化。此外,表格、空白(Spacer)、分页符(PageBreak)、图片(Image)都是flowables。图片无法拆分,当框架太小时,它将移动到下一个框架,所以这些元素和坐标系就没有了联系,我们排版布局时,就不用考虑元素的坐标。只需要选择合适的文档和页面模板,设计不同的框架容器,然后依次放入页面元素,即可生成一个PDF文档。

(1)段落

制作段落需要用platypus子目录中paragraph模块的Paragraph类,其语法如下。

Paragraph(text, style, bulletText=None, caseSensitive=1)

它可以将文字和样式生成PDF文档中的段落。

参数text表示各个段落的文本内容。

>>> txt_0='什么是汽车金融?'
>>> txt_1='''汽车金融是汽车全产业链覆盖的资本流动。狭义的汽车金融隶属于消费金融,广义的汽车金融贯穿全产业链。汽车金融的概念最早源于美国,狭义的汽车金融,更多地关注汽车销售环节,为下游客户提供融资性金融服务,隶属于消费金融。广义的汽车金融,是贯穿汽车的生产、流通、销售、使用、回收等环节中的资金流动,提高资本利用率和资金周转率。'''
>>> txt_2='''我国汽车消费金融业萌芽于商业银行贷款,后经政策放宽,形成汽车金融公司、汽车融资租赁公司、互联网汽车金融公司等多元主体并存的局面。'''
>>> txt_3='''中国汽车消费金融渗透率与海外成熟市场差距很大。汽车金融的渗透率,指通过贷款、融资等金融方式购买的车辆数量与汽车销量之比。中国汽车消费金融渗透率一直处于较低水平。'''

参数style表示段落样式。调用lib子目录中styles模块的getSampleStyleSheet函数。

>>> from reportlab.lib.styles import getSampleStyleSheet
>>> s=getSampleStyleSheet()
>>> s
<reportlab.lib.styles.StyleSheet1 object at 0x0000000002BBDDD8>

返回的是样式表StyleSheet1对象,它里面有一些基本的样式可供我们直接使用。用dir函数查看对象的属性和方法,主要包括:add、byAlias、byName、get、has_key、list。

使用list方法输出全部样式的样式设置。

>>> s.list()

其中,Normal、Title样式的主要默认属性说明见表8-1。

表8-1

属性

说明

Normal

Title

name

样式名称

Normal

Title

parent

父对象

None

<'Normal'>

alignment

文字对齐

0

1

allowOrphans

页底段落最小行数

0

0

allowWidows

页顶段落最小行数

1

1

backColor

背景颜色

None

None

borderColor

边框颜色

None

None

borderPadding

内容与边距的距离

0

0

borderRadius

圆角的边框

None

None

borderWidth

边框宽度

0

0

firstLineIndent

首行缩进

0

0

fontName

字体名称

Helvetica

Helvetica-Bold

fontSize

字体大小

10

18

leading

行距

12

22

leftIndent

左缩进

0

0

rightIndent

右缩进

0

0

spaceAfter

段后间隔

0

6

spaceBefore

段前间隔

0

0

textColor

文字颜色

Color(0,0,0,1)

Color(0,0,0,1)

wordWrap

单词中换行

None

None

可以修改样式的默认属性值。

>>> from reportlab.pdfbase.ttfonts import TTFont
>>> from reportlab.pdfbase import pdfmetrics
>>> pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
>>> s['Title'].fontName,s['Title'].fontSize='微软雅黑' ,30
>>> s['Title'].spaceAfter,s['Normal'].spaceBefore=30,10
>>> s['Normal'].fontName,s['Normal'].fontSize='微软雅黑',20
>>> s['Normal'].leading=30
>>> s['Normal'].firstLineIndent=40

下面生成段落。

由于platypus子目录中的__init__.py中有语句“from .paragraph import *”,所以可以直接调用Paragraph类。

>>> from reportlab.platypus import Paragraph

代入文本和样式参数,生成第1个段落对象。

>>> p_0=Paragraph(txt_0,s['Title'])
>>> type(p_0)
<class 'reportlab.platypus.paragraph.Paragraph'>
>>> p_1=Paragraph(txt_1,s['Normal'])
>>> p_2=Paragraph(txt_2,s['Normal'])
>>> p_3=Paragraph(txt_3,s['Normal'])

使用platypus目录中doctemplate模块的SimpleDocTemplate类。

>>> from reportlab.platypus import SimpleDocTemplate
>>> doc=SimpleDocTemplate(r'H:\示例\第8章\mydoc.pdf',pagesize=(1200,800))
>>> doc
<reportlab.platypus.doctemplate.SimpleDocTemplate object at 0x0000000004A6DDD8>

使用SimpleDocTemplate对象的build方法,它可以将页面元素放入文档,生成最终的PDF文档。

build(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing, canvasmaker=canvas.Canvas)

build方法必要的参数是页面元素,段落就是一种页面元素,但是要将其转为列表,才能作为build方法的参数。

>>> story_text=[p_0,p_1,p_2,p_3]
>>> type(story_text)
<class 'list'>

代入参数,生成文件。

>>> doc.build(story_text)

打开生成的PDF文档,效果如图8-8所示。

除了修改样式,我们还可以使用add(style, alias=None)方法添加样式。

>>> from reportlab.lib.styles import ParagraphStyle
>>> s_par=ParagraphStyle(name='A1',fontName='微软雅黑',fontSize=40,firstLineIndent=0)
>>> s_par
<ParagraphStyle 'A1'>
>>> s.add(s_par)
>>> p=Paragraph('微软雅黑40号字体',s['A1'])

图8-8

(2)表格

一般来说,PDF文档中的表格和图表都是通过Excel表格生成,再以图片的形式插入PDF文档中,但是这种图像在放大以后就会变得很模糊,下面尝试直接在PDF文档中绘制表格和图表。

和段落一样,表格也是一种页面元素。

下面需要用platypus子目录中tables模块的Table类制作表格,其语法如下。

Table(data,colWidths=None,rowHeights=None,style=None,repeatRows=0,repeatCols=0,splitByRow=1,emptyTableAction=None,ident=None,hAlign=None,vAlign=None,normalizedData=0,cellStyles=None,rowSplitRange=None, spaceBefore=None,spaceAfter=None,longTableOptimize=None,minRowHeights=None)

数据源data是必须指定的,它是一个二维数组,和要显示的表的每一行、每一列对应。其余的都是可选参数,常用的包括前3个。参数colWidths是一个列表,表示各列的宽度,例如col_widths=[100,50, 50]表示第1列宽100,第2、3列宽50;参数rowHeights表示行高,其设置方法与列宽类似,如果不设置这两个参数,列宽和行高就会变成自适应;参数style表示表格的样式,具体使用TableStyle对象来逐个项目逐个单元格地设置。

首先,构造表格数据参数。

>>> data=[['姓名','一季度','二季度','三季度','四季度'],
...['小赵',100,110,125,135], ['小钱',110,114,126,123],
...['小孙',120,115,127,141],['小李',130,117,128,165],
...['小王',120,127,122,125]]

其次,构造表格列宽、行高参数。

>>> col_widths, row_heights=[80,100,100,100,100],[60,50,50,50,50,50]

然后,构造表格样式参数。调用platypus子目录中Table模块的TableStyle类。

>>> from reportlab.platypus import TableStyle
>>> from reportlab.lib import colors
>>> from reportlab.pdfbase.ttfonts import TTFont
>>> from reportlab.pdfbase import pdfmetrics
>>> pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
>>> table_style=TableStyle([
...         ('FONT', (0, 0), (0, -1), '微软雅黑', 30),
...         ('FONT', (0, 0), (-1, 0), '微软雅黑', 30),
...         ('FONT', (1, 1), (-1, -1), '微软雅黑', 15),
...         ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
...         ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
...         ('GRID', (0,0), (-1,-1), 0.5, colors.black),
...         ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
...         ('BOX', (0,0), (-1,-1), 0.25, colors.black),
...         ('BACKGROUND',(0,0),(-1,-1),colors.white)])

设置表格样式的语法比较特殊,它使用“属性,左上角,右下角,属性值”,表示对某个单元格区域设置属性。0表示第一行或者第一列,−1表示最后一行或最后一列。例如(0, 0)表示左上角单元格,(−1, −1)表示右下角单元格,围起来的区域就是整个表格。

有了全部参数,下面使用Table类实例化一个表格。

>>> from reportlab.platypus import Table
>>> 
table=Table(data,colWidths=col_widths,rowHeights=row_heights,style=table_style)
>>> type(table)
<class 'reportlab.platypus.tables.Table'>

给表格增加一个标题。

>>> tabletitle='''<para alignment=center fontName='微软雅黑' fontSize=20 spaceAfter=30>表1: 销售情况表</para>'''
>>> from reportlab.lib.styles import getSampleStyleSheet
>>> styles=getSampleStyleSheet()
>>> from reportlab.platypus import Paragraph

一起放入列表。

>>> story_table=[Paragraph(tabletitle,styles['Normal']),table]

调用SimpleDocTemplate类的build方法,生成PDF文档。

>>> from reportlab.platypus import SimpleDocTemplate
>>> doc=SimpleDocTemplate(r'H:\示例\第8章\mydoc_table.pdf',pagesize=(1200,800))
>>> doc.build(story_table)

打开生成的PDF文档,效果如图8-9所示。

(3)图表

在PDF文档中添加各种图形,需要用到graphics子目录中的各个模块。下面尝试直接在PDF文档中绘制图表。

调用shapes模块的Drawing类。

>>> from reportlab.graphics.shapes import Drawing

实例化Drawing类,指定绘图区的宽、高。

>>> d=Drawing(100, 100)
>>> d
<reportlab.graphics.shapes.Drawing object at 0x00000000051A2518>

获得一个绘图区Drawing对象,用dir函数查看对象的属性和方法,主要包括:add、asDrawing、asGroup、asString、background、contents、copy、draw、drawOn、dumpProperties、expandUserNodes、getBounds、getContents、getKeepWithNext、getProperties、getSpaceAfter、getSpaceBefore、hAlign、height、identity、insert、isIndexing、minWidth、renderScale、resized、rotate、save、scale、setProperties、shift', 'skew、split、splitOn、transform、translate、vAlign、verify、width、wrap、wrapOn。

图8-9

有了绘图区,下一步就是绘制条形图。

绘制条形图需要使用barcharts模块中的VerticalBarChart类。

>>> from reportlab.graphics.charts.barcharts import VerticalBarChart
>>> bar=VerticalBarChart()
>>> bar
<reportlab.graphics.charts.barcharts.VerticalBarChart object at 0x00000000051A23C8>

获得一个垂直条形图VerticalBarChart对象,用dir函数查看对象的属性和方法,主要包括:background、barLabelArray、barLabelFormat、barLabels、barSpacing、barWidth、bars、calcBarPositions、categoryAxis、categoryNALabel、data、debug、demo、draw、dumpProperties、fillColor、getBounds、getProperties、getSeriesName、getSeriesOrder、groupSpacing、height、makeBackground、makeBars、makeSwatchSample、naLabel、provideNode、reversePlotOrder、setProperties、strokeColor、strokeWidth、useAbsolute、valueAxis、verify、width、x、y、zIndexOverrides。

下面设置对象的各种属性。

>>> bar.x,bar.y,bar.height,bar.width,bar.valueAxis.valueMin=50,-150,280,500,0
>>> bar.categoryAxis.categoryNames=['2012','2013','2014','2015','2016']
>>> bar.data=[[16, 17, 18, 24, 25]]
>>> bar.bars[0].fillColor,bar.barLabels.nudge=colors.black,18
>>> bar.barLabelFormat,bar.valueAxis.labels.fontSize='%0.0f',20
>>> bar.categoryAxis.labels.fontSize,bar.barLabels.fontSize=20,30

通过Drawing对象的add方法将条形图放入绘图区。

>>> d.add(bar)

下面在绘图区中添加一个标题。

>>> from reportlab.graphics.charts.textlabels import Label
>>> title=Label()
>>> title.setText('图1: 汽车金融公司数量')
>>> title.fontSize,title.fontName,title.dx,title.dy=20,'微软雅黑',260,160
>>> d.add(title)

将绘图区放入列表,为了防止太靠近顶端,在绘图区上方添加空格。

>>> from reportlab.platypus import Spacer
>>> story_chart=[Spacer(1,75),d]

调用SimpleDocTemplate类的build方法,生成PDF文档。

>>> from reportlab.platypus import SimpleDocTemplate
>>> doc=SimpleDocTemplate(r'H:\示例\第8章\mydoc_chart.pdf',pagesize=(1200,800))
>>> doc.build(story_chart)

打开生成的PDF文档,效果如图8-10所示。

图8-10

本例中的图表是矢量化的图表,即使放大也不会变模糊。

绘图区的保存方式有多种。

>>> from reportlab.pdfgen import canvas
>>> my_canvas=canvas.Canvas(r'H:\示例\第8章\mydoc_chart.pdf', pagesize=(1200,800))
>>> d.drawOn(my_canvas, 100, 100)
>>> my_canvas.save()

或者以下方式。

>>> d.save(formats=['pdf'],fnRoot=r'H:\示例\第8章\mydoc_chart')

或者以下方式。

>>> from reportlab.graphics import renderPDF
>>> renderPDF.drawToFile(d,r'H:\示例\第8章\mydoc_chart.pdf',autoSize=0)

3.页面布局设计

单个的段落、表格、图表都容易实现,但有时候我们需要将其混排在一起。前面提到的段落、表格、图表都属于Flowable对象,其位置和坐标没关系,是可以变化的,那么如何才能准确地排版呢?那就需要把它们放置在固定的区域内。使用框架可以将复杂的PDF页面分为不同的区域,用来放置文字、表格、图表等内容。

导入框架类Frame。

>>> from reportlab.platypus import Frame

查看Frame类的帮助信息。

>>> help(Frame)

在帮助文档中可以查到Frame类的实例化参数。

class Frame(builtins.object)
Frame(x1, y1, width,height, leftPadding=6, bottomPadding=6, rightPadding=6, topPadding=6, id=None, showBoundary=0)

Frame的外观示意图如图8-11所示。

图8-11

Frame主要用于界定了画布上可以放元素的区域。我们看到Frame的左下角的坐标为(x1,y1),该坐标相对于使用时的画布;尺寸为width×height;Padding是指定边距,扣除边距剩下的就是可供绘图的空间;参数id表示识别符;参数showBoundary表示边界线。

下面将页面分为3个区域,分别放入文字、图表、表格。

>>> f1=Frame(0, 0, 600, 400, showBoundary=1, id='f1')
>>> f2=Frame(600, 0, 600, 400, showBoundary=1, id='f2')
>>> f3=Frame(0, 400, 1200, 400, showBoundary=1, id='f3')
>>> f3
<reportlab.platypus.frames.Frame object at 0x0000000004F86208>

用dir函数查看Frame对象的方法和属性,主要包括:add、addFromList、add_generated_content、drawBoundary、id、showBoundary、split。

可以通过设置showBoundary=0不显示框架的线条,这样既可以对齐内容,又不会显得页面太乱,即使是复杂的版式也显得井井有条。

有了框架,我们就再也不用担心画布上的元素无法对齐了。下面创建一个画布。

>>> from reportlab.pdfgen.canvas import Canvas
>>> c=Canvas(r'H:\示例\第8章\mydoc_Frame.pdf')
>>> c.setPageSize((1200,800))

使用Frame对象的addFromList(drawlist, canv)方法,可以将元素列表(包含flowables的list)按照框架规定的位置放到画布上面。story_chart、story_table、story_text的制作过程前面已经介绍过,此处不再赘述。

>>> f1.addFromList(story_chart,c)
>>> f2.addFromList(story_table,c)
>>> f3.addFromList(story_text,c)
>>> c.save()

打开生成的PDF文档,效果如图8-12所示。

图8-12

有时候我们需要在每一页都添加固定的内容,如公司Logo、页码等信息,这时就要用到页眉和页脚。页眉和页脚应当是自动化生成的,在前面调用doForm方法的案例中,我们插入的logo不是页眉,因为下一页的logo还要手动插入,而无法自动生成。

前面我们用到的build方法,还有两个参数onFirstPage和onLaterPages,用于指定在首页的操作和在后面所有页的操作。

我们看一个例子。

from reportlab.platypus import SimpleDocTemplate, Paragraph,PageBreak, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
def header_footer(c, doc):
    c.drawImage(r'H:\示例\第8章\logo.png',1200-190,800-72,190,72)
    c.setFont('微软雅黑',20)
    c.drawString(50, 60, '因 / 为 / 专 / 注 / 所 / 以 / 专 / 业')
    c.setLineWidth(3)
    c.line(0, 50,1200 ,50 )
    c.line(0, 800-75,1200 ,800-75 )
    c.setFont('微软雅黑',20)
    c.drawString(50, 30, '本产品保密并受到版权法保护')
    c.drawRightString(1150, 30, 'Confidential and Protected by Copyright Laws')
    page_num=c.getPageNumber()
    c.setFont('微软雅黑',30)
    text='第 %s页' % page_num
    c.drawRightString(580,20, text)
    c.setFont('微软雅黑',50)
    c.rotate(30)
    c.setFillAlpha(0.2)
    c.drawString(600, 0, '版权所有 南山金融研究')
    c.rotate(-30)
myPDF=SimpleDocTemplate(r'H:\示例\第8章\mydoc.pdf',pagesize=(1200,800))
story=[]
list=['2020年汽车金融专题研究报告','2020年消费金融专题研究报告',         
      '2020年融资租赁专题研究报告','2020年汽车销售专题研究报告']
styles=getSampleStyleSheet()
styles['Normal'].fontName='微软雅黑' 
styles['Normal'].fontSize=40
for item in list:
    story.append(Spacer(1,200))
    story.append(Paragraph(item, styles['Normal']))
    story.append(PageBreak())
myPDF.build(story, onFirstPage=header_footer, onLaterPages=header_footer)

函数header_footer定义了制作页眉和页脚的操作,build方法的参数传入了函数名header_footer,即onFirstPage=header_footer、onLaterPages=header_footer,表示每一页都会自动完成添加页眉和页脚的操作。

打开生成的PDF文档,效果如图8-13所示。

图8-13

本例还实现了在新建文件中添加水印的效果,给已有的文件添加水印,将用其他库来实现。当然,这种水印也很容易去除。还可以将PDF文档的页面转换成图片,然后在图片上加水印,最后将加完水印的图片组合生成PDF文档,这样的水印就难以去除了。

本文截选自《学Python 不加班 轻松实现办公自动化

这是一本关于如何利用Python提高日常办公效率的书,书中凝聚了作者多年的实践经验和独特思考,旨在帮助读者准确、高效地完成大量高重复度的工作。

《学Python,不加班:轻松实现办公自动化》汇集了日常办公和处理文档时常见的问题,通过实例的演示与讲解,帮助读者灵活有效地使用Python处理工作中遇到的问题。全书共11章,涵盖Python的各种应用场景,具体包括文件管理自动化,网络信息自动获取,TXT、XLS/XLSX、DOC/DOCX、PPT、PDF、图片文件的自动化处理,模拟鼠标、键盘操控本地软件,自动化运行管理等。本书力图淡化编程中的抽象概念,贴合工作场景,注重实战效果,通过对Python技术的巧妙讲解,帮助读者成为高效率的办公室“超人”。

《学Python,不加班:轻松实现办公自动化》适合任何想要学习Python编程的读者,尤其适合缺乏编程经验的初学者。同时本书提供所有案例的源代码文件,方便读者边学边练,爱上Python编程。

Guess you like

Origin blog.csdn.net/epubit17/article/details/119771322