1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
7 # Author : Nicolas Bessi (Camptocamp)
8 # Contributor(s) : Florent Xicluna (Wingo SA)
10 # WARNING: This program as such is intended to be used by professional
11 # programmers who take the whole responsability of assessing all potential
12 # consequences resulting from its eventual inadequacies and bugs
13 # End users who are looking for a ready-to-use solution with commercial
14 # garantees and support are strongly adviced to contract a Free Software
17 # This program is Free Software; you can redistribute it and/or
18 # modify it under the terms of the GNU General Public License
19 # as published by the Free Software Foundation; either version 2
20 # of the License, or (at your option) any later version.
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU General Public License for more details.
27 # You should have received a copy of the GNU General Public License
28 # along with this program; if not, write to the Free Software
29 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 ##############################################################################
40 from mako.template import Template
41 from mako.lookup import TemplateLookup
42 from mako import exceptions
46 from report_helper import WebKitHelper
47 from report.report_sxw import *
50 from tools.translate import _
51 from osv.osv import except_osv
53 logger = logging.getLogger('report_webkit')
55 def mako_template(text):
56 """Build a Mako template.
58 This template uses UTF-8 encoding
60 tmp_lookup = TemplateLookup() #we need it in order to allow inclusion and inheritance
61 return Template(text, input_encoding='utf-8', output_encoding='utf-8', lookup=tmp_lookup)
64 class WebKitParser(report_sxw):
65 """Custom class that use webkit to render HTML reports
66 Code partially taken from report openoffice. Thanks guys :)
69 def __init__(self, name, table, rml=False, parser=False,
70 header=True, store=False):
71 self.parser_instance = False
73 report_sxw.__init__(self, name, table, rml, parser,
76 def get_lib(self, cursor, uid, company) :
77 """Return the lib wkhtml path"""
78 #TODO Detect lib in system first
79 path = self.pool.get('res.company').read(cursor, uid, company, ['lib_path',])
80 path = path['lib_path']
83 _('Wkhtmltopdf library path is not set in company'),
84 _('Please install executable on your system'+
85 ' (sudo apt-get install wkhtmltopdf) or download it from here:'+
86 ' http://code.google.com/p/wkhtmltopdf/downloads/list and set the'+
87 ' path to the executable on the Company form.'+
88 'Minimal version is 0.9.9')
90 if os.path.isabs(path) :
91 if (os.path.exists(path) and os.access(path, os.X_OK)\
92 and os.path.basename(path).startswith('wkhtmltopdf')):
96 _('Wrong Wkhtmltopdf path set in company'+
97 'Given path is not executable or path is wrong'),
102 _('path to Wkhtmltopdf is not absolute'),
105 def generate_pdf(self, comm_path, report_xml, header, footer, html_list, webkit_header=False):
106 """Call webkit in order to generate pdf"""
107 if not webkit_header:
108 webkit_header = report_xml.webkit_header
109 tmp_dir = tempfile.gettempdir()
110 out = report_xml.name+str(time.time())+'.pdf'
111 out = os.path.join(tmp_dir, out.replace(' ',''))
115 command = [comm_path]
117 command = ['wkhtmltopdf']
119 command.append('--quiet')
120 # default to UTF-8 encoding. Use <meta charset="latin-1"> to override.
121 command.extend(['--encoding', 'utf-8'])
123 head_file = file( os.path.join(
125 str(time.time()) + '.head.html'
129 head_file.write(header)
131 file_to_del.append(head_file.name)
132 command.extend(['--header-html', head_file.name])
134 foot_file = file( os.path.join(
136 str(time.time()) + '.foot.html'
140 foot_file.write(footer)
142 file_to_del.append(foot_file.name)
143 command.extend(['--footer-html', foot_file.name])
145 if webkit_header.margin_top :
146 command.extend(['--margin-top', str(webkit_header.margin_top).replace(',', '.')])
147 if webkit_header.margin_bottom :
148 command.extend(['--margin-bottom', str(webkit_header.margin_bottom).replace(',', '.')])
149 if webkit_header.margin_left :
150 command.extend(['--margin-left', str(webkit_header.margin_left).replace(',', '.')])
151 if webkit_header.margin_right :
152 command.extend(['--margin-right', str(webkit_header.margin_right).replace(',', '.')])
153 if webkit_header.orientation :
154 command.extend(['--orientation', str(webkit_header.orientation).replace(',', '.')])
155 if webkit_header.format :
156 command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
158 for html in html_list :
159 html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
161 html_file.write(html)
163 file_to_del.append(html_file.name)
164 command.append(html_file.name)
166 generate_command = ' '.join(command)
168 status = subprocess.call(command, stderr=subprocess.PIPE) # ignore stderr
171 _('Webkit raise an error' ),
175 for f_to_del in file_to_del :
178 pdf = file(out, 'rb').read()
179 for f_to_del in file_to_del :
185 def translate_call(self, src):
186 """Translate String."""
187 ir_translation = self.pool.get('ir.translation')
188 res = ir_translation._get_source(self.parser_instance.cr, self.parser_instance.uid,
189 self.name, 'report', self.parser_instance.localcontext.get('lang', 'en_US'), src)
194 # override needed to keep the attachments storing procedure
195 def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
196 """generate the PDF"""
201 if report_xml.report_type != 'webkit':
202 return super(WebKitParser,self).create_single_pdf(cursor, uid, ids, data, report_xml, context=context)
204 self.parser_instance = self.parser(cursor,
209 self.pool = pooler.get_pool(cursor.dbname)
210 objs = self.getObjects(cursor, uid, ids, context)
211 self.parser_instance.set_context(objs, data, ids, report_xml.report_type)
215 if report_xml.report_file :
216 path = addons.get_module_resource(report_xml.report_file)
217 if os.path.exists(path) :
218 template = file(path).read()
219 if not template and report_xml.report_webkit_data :
220 template = report_xml.report_webkit_data
222 raise except_osv(_('Error!'), _('Webkit Report template not found !'))
223 header = report_xml.webkit_header.html
224 footer = report_xml.webkit_header.footer_html
225 if not header and report_xml.header:
227 _('No header defined for this Webkit report!'),
228 _('Please set a header in company settings')
230 if not report_xml.header :
232 default_head = addons.get_module_resource('report_webkit', 'default_header.html')
233 with open(default_head,'r') as f:
235 css = report_xml.webkit_header.css
238 user = self.pool.get('res.users').browse(cursor, uid, uid)
239 company= user.company_id
241 #default_filters=['unicode', 'entity'] can be used to set global filter
242 body_mako_tpl = mako_template(template)
243 helper = WebKitHelper(cursor, uid, report_xml.id, context)
244 if report_xml.precise_mode:
246 self.parser_instance.localcontext['objects'] = [obj]
248 html = body_mako_tpl.render(helper=helper,
250 _=self.translate_call,
251 **self.parser_instance.localcontext)
254 msg = exceptions.text_error_template().render()
256 raise except_osv(_('Webkit render'), msg)
259 html = body_mako_tpl.render(helper=helper,
261 _=self.translate_call,
262 **self.parser_instance.localcontext)
265 msg = exceptions.text_error_template().render()
267 raise except_osv(_('Webkit render'), msg)
268 head_mako_tpl = mako_template(header)
270 head = head_mako_tpl.render(helper=helper,
272 _=self.translate_call,
274 **self.parser_instance.localcontext)
276 raise except_osv(_('Webkit render'),
277 exceptions.text_error_template().render())
280 foot_mako_tpl = mako_template(footer)
282 foot = foot_mako_tpl.render(helper=helper,
284 _=self.translate_call,
285 **self.parser_instance.localcontext)
287 msg = exceptions.text_error_template().render()
289 raise except_osv(_('Webkit render'), msg)
290 if report_xml.webkit_debug :
292 deb = head_mako_tpl.render(helper=helper,
294 _debug=tools.ustr("\n".join(htmls)),
295 _=self.translate_call,
296 **self.parser_instance.localcontext)
298 msg = exceptions.text_error_template().render()
300 raise except_osv(_('Webkit render'), msg)
302 bin = self.get_lib(cursor, uid, company.id)
303 pdf = self.generate_pdf(bin, report_xml, head, foot, htmls)
307 def create(self, cursor, uid, ids, data, context=None):
308 """We override the create function in order to handle generator
309 Code taken from report openoffice. Thanks guys :) """
310 pool = pooler.get_pool(cursor.dbname)
311 ir_obj = pool.get('ir.actions.report.xml')
312 report_xml_ids = ir_obj.search(cursor, uid,
313 [('report_name', '=', self.name[7:])], context=context)
316 report_xml = ir_obj.browse(cursor,
320 report_xml.report_rml = None
321 report_xml.report_rml_content = None
322 report_xml.report_sxw_content_data = None
323 report_rml.report_sxw_content = None
324 report_rml.report_sxw = None
326 return super(WebKitParser, self).create(cursor, uid, ids, data, context)
327 if report_xml.report_type != 'webkit' :
328 return super(WebKitParser, self).create(cursor, uid, ids, data, context)
329 result = self.create_source_pdf(cursor, uid, ids, data, report_xml, context)
334 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: