- 官方Demo
官網提供了一個demo,下載地址:
http://www.reportlab.com/static/cms/files/RLtutorial.zip
在運行前需要安裝相關的依賴包。在ReportLab網站注冊并登陸后會有更完整的文檔。
- rlextra: 安裝該包前需要注冊ReportLab用戶,登陸后才能下載。
pip install rlextra -i https://www.reportlab.com/pypi
在下載完成rlextra后會自動下載其他依賴包。 - preppy: 一種預處理程序,利用其來讀取模板template。
參考: https://bitbucket.org/rptlab/preppy - reportlab
- pyRXP
- 等等
安裝完成相關包后執行文件夾下product_catalog.py
,仍發現有很多錯誤,主要集中在編碼,StringIO/BytesIO(Demo文件中是用了StringIO作為buf,但執行報錯,查看相關源碼文件后發現需要用BytesIO。)上,除此之外Demo中的數據是通過解析XML文件得到的,可能以后使用中不會使用XML來獲取數據,所以決定自己重新寫一個新的Demo。
- 自己編寫的SimpleDemo
簡單的生成一份pdf報表主要需要三方面的準備:數據,模板文件(prep, rml),相關簡單工具方法。編寫模板文件和數據準備相比較更重要,更繁瑣,而簡單的編寫工具方法比較輕松,不過后期肯定需要優化。
- 數據: 從mongodb數據庫中獲取。
- 模板文件: 利用RML(Report Markup Language)編寫模板,可在其中嵌套Python代碼,詳細文檔可登陸ReportLab后下載。下面是一個最基本的RML模板(取自官方RML2PDF with Django Demo):
<!DOCTYPE document SYSTEM "rml.dtd">
<document filename="hello.pdf">
<template showBoundary="0">
<pageTemplate id="main">
<frame id="first" x1="50" y1="200" width="450" height="300"/>
</pageTemplate>
</template>
<stylesheet>
<paraStyle name="textstyle1" fontName="Helvetica" fontSize="24" leading="24" />
</stylesheet>
<story>
<para style="textstyle1">
Welcome<b>{{name}}<b>, to the world of RML!
</para>
</story>
</document>
- 相關簡單工具方法:
from rlextra.rml2pdf import rml2pdf
import preppy
from io import BytesIO
class PDFUtils(object):
def __init__(self, template_name, namespace, output_filename=None):
self.template_name = template_name
self.namespace = namespace
self.output_filename = output_filename
# 其他相關操作(注冊字體等)
def create_pdf(self):
source_text = open(self.template_name, 'r', encoding='utf-8').read()
template = preppy.getModule(self.template_name, sourcetext=source_text)
# template = preppy.getModule(self.template_name)
rml = template.getOutput(self.namespace)
if self.output_filename:
rml2pdf.go(rml, outputFileName=self.output_filename)
return True
else:
buf = BytesIO()
rml2pdf.go(rml, outputFileName=buf)
pdf_data = buf.getvalue()
return pdf_data
需要注意的是,若要輸出IO流(如web請求時下載pdf報表文件),這里用的是BytesIO。若像官方Demo中用StringIO,在執行rml2pdf.go()
時會拋出TypeError: string argument expected, got 'bytes'
異常,查看文件pdfdoc.py源碼:
data = self.GetPDFData(canvas)
if isUnicode(data):
data = data.encode('latin1')
f.write(data)
這里self.GetPDFData(canvas)
返回的是bytes,在f.write()
時需要BytesIO類型。(暫時不知道其他解決辦法)
除此之外,在利用preppy.getModule()
解析rml文件時,若rml文件中出現中文字符直接利用preppy.getModule(template_name)
會出現gbk無法解析中文的情況,查看源碼發現當只有一個name
參數時,getModule()
會默認open(name, 'r').read()
出現UnicodeDecodeError
。所以使用利用sourcetext
參數,先按指定編碼讀取文件內容再解析,即
source_text = open(template_name, 'r', encoding='utf-8').read()
template = preppy.getModule(template_name, sourcetext=source_text)
```,同時需要在rml文件中注冊中文字體。
* main():
if name == 'main':
# 模板文件
template = 'hello.rml'
# 輸出的pdf
output_filename = 'output/simple_demo.pdf'
# 數據
namespace = {
'name': 'JiangW'
}
p = PDFUtils(template, namespace, output_filename=output_filename)
p.create_pdf()