項目中生成了一個實例,須要提供給用戶下載pdf文件功能。html
最開始想到的是使用前段技術,直接將html頁面保存爲pdf文件。這個過程使用了html2pdf,這個純js項目。該項目會將指定的DOM元素經過html2canvas工具保存爲圖片,而後經過jsPDF工具將保存的圖片最終以pdf文件形式展示,並直接供下載。git
這樣作有個缺點就是當頁面很是長時,就有可能出現一行文字被從中間截斷,展現在兩頁pdf文件中的狀況。於是須要尋找更高級,輸出格式更優雅的解決方案。最後發現了能夠經過Reportlab套件來完成工做。github
要想經過Reportlab套件來生成pdf文件的工做流程主要分三步:數據庫
第1和第3步能夠參考官方outputting pdf文檔django
比較關鍵的是第2步如何實現,下面將針對RML、以及如何調用reportlab套件生成二進制數據進行詳細講解canvas
關於RML能夠參考Reportlab提供的RML-for-Idiots和RML User Guide兩個文檔進行學習,這裏並不贅述。只介紹我的使用的一些體會。app
要想實現第一頁和後面其他頁使用不一樣的模板,在<story>
上添加firstPageTemplate
屬性,指定第一頁的模板。緊接着使用<setNextTempate name='main'>
來指定剩餘頁(若是有的話)使用的模板。ide
若是想整個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>
我的在使用過程當中發現沒法在RML模板中進行嵌套循環,oop
{{ 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))
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