[PYTHON] Django 사이트에서 HTML을 PDF로 렌더링
PYTHONDjango 사이트에서 HTML을 PDF로 렌더링
내 장고 전원 사이트 들어, 난 PDF로 동적 HTML 페이지를 변환하는 쉬운 솔루션을 찾고 있는데요.
페이지에는 Google 시각화 API의 HTML 및 차트가 포함됩니다 (자바 스크립트 기반이지만 그래프는 필수입니다).
해결법
-
==============================
1.Reportlab에서 솔루션을 사용해보십시오.
Reportlab에서 솔루션을 사용해보십시오.
Python setup.py install을 사용하여 다운로드하고 평소대로 설치하십시오.
easy_install과 함께 xhtml2pdf, html5lib, pypdf 모듈을 설치해야합니다.
다음은 사용 예입니다.
먼저이 함수를 정의하십시오.
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))
그러면 다음과 같이 사용할 수 있습니다.
def myview(request): #Retrieve data or whatever you need return render_to_pdf( 'mytemplate.html', { 'pagesize':'A4', 'mylist': results, } )
템플릿 :
<!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>
희망이 도움이됩니다.
-
==============================
2.방금 CBV를 위해 이것을 채찍질했습니다. 프로덕션에서는 사용되지 않지만 PDF는 생성합니다. 아마 사물의 오류보고 측면에 대한 작업이 필요하지만 지금까지는 그 트릭을 수행합니다.
방금 CBV를 위해 이것을 채찍질했습니다. 프로덕션에서는 사용되지 않지만 PDF는 생성합니다. 아마 사물의 오류보고 측면에 대한 작업이 필요하지만 지금까지는 그 트릭을 수행합니다.
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
사용 :
class MyPdfView(PDFTemplateView): template_name = 'things/pdf.html'
-
==============================
3.https://github.com/nigma/django-easy-pdf
https://github.com/nigma/django-easy-pdf
주형:
{% extends "easy_pdf/base.html" %} {% block content %} <div id="content"> <h1>Hi there!</h1> </div> {% endblock %}
전망:
from easy_pdf.views import PDFTemplateView class HelloPDFView(PDFTemplateView): template_name = "hello.html"
Python 3에서 django-easy-pdf를 사용하려면 여기에 제안 된 해결책을 확인하십시오.
-
==============================
4.다음 래퍼 중 하나를 사용하여 wkhtmltopdf를 시도하십시오.
다음 래퍼 중 하나를 사용하여 wkhtmltopdf를 시도하십시오.
django-wkhtmltopdf 또는 python-pdfkit
이것은 나를 위해 위대한, 웹캠 브라우저가 지원하는 그 문제에 대한 자바 스크립트와 CSS 또는 아무것도 지원했습니다.
더 자세한 자습서를 보려면이 블로그 게시물을 참조하십시오.
-
==============================
5.너무 많은 시간 동안이 작업을 시도한 후에, 나는 이것을 결국 발견 : https://github.com/vierno/django-xhtml2pdf
너무 많은 시간 동안이 작업을 시도한 후에, 나는 이것을 결국 발견 : https://github.com/vierno/django-xhtml2pdf
일반적인 클래스 기반보기에 대한 믹스 인을 제공하는 https://github.com/chrisglass/django-xhtml2pdf 포크입니다. 나는 이것을 다음과 같이 사용했다.
# 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> ...
템플리트 필드를 채울 때보기에서 정의한 모델 이름을 모두 소문자로 사용하십시오. GCBV이기 때문에 urls.py에서 '.as_view'로 호출하면됩니다.
# 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", ),
-
==============================
6.iReport 편집기를 사용하여 레이아웃을 정의하고 재스퍼 보고서 서버에 보고서를 게시 할 수 있습니다. 게시 한 후 나머지 API를 호출하여 결과를 얻을 수 있습니다.
iReport 편집기를 사용하여 레이아웃을 정의하고 재스퍼 보고서 서버에 보고서를 게시 할 수 있습니다. 게시 한 후 나머지 API를 호출하여 결과를 얻을 수 있습니다.
다음은 기능 테스트입니다.
from django.test import TestCase from x_reports_jasper.models import JasperServerClient """ to try integraction with jasper server through rest """ class TestJasperServerClient(TestCase): # define required objects for tests def setUp(self): # load the connection to remote server try: self.j_url = "http://127.0.0.1:8080/jasperserver" self.j_user = "jasperadmin" self.j_pass = "jasperadmin" self.client = JasperServerClient.create_client(self.j_url,self.j_user,self.j_pass) except Exception, e: # if errors could not execute test given prerrequisites raise # test exception when server data is invalid def test_login_to_invalid_address_should_raise(self): self.assertRaises(Exception,JasperServerClient.create_client, "http://127.0.0.1:9090/jasperserver",self.j_user,self.j_pass) # test execute existent report in server def test_get_report(self): r_resource_path = "/reports/<PathToPublishedReport>" r_format = "pdf" r_params = {'PARAM_TO_REPORT':"1",} #resource_meta = client.load_resource_metadata( rep_resource_path ) [uuid,out_mime,out_data] = self.client.generate_report(r_resource_path,r_format,r_params) self.assertIsNotNone(uuid)
다음은 호출 구현의 예제입니다.
from django.db import models import requests import sys from xml.etree import ElementTree import logging # module logger definition logger = logging.getLogger(__name__) # Create your models here. class JasperServerClient(models.Manager): def __handle_exception(self, exception_root, exception_id, exec_info ): type, value, traceback = exec_info raise JasperServerClientError(exception_root, exception_id), None, traceback # 01: REPORT-METADATA # get resource description to generate the report def __handle_report_metadata(self, rep_resourcepath): l_path_base_resource = "/rest/resource" l_path = self.j_url + l_path_base_resource logger.info( "metadata (begin) [path=%s%s]" %( l_path ,rep_resourcepath) ) resource_response = None try: resource_response = requests.get( "%s%s" %( l_path ,rep_resourcepath) , cookies = self.login_response.cookies) except Exception, e: self.__handle_exception(e, "REPORT_METADATA:CALL_ERROR", sys.exc_info()) resource_response_dom = None try: # parse to dom and set parameters logger.debug( " - response [data=%s]" %( resource_response.text) ) resource_response_dom = ElementTree.fromstring(resource_response.text) datum = "" for node in resource_response_dom.getiterator(): datum = "%s<br />%s - %s" % (datum, node.tag, node.text) logger.debug( " - response [xml=%s]" %( datum ) ) # self.resource_response_payload= resource_response.text logger.info( "metadata (end) ") except Exception, e: logger.error( "metadata (error) [%s]" % (e)) self.__handle_exception(e, "REPORT_METADATA:PARSE_ERROR", sys.exc_info()) # 02: REPORT-PARAMS def __add_report_params(self, metadata_text, params ): if(type(params) != dict): raise TypeError("Invalid parameters to report") else: logger.info( "add-params (begin) []" ) #copy parameters l_params = {} for k,v in params.items(): l_params[k]=v # get the payload metadata metadata_dom = ElementTree.fromstring(metadata_text) # add attributes to payload metadata root = metadata_dom #('report'): for k,v in l_params.items(): param_dom_element = ElementTree.Element('parameter') param_dom_element.attrib["name"] = k param_dom_element.text = v root.append(param_dom_element) # metadata_modified_text =ElementTree.tostring(metadata_dom, encoding='utf8', method='xml') logger.info( "add-params (end) [payload-xml=%s]" %( metadata_modified_text ) ) return metadata_modified_text # 03: REPORT-REQUEST-CALL # call to generate the report def __handle_report_request(self, rep_resourcepath, rep_format, rep_params): # add parameters self.resource_response_payload = self.__add_report_params(self.resource_response_payload,rep_params) # send report request l_path_base_genreport = "/rest/report" l_path = self.j_url + l_path_base_genreport logger.info( "report-request (begin) [path=%s%s]" %( l_path ,rep_resourcepath) ) genreport_response = None try: genreport_response = requests.put( "%s%s?RUN_OUTPUT_FORMAT=%s" %(l_path,rep_resourcepath,rep_format),data=self.resource_response_payload, cookies = self.login_response.cookies ) logger.info( " - send-operation-result [value=%s]" %( genreport_response.text) ) except Exception,e: self.__handle_exception(e, "REPORT_REQUEST:CALL_ERROR", sys.exc_info()) # parse the uuid of the requested report genreport_response_dom = None try: genreport_response_dom = ElementTree.fromstring(genreport_response.text) for node in genreport_response_dom.findall("uuid"): datum = "%s" % (node.text) genreport_uuid = datum for node in genreport_response_dom.findall("file/[@type]"): datum = "%s" % (node.text) genreport_mime = datum logger.info( "report-request (end) [uuid=%s,mime=%s]" %( genreport_uuid, genreport_mime) ) return [genreport_uuid,genreport_mime] except Exception,e: self.__handle_exception(e, "REPORT_REQUEST:PARSE_ERROR", sys.exc_info()) # 04: REPORT-RETRIEVE RESULTS def __handle_report_reply(self, genreport_uuid ): l_path_base_getresult = "/rest/report" l_path = self.j_url + l_path_base_getresult logger.info( "report-reply (begin) [uuid=%s,path=%s]" %( genreport_uuid,l_path) ) getresult_response = requests.get( "%s%s/%s?file=report" %(self.j_url,l_path_base_getresult,genreport_uuid),data=self.resource_response_payload, cookies = self.login_response.cookies ) l_result_header_mime =getresult_response.headers['Content-Type'] logger.info( "report-reply (end) [uuid=%s,mime=%s]" %( genreport_uuid, l_result_header_mime) ) return [l_result_header_mime, getresult_response.content] # public methods --------------------------------------- # tries the authentication with jasperserver throug rest def login(self, j_url, j_user,j_pass): self.j_url= j_url l_path_base_auth = "/rest/login" l_path = self.j_url + l_path_base_auth logger.info( "login (begin) [path=%s]" %( l_path) ) try: self.login_response = requests.post(l_path , params = { 'j_username':j_user, 'j_password':j_pass }) if( requests.codes.ok != self.login_response.status_code ): self.login_response.raise_for_status() logger.info( "login (end)" ) return True # see http://blog.ianbicking.org/2007/09/12/re-raising-exceptions/ except Exception, e: logger.error("login (error) [e=%s]" % e ) self.__handle_exception(e, "LOGIN:CALL_ERROR",sys.exc_info()) #raise def generate_report(self, rep_resourcepath,rep_format,rep_params): self.__handle_report_metadata(rep_resourcepath) [uuid,mime] = self.__handle_report_request(rep_resourcepath, rep_format,rep_params) # TODO: how to handle async? [out_mime,out_data] = self.__handle_report_reply(uuid) return [uuid,out_mime,out_data] @staticmethod def create_client(j_url, j_user, j_pass): client = JasperServerClient() login_res = client.login( j_url, j_user, j_pass ) return client class JasperServerClientError(Exception): def __init__(self,exception_root,reason_id,reason_message=None): super(JasperServerClientError, self).__init__(str(reason_message)) self.code = reason_id self.description = str(exception_root) + " " + str(reason_message) def __str__(self): return self.code + " " + self.description
-
==============================
7.html 템플릿에서 PDF를 생성하는 코드를 얻습니다.
html 템플릿에서 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 https://stackoverflow.com/questions/1377446/render-html-to-pdf-in-django-site by cc-by-sa and MIT license
'PYTHON' 카테고리의 다른 글
[PYTHON] 올바른 방법으로 곡선을 부드럽게하는 방법은 무엇입니까? (0) | 2018.10.09 |
---|---|
[PYTHON] 파이썬에서 비동기 메서드 호출? (0) | 2018.10.09 |
[PYTHON] 파이썬 대 Cpython (0) | 2018.10.09 |
[PYTHON] Project Euler와의 속도 비교 : C vs Python vs. Erlang vs Haskell (0) | 2018.10.09 |
[PYTHON] 날짜를 기반으로 한 R 또는 Python의 요소 값 붙여 넣기 - 학교 간 나누기 만들기 (0) | 2018.10.09 |