软件设计有两种方式:一种方式是,使软件过于简单,明显没有缺陷;另一种方式是,使软件过于复杂,没有明显的缺陷
我们python有数量庞大,各种各样的库,那有没有一种库用来帮我们操作pdf呢,有的 ,reportlab
官方介绍:
Generating PDFs from Wall Street to Wikipedia
We build solutions to generate rich, attractive and fully bespoke PDF documents at incredible speeds. This can let you serve personalised documents in real time, produce high-quality output, and support all kinds of delivery from web downloads through to personalised digital print.
简要来说,就是 快速实时生成丰富的、定制的PDF文档。用于实时个性化定制,生成高质量的输出,支持各种交付,可以从网络下载到个人输出等。
要想操作pdf,我们首先得有一个pdf
file_name="paragraph_text.pdf"
f =open(file_name,"wb")
该库还提供pdf加密方法
enc=pdfencrypt.StandardEncryption("liuyue",canPrint=1)
def __init__(self, userPassword, ownerPassword=None, canPrint=1, canModify=1, canCopy=1, canAnnotate=1, strength=40)
该方法有7个参数,分别是控制用户密码,所有者密码 能不能打印,能不能修改,能不能赋值,能不能注释,最后一个没弄清楚,好像是控制修订版本的?
不过代码中有说明这些加密措施强度很弱,不要依赖这个进行加密
doc=SimpleDocTemplate(f,author="liuyue",title="第一个报表",leftMargin=15,
rightMargin=15,pdfencrypt=enc)
调用SimpleDocTemplate生成文档对象,第一个参数代表文件,author传作者,title传标题,leftMargin和rightMargin分别控制左右边距,最后一个pdfencrypt是加密措施
要想在里面写东西可以通过multiBuild,看名字就知道这是个建立多行的东西,需要传一个列表承载要写入的信息
要写入的信息必要要装入规定的对象中,我们来写一个生成承载文字对象的方法
def generate_paragrap(content,align="center",style_name="Heading3"):
"""
:param content: 文字内容
:param align: 对齐方式
:param style_name: 样式名称
:return: Paragraph instance
"""
text="""
%s """%(align,content)
return Paragraph(text,getSampleStyleSheet()[style_name])
可以注意到控制文字样式是通过html的style
datas=[]
datas.append(generate_paragrap("很高兴认识你,我叫流月"))
doc.multiBuild(datas)
f.close()
完毕,运行程序便会有一个pdf文件在你指定的路径下生成,但打开pdf文件你会发现一个问题,乱码,你换成英文就好了,但如果我们想要显示中文怎么办呢?
我们可以通过引入字体的办法来解决这个问题,字体文件需要自己去网上找
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont("FZLTXH","FZLTXIHK.TTF"))
这样一个带有文字的pdf就生成了,如果你想添加更多的内容可以不断向列表中添加,不断改变样式,附上一份完整代码
from reportlab.platypus import SimpleDocTemplate
from reportlab.platypus import Paragraph
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib import pdfencrypt
#解决中文乱码
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont("FZLTXH","FZLTXIHK.TTF"))
def generate_paragrap(content,align="center",style_name="Heading3"):
"""
:param content: 文字内容
:param align: 对齐方式
:param style_name: 样式名称
:return: Paragraph instance
"""
text="""
%s """%(align,content)
return Paragraph(text,getSampleStyleSheet()[style_name])
file_name="paragraph_text.pdf"
f =open(file_name,"wb")
enc=pdfencrypt.StandardEncryption("liuyue",canPrint=1)
doc=SimpleDocTemplate(f,author="liuyue",title="第一个报表",leftMargin=15,
rightMargin=15,pdfencrypt=enc)
datas=[]
datas.append(generate_paragrap("很高兴认识你,我叫流月"))
doc.multiBuild(datas)
f.close()
那报表中也不能只有文字啊,必须有数据啊,经常得放一些表格啊,那么你得引入这些类
from reportlab.platypus import Table
from reportlab.platypus import TableStyle
from reportlab.lib import colors
承载图表需要一个Table类,它的参数非常多,定制的空间比较大
def __init__(self, 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)
前四个参数用的比较多,分别是表数据,列宽,行高,样式
表数据是一个二维数组,和你要显示的表的每一行每一列对应
tables_datas=[
["姓名","年龄","手机号","部门","工号"],
["流月1号","13","15603333319","玩乐部","001"],
["流月2号", "18", "23456543233", "玩乐部", "001"],
["流月3号", "25", "87654565454", "开心部", "002"],
["流月4号", "30", "34565434543", "思考部", "003"],
["流月5号", "40", "09089786765", "学习部", "004"],
["流月6号", "60", "45678874345", "工作部", "005"]
]
列宽是一个一位数组
col_widths=[80,100,100,100,100]
如果你不设置就会变成自适应,主要来看一下样式,生成一个TableStyle的对象来承载样式
table_style=TableStyle([
("FONTNAME",(0,0),(-1,-1),"FZLTXH"),#字体
("FONTSIZE",(0,0),(-1,0),9),#列表头字体大小
("FONTSIZE",(0,1),(-1,-1),8),#列表内容字体大小
("BACKGROUND",(0,0),(-1,0),colors.HexColor("#F6F6F6")),#列表头背景颜色
("ALIGN",(0,0),(-1,0),"LEFT"),#列表头对齐方式为左对齐
("LEFTPADDING",(0,0),(-1,-1),1),#表格左边内部边距为1
("RIGHTPADDING",(0,0),(-1,-1),1),#表格右边内部边距为1
("BOTTOMPADDING",(0,0),(-1,-1),1),#表格底部内部边距为1
("ALIGN",(0,1),(-1,-1),"LEFT"),#对齐方式为左对齐
("TEXTCOLOR",(0,1),(-1,-1),colors.black),#设置表格内文字颜色
("GRID",(0,0),(-1,-1),0.8,colors.HexColor("#C9C9C9"))#设置表格框线为灰色,线宽为0.5
])
#设置样式
tab.setStyle(table_style)
然后就和之前一样了
from reportlab.platypus import SimpleDocTemplate
#表格
from reportlab.platypus import Table
from reportlab.platypus import TableStyle
from reportlab.lib import colors
#解决中文乱码
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont("FZLTXH","FZLTXIHK.TTF"))
file_name="paragraph_table.pdf"
f =open(file_name,"wb")
doc=SimpleDocTemplate(f,author="liuyue",title="第一个报表",leftMargin=15,
rightMargin=15)
datas=[]
tables_datas=[
["姓名","年龄","手机号","部门","工号"],
["流月1号","13","15603333319","玩乐部","001"],
["流月2号", "18", "23456543233", "玩乐部", "001"],
["流月3号", "25", "87654565454", "开心部", "002"],
["流月4号", "30", "34565434543", "思考部", "003"],
["流月5号", "40", "09089786765", "学习部", "004"],
["流月6号", "60", "45678874345", "工作部", "005"]
]
col_widths=[80,100,100,100,100]
tab=Table(tables_datas,colWidths=col_widths)
table_style=TableStyle([
("FONTNAME",(0,0),(-1,-1),"FZLTXH"),#字体
("FONTSIZE",(0,0),(-1,0),9),#列表头字体大小
("FONTSIZE",(0,1),(-1,-1),8),#列表内容字体大小
("BACKGROUND",(0,0),(-1,0),colors.HexColor("#F6F6F6")),#列表头背景颜色
("ALIGN",(0,0),(-1,0),"LEFT"),#列表头对齐方式为左对齐
("LEFTPADDING",(0,0),(-1,-1),1),#表格左边内部边距为1
("RIGHTPADDING",(0,0),(-1,-1),1),#表格右边内部边距为1
("BOTTOMPADDING",(0,0),(-1,-1),1),#表格底部内部边距为1
("ALIGN",(0,1),(-1,-1),"LEFT"),#对齐方式为左对齐
("TEXTCOLOR",(0,1),(-1,-1),colors.black),#设置表格内文字颜色
("GRID",(0,0),(-1,-1),0.8,colors.HexColor("#C9C9C9"))#设置表格框线为灰色,线宽为0.5
])
tab.setStyle(table_style)
datas.append(tab)
doc.multiBuild(datas)
f.close()
看下效果:
那这还不够,我们还缺一些表来展示数据,先来个饼图
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.piecharts import Pie
饼图是用Pie类来承载的,但想要添加到pdf中还需要再套个Drawing类
def get_pie_image(width,height,x,y,datas,lables,_colors,
visable_lable=False):
"""
:param width: 宽度
:param height: 高度
:param x: x坐标
:param y: y坐标
:param datas: 数据
:param lables: 标签名称集合
:param _colors: 颜色集合
"""
drawing=Drawing(width,height)
pie=Pie()
pie.width=100
pie.height=100
pie.x=x
pie.y=y
pie.data=datas
pie.sideLabels=False
if visable_lable:
pie.labels=lables
pie.sideLabels=True
pie.slices.strokeWidth=0.8
pie.checkLabelOverlap=True
for i in range(len(lables)):
pie.slices[i].fontName="FZLTXH"
pie.slices[i].fontSize=8
pie.slices[i].fillColor=_colors[i]
drawing.add(pie)
return drawing
生成Pie对象,width height控制饼图的宽和高,x y控制饼图位置,datas里放数据,饼图会根据这里的数值进行划分,lables 里是和数据一一对应的名字 ,colors 是和数据一一对应的颜色
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
__title__ = ''
__author__ = 'liuyue'
__mtime__ = '2018/8/31'
# code is far away from bugs with the god animal protecting
I love animals. They taste delicious.
┏┓ ┏┓
┏┛┻━━━┛┻┓
┃ ☃ ┃
┃ ┳┛ ┗┳ ┃
┃ ┻ ┃
┗━┓ ┏━┛
┃ ┗━━━┓
┃ 神兽保佑 ┣┓
┃ 永无BUG! ┏┛
┗┓┓┏━┳┓┏┛
┃┫┫ ┃┫┫
┗┻┛ ┗┻┛
"""
from reportlab.platypus import SimpleDocTemplate
#解决中文乱码
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont("FZLTXH","FZLTXIHK.TTF"))
#饼图
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.piecharts import Pie
from reportlab.lib import colors
def get_pie_image(width,height,x,y,datas,lables,_colors,
visable_lable=False):
"""
:param width: 宽度
:param height: 高度
:param x: x坐标
:param y: y坐标
:param datas: 数据
:param lables: 标签名称集合
:param _colors: 颜色集合
"""
drawing=Drawing(width,height)
pie=Pie()
pie.width=100
pie.height=100
pie.x=x
pie.y=y
pie.data=datas
pie.sideLabels=False
if visable_lable:
pie.labels=lables
pie.sideLabels=True
pie.slices.strokeWidth=0.8
pie.checkLabelOverlap=True
for i in range(len(lables)):
pie.slices[i].fontName="FZLTXH"
pie.slices[i].fontSize=8
pie.slices[i].fillColor=_colors[i]
drawing.add(pie)
return drawing
file_name="paragraph_cake.pdf"
f =open(file_name,"wb")
doc=SimpleDocTemplate(f,author="liuyue",title="第一个报表",leftMargin=15,
rightMargin=15)
datas=[]
pie_data=[3,1,2,2,4]
_labels=["python","java","test","js","android"]
_colors=[colors.red,colors.green,colors.yellow,colors.black,colors.blue]
pie_drawing=get_pie_image(480,120,220,40,pie_data,_labels,_colors,visable_lable=True)
datas.append(pie_drawing)
doc.multiBuild(datas)
f.close()
生成柱状图
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.lib.colors import HexColor
def draw_bar_chart(min, max, x_list, data=[()], x_label_angle=0, bar_color=HexColor("#7BB8E7"), height=125, width=280):
'''
:param min: 设置y轴的最小值
:param max: 设置y轴的最大值
:param x_list: x轴上的标签
:param data: y轴对应标签的值
:param x_label_angle: x轴上标签的倾斜角度
:param bar_color: 柱的颜色 可以是含有多种颜色的列表
:param height: 柱状图的高度
:param width: 柱状图的宽度
:return:
'''
bc = VerticalBarChart()
bc.x = 50 # x和y是柱状图在框中的坐标
bc.y = 50
bc.height = height # 柱状图的高度
bc.width = width # 柱状图的宽度
bc.data = data
for j in xrange(len(x_list)):
setattr(bc.bars[j], 'fillColor', bar_color) # bar_color若含有多种颜色在这里分配bar_color[j]
# 调整step
minv = min * 0.5
maxv = max * 1.5
maxAxis = int(height/10)
# 向上取整
minStep = int((maxv-minv+maxAxis-1)/maxAxis)
bc.valueAxis.valueMin = min * 0.5 #设置y轴的最小值
bc.valueAxis.valueMax = max * 1.5 #设置y轴的最大值
bc.valueAxis.valueStep = (max-min)/4 #设置y轴的最小度量单位
if bc.valueAxis.valueStep < minStep:
bc.valueAxis.valueStep = minStep
if bc.valueAxis.valueStep == 0:
bc.valueAxis.valueStep = 1
bc.categoryAxis.labels.boxAnchor = 'ne' # x轴下方标签坐标的开口方向
bc.categoryAxis.labels.dx = -5 # x和y是x轴下方的标签距离x轴远近的坐标
bc.categoryAxis.labels.dy = -5
bc.categoryAxis.labels.angle = x_label_angle # x轴上描述文字的倾斜角度
# bc.categoryAxis.labels.fontName = 'song'
x_real_list = []
if len(x_list) > 10:
for i in range(len(x_list)):
tmp = '' if i%5 != 0 else x_list[i]
x_real_list.append(tmp)
else:
x_real_list = x_list
bc.categoryAxis.categoryNames = x_real_list
return bc
z = autoLegender(draw_bar_chart(100, 300, ['a', 'b', 'c'], [(100, 200, 120)]))
pdf=SimpleDocTemplate('ppff.pdf')
pdf.multiBuild([z])
里面有很多方法,可以用来生成各种图标,折线图啊,柱状累加图啊等等,感兴趣的可以去网上搜搜,这里不过多介绍,我们来说说怎么放图,放图比放表简单多了,直接引入Image,传入路径和大小生成对象,添加到列表,生成就完事了
from reportlab.platypus import SimpleDocTemplate
from reportlab.platypus import Image
file_name="paragraph_img.pdf"
f =open(file_name,"wb")
doc=SimpleDocTemplate(f,author="liuyue",title="第一个报表",leftMargin=15,
rightMargin=15)
datas=[]
img_path="resources/love.jpg"
datas.append(Image(img_path,480,300))
doc.multiBuild(datas)
f.close()