ReportLab 导出 PDF(文档创建)
ReportLab 导出 PDF(页面布局)
ReportLab 导出 PDF(图文表格)
文章目录
1. Paragraph(段落)
官网:
https://docs.reportlab.com/reportlab/userguide/ch6_paragraphs/
https://blog.csdn.net/weixin_49278803/article/details/132907427
1.1. 文本对齐
canvas.drawString(80, 700, 'Standard String') # 标准绘制函数
canvas.drawRightString(80, 680, 'Right String') # x轴右对齐绘制函数
canvas.drawAlignedString(60, y, str(number)) # 轴对称绘制函数
canvas.drawCentredString(width/2, 550, 'Centered String') # 中心对称绘制函数
https://blog.csdn.net/icloudend/article/details/98902717
1.2. 中英混编显示长度对齐
当字符串包含中文、日文等全角字符时,len() 1个汉字与1个英文字母都被计算为1个单位长度,但实际上它们在显示时占据的宽度是不同的!
import unicodedata
'''
# F 全角 (Fullwidth) 中文字符 2
# W 宽 (Wide) 日文字符 2
# Na 窄 (Narrow) 英文字母 1
# H 半角 (Halfwidth) 半角假名 1
'''
def calculate_display_length(text, max_length=20):
text_align = ""
length = 0
for char in text:
text_align += char
# 获取字符的东亚宽度属性
east_asian_width = unicodedata.east_asian_width(char)
if east_asian_width in 'FWA': # 全宽、宽字符、模糊字符
length += 2
else:
length += 1
if length >= max_length:
break
print(f"{length:<10}:> {text_align}")
print(f"{len(text_align):<10}:> {text_align}")
return text_align
if __name__ == '__main__':
text = "1. 中文目录nil中文目录"
text_align = calculate_display_length(text)
text = "1.1. 中文目录ni中文目录"
text_align = calculate_display_length(text)
text = "1.2. 英文hell lslllssssss"
text_align = calculate_display_length(text)
text = "1.2.1. 中文目录✋✊✌️❤️⭐️✨⚡️"
text_align = calculate_display_length(text)
运行结果:
20 :> 1. 中文目录nil中文目
13 :> 1. 中文目录nil中文目
21 :> 1.1. 中文目录ni中文目
14 :> 1.1. 中文目录ni中文目
20 :> 1.2. 英文hell lsllls
18 :> 1.2. 英文hell lsllls
20 :> 1.2.1. 中文目录✋✊✌
14 :> 1.2.1. 中文目录✋✊✌
2. Table(表格)
https://www.cnblogs.com/jilingxf/p/15857940.html
from reportlab.platypus import SimpleDocTemplate
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.graphics.charts.barcharts import VerticalBarChart # 图表类
from reportlab.graphics.charts.legends import Legend # 图例类
from reportlab.graphics.shapes import Drawing # 绘图工具
from reportlab.platypus import Table, Paragraph, TableStyle
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
def draw_table1(datas: list):
col_widths, row_heights = [80, 100, 100, 100, 100], [60, 50, 50, 50, 50, 50]
# 表格行列的表达形式为(x, y):左上方第一个单元格为(0, 0), 右下角单元格为(-1, -1)
table_style = TableStyle([
("FONT", (0, 0), (0, -1), "simsun", 15), # 第一列:配置字体
("FONT", (0, 0), (-1, 0), "simsun", 15), # 第一行:
("FONT", (1, 1), (-1, -1), "simsun", 12),
("ALIGN", (0, 0), (-1, -1), "CENTER"), # 水平居中
("VALIGN", (0, 0), (-1, -1), "MIDDLE"), # 垂直居中
("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), # 单元格分割线
("BOX", (0, 0), (-1, -1), 0.25, colors.black), # 边框
("BACKGROUND", (0, 0), (-1, -1), colors.lightgrey), # 背景色(默认为白色)
("TEXTCOLOR", (0, 0), (-1, 0), colors.red), # 第一行: 区域字体颜色
("LINEBELOW", (0,-1), (-1,-1), 0, colors.white), # 移除最后一行的下框线,延申LINEABOVE(上框线)、LINEBEFORE(左框线)、LINEAFTER(右框线)
# ("GRID", (0, 0), (-1, -1), 0.5, colors.black), # 表格框线为灰色,线宽为0.5
# ("SPAN", (0, 3), (-1, 3)), # 合并单元格
])
return Table(datas, colWidths=col_widths, rowHeights=row_heights, style=table_style)
def draw_table2(datas: list):
col_widths, row_heights = [80, 100, 100, 100, 100], [30, 25, 25, 25, 25, 25]
# 表格行列的表达形式为(x, y):左上方第一个单元格为(0, 0), 右下角单元格为(-1, -1)
table_style = TableStyle([
("FONT", (0, 0), (0, -1), "simsun", 20), # 第一列:配置字体
("FONT", (0, 0), (-1, 0), "simsun", 15), # 第一行:
("FONT", (1, 1), (-1, -1), "simsun", 15),
("ALIGN", (0, 0), (-1, -1), "CENTER"), # 水平居中
("VALIGN", (0, 0), (-1, -1), "MIDDLE"), # 垂直居中
("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), # 单元格分割线
("BOX", (0, 0), (-1, -1), 0.25, colors.black), # 边框
# ("BACKGROUND", (0, 0), (-1, -1), colors.lightgrey), # 背景色(默认为白色)
("TEXTCOLOR", (0, 0), (-1, 0), colors.red), # 第一行: 区域字体颜色
# ("LINEBELOW", (0,-1), (-1,-1), 0, colors.white), # 移除最后一行的下框线,延申LINEABOVE(上框线)、LINEBEFORE(左框线)、LINEAFTER(右框线)
# ("GRID", (0, 0), (-1, -1), 0.5, colors.black), # 表格框线为灰色,线宽为0.5
("SPAN", (0, 3), (-1, 3)), # 合并单元格
])
return Table(datas, colWidths=col_widths, rowHeights=row_heights, style=table_style)
def main(filename):
# pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
pdfmetrics.registerFont(TTFont('simsun', "simsun.ttc"))
doc = SimpleDocTemplate(filename, pagesize=A4)
Story = []
datas = [
["姓名", "语文", "数学", "英语", "体育"],
["张三", 91, 97, 79, "良好"],
["李四", 99, 87, 73, "优秀"],
["王五", 86, 89, 83, "良好"],
["赵六", 95, 88, 86, "良好"],
# 当需要将数据在一个单元格分两列的话,可以用下面的语法
["孙七", 79, 95, 98, "良"+"\n"+"好"],
]
# 编辑表格标题及样式
tabletitle = """<para alignment=center fontName="simsun" fontSize=20 spaceAfter=30>表1: 学生成绩表</para>"""
# 可以配置上下左右页边距,topMargin=1*cm,bottomMargin=1*cm,leftMargin=1*cm,rightMargin=1*cm
Story.append(Paragraph(tabletitle, getSampleStyleSheet()["Normal"]))
Story.append(draw_table1(datas))
tabletitle = """<para alignment=center fontName="simsun" fontSize=20 spaceBefore=30 spaceAfter=30>表1: 学生成绩表</para>"""
Story.append(Paragraph(tabletitle))
Story.append(draw_table2(datas))
doc.build(Story)
if __name__ == '__main__':
main(filename='example.pdf')
3. VerticalBarChart(柱形图表)
参考:
https://blog.csdn.net/weixin_49278803/article/details/132907427
https://www.cnblogs.com/hujq1029/p/7767980.html
https://zhuanlan.zhihu.com/p/528669672
3.1. 多组多条数据
from reportlab.platypus import SimpleDocTemplate
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.graphics.charts.barcharts import VerticalBarChart # 图表类
from reportlab.graphics.charts.legends import Legend # 图例类
from reportlab.graphics.shapes import Drawing # 绘图工具
# 多组多列
def draw_group_bars1(datas: list, categorys: list, legends: list):
drawing = Drawing(500, 250)
bc = VerticalBarChart()
bc.x = 45 # 整个图表的x坐标
bc.y = 45 # 整个图表的y坐标
bc.height = 200 # 图表的高度
bc.width = 350 # 图表的宽度
bc.data = datas
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = 5000 # 设置y坐标的最小值
bc.valueAxis.valueMax = 26000 # 设置y坐标的最大值
bc.valueAxis.valueStep = 2000 # 设置y坐标的步长
bc.groupSpacing = 10 # 每组柱状图之间的间隔
# bc.barSpacing = 1 # 每个柱状图之间的间隔
bc.categoryAxis.labels.dx = 2 # x和y是x轴下方的标签距离x轴远近的坐标
bc.categoryAxis.labels.dy = -8
# bc.categoryAxis.labels.angle = 20 # x轴上描述文字的倾斜角度
bc.categoryAxis.categoryNames = categorys
bc.bars[0].fillColor = colors.red
bc.bars[1].fillColor = colors.orange
bc.bars[2].fillColor = colors.yellow
bc.bars[3].fillColor = colors.green
# 图示
leg = Legend()
leg.fontName = 'simsun'
leg.alignment = 'right' # 文字在色块的左或者右
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 10 # 色块与文字之间的间距
leg.columnMaximum = len(legends)
leg.colorNamePairs = legends
drawing.add(leg)
drawing.add(bc)
return drawing
# 多组多列
def draw_group_bars2(datas: list, categorys: list, legends: list):
drawing = Drawing(500, 250)
bc = VerticalBarChart()
bc.x = 45 # 整个图表的x坐标
bc.y = 45 # 整个图表的y坐标
bc.height = 200 # 图表的高度
bc.width = 350 # 图表的宽度
bc.data = datas
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = 5000 # 设置y坐标的最小值
bc.valueAxis.valueMax = 26000 # 设置y坐标的最大值
bc.valueAxis.valueStep = 2000 # 设置y坐标的步长
bc.groupSpacing = 10 # 每组柱状图之间的间隔
# bc.barSpacing = 1 # 每个柱状图之间的间隔
bc.categoryAxis.labels.dx = 2 # x和y是x轴下方的标签距离x轴远近的坐标
bc.categoryAxis.labels.dy = -8
bc.categoryAxis.labels.angle = 20 # x轴上描述文字的倾斜角度
bc.categoryAxis.categoryNames = categorys
bc.bars[0].fillColor = colors.red
bc.bars[1].fillColor = colors.orange
bc.bars[2].fillColor = colors.yellow
bc.bars[3].fillColor = colors.green
# 图示
leg = Legend()
leg.fontName = 'simsun'
leg.alignment = 'left' # 文字在色块的左或者右
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 20 # 色块与文字之间的间距
leg.columnMaximum = len(legends)
leg.colorNamePairs = legends
drawing.add(leg)
drawing.add(bc)
return drawing
def main(filename):
# pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
pdfmetrics.registerFont(TTFont('simsun', "simsun.ttc"))
doc = SimpleDocTemplate(filename, pagesize=A4)
Story = []
datas = [(25400, 12900, 20100, 20300, 20300, 17400),
(15800, 9700, 12982, 20100, 13900, 7623),
(20100, 13900, 9700, 12982, 7623, 15800),
(12900, 13900, 20300, 9283, 25400, 9700)]
categorys = ['BeiJing', 'ChengDu', 'ShenZhen', 'ShangHai', 'HangZhou', 'NanJing']
legends = [(colors.red, '标准1'), (colors.orange, '标准2'), (colors.yellow, '标准3'), (colors.green, '标准4')]
Story.append(draw_group_bars1(datas, categorys, legends))
Story.append(draw_group_bars2(datas, categorys, legends))
doc.build(Story)
if __name__ == '__main__':
main(filename='example.pdf')
3.2. 多组单条数据
参考:https://dev59.com/uF7Va4cB1Zd3GeqPKod0
from reportlab.platypus import SimpleDocTemplate
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.graphics.charts.barcharts import VerticalBarChart # 图表类
from reportlab.graphics.charts.legends import Legend # 图例类
from reportlab.graphics.shapes import Drawing # 绘图工具
from reportlab.graphics.shapes import String
from reportlab.graphics.charts.textlabels import Label
# 多组单条
def draw_group_bar1(datas: list, categorys: list, legends: list):
drawing = Drawing(500, 280)
# drawing.add(String(45, 260, "多组单条柱状图", fontSize=14, fontName='simsun', fillColor=colors.black))
# 配置图表标题
title = Label()
title.setText("多组单条柱状图")
# title.fontSize, title.fontName, title.dx, title.dy = 14, "simsun", 45, 260
title.fontSize, title.fontName, title.x, title.y = 14, "simsun", 45, 260
drawing.add(title)
bc = VerticalBarChart()
bc.x = 45 # 整个图表的x坐标
bc.y = 45 # 整个图表的y坐标
bc.height = 200 # 图表的高度
bc.width = 350 # 图表的宽度
bc.data = datas
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = 5000 # 设置y坐标的最小值
bc.valueAxis.valueMax = 26000 # 设置y坐标的最大值
bc.valueAxis.valueStep = 2000 # 设置y坐标的步长
bc.groupSpacing = 10 # 每组柱状图之间的间隔
# bc.barSpacing = 1 # 每个柱状图之间的间隔
bc.categoryAxis.labels.dx = 2
bc.categoryAxis.labels.dy = -8
# bc.categoryAxis.labels.angle = 20
bc.categoryAxis.categoryNames = categorys
# 图形柱上标注文字
# bc.barLabels.nudge = -10 # 文字在图形柱的上下位置
bc.barLabels.nudge = 10 # 文字在图形柱的上下位置
# bc.barLabelArray = labels # 要添加的文字
# bc.barLabelFormat = 'values' # 以字符串类型,还有函数类型,不会用
# bc.barLabelFormat = "%0.0f"
bc.barLabelFormat = "%d"
bc.bars[0].fillColor = colors.red
bc.bars[1].fillColor = colors.orange
bc.bars[2].fillColor = colors.yellow
bc.bars[3].fillColor = colors.green
# 图示
leg = Legend()
leg.fontName = 'simsun'
leg.alignment = 'right' # 文字在色块的左或者右
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 10 # 色块与文字之间的间距
leg.columnMaximum = len(legends)
leg.colorNamePairs = legends
drawing.add(leg)
drawing.add(bc)
return drawing
def draw_group_bar2(datas: list, labels: list, categorys: list, legends: list):
drawing = Drawing(500, 280)
drawing.add(String(45, 260, "多组单条柱状图", fontSize=14, fontName='simsun', fillColor=colors.black))
bc = VerticalBarChart()
bc.x = 45 # 整个图表的x坐标
bc.y = 45 # 整个图表的y坐标
bc.height = 200 # 图表的高度
bc.width = 350 # 图表的宽度
bc.data = datas
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = 5000 # 设置y坐标的最小值
bc.valueAxis.valueMax = 26000 # 设置y坐标的最大值
bc.valueAxis.valueStep = 2000 # 设置y坐标的步长
bc.groupSpacing = 10 # 每组柱状图之间的间隔
# bc.barSpacing = 1 # 每个柱状图之间的间隔
bc.categoryAxis.labels.dx = 2
bc.categoryAxis.labels.dy = -8
bc.categoryAxis.labels.angle = 20
bc.categoryAxis.categoryNames = categorys
# 图形柱上标注文字
# bc.barLabels.nudge = -10 # 文字在图形柱的上下位置
bc.barLabels.nudge = 10 # 文字在图形柱的上下位置
bc.barLabelArray = labels # 要添加的文字
bc.barLabelFormat = 'values' # 以字符串类型,还有函数类型,不会用
# bc.barLabelFormat = "%0.0f"
# 每组0-3条的颜色
bc.bars[0].fillColor = colors.red
bc.bars[1].fillColor = colors.orange
bc.bars[2].fillColor = colors.yellow
bc.bars[3].fillColor = colors.green
# 每组第0条 也就是数组中 第一个元组的颜色
bc.bars[(0,0)].fillColor = colors.yellowgreen
bc.bars[(0,1)].fillColor = colors.blue
bc.bars[(0,2)].fillColor = colors.purple
bc.bars[(0,3)].fillColor = colors.peru
bc.bars[(0,4)].fillColor = colors.pink
bc.bars[(0,5)].fillColor = colors.coral
# 第1组(也就是第二组) 第1条(第二条)
bc.bars[(1,1)].fillColor = colors.gray
# 图示
leg = Legend()
leg.fontName = 'simsun'
leg.alignment = 'left' # 文字在色块的左或者右
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 10 # 色块与文字之间的间距
leg.columnMaximum = len(legends)
leg.colorNamePairs = legends
# drawing.add(leg)
drawing.add(bc)
return drawing
def main(filename):
# pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
pdfmetrics.registerFont(TTFont('simsun', "simsun.ttc"))
doc = SimpleDocTemplate(filename, pagesize=A4)
Story = []
datas = [(25400, 12900, 20100, 20300, 20300, 17400)]
categorys = ['BeiJing', 'ChengDu', 'ShenZhen', 'ShangHai', 'HangZhou', 'NanJing']
legends = [(colors.red, '标准1'), (colors.orange, '标准2'), (colors.yellow, '标准3'), (colors.green, '标准4')]
Story.append(draw_group_bar1(datas, categorys, legends))
datas = [(25400, 12900, 20100, 20300, 20300, 17400),
(15800, 9700, 12982, 20100, 13900, 7623),]
labels = [("25400", "12900", "20100", "20300", "20300", "17400"),
("15800", "9700", "12982", "20100", "13900", "7623"),]
categorys = ['BeiJing', 'ChengDu', 'ShenZhen', 'ShangHai', 'HangZhou', 'NanJing']
legends = [(colors.red, '标准1'), (colors.orange, '标准2'), (colors.yellow, '标准3'), (colors.green, '标准4')]
Story.append(draw_group_bar2(datas, labels, categorys, legends))
doc.build(Story)
if __name__ == '__main__':
main(filename='example.pdf')
3.3. 柱状图累加
from reportlab.platypus import SimpleDocTemplate
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.graphics.charts.barcharts import VerticalBarChart # 图表类
from reportlab.graphics.charts.legends import Legend # 图例类
from reportlab.graphics.shapes import Drawing # 绘图工具
from reportlab.graphics.shapes import String
from reportlab.graphics.charts.textlabels import Label
def func(datas: list):
minv, maxv, tmpv = 0, 0, 0
# print(len(datas), datas)
for idxj,val in enumerate(datas[0]):
tmpv = 0
for idxi in range(len(datas)):
tmpv += datas[idxi][idxj]
# print("#########333", tmpv)
if tmpv < minv:
minv = tmpv
if tmpv > maxv:
maxv = tmpv
return minv, maxv
# 柱状图累加
def draw_group_bar1(datas: list, categorys: list, legends: list):
drawing = Drawing(500, 280)
# drawing.add(String(45, 260, "柱状图累加", fontSize=14, fontName='simsun', fillColor=colors.black))
# 配置图表标题
title = Label()
title.setText("柱状图累加")
# title.fontSize, title.fontName, title.dx, title.dy = 14, "simsun", 45, 260
title.fontSize, title.fontName, title.x, title.y = 14, "simsun", 45, 260
drawing.add(title)
width = 350
height = 200
### 调整step
minv, maxv = func(datas)
maxAxis = int(height/20)
# step = int((maxv-minv+maxAxis-1)/maxAxis) # 向上取整
step = int((maxv-minv)/maxAxis) # 向上取整
bc = VerticalBarChart()
bc.x = 45 # 整个图表的x坐标
bc.y = 45 # 整个图表的y坐标
bc.height = height # 图表的高度
bc.width = width # 图表的宽度
bc.data = datas
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = minv # 设置y坐标的最小值
bc.valueAxis.valueMax = maxv*1.2 # 设置y坐标的最大值
bc.valueAxis.valueStep = step # 设置y坐标的步长
bc.groupSpacing = 10 # 每组柱状图之间的间隔
# bc.barSpacing = 1 # 每条柱状图之间的间隔
bc.categoryAxis.labels.dx = 2
bc.categoryAxis.labels.dy = -8
bc.categoryAxis.labels.angle = 20
bc.categoryAxis.categoryNames = categorys
# 图形柱上标注文字
# bc.barLabels.nudge = -10 # 文字在图形柱的上下位置
bc.barLabels.nudge = 10 # 文字在图形柱的上下位置
bc.barLabelFormat = "%0.0f"
# 柱状图累加
bc.categoryAxis.style = 'stacked'
# 图形柱上标注文字
# bc.barLabels.nudge = -10 # 文字在图形柱的上下位置
bc.barLabels.nudge = 10 # 文字在图形柱的上下位置
# bc.barLabelArray = labels # 要添加的文字
# bc.barLabelFormat = 'values' # 以字符串类型,还有函数类型,不会用
# bc.barLabelFormat = "%0.0f"
bc.barLabelFormat = "%d"
## 0-3条柱形图的颜色
# bc.bars[0].fillColor = colors.red
# bc.bars[1].fillColor = colors.orange
# bc.bars[2].fillColor = colors.yellow
# bc.bars[3].fillColor = colors.green
colorsb = [colors.yellowgreen, colors.blue, colors.purple, colors.peru, colors.pink, colors.coral]
colorsg = [colors.red, colors.orange, colors.yellow, colors.green]
for idxi in range(len(datas)):
setattr(bc.bars[idxi], 'fillColor', colorsg[idxi]) # bar_color若含有多种颜色在这里分配bar_color[j]
# 图示
leg = Legend()
leg.fontName = 'simsun'
leg.alignment = 'right' # 文字在色块的左或者右
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 10 # 色块与文字之间的间距
leg.columnMaximum = len(legends)
leg.colorNamePairs = legends
drawing.add(leg)
drawing.add(bc)
return drawing
def draw_group_bar2(datas: list, categorys: list, legends: list):
drawing = Drawing(500, 280)
drawing.add(String(45, 260, "多组单条柱状图", fontSize=14, fontName='simsun', fillColor=colors.black))
width = 350
height = 200
# 调整step
minv, maxv = func(datas)
maxAxis = int(height/20)
# step = int((maxv-minv+maxAxis-1)/maxAxis) # 向上取整
step = int((maxv-minv)/maxAxis) # 向上取整
bc = VerticalBarChart()
bc.x = 45 # 整个图表的x坐标
bc.y = 45 # 整个图表的y坐标
bc.height = height # 图表的高度
bc.width = width # 图表的宽度
bc.data = datas
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = minv # 设置y坐标的最小值
bc.valueAxis.valueMax = maxv*1.2 # 设置y坐标的最大值
bc.valueAxis.valueStep = step # 设置y坐标的步长
bc.groupSpacing = 10 # 每组柱状图之间的间隔
# bc.barSpacing = 1 # 每条柱状图之间的间隔
bc.categoryAxis.labels.dx = 2
bc.categoryAxis.labels.dy = -8
bc.categoryAxis.labels.angle = 20
bc.categoryAxis.categoryNames = categorys
# 图形柱上标注文字
# bc.barLabels.nudge = -10 # 文字在图形柱的上下位置
bc.barLabels.nudge = 10 # 文字在图形柱的上下位置
bc.barLabelFormat = "%0.0f"
# 柱状图累加
bc.categoryAxis.style = 'stacked'
colorsb = [colors.yellowgreen, colors.blue, colors.purple, colors.peru, colors.pink, colors.coral]
colorsg = [colors.red, colors.orange, colors.yellow, colors.green]
for idxi in range(len(datas)):
for idxj in range(len(datas[0])):
if idxi == 0:
setattr(bc.bars[(idxi, idxj)], 'fillColor', colorsb[idxj])
else:
setattr(bc.bars[(idxi, idxj)], 'fillColor', colorsg[idxi])
# 图示
leg = Legend()
leg.fontName = 'simsun'
leg.alignment = 'left' # 文字在色块的左或者右
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 10 # 色块与文字之间的间距
leg.columnMaximum = len(legends)
leg.colorNamePairs = legends
# drawing.add(leg)
drawing.add(bc)
return drawing
def main(filename):
# pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
pdfmetrics.registerFont(TTFont('simsun', "simsun.ttc"))
doc = SimpleDocTemplate(filename, pagesize=A4)
Story = []
datas = [(25400, 12900, 20100, 20300, 20300, 17400),
(15800, 9700, 12982, 20100, 13900, 7623),]
categorys = ['BeiJing', 'ChengDu', 'ShenZhen', 'ShangHai', 'HangZhou', 'NanJing']
legends = [(colors.red, '标准1'), (colors.orange, '标准2'), (colors.yellow, '标准3'), (colors.green, '标准4')]
Story.append(draw_group_bar1(datas, categorys, legends))
Story.append(draw_group_bar2(datas, categorys, legends))
doc.build(Story)
if __name__ == '__main__':
main(filename='example.pdf')
3.4. 调整 Drawing 布局
from reportlab.platypus import SimpleDocTemplate
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.graphics.charts.barcharts import VerticalBarChart # 图表类
from reportlab.graphics.charts.legends import Legend # 图例类
from reportlab.graphics.shapes import Drawing # 绘图工具
from reportlab.graphics.shapes import String, Rect
from reportlab.graphics.charts.textlabels import Label
# 多组单条
def draw_group_bar1(datas: list, categorys: list, legends: list):
drawing = Drawing(500, 280)
drawing.hAlign = 'CENTRE' # "LEFT", "RIGHT", "CENTER", "CENTRE" # 控制左右布局
drawing.add(Rect(0, 0, 500, 280, fillColor=colors.violet)) # 添加背景色
# drawing.vAlign = "TOP" # , "BOTTOM", "CENTER", "CENTRE"
# drawing.add(String(45, 260, "多组单条柱状图", fontSize=14, fontName='simsun', fillColor=colors.black))
# 配置图表标题
title = Label()
title.setText("多组单条柱状图")
# title.fontSize, title.fontName, title.dx, title.dy = 14, "simsun", 45, 260
title.fontSize, title.fontName, title.x, title.y = 14, "simsun", 500/2, 260
drawing.add(title)
bc = VerticalBarChart()
bc.x = 45 # 整个图表的x坐标
bc.y = 45 # 整个图表的y坐标
bc.height = 200 # 图表的高度
bc.width = 350 # 图表的宽度
bc.data = datas
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = 5000 # 设置y坐标的最小值
bc.valueAxis.valueMax = 26000 # 设置y坐标的最大值
bc.valueAxis.valueStep = 2000 # 设置y坐标的步长
bc.groupSpacing = 10 # 每组柱状图之间的间隔
# bc.barSpacing = 1 # 每个柱状图之间的间隔
bc.categoryAxis.labels.dx = 2
bc.categoryAxis.labels.dy = -8
# bc.categoryAxis.labels.angle = 20
bc.categoryAxis.categoryNames = categorys
# bc.categoryAxis.labels.boxAnchor = 'ne' # x轴下方标签坐标的开口方向
# 图形柱上标注文字
# bc.barLabels.nudge = -10 # 文字在图形柱的上下位置
bc.barLabels.nudge = 10 # 文字在图形柱的上下位置
# bc.barLabelArray = labels # 要添加的文字
# bc.barLabelFormat = 'values' # 以字符串类型,还有函数类型,不会用
# bc.barLabelFormat = "%0.0f"
bc.barLabelFormat = "%d"
bc.bars[0].fillColor = colors.red
bc.bars[1].fillColor = colors.orange
bc.bars[2].fillColor = colors.yellow
bc.bars[3].fillColor = colors.green
# 图示
leg = Legend()
leg.fontName = 'simsun'
leg.alignment = 'right' # 文字在色块的左或者右
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 10 # 色块与文字之间的间距
leg.columnMaximum = len(legends)
leg.colorNamePairs = legends
drawing.add(leg)
drawing.add(bc)
return drawing
def draw_group_bar2(datas: list, labels: list, categorys: list, legends: list):
drawing = Drawing(500, 280)
drawing.hAlign = 'LEFT' # "LEFT", "RIGHT", "CENTER", "CENTRE"
drawing.add(Rect(0, 0, 500, 250, fillColor=colors.lightblue))
# drawing.add(String(45, 260, "多组单条柱状图", fontSize=14, fontName='simsun', fillColor=colors.black))
drawing.add(String(500/2, 255, "多组单条柱状图", fontSize=14, fontName='simsun', fillColor=colors.black, textAnchor='middle'))
bc = VerticalBarChart()
# bc.x = 45 # 整个图表的x坐标
# bc.y = 45 # 整个图表的y坐标
bc.x = 0 # 整个图表的x坐标
bc.y = 0 # 整个图表的y坐标
bc.height = 200 # 图表的高度
bc.width = 350 # 图表的宽度
bc.data = datas
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = 5000 # 设置y坐标的最小值
bc.valueAxis.valueMax = 26000 # 设置y坐标的最大值
bc.valueAxis.valueStep = 2000 # 设置y坐标的步长
bc.groupSpacing = 10 # 每组柱状图之间的间隔
# bc.barSpacing = 1 # 每个柱状图之间的间隔
bc.categoryAxis.labels.dx = 2
bc.categoryAxis.labels.dy = -8
bc.categoryAxis.labels.angle = 20
bc.categoryAxis.categoryNames = categorys
bc.categoryAxis.labels.boxAnchor = 'ne' # x轴下方标签坐标的开口方向
# 图形柱上标注文字
# bc.barLabels.nudge = -10 # 文字在图形柱的上下位置
bc.barLabels.nudge = 10 # 文字在图形柱的上下位置
bc.barLabelArray = labels # 要添加的文字
bc.barLabelFormat = 'values' # 以字符串类型,还有函数类型,不会用
# bc.barLabelFormat = "%0.0f"
# 每组0-3条的颜色
bc.bars[0].fillColor = colors.red
bc.bars[1].fillColor = colors.orange
bc.bars[2].fillColor = colors.yellow
bc.bars[3].fillColor = colors.green
# 每组第0条 也就是数组中 第一个元组的颜色
bc.bars[(0,0)].fillColor = colors.yellowgreen
bc.bars[(0,1)].fillColor = colors.blue
bc.bars[(0,2)].fillColor = colors.purple
bc.bars[(0,3)].fillColor = colors.peru
bc.bars[(0,4)].fillColor = colors.pink
bc.bars[(0,5)].fillColor = colors.coral
# 第1组(也就是第二组) 第1条(第二条)
bc.bars[(1,1)].fillColor = colors.gray
# 图示
leg = Legend()
leg.fontName = 'simsun'
leg.alignment = 'left' # 文字在色块的左或者右
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 10 # 色块与文字之间的间距
leg.columnMaximum = len(legends)
leg.colorNamePairs = legends
# drawing.add(leg)
drawing.add(bc)
return drawing
def main(filename):
# pdfmetrics.registerFont(TTFont('微软雅黑', 'msyh.ttf'))
pdfmetrics.registerFont(TTFont('simsun', "simsun.ttc"))
doc = SimpleDocTemplate(filename, pagesize=A4)
Story = []
datas = [(25400, 12900, 20100, 20300, 20300, 17400)]
categorys = ['BeiJing', 'ChengDu', 'ShenZhen', 'ShangHai', 'HangZhou', 'NanJing']
legends = [(colors.red, '标准1'), (colors.orange, '标准2'), (colors.yellow, '标准3'), (colors.green, '标准4')]
Story.append(draw_group_bar1(datas, categorys, legends))
datas = [(25400, 12900, 20100, 20300, 20300, 17400),
(15800, 9700, 12982, 20100, 13900, 7623),]
labels = [("25400", "12900", "20100", "20300", "20300", "17400"),
("15800", "9700", "12982", "20100", "13900", "7623"),]
categorys = ['BeiJing', 'ChengDu', 'ShenZhen', 'ShangHai', 'HangZhou', 'NanJing']
legends = [(colors.red, '标准1'), (colors.orange, '标准2'), (colors.yellow, '标准3'), (colors.green, '标准4')]
Story.append(draw_group_bar2(datas, labels, categorys, legends))
doc.build(Story)
if __name__ == '__main__':
main(filename='example.pdf')
4. 饼状图
5. Image(图像)
6. 颜色
6.1. RGB 颜色
from reportlab.lib.colors import Color, black, blue, red
# 透明度为50%的红色
# 最大值好像是100,也支持(0,1]。如果RGB 256的话,需要转换成浮点数。
red50transparent = Color(100, 0, 0, alpha=0.5)
6.1.1. 颜色控制实例
from reportlab.pdfgen import canvas
LEFT = 20
def colorsRGB(canvas):
from reportlab.lib import colors
from reportlab.lib.units import inch
# 使用color模块生成颜色
black = colors.black
y = 500
# x = LEFT; dy=inch*3/4.0; dx=inch*5.5/5; w=h=dy/2; rdx=(dx-w)/2
x = LEFT
dy=inch*3/4.0; dx=inch*7.5/5; w=h=dy/2; rdx=(dx-w)/2
rdy=h/5.0; texty=h+2*rdy
canvas.setFont("Helvetica",10)
# 使用预定义颜色
for [namedcolor, name] in (
[colors.lavenderblush, "lavenderblush"],
[colors.lawngreen, "lawngreen"],
[colors.lemonchiffon, "lemonchiffon"],
[colors.lightblue, "lightblue"],
[colors.lightcoral, "lightcoral"]):
canvas.setFillColor(namedcolor)
canvas.rect(x+rdx, y+rdy, w, h, fill=1)
canvas.setFillColor(black)
canvas.drawCentredString(x+dx/2, y+texty, name)
x = x+dx
# 使用RGB值生成颜色
y = y + dy; x = LEFT
for rgb in [(1,0,0), (0,1,0), (0,0,1), (0.5,0.3,0.1), (0.4,0.5,0.3)]:
r,g,b = rgb
canvas.setFillColorRGB(r,g,b)
canvas.rect(x+rdx, y+rdy, w, h, fill=1)
canvas.setFillColor(black)
canvas.drawCentredString(x+dx/2, y+texty, "r%s g%s b%s"%rgb)
x = x+dx
# 使用RGB值生成颜色
y = y + dy; x = LEFT
alpha = 0.5
for rgb in [(1,0,0), (0,1,0), (0,0,1), (0.5,0.3,0.1), (0.4,0.5,0.3)]:
r,g,b = rgb
canvas.setFillColorRGB(r,g,b, alpha=alpha)
canvas.rect(x+rdx, y+rdy, w, h, fill=1)
canvas.setFillColor(black)
canvas.drawCentredString(x+dx/2, y+texty, "r%s g%s b%s alpha0.5"%rgb)
x = x+dx
# 使用RGB值生成颜色
y = y + dy; x = LEFT
alpha = 0.2
for rgb in [(1,0,0), (0,1,0), (0,0,1), (0.5,0.3,0.1), (0.4,0.5,0.3)]:
r,g,b = rgb
canvas.setFillColorRGB(r,g,b, alpha=alpha)
canvas.rect(x+rdx, y+rdy, w, h, fill=1)
canvas.setFillColor(black)
canvas.drawCentredString(x+dx/2, y+texty, "r%s g%s b%s alpha0.2"%rgb)
x = x+dx
y = y + dy; x = LEFT
# 使用灰度生成颜色
for gray in (0.0, 0.25, 0.50, 0.75, 1.0):
canvas.setFillGray(gray)
canvas.rect(x+rdx, y+rdy, w, h, fill=1)
canvas.setFillColor(black)
canvas.drawCentredString(x+dx/2, y+texty, "gray: %s"%gray)
x = x+dx
c= canvas.Canvas("colorsRGB.pdf")
colorsRGB(c)
c.showPage()
c.save()
6.2. 常用颜色
from reportlab.lib.colors import Color, black, blue, red
colors.red
7. 字体与编码
https://blog.csdn.net/qq_40596572/article/details/102896520
字体
https://blog.csdn.net/qtlyx/article/details/99653081