- 官方Demo
官網(wǎng)提供了一個(gè)demo押蚤,下載地址:
http://www.reportlab.com/static/cms/files/RLtutorial.zip
在運(yùn)行前需要安裝相關(guān)的依賴包券勺。在ReportLab網(wǎng)站注冊(cè)并登陸后會(huì)有更完整的文檔。
- rlextra: 安裝該包前需要注冊(cè)ReportLab用戶,登陸后才能下載刑然。
pip install rlextra -i https://www.reportlab.com/pypi
在下載完成rlextra后會(huì)自動(dòng)下載其他依賴包括享。 - preppy: 一種預(yù)處理程序,利用其來(lái)讀取模板template断楷。
參考: https://bitbucket.org/rptlab/preppy - reportlab
- pyRXP
- 等等
安裝完成相關(guān)包后執(zhí)行文件夾下product_catalog.py
,仍發(fā)現(xiàn)有很多錯(cuò)誤崭别,主要集中在編碼冬筒,StringIO/BytesIO(Demo文件中是用了StringIO作為buf,但執(zhí)行報(bào)錯(cuò)茅主,查看相關(guān)源碼文件后發(fā)現(xiàn)需要用BytesIO账千。)上,除此之外Demo中的數(shù)據(jù)是通過(guò)解析XML文件得到的暗膜,可能以后使用中不會(huì)使用XML來(lái)獲取數(shù)據(jù)匀奏,所以決定自己重新寫(xiě)一個(gè)新的Demo。
- 自己編寫(xiě)的SimpleDemo
簡(jiǎn)單的生成一份pdf報(bào)表主要需要三方面的準(zhǔn)備:數(shù)據(jù)学搜,模板文件(prep, rml)娃善,相關(guān)簡(jiǎn)單工具方法。編寫(xiě)模板文件和數(shù)據(jù)準(zhǔn)備相比較更重要瑞佩,更繁瑣聚磺,而簡(jiǎn)單的編寫(xiě)工具方法比較輕松,不過(guò)后期肯定需要優(yōu)化炬丸。
- 數(shù)據(jù): 從mongodb數(shù)據(jù)庫(kù)中獲取瘫寝。
- 模板文件: 利用RML(Report Markup Language)編寫(xiě)模板蜒蕾,可在其中嵌套Python代碼,詳細(xì)文檔可登陸ReportLab后下載焕阿。下面是一個(gè)最基本的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>
- 相關(guān)簡(jiǎn)單工具方法:
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
# 其他相關(guān)操作(注冊(cè)字體等)
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請(qǐng)求時(shí)下載pdf報(bào)表文件),這里用的是BytesIO暮屡。若像官方Demo中用StringIO撤摸,在執(zhí)行rml2pdf.go()
時(shí)會(huì)拋出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()
時(shí)需要BytesIO類(lèi)型准夷。(暫時(shí)不知道其他解決辦法)
除此之外,在利用preppy.getModule()
解析rml文件時(shí)莺掠,若rml文件中出現(xiàn)中文字符直接利用preppy.getModule(template_name)
會(huì)出現(xiàn)gbk無(wú)法解析中文的情況衫嵌,查看源碼發(fā)現(xiàn)當(dāng)只有一個(gè)name
參數(shù)時(shí),getModule()
會(huì)默認(rèn)open(name, 'r').read()
出現(xiàn)UnicodeDecodeError
彻秆。所以使用利用sourcetext
參數(shù)楔绞,先按指定編碼讀取文件內(nèi)容再解析,即
source_text = open(template_name, 'r', encoding='utf-8').read()
template = preppy.getModule(template_name, sourcetext=source_text)
```掖棉,同時(shí)需要在rml文件中注冊(cè)中文字體。
* main():
if name == 'main':
# 模板文件
template = 'hello.rml'
# 輸出的pdf
output_filename = 'output/simple_demo.pdf'
# 數(shù)據(jù)
namespace = {
'name': 'JiangW'
}
p = PDFUtils(template, namespace, output_filename=output_filename)
p.create_pdf()