我们经常将DOC/DOCX、PPT文档另存或者转换为PDF文档。但是这个转换过程不可控,结果不一定能够达到我们的版式需求,因此本节介绍如何使用库从零开始制作PDF文档。
ReportLab是一个用于创建PDF文档的Python库,其功能非常强大,安装方法也非常简单,直接用pip命令安装即可。
下面我们看一下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类的一个实例对象,赋值给变量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)。
要想提升效率,就要减少重复劳动,多用模板和样式。在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
返回的是样式表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)
>>> 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
使用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)
代入参数,生成文件。
>>> 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
>>> 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)
给表格增加一个标题。
>>> tabletitle='''表1: 销售情况表 '''
>>> 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
获得一个绘图区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
获得一个垂直条形图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)
单个的段落、表格、图表都容易实现,但有时候我们需要将其混排在一起。前面提到的段落、表格、图表都属于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
用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编程。