Django 生成PDF(二)

 Django 生成PDF(二)


   接上文,我們本次使用RML來實現對PDF的生成與保存。

   關於reportlab的詳細信息,請戳以下鏈接:http://www.reportlab.com/software/opensource/。

   本人用reportlab生成PDF的詳細流程如下:

   第一步,下載安裝reportlab。這一步我就不贅述了。

   第二步,實現Demo。值得高興的是,它提供了一個在線的Demo,http://www.reportlab.com/static/cms/files/RLtutorial.zip ,不過很遺憾,下載下來後不能用,報如下錯誤:

>>python product_catalog.py
Traceback (most recent call last):
  File "product_catalog.py", line 5, in <module>
    import pyRXPU
ImportError: No module named pyRXPU
   折騰了很久,還是沒整成功,後來從其官方文檔中得知:reportlab依賴於“preppy”、“pyrxp”、“rml2pdf”,而在新版本中reportlab已經將“preppy”、“pyrxp”、“rml2pdf”分拆出來,所以Demo中原來封裝在“rlextra”中的包已經被分拆了。找了好久,從各個角落找到了相關包,如果需要的可以戳下面的鏈接進行下載:

  (1)reportlab-preppy :

資源地址:http://download.csdn.net/detail/yima1006/6433059

文檔地址:http://download.csdn.net/detail/yima1006/6433087

2)reportlab-pyrxp:

 資源地址:http://download.csdn.net/detail/yima1006/6433065

(3)trml2pdf:

        資源地址:http://download.csdn.net/detail/yima1006/6433071

        文檔地址:http://download.csdn.net/detail/yima1006/6433095

(4)reportlab:

       文檔地址:http://download.csdn.net/detail/yima1006/6433077

在實際使用中,需要要裝以上包,不過在生成中文文檔時,需要自己註冊中文字體,LZ在RML中註冊中文字體始終沒有解決,於是在工具類中手動註冊成功。(備註:LZ用的字體是宋體,直接在windows字體文件炸中複製即可)。

RML文檔爲:

<?xml version="1.0" encoding="utf-8" standalone="no" ?> 
<!DOCTYPE document SYSTEM "rml.dtd"> 
<document filename="financial_products.pdf" compression='1'> 
{{script}}#coding=utf-8{{endscript}}
{{script}}import locale;locale.setlocale(locale.LC_ALL, '');{{endscript}}
<docinit>
	<color id="BLUE" CMYK="[1,0.67,0,0.23]"/>
</docinit>
<template pageSize="(595, 842)" leftMargin="72" rightMargin="72" >

	<pageTemplate id="head" pageSize="a4 portrait">
        <pageGraphics>
            <image file="{{STATIC_DIR}}/img/db_logo.jpg" x="50" y="780" width="302" height="53"/>
            <fill color="black"/>
            <setFont name="song" size="10"/>
			<lineMode width="2"/>
			<lines>
			40 765 555 765
			</lines>
			<fill color="#ADD8E6"/>
			<lineMode width="1" />
			<lines>
			0 60 595 60
			</lines>
			<fill color="#778899"/>
			<setFont name="song" size="10"/>
            <drawString x="235" y="40">客服熱線:4006-816-886 </drawString>
			<drawString x="450" y="40">當前第<pageNumber countingFrom="1"/>頁 / 總共4頁</drawString>
        </pageGraphics>
		<frame id="second" x1="35" y1="85" width="525" height="660"/>
    </pageTemplate>
</template>

<stylesheet>
	<initialize>
		<alias id="style.normal" value="style.Normal"/>
	</initialize>
	<paraStyle name="common"  fontName="song" fontSize="12" leading="18" spaceBefore="6" spaceAfter="12" firstLineIndent="0.5in" />
	<paraStyle name="alignLeft" alignment="left" fontName="song" fontSize="12" leading="15" spaceBefore="6" spaceAfter="8" firstLineIndent="0.2in" />
	<paraStyle name="noIndent" alignment="left" fontName="song" fontSize="12" leading="15" spaceBefore="6" spaceAfter="8" />
	<paraStyle name="alignRight" alignment="right"  fontName="song" fontSize="12" leading="15" spaceBefore="6" spaceAfter="12" firstLineIndent="0.5in" />
	<paraStyle name="title" fontName="song" fontSize="30" leading="24" spaceBefore="6" spaceAfter="50" alignment="center"/>
	<paraStyle name="paddingLeft" alignment="left" fontName="song" fontSize="12" leading="15" spaceBefore="6" spaceAfter="8" firstLineIndent="250" />
</stylesheet>

<story>
<setNextTemplate name="head"/>
<para style='title'><b>標題</b></para>
<para style="alignRight">備案號:<b>{{data.get_record_num()}}</b></para>
<para style="noIndent"><b>甲方:</b></para>
<para style="noIndent">姓名:{{data.owner.userprofile.realname and data.owner.userprofile.realname.encode('utf-8') or data.owner.userprofile.nickname.encode('utf-8')}}</para>
<para style="noIndent">
  證件名稱:{{data.owner.userprofile.id_type and data.owner.userprofile.id_type.encode('utf-8') or ''}} , 證件號碼:{{data.owner.userprofile.id_no}}
</para>
<para style="noIndent"></para>
<para style="noIndent"><b>乙方:</b></para>
<para style="noIndent">某某某公司</para>
<para style="noIndent"></para>
<para style="alignLeft">本協議爲甲方與乙方之間的產品服務合同,雙方在簽署上述產品合約前,已認真閱讀及同意接受合約條款。本合同系雙方經平等自願協商一致,根據《中華人民共和國合同法》等法律、行政法規的規定訂立。</para>
<para style="noIndent"></para>
<para style="alignLeft"><b><u>1. 定義與說明</u></b></para>
<para style="alignLeft"><b>1.1</b> 乙方接受甲方的委託和授權,按照與甲方事先約定的投資計劃和方式進行投資和資產管理,根據約定條件和實際投資收益情況向甲方支付收益並按約定條件和比例收取管理服務費,投資風險由甲方自行承擔。</para>
<para style="alignLeft"><b>1.2</b> 在本合同中,除非另有明確說明,否則下列詞語具有如下含義:</para>
<para style="alignLeft"><b>賬戶:</b>指乙方爲甲方開立的貸幫網賬戶。</para>
<para style="alignLeft">
  <b>起息日:</b>指投資收益計算的起始日,即產品合約中約定的起息日。</para>
<para style="alignLeft">
  <b>到期日:</b>指產品合約中約定的到期日。</para>
<para style="alignLeft">
  <b>期限:</b>指起息日至到期日或(提前終止日)之間的期限。</para>
<para style="alignLeft">
  <b>收益:</b>指甲方投資本金在產品期限內產生的收益。</para>
<para style="alignLeft">
  <b>支付週期:</b>指乙方在甲方提出贖回申請後完成支付所需時間。</para>
<para style="alignLeft">
  <b>工作日:</b>指乙方對外辦理一般業務的任何一天,不包括法定節假日和週六、週日(但包括國家臨時規定應當工作的週六或週日)。</para>
<para style="alignLeft">
  <b>推介期:</b>指乙方計劃完成推廣本產品所需時間段。 </para>
<para style="alignLeft">
  <b>封閉期:</b>指乙方受甲方委託完成投資所需的時間段。</para>
<para style="alignLeft">
  <b>簽約日:</b>指雙方簽署的產品合約中列明的簽約日期。</para>

<para style="noIndent"></para>
<para style="alignLeft"><b><u>2. 聲明與保證</u></b></para>
<para style="alignLeft">
  <b>2.1</b> 雙方均分別向另一方聲明並保證該方具有完全適當的資格與能力訂立、接收及履行本合同以及以其爲一方的其他任何有關文件。</para>
<para style="noIndent"></para>
<para style="noIndent"></para>
<para style="noIndent"></para>
<para style="paddingLeft">某某公司</para>
<para style="noIndent">甲方簽字:<u>{{data.owner.userprofile.realname and data.owner.userprofile.realname.encode('utf-8') or data.owner.userprofile.nickname.encode('utf-8')}}</u>
</para>
<para style="paddingLeft">經辦人(加蓋個人名章或簽字):</para>
<image file="{{STATIC_DIR}}/img/common/cachet.gif" x="350" y="350" width="168" height="168"/>
</story>
</document>


Python工具類:

#!/usr/bin/python
# -*- coding: utf-8 -*-

__author__ = '喵喵'


import os
import preppy
import logging
import traceback
import trml2pdf
from django.conf import settings

import reportlab.lib.styles
from reportlab.pdfbase import pdfmetrics, ttfonts
from reportlab.lib.fonts import addMapping

operation_logger = logging.getLogger('operation')
bug_log = logging.getLogger('bug')


class PDFUtils(object):

    """ PDF 生成工具類 

    將一個標準的RML文件正常解析爲PDF文件,保存並返回。具體參數如下"""

    def __init__(self, font_dir=os.path.join(settings.HERE, 'utils', 'pdf', 'fonts'),
            static_dir='http://%s/static' % settings.HOST_NAME):
        """ 構造方法

        @param font_dir 需要註冊的字體文件目錄
        @param static_dir 靜態文件地址目錄 
        """

        super(PDFUtils, self).__init__()
        self.STATIC_DIR = static_dir
        try:
            # 註冊宋體字體
            pdfmetrics.registerFont(ttfonts.TTFont('song', os.path.join(font_dir, 'STSONG.TTF')))
            # 註冊宋體粗體字體
            pdfmetrics.registerFont(ttfonts.TTFont('song_b', os.path.join(font_dir, 'STZHONGS.TTF')))
        except:
            bug_log.error(traceback.format_exc())

        addMapping('song', 0, 0, 'song')     # normal
        addMapping('song', 0, 1, 'song')     # italic
        addMapping('song', 1, 1, 'song_b')     # bold, italic
        addMapping('song', 1, 0, 'song_b')     # bold

        # 設置自動換行
        reportlab.lib.styles.ParagraphStyle.defaults['wordWrap'] = "CJK"


    def create_pdf(self, data, templ, save_file):
        """從二進制流中創建PDF並返回

        @param data  渲染XML的數據字典
        @param templ 需要渲染的XML文件地址(全路徑)
        @param save_file PDF文件保存的地址(全路徑)
        """
        # 讀取模板文件
        template = preppy.getModule(templ)
        # 渲染模板文件
        namespace = {
            'data': data,
            'STATIC_DIR': self.STATIC_DIR,
            }
        # 渲染PDF頁面
        rml = template.getOutput(namespace)
        # 生成PDF
        pdf =  trml2pdf.parseString(rml)
        # 保存PDF
        open(save_file,'wb').write(pdf)
        return True
        

if __name__ == '__main__':
    
    pu = PDFUtils()
    # 模板頁面地址
    temp_path = os.path.join(settings.HERE, 'templates', 'pact', 'products.prep')
    for c in cerfts:
        pdf_path = os.path.join(settings.HERE, 'medias', 'pdf_pact', 'product.pdf')
        # 如果PDF不存在則重新生成
        if not os.path.exists(pdf_path):
            pu.create_pdf(c, temp_path, pdf_path)
    print 'done'

更多詳情請參見下文。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章