[MODULE] +v1.1.0 from https://www.odoo.com/apps/7.0/account_financial_report_webkit/
[burette/account_financial_report_webkit.git] / report / webkit_parser_header_fix.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)
5 #
6 # Author: Guewen Baconnier (Camptocamp)
7 #
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
14 #
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #
29 ##############################################################################
30 import os
31 import subprocess
32 import tempfile
33 import logging
34 from functools import partial
35
36
37 from mako import exceptions
38 from openerp.osv.osv import except_osv
39 from openerp.tools.translate import _
40 from openerp import addons
41 from openerp import pooler
42 from openerp import tools
43 from openerp.addons.report_webkit import webkit_report
44 from openerp.addons.report_webkit.report_helper import WebKitHelper
45
46 _logger = logging.getLogger('financial.reports.webkit')
47
48 # Class used only as a workaround to bug:
49 # http://code.google.com/p/wkhtmltopdf/issues/detail?id=656
50
51 # html headers and footers do not work on big files (hundreds of pages) so we
52 # replace them by text headers and footers passed as arguments to wkhtmltopdf
53 # this class has to be removed once the bug is fixed
54
55 # in your report class, to print headers and footers as text, you have to add
56 # them in the localcontext with a key 'additional_args'
57 # for instance:
58 # header_report_name = _('PARTNER LEDGER')
59 # footer_date_time = self.formatLang(str(datetime.today()), date_time=True)
60 # self.localcontext.update({
61 # 'additional_args': [
62 # ('--header-font-name', 'Helvetica'),
63 # ('--footer-font-name', 'Helvetica'),
64 # ('--header-font-size', '10'),
65 # ('--footer-font-size', '7'),
66 # ('--header-left', header_report_name),
67 # ('--footer-left', footer_date_time),
68 # ('--footer-right', ' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
69 # ('--footer-line',),
70 # ],
71 # })
72
73
74 # redefine mako_template as this is overriden by jinja since saas-1
75 # from openerp.addons.report_webkit.webkit_report import mako_template
76 from mako.template import Template
77 from mako.lookup import TemplateLookup
78
79
80 def mako_template(text):
81 """Build a Mako template.
82
83 This template uses UTF-8 encoding
84 """
85 tmp_lookup = TemplateLookup() # we need it in order to allow inclusion and inheritance
86 return Template(text, input_encoding='utf-8', output_encoding='utf-8', lookup=tmp_lookup)
87
88
89 class HeaderFooterTextWebKitParser(webkit_report.WebKitParser):
90
91 def generate_pdf(self, comm_path, report_xml, header, footer, html_list,
92 webkit_header=False, parser_instance=False):
93 """Call webkit in order to generate pdf"""
94 if not webkit_header:
95 webkit_header = report_xml.webkit_header
96 fd, out_filename = tempfile.mkstemp(suffix=".pdf",
97 prefix="webkit.tmp.")
98 file_to_del = [out_filename]
99 if comm_path:
100 command = [comm_path]
101 else:
102 command = ['wkhtmltopdf']
103
104 command.append('--quiet')
105 # default to UTF-8 encoding. Use <meta charset="latin-1"> to override.
106 command.extend(['--encoding', 'utf-8'])
107
108 if webkit_header.margin_top:
109 command.extend(['--margin-top', str(webkit_header.margin_top).replace(',', '.')])
110 if webkit_header.margin_bottom:
111 command.extend(['--margin-bottom', str(webkit_header.margin_bottom).replace(',', '.')])
112 if webkit_header.margin_left:
113 command.extend(['--margin-left', str(webkit_header.margin_left).replace(',', '.')])
114 if webkit_header.margin_right:
115 command.extend(['--margin-right', str(webkit_header.margin_right).replace(',', '.')])
116 if webkit_header.orientation:
117 command.extend(['--orientation', str(webkit_header.orientation).replace(',', '.')])
118 if webkit_header.format:
119 command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
120
121 if parser_instance.localcontext.get('additional_args', False):
122 for arg in parser_instance.localcontext['additional_args']:
123 command.extend(arg)
124
125 count = 0
126 for html in html_list:
127 with tempfile.NamedTemporaryFile(suffix="%d.body.html" % count,
128 delete=False) as html_file:
129 count += 1
130 html_file.write(self._sanitize_html(html))
131 file_to_del.append(html_file.name)
132 command.append(html_file.name)
133 command.append(out_filename)
134 stderr_fd, stderr_path = tempfile.mkstemp(text=True)
135 file_to_del.append(stderr_path)
136 try:
137 status = subprocess.call(command, stderr=stderr_fd)
138 os.close(stderr_fd) # ensure flush before reading
139 stderr_fd = None # avoid closing again in finally block
140 fobj = open(stderr_path, 'r')
141 error_message = fobj.read()
142 fobj.close()
143 if not error_message:
144 error_message = _('No diagnosis message was provided')
145 else:
146 error_message = _('The following diagnosis message was provided:\n') + error_message
147 if status:
148 raise except_osv(_('Webkit error'),
149 _("The command 'wkhtmltopdf' failed with error code = %s. Message: %s") % (status, error_message))
150 with open(out_filename, 'rb') as pdf_file:
151 pdf = pdf_file.read()
152 os.close(fd)
153 finally:
154 if stderr_fd is not None:
155 os.close(stderr_fd)
156 for f_to_del in file_to_del:
157 try:
158 os.unlink(f_to_del)
159 except (OSError, IOError), exc:
160 _logger.error('cannot remove file %s: %s', f_to_del, exc)
161 return pdf
162
163 # override needed to keep the attachments' storing procedure
164 def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
165 """generate the PDF"""
166
167 if context is None:
168 context = {}
169 htmls = []
170 if report_xml.report_type != 'webkit':
171 return super(HeaderFooterTextWebKitParser, self
172 ).create_single_pdf(cursor, uid, ids, data,
173 report_xml, context=context)
174
175 parser_instance = self.parser(cursor,
176 uid,
177 self.name2,
178 context=context)
179
180 self.pool = pooler.get_pool(cursor.dbname)
181 objs = self.getObjects(cursor, uid, ids, context)
182 parser_instance.set_context(objs, data, ids, report_xml.report_type)
183
184 template = False
185
186 if report_xml.report_file:
187 path = addons.get_module_resource(*report_xml.report_file.split(os.path.sep))
188 if os.path.exists(path):
189 template = file(path).read()
190 if not template and report_xml.report_webkit_data:
191 template = report_xml.report_webkit_data
192 if not template:
193 raise except_osv(_('Error!'), _('Webkit Report template not found !'))
194 header = report_xml.webkit_header.html
195
196 if not header and report_xml.header:
197 raise except_osv(
198 _('No header defined for this Webkit report!'),
199 _('Please set a header in company settings.')
200 )
201
202 css = report_xml.webkit_header.css
203 if not css:
204 css = ''
205
206 translate_call = partial(self.translate_call, parser_instance)
207 #default_filters=['unicode', 'entity'] can be used to set global filter
208 body_mako_tpl = mako_template(template)
209 helper = WebKitHelper(cursor, uid, report_xml.id, context)
210 if report_xml.precise_mode:
211 for obj in objs:
212 parser_instance.localcontext['objects'] = [obj]
213 try:
214 html = body_mako_tpl.render(helper=helper,
215 css=css,
216 _=translate_call,
217 **parser_instance.localcontext)
218 htmls.append(html)
219 except Exception:
220 msg = exceptions.text_error_template().render()
221 _logger.error(msg)
222 raise except_osv(_('Webkit render'), msg)
223 else:
224 try:
225 html = body_mako_tpl.render(helper=helper,
226 css=css,
227 _=translate_call,
228 **parser_instance.localcontext)
229 htmls.append(html)
230 except Exception:
231 msg = exceptions.text_error_template().render()
232 _logger.error(msg)
233 raise except_osv(_('Webkit render'), msg)
234
235 # NO html footer and header because we write them as text with
236 # wkhtmltopdf
237 head = foot = False
238
239 if report_xml.webkit_debug:
240 try:
241 deb = body_mako_tpl.render(helper=helper,
242 css=css,
243 _debug=tools.ustr("\n".join(htmls)),
244 _=translate_call,
245 **parser_instance.localcontext)
246 except Exception:
247 msg = exceptions.text_error_template().render()
248 _logger.error(msg)
249 raise except_osv(_('Webkit render'), msg)
250 return (deb, 'html')
251 bin = self.get_lib(cursor, uid)
252 pdf = self.generate_pdf(bin, report_xml, head, foot, htmls,
253 parser_instance=parser_instance)
254 return (pdf, 'pdf')