生成Pdf
- 页眉, 页尾, 画图, 表格, 画线...
from reportlab.pdfbase import pdfmetrics # 注册字体
from reportlab.pdfbase.ttfonts import TTFont # 字体类
from reportlab.platypus import Table, SimpleDocTemplate, Paragraph, Image ,Frame, PageTemplate, Spacer, TableStyle, BaseDocTemplate# 报告内容相关类,
from reportlab.lib.pagesizes import letter, landscape # 页面的标志尺寸(8.5*inch, 11*inch)
from reportlab.lib.styles import getSampleStyleSheet # 文本样式
from reportlab.lib import colors # 颜色模块
from reportlab.graphics.charts.barcharts import VerticalBarChart # 图表类
from reportlab.graphics.charts.legends import Legend # 图例类
from reportlab.graphics.shapes import Drawing # 绘图工具
from reportlab.lib.units import cm # 单位:cm
import io
# 注册字体(提前准备好字体文件, 如果同一个文件需要多种字体可以注册多个)
# pdfmetrics.registerFont(TTFont('SimSun', 'SimSun.ttf'))
class Graphs(object):
def __init__(self):
# 获取所有样式表
self.style = getSampleStyleSheet()
# 绘制标题
def draw_1_title(self, title1: str):
# 拿到标题样式
ct = self.style['Heading1']
# 单独设置样式相关属性
ct.fontName = 'hei' # 字体名
ct.fontSize = 20 # 字体大小
ct.leading = 1.5 * ct.fontSize # 行间距
ct.textColor = colors.black # 字体颜色
ct.alignment = 1 # 居中
return Paragraph(title1)
# 绘制小标题(黑色)
def draw_2_title(self, title: str, content: str):
# 拿到标题样式
ct = self.style['Normal']
# 单独设置样式相关属性
ct.fontName = 'song' # 字体名
ct.fontSize = 15 # 字体大小
ct.leading = 1.5 * ct.fontSize # 行间距
ct.textColor = colors.black # 字体颜色
text = title+ content
return Paragraph(text)
# 绘制普通段落内容
def draw_text(self, text: str):
# 获取普通样式
ct = self.style['Normal']
ct.fontName = 'song'
ct.fontSize = 12
ct.leading = 1.5 * ct.fontSize # 行间距
ct.wordWrap = 'CJK' # 设置自动换行
ct.alignment = 0 # 左对齐
ct.firstLineIndent = 32 # 第一行开头空格
ct.leading = 25
return Paragraph(text, ct)
# 绘制表格标题
def draw_table_title(self, title: str):
# 拿到标题样式
ct = self.style['Heading1']
# 单独设置样式相关属性
ct.fontName = 'Dengb' # 字体名
ct.fontSize = 14 # 字体大小
ct.leading = 15 # 行间距
ct.textColor = colors.black # 字体颜色
ct.alignment = 1 # 居中
ct.bold = True
# 创建标题对应的段落,并且返回
return Paragraph(title)
# 绘制表格
@staticmethod
def draw_table(*args):
# 列宽度
col_width = [50, 300, 50, 50]
style = TableStyle(
[
# ('FONTNAME', (0, 0), (-1, -1), 'song'), # 全表字体
# ('FONTNAME', (0, 0), (-1, 0), 'Dengb'), # 首行字体
('FONTSIZE', (0, 0), (-1, 0), 12), # 第一行的字体大小
('FONTSIZE', (0, 1), (-1, -1), 10), # 第二行到最后一行的字体大小
('ALIGN', (0, 0), (-1, -1), 'CENTER'), # 第一行水平居中
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), # 所有表格上下居中对齐
('TEXTCOLOR', (0, 0), (-1, -1), colors.black), # 设置表格内文字颜色(不包含第一行)
('GRID', (0, 0), (-1, -1), 0.5, colors.black), # 设置表格框线为grey色,线宽为0.5
# ('SPAN', (0, 1), (0, 2)), # 合并第一列二三行
# ('SPAN', (0, 3), (0, 4)), # 合并第一列三四行
# ('SPAN', (0, 5), (0, 6)), # 合并第一列五六行
# ('SPAN', (0, 7), (0, 8)), # 合并第一列五六行
])
table = Table(args, colWidths=col_width, style=style)
return table
# 创建图表
@staticmethod
def draw_bar(bar_data: list, ax: list, items: list):
drawing = Drawing(500, 250)
bc = VerticalBarChart()
bc.x = 45 # 整个图表的x坐标
bc.y = 45 # 整个图表的y坐标
bc.height = 200 # 图表的高度
bc.width = 350 # 图表的宽度
bc.data = bar_data
bc.strokeColor = colors.black # 顶部和右边轴线的颜色
bc.valueAxis.valueMin = 5000 # 设置y坐标的最小值
bc.valueAxis.valueMax = 26000 # 设置y坐标的最大值
bc.valueAxis.valueStep = 2000 # 设置y坐标的步长
bc.categoryAxis.labels.dx = 2
bc.categoryAxis.labels.dy = -8
bc.categoryAxis.labels.angle = 20
bc.categoryAxis.categoryNames = ax
# 图示
leg = Legend()
leg.fontName = 'song'
leg.alignment = 'right'
leg.boxAnchor = 'ne'
leg.x = 475 # 图例的x坐标
leg.y = 240
leg.dxTextSpace = 10
leg.columnMaximum = 3
leg.colorNamePairs = items
drawing.add(leg)
drawing.add(bc)
return drawing
# 绘制图片
@staticmethod
def draw_img(path):
img = Image(path, width=50, height=50, kind='bound') # 读取指定路径下的图片
return img
# 设置页脚
def footer(self, canvas, doc):
"""
设置页脚--页码
:param canvas:Canvas类型 pdf画布
:param doc:doc类型 整个pdf文件
""" # 拿到标题样式
# 1.
canvas.saveState() # 先保存当前的画布状态
pageNumber = ("%s" % canvas.getPageNumber()) # 获取当前的页码
p = Paragraph(pageNumber)
p.wrap(1 * cm, 1 * cm) # 申请一块1cm大小的空间,返回值是实际使用的空间
p.drawOn(canvas, 720, 50) # 将页码放在指示坐标处
p = Paragraph("time: 2020-20-20")
p.wrap(3 * cm, 3 * cm) # 申请一块1cm大小的空间,返回值是实际使用的空间
p.drawOn(canvas, 20, 50) # 将页码放在指示坐标处
p = Paragraph("page No: 1")
p.wrap(4 * cm, 4 * cm) # 申请一块1cm大小的空间,返回值是实际使用的空间
p.drawOn(canvas, 400, 50) # 将页码放在指示坐标处
canvas.restoreState()
# 2.
# canvas.saveState() # 先保存当前的画布状态
# rows = [
# ['time', 'page no', str(canvas.getPageNumber())],
# ]
# t = self.draw_table(*rows)
# canvas.restoreState()
# 设置页眉
def header(self, canvas, doc):
"""
设置页眉
:param canvas:Canvas类型 pdf画布
:param doc:doc类型 整个pdf文件
https://cxybb.com/article/liyadian/81253117
"""
canvas.saveState()
img = self.draw_img("utils/map_png/image/logo.png")
# Image("utils/map_png/image/logo.png", height=20, width=98)
p = Paragraph("header_title")
p.wrap(3 * cm, 3 * cm) # 申请一块1cm大小的空间,返回值是实际使用的空间
p.drawOn(canvas, 400, 550) # 将页码放在指示坐标处
canvas.restoreState()
import datetime
def main():
# 获取Graphs类
graphs = Graphs()
# 创建内容对应的空列表
content = list()
# 1.添加标题
content.append(graphs.draw_1_title('date-title'))
# 隔行的高度
content.append(Spacer(0, 10))
# 2.添加小标题
content.append(graphs.draw_2_title("table create date: ", str(datetime.date.today().strftime("%Y/%m/%d"))))
content.append(Spacer(0, 10))
# 3.添加图片
# base = Path(__file__).resolve().parent
content.append(graphs.draw_table_title('image: 1'))
# img_path = os.path.join(base, 'map_png', out_file)
img_path = "G:\\python_work\\tests\\1.png"
content.append(graphs.draw_img(img_path))
content.append(Spacer(0, 10))
# 4.添加表格标题
content.append(graphs.draw_table_title('table:1'))
# 5.添加表格数据
data = [
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['1', '2', '3'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
['2', '3', '4'],
]
content.append(graphs.draw_table(*data))
# 生成图表
# content.append(Graphs.draw_title(''))
# content.append(Graphs.draw_little_title('热门城市的就业情况'))
# b_data = [(25400, 12900, 20100, 20300, 20300, 17400), (15800, 9700, 12982, 9283, 13900, 7623)]
# ax_data = ['BeiJing', 'ChengDu', 'ShenZhen', 'ShangHai', 'HangZhou', 'NanJing']
# leg_items = [(colors.red, '平均薪资'), (colors.green, '招聘量')]
# content.append(Graphs.draw_bar(b_data, ax_data, leg_items))
# 添加文字
# content.append(Graphs.draw_text(
# '众所周知,大数据分析师岗位是香饽饽,近几年数据分析热席卷了整个互联网行业,与数据分析的相关的岗位招聘、培训数不胜数。很多人前赴后继,想要参与到这波红利当中。那么数据分析师就业前景到底怎么样呢?'))
# 生成pdf文件
# 方式一:
# doc = SimpleDocTemplate('report.pdf', pagesize=letter)
# 方式二-存储文件:
# doc = BaseDocTemplate('report.pdf')
# 方式二-流文件格式
# buffer = io.BytesIO() # 重点 起一个 io
doc = BaseDocTemplate("report.pdf", pagesize=landscape(letter))
# 声明一块Frame,存放页码
frame_footer = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='normal')
# 设置页面模板,在加载页面时先运行herder函数,在加载完页面后运行footer函数
template = PageTemplate(id='test', frames=frame_footer, onPage=graphs.header, onPageEnd=graphs.footer)
doc.addPageTemplates([template])
doc.build(content)
return doc
from reportlab.platypus import Table, TableStyle,Image ,Paragraph, Frame, PageTemplate, BaseDocTemplate, LongTable
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.pagesizes import A4, landscape, letter
from reportlab.lib import colors
from reportlab.lib.units import cm
最新版本
def pdf_response(pdf_header, pdf_rows, file_name,start_date, end_date, username):
"""
pdf响应
"""
for index, header_field in enumerate(pdf_header):
pdf_header[index] = str(header_field).replace(" ", "\n")
pdf_buffer = io.BytesIO()
elements = []
col_width = [25, 65, 40, 55, 65, 115, 40] + [35] * 11 # 表格宽度
def draw_table(*args):
table_style = TableStyle([
('FONTSIZE', (0, 0), (-1, -1), 7),
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('BOX', (0, 0), (-1, -1), 0.25, colors.white),
])
styles = getSampleStyleSheet()
styleN = styles['Normal']
styleN.fontSize = 7
styleN.wordWrap = 'CJK'
data2 = [[Paragraph(cell, styleN) for cell in row] for row in args] # 自动换行,设置成段落
table_part = Table(data2, colWidths=col_width, style=table_style)
table_part.hAlign = "LEFT"
return table_part
def set_footer(canvas, doc):
"""
设置页脚--页码
:param canvas:Canvas类型 pdf画布
:param doc:doc类型 整个pdf文件
"""
canvas.saveState()
pageNumber = canvas.getPageNumber()
p = Paragraph(f"Page {pageNumber}") # 绘画文字段落
p.wrap(3 * cm, 1 * cm)
p.drawOn(canvas, 400, 50)
p = Paragraph(f"Printed on: {timezone.now().strftime('%Y-%m-%d %H:%M:%S')}")
p.wrap(6 * cm, 3 * cm)
p.drawOn(canvas, 20, 50)
p = Paragraph(f"Generate By: {username}")
p.wrap(4 * cm, 4 * cm)
p.drawOn(canvas, 650, 50)
canvas.restoreState()
def set_header(canvas, doc):
"""
设置页眉
:param canvas:Canvas类型 pdf画布
:param doc:doc类型 整个pdf文件
"""
canvas.saveState()
img = Image(os.path.join(settings.BASE_DIR, "mtr_logo.png")) # 绘画图片
img.drawWidth = 2.5*cm
img.drawHeight = 1*cm
img.drawOn(canvas, 14, 555)
table_header = Table([pdf_header], colWidths=col_width) # 绘画表格
table_header_style = TableStyle([
('FONTNAME', (0, 0), (-1, -1), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, -1), 8),
('BOTTOMPADDING', (0, 0), (-1, -1), 6),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('TEXTCOLOR', (0, 0), (-1, -1), '#000000'),
('BOX', (0, 0), (-1, -1), 0.25, colors.white),
])
table_header.setStyle(table_header_style)
table_header.wrap(20 *cm, 5 * cm)
table_header.drawOn(canvas, 10, 495)
canvas.setStrokeColor(colors.black)
canvas.line(doc.leftMargin + 5, doc.height - 30 , doc.width + doc.leftMargin, doc.height - 30) # 画线
canvas.line(doc.leftMargin + 5, doc.height -5 , doc.width + doc.leftMargin, doc.height -5)
top_font_style = ParagraphStyle(name="h3", fontSize=12, leading=21, alignment=1,fontName="Helvetica-Bold")
top = Paragraph("Notification of Completion of Incoming Goods Inspection (CEWS)", top_font_style) # 绘画文字
top.wrap(15 * cm, 5 * cm)
top.drawOn(canvas, 200, 545)
date_font_style = ParagraphStyle(name="date", fontSize=10, fontName="Helvetica-Bold")
date_range = Paragraph(f"Completion Date : {start_date} to {end_date}", date_font_style)
date_range.wrap(10 * cm, 4 * cm)
date_range.drawOn(canvas, 12, 535)
canvas.restoreState()
# 主体内容
if pdf_rows:
elements.append(draw_table(*pdf_rows))
else:
elements.append(Paragraph("No Data"))
pdf = BaseDocTemplate(pdf_buffer, pagesize=landscape(letter))
pdf.leftMargin = 5
pdf.rightMargin = 5
pdf.topMargin = 20
pdf.bottomMargin = 60
# 页眉,页尾
frame_footer = Frame(pdf.leftMargin, pdf.bottomMargin, pdf.width, pdf.height - 20, id='normal')
template = PageTemplate(id='test', frames=frame_footer, onPage=set_header, onPageEnd=set_footer)
pdf.addPageTemplates([template])
pdf.build(elements)
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = f"attachment; filename={file_name}"
response.write(pdf_buffer.getvalue())
pdf_buffer.close()
return response