ReportLab 导出 PDF(图文表格)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值