Python 在Django站点中将HTML呈现为PDF

Python 在Django站点中将HTML呈现为PDF,python,html,django,pdf,pdf-generation,Python,Html,Django,Pdf,Pdf Generation,对于我的django支持的站点,我正在寻找一个简单的解决方案,将动态html页面转换为pdf 页面包含来自Google visualization API的HTML和图表(基于javascript,但必须包含这些图形)。请从中尝试解决方案 下载它并使用python setup.py安装像往常一样安装它 您还需要安装以下模块:xhtml2pdf、html5lib、pypdf和easy_install 下面是一个使用示例: 首先定义此函数: import cStringIO as StringIO

对于我的django支持的站点,我正在寻找一个简单的解决方案,将动态html页面转换为pdf

页面包含来自Google visualization API的HTML和图表(基于javascript,但必须包含这些图形)。

请从中尝试解决方案

下载它并使用python setup.py安装像往常一样安装它

您还需要安装以下模块:xhtml2pdf、html5lib、pypdf和easy_install

下面是一个使用示例:

首先定义此函数:

import cStringIO as StringIO
from xhtml2pdf import pisa
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
from cgi import escape


def render_to_pdf(template_src, context_dict):
    template = get_template(template_src)
    context = Context(context_dict)
    html  = template.render(context)
    result = StringIO.StringIO()

    pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result)
    if not pdf.err:
        return HttpResponse(result.getvalue(), content_type='application/pdf')
    return HttpResponse('We had some errors<pre>%s</pre>' % escape(html))
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <title>My Title</title>
        <style type="text/css">
            @page {
                size: {{ pagesize }};
                margin: 1cm;
                @frame footer {
                    -pdf-frame-content: footerContent;
                    bottom: 0cm;
                    margin-left: 9cm;
                    margin-right: 9cm;
                    height: 1cm;
                }
            }
        </style>
    </head>
    <body>
        <div>
            {% for item in mylist %}
                RENDER MY CONTENT
            {% endfor %}
        </div>
        <div id="footerContent">
            {%block page_foot%}
                Page <pdf:pagenumber>
            {%endblock%}
        </div>
    </body>
</html>
import StringIO
from cgi import escape
from xhtml2pdf import pisa
from django.http import HttpResponse
from django.template.response import TemplateResponse
from django.views.generic import TemplateView

class PDFTemplateResponse(TemplateResponse):

    def generate_pdf(self, retval):

        html = self.content

        result = StringIO.StringIO()
        rendering = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result)

        if rendering.err:
            return HttpResponse('We had some errors<pre>%s</pre>' % escape(html))
        else:
            self.content = result.getvalue()

    def __init__(self, *args, **kwargs):
        super(PDFTemplateResponse, self).__init__(*args, mimetype='application/pdf', **kwargs)
        self.add_post_render_callback(self.generate_pdf)


class PDFTemplateView(TemplateView):
    response_class = PDFTemplateResponse
模板:

import cStringIO as StringIO
from xhtml2pdf import pisa
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
from cgi import escape


def render_to_pdf(template_src, context_dict):
    template = get_template(template_src)
    context = Context(context_dict)
    html  = template.render(context)
    result = StringIO.StringIO()

    pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result)
    if not pdf.err:
        return HttpResponse(result.getvalue(), content_type='application/pdf')
    return HttpResponse('We had some errors<pre>%s</pre>' % escape(html))
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <title>My Title</title>
        <style type="text/css">
            @page {
                size: {{ pagesize }};
                margin: 1cm;
                @frame footer {
                    -pdf-frame-content: footerContent;
                    bottom: 0cm;
                    margin-left: 9cm;
                    margin-right: 9cm;
                    height: 1cm;
                }
            }
        </style>
    </head>
    <body>
        <div>
            {% for item in mylist %}
                RENDER MY CONTENT
            {% endfor %}
        </div>
        <div id="footerContent">
            {%block page_foot%}
                Page <pdf:pagenumber>
            {%endblock%}
        </div>
    </body>
</html>
import StringIO
from cgi import escape
from xhtml2pdf import pisa
from django.http import HttpResponse
from django.template.response import TemplateResponse
from django.views.generic import TemplateView

class PDFTemplateResponse(TemplateResponse):

    def generate_pdf(self, retval):

        html = self.content

        result = StringIO.StringIO()
        rendering = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result)

        if rendering.err:
            return HttpResponse('We had some errors<pre>%s</pre>' % escape(html))
        else:
            self.content = result.getvalue()

    def __init__(self, *args, **kwargs):
        super(PDFTemplateResponse, self).__init__(*args, mimetype='application/pdf', **kwargs)
        self.add_post_render_callback(self.generate_pdf)


class PDFTemplateView(TemplateView):
    response_class = PDFTemplateResponse

我的头衔
@页面{
大小:{{pagesize}};
边缘:1cm;
@框架页脚{
-pdf框架内容:页脚内容;
底部:0厘米;
左边距:9厘米;
右边距:9厘米;
高度:1厘米;
}
}
{mylist%中项目的%s}
呈现我的内容
{%endfor%}
{%block page_foot%}
页
{%endblock%}

希望有帮助。

我刚刚为CBV做了这个。不用于生产,但为我生成PDF。可能需要在错误报告方面做些工作,但到目前为止已经做到了

class MyPdfView(PDFTemplateView):
    template_name = 'things/pdf.html'

您可以使用iReport编辑器定义布局,并在jasper reports server中发布报告。发布后,您可以调用RESTAPI来获得结果

以下是对功能的测试:

{% extends "easy_pdf/base.html" %}

{% block content %}
    <div id="content">
        <h1>Hi there!</h1>
    </div>
{% endblock %}
来自django.test的
导入测试用例
从x_reports_jasper.models导入JasperServerClient
"""
尝试通过rest与jasper服务器集成
"""
类TestJasperServerClient(TestCase):
#定义测试所需的对象
def设置(自):
#将连接加载到远程服务器
尝试:
self.j_url=”http://127.0.0.1:8080/jasperserver"
self.j_user=“jasperamin”
self.j_pass=“jasperamin”
self.client=JasperServerClient.create_客户端(self.j_url、self.j_用户、self.j_密码)
除例外情况外,e:
#如果出现错误,则无法执行给定条件下的测试
提升
#服务器数据无效时的测试异常
def测试\u登录到\u无效\u地址\u应引发(自我):
self.assertRaises(异常,JasperServerClient.create_client,“http://127.0.0.1:9090/jasperserver,self.j_用户,self.j_通行证)
#在服务器中测试并执行现有报表
def测试获取报告(自我):
r_resource_path=“/reports/”
r_format=“pdf”
r_params={'PARAM_TO_REPORT':“1”,}
#resource\u meta=client.load\u resource\u元数据(rep\u resource\u路径)
[uuid,out\u mime,out\u data]=self.client.generate\u报告(r\u资源路径,r\u格式,r\u参数)
self.assertionsnotnone(uuid)
下面是调用实现的一个示例:

from easy_pdf.views import PDFTemplateView

class HelloPDFView(PDFTemplateView):
    template_name = "hello.html"
来自django.db导入模型的

导入请求
导入系统
从xml.etree导入元素树
导入日志记录
#模块记录器定义
logger=logging.getLogger(_名称__)
#在这里创建您的模型。
类JasperServerClient(models.Manager):
定义句柄异常(自身、异常根、异常id、执行信息):
类型、值、回溯=执行信息
raise JasperServerClientError(异常根,异常id),无,回溯
#01:报告元数据
#获取资源描述以生成报告
定义处理报告元数据(自我、代表资源路径):
l_path_base_resource=“/rest/resource”
l_path=self.j_url+l_path_base_资源
logger.info(“元数据(开始)[path=%s%s]”%(l\u path,rep\u resourcepath))
资源\u响应=无
尝试:
resource\u response=requests.get(“%s%s”%(l\u path,rep\u resourcepath),cookies=self.login\u response.cookies)
除例外情况外,e:
self.\u handle\u异常(例如,“报告元数据:调用错误”,sys.exc\u info())
资源\响应\ dom=无
尝试:
#解析到dom并设置参数
logger.debug(“-response[data=%s]”%(resource\u response.text))
resource\u response\u dom=ElementTree.fromstring(resource\u response.text)
datum=“”
对于资源\响应\ dom.getiterator()中的节点:
datum=“%s
%s-%s”%(datum,node.tag,node.text) debug(“-response[xml=%s]”%(数据)) # self.resource\u response\u payload=resource\u response.text logger.info(“元数据(结束)”) 除例外情况外,e: logger.error(“元数据(错误)[%s]”%(e)) self.\u handle\u异常(例如,“报告元数据:解析错误”,sys.exc\u info()) #02:REPORT-PARAMS 定义添加报告参数(自身、元数据、文本、参数): 如果(类型(参数)!=dict): raise TypeError(“要报告的参数无效”) 其他: logger.info(“添加参数(开始)[]”) #复制参数 l_参数={} 对于参数项()中的k,v: l_参数[k]=v #获取有效负载元数据 metadata\u dom=ElementTree.fromstring(元数据\u文本) #向有效负载元数据添加属性 root=metadata_dom#('report'): 对于l_参数项()中的k,v: param_dom_element=ElementTree.element('parameter') param_dom_element.attrib[“name”]=k param_dom_element.text=v 追加(param_dom_元素) # metadata\u modified\u text=ElementTree.tostring(metadata\u dom,encoding='utf8',method='xml') info(“添加参数(结束)[有效负载xml=%s]”%(元数据\u修改的\u文本)) 返回元数据\u修改的\u文本 #03:REPORT-REQUEST-CALL #调用以生成报告 def_uu ha
    # views.py
    from django_xhtml2pdf.views import PdfMixin
    class GroupPDFGenerate(PdfMixin, DetailView):
        model = PeerGroupSignIn
        template_name = 'groups/pdf.html'

    # templates/groups/pdf.html
    <html>
    <style>
    @page { your xhtml2pdf pisa PDF parameters }
    </style>
    </head>
    <body>
        <div id="header_content"> (this is defined in the style section)
            <h1>{{ peergroupsignin.this_group_title }}</h1>
            ...
    # urls.py (using url namespaces defined in the main urls.py file)
    url(
        regex=r"^(?P<pk>\d+)/generate_pdf/$",
        view=views.GroupPDFGenerate.as_view(),
        name="generate_pdf",
       ),
    import os

    from weasyprint import HTML

    from django.template import Template, Context
    from django.http import HttpResponse 


    def generate_pdf(self, report_id):

            # Render HTML into memory and get the template firstly
            template_file_loc = os.path.join(os.path.dirname(__file__), os.pardir, 'templates', 'the_template_pdf_generator.html')
            template_contents = read_all_as_str(template_file_loc)
            render_template = Template(template_contents)

            #rendering_map is the dict for params in the template 
            render_definition = Context(rendering_map)
            render_output = render_template.render(render_definition)

            # Using Rendered HTML to generate PDF
            response = HttpResponse(content_type='application/pdf')
            response['Content-Disposition'] = 'attachment; filename=%s-%s-%s.pdf' % \
                                              ('topic-test','topic-test', '2018-05-04')
            # Generate PDF
            pdf_doc = HTML(string=render_output).render()
            pdf_doc.pages[0].height = pdf_doc.pages[0]._page_box.children[0].children[
                0].height  # Make PDF file as single page file 
            pdf_doc.write_pdf(response)
            return response

    def read_all_as_str(self, file_loc, read_method='r'):
        if file_exists(file_loc):
            handler = open(file_loc, read_method)
            contents = handler.read()
            handler.close()
            return contents
        else:
            return 'file not exist'  
from django.template.loader import get_template
import pdfkit
from django.conf import settings

context={....}
template = get_template('reports/products.html')
html_string = template.render(context)
pdfkit.from_string(html_string, os.path.join(settings.BASE_DIR, "media", 'products_report-%s.pdf'%(id)))