您的位置:首页 > 编程语言 > Go语言

Django使用reportlab套件生成PDF文件

2018-04-20 13:39 507 查看

简介

项目中生成了一个实例,需要提供给用户下载pdf文件功能。

最开始想到的是使用前段技术,直接将html页面保存为pdf文件。这个过程使用了html2pdf,这个纯js项目。该项目会将指定的DOM元素通过html2canvas工具保存为图片,然后通过jsPDF工具将保存的图片最终以pdf文件形式展现,并直接供下载。

这样做有个缺点就是当页面非常长时,就有可能出现一行文字被从中间截断,展示在两页pdf文件中的情况。因而需要寻找更高级,输出格式更优雅的解决方案。最后发现了可以通过Reportlab套件来完成工作。

工作流

要想通过Reportlab套件来生成pdf文件的工作流程主要分三步:

django后台通过与数据库或者其他调用获取数据

将这些数据传入到RML(Report Markable Language)模板,并调用reportlab套件生成二进制数据

django根据生成的数据返回响应

第1和第3步可以参考官方outputting pdf文档

比较关键的是第2步如何实现,下面将针对RML、以及如何调用reportlab套件生成二进制数据进行详细讲解

RML和Reportlab套件使用

关于RML可以参考Reportlab提供的RML-for-IdiotsRML User Guide两个文档进行学习,这里并不赘述。只介绍个人使用的一些体会。

首页与其余页使用不同模板

要想实现第一页和后面其余页使用不同的模板,在
<story>
上添加
firstPageTemplate
属性,指定第一页的模板。紧接着使用
<setNextTempate name='main'>
来指定剩余页(如果有的话)使用的模板。

如果想整个pdf使用同一个模板的话,则无须使用
<setNextTempate name='main'>


<?xml version="1.0" encoding="utf-8" standalone="no" ?>
<!DOCTYPE document SYSTEM "rml_1_0.dtd">
<document filename="rml.pdf">

<template pagesize="(595, 842)" leftMargin="72">
<pageTemplate id="head">
<pageGraphics>
<setFont name="bighei" size="24"/>
<drawCentredString x="297.5" y="760">{{ name }}</drawCentredString>
</pageGraphics>
<frame id="first" x1="80" y1="80" width="435" height="550"/>
</pageTemplate>
<pageTemplate id="main">
<frame id="first" x1="80" y1="80" width="435" height="682"/>
</pageTemplate>
</template>

<stylesheet>
<initialize>
<alias id="style.normal" value="style.Normal"/>
</initialize>

<paraStyle name="chapter title"    fontName="bighei" fontSize="20" leading="36"                  spaceAfter="10"/>

<paraStyle name="question stem"    fontName="bighei" fontSize="12" leading="12" spaceBefore="15" spaceAfter="10"/>

<paraStyle name="question options" fontName="bighei" fontSize="10" leading="12"                  spaceAfter="5" />
</stylesheet>

<story firstPageTemplate="head">

<setNextTemplate name="main" />
{{ paras }}

</story>

</document>


通过Django template解决RML不能嵌套循环问题

个人在使用过程中发现无法在RML模板中进行嵌套循环,

{{ for outter_item in outter_list }}
{{ for inner_item in outter_item }}
{{ innger_item }}
{{ endfor }}
{{ endfor }}

但是Django模板中并无此限制,因而可以直接调用
render_to_string
方法将RML需要的字符串使用django的机制来生成,然后再传给RML解决这个问题。

from io import BytesIO
import preppy
import trml2pdf

TEMPLATE = os.path.join(PATH_TO_TEMPLATE_DIR, 'template.prep')
def generate_pdf():

django_context = dict()

paras = render_to_string('django_templates.html', django_context)

rml_context = dict(
name='RML Test',
paras=paras.encode("utf-8")
)

template = preppy.getModule(template_path)
rml = template.getOutput(rml_context)

return trml2pdf.parseString(rml)


解决中文乱码问题

RML默认不支持中文,需要自己注册支持中文的字体

from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase import ttfonts

pdfmetrics.registerFont(ttfonts.TTFont(font_name, font_path))


Django views

def test_rml(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="rml.pdf"'

pdf = generate_pdf()

response.write(pdf)

return response
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: