Django使用reportlab套件生成PDF文件

簡介

項目中生成了一個實例,須要提供給用戶下載pdf文件功能。html

最開始想到的是使用前段技術,直接將html頁面保存爲pdf文件。這個過程使用了html2pdf,這個純js項目。該項目會將指定的DOM元素經過html2canvas工具保存爲圖片,而後經過jsPDF工具將保存的圖片最終以pdf文件形式展示,並直接供下載。git

這樣作有個缺點就是當頁面很是長時,就有可能出現一行文字被從中間截斷,展現在兩頁pdf文件中的狀況。於是須要尋找更高級,輸出格式更優雅的解決方案。最後發現了能夠經過Reportlab套件來完成工做。github

工做流

要想經過Reportlab套件來生成pdf文件的工做流程主要分三步:數據庫

  1. django後臺經過與數據庫或者其餘調用獲取數據
  2. 將這些數據傳入到RML(Report Markable Language)模板,並調用reportlab套件生成二進制數據
  3. django根據生成的數據返回響應

第1和第3步能夠參考官方outputting pdf文檔django

比較關鍵的是第2步如何實現,下面將針對RML、以及如何調用reportlab套件生成二進制數據進行詳細講解canvas

RML和Reportlab套件使用

關於RML能夠參考Reportlab提供的RML-for-IdiotsRML 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>
  • 經過Django template解決RML不能嵌套循環問題

我的在使用過程當中發現沒法在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))
  • 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
相關文章
相關標籤/搜索