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 ##############################################################################
38 from mako.template import Template
39 from mako import exceptions
42 from report_helper import WebKitHelper
43 from report.report_sxw import *
46 from tools.translate import _
47 from osv.osv import except_osv
50 def mako_template(text):
51 """Build a Mako template.
53 This template uses UTF-8 encoding
55 # default_filters=['unicode', 'h'] can be used to set global filters
56 return Template(text, input_encoding='utf-8', output_encoding='utf-8')
58 class WebKitParser(report_sxw):
59 """Custom class that use webkit to render HTML reports
60 Code partially taken from report openoffice. Thanks guys :)
62 def __init__(self, name, table, rml=False, parser=False,
63 header=True, store=False):
64 self.parser_instance = False
66 report_sxw.__init__(self, name, table, rml, parser,
69 def get_lib(self, cursor, uid, company) :
70 """Return the lib wkhtml path"""
71 #TODO Detect lib in system first
72 path = self.pool.get('res.company').read(cursor, uid, company, ['lib_path',])
73 path = path['lib_path']
76 _('Wkhtmltopdf library path is not set in company'),
77 _('Please install executable on your system'+
78 ' (sudo apt-get install wkhtmltopdf) or download it from here:'+
79 ' http://code.google.com/p/wkhtmltopdf/downloads/list and set the'+
80 ' path to the executable on the Company form.'+
81 'Minimal version is 0.9.9')
83 if os.path.isabs(path) :
84 if (os.path.exists(path) and os.access(path, os.X_OK)\
85 and os.path.basename(path).startswith('wkhtmltopdf')):
89 _('Wrong Wkhtmltopdf path set in company'+
90 'Given path is not executable or path is wrong'),
95 _('path to Wkhtmltopdf is not absolute'),
98 def generate_pdf(self, comm_path, report_xml, header, footer, html_list, webkit_header=False):
99 """Call webkit in order to generate pdf"""
100 if not webkit_header:
101 webkit_header = report_xml.webkit_header
102 tmp_dir = tempfile.gettempdir()
103 out_filename = tempfile.mktemp(suffix=".pdf", prefix="webkit.tmp.")
107 command = [comm_path]
109 command = ['wkhtmltopdf']
111 command.append('--quiet')
112 # default to UTF-8 encoding. Use <meta charset="latin-1"> to override.
113 command.extend(['--encoding', 'utf-8'])
115 head_file = file( os.path.join(
117 str(time.time()) + '.head.html'
121 head_file.write(header)
123 file_to_del.append(head_file.name)
124 command.extend(['--header-html', head_file.name])
126 foot_file = file( os.path.join(
128 str(time.time()) + '.foot.html'
132 foot_file.write(footer)
134 file_to_del.append(foot_file.name)
135 command.extend(['--footer-html', foot_file.name])
137 if webkit_header.margin_top :
138 command.extend(['--margin-top', str(webkit_header.margin_top).replace(',', '.')])
139 if webkit_header.margin_bottom :
140 command.extend(['--margin-bottom', str(webkit_header.margin_bottom).replace(',', '.')])
141 if webkit_header.margin_left :
142 command.extend(['--margin-left', str(webkit_header.margin_left).replace(',', '.')])
143 if webkit_header.margin_right :
144 command.extend(['--margin-right', str(webkit_header.margin_right).replace(',', '.')])
145 if webkit_header.orientation :
146 command.extend(['--orientation', str(webkit_header.orientation).replace(',', '.')])
147 if webkit_header.format :
148 command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
150 for html in html_list :
151 html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
153 html_file.write(html)
155 file_to_del.append(html_file.name)
156 command.append(html_file.name)
157 command.append(out_filename)
159 status = subprocess.call(command, stderr=subprocess.PIPE) # ignore stderr
162 _('Webkit raise an error' ),
166 for f_to_del in file_to_del :
169 pdf = file(out_filename, 'rb').read()
170 for f_to_del in file_to_del :
173 os.unlink(out_filename)
177 def setLang(self, lang):
180 self.localcontext['lang'] = lang
182 def translate_call(self, src):
183 """Translate String."""
184 ir_translation = self.pool.get('ir.translation')
185 res = ir_translation._get_source(self.parser_instance.cr, self.parser_instance.uid, None, 'report', self.localcontext.get('lang', 'en_US'), src)
190 def formatLang(self, value, digits=None, date=False, date_time=False, grouping=True, monetary=False):
191 """format using the know cursor, language from localcontext"""
193 digits = self.parser_instance.get_digits(value)
194 if isinstance(value, (str, unicode)) and not value:
196 pool_lang = self.pool.get('res.lang')
197 lang = self.localcontext['lang']
199 lang_ids = pool_lang.search(self.parser_instance.cr, self.parser_instance.uid, [('code','=',lang)])[0]
200 lang_obj = pool_lang.browse(self.parser_instance.cr, self.parser_instance.uid, lang_ids)
202 if date or date_time:
206 date_format = lang_obj.date_format
207 parse_format = '%Y-%m-%d'
209 value=value.split('.')[0]
210 date_format = date_format + " " + lang_obj.time_format
211 parse_format = '%Y-%m-%d %H:%M:%S'
212 if not isinstance(value, time.struct_time):
213 return time.strftime(date_format, time.strptime(value, parse_format))
216 date = datetime(*value.timetuple()[:6])
217 return date.strftime(date_format)
219 return lang_obj.format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary)
221 # override needed to keep the attachments' storing procedure
222 def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
223 """generate the PDF"""
228 if report_xml.report_type != 'webkit':
229 return super(WebKitParser,self).create_single_pdf(cursor, uid, ids, data, report_xml, context=context)
231 self.parser_instance = self.parser(
238 self.pool = pooler.get_pool(cursor.dbname)
239 objs = self.getObjects(cursor, uid, ids, context)
240 self.parser_instance.set_context(objs, data, ids, report_xml.report_type)
244 if report_xml.report_file :
245 path = addons.get_module_resource(report_xml.report_file)
246 if path and os.path.exists(path) :
247 template = file(path).read()
248 if not template and report_xml.report_webkit_data :
249 template = report_xml.report_webkit_data
251 raise except_osv(_('Error!'), _('Webkit Report template not found !'))
252 header = report_xml.webkit_header.html
253 footer = report_xml.webkit_header.footer_html
254 if not header and report_xml.header:
256 _('No header defined for this Webkit report!'),
257 _('Please set a header in company settings')
259 if not report_xml.header :
260 #I know it could be cleaner ...
264 <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
265 <style type="text/css">
271 var x=document.location.search.substring(1).split('&');
272 for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
273 var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
275 var y = document.getElementsByClassName(x[i]);
276 for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
281 <body style="border:0; margin: 0;" onload="subst()">
284 css = report_xml.webkit_header.css
287 user = self.pool.get('res.users').browse(cursor, uid, uid)
288 company= user.company_id
290 #default_filters=['unicode', 'entity'] can be used to set global filter
291 body_mako_tpl = mako_template(template)
292 helper = WebKitHelper(cursor, uid, report_xml.id, context)
293 self.parser_instance.localcontext.update({'setLang':self.setLang})
294 self.parser_instance.localcontext.update({'formatLang':self.formatLang})
296 html = body_mako_tpl.render( helper=helper,
298 _=self.translate_call,
299 **self.parser_instance.localcontext
302 msg = exceptions.text_error_template().render()
303 netsvc.Logger().notifyChannel('Webkit render', netsvc.LOG_ERROR, msg)
304 raise except_osv(_('Webkit render'), msg)
305 head_mako_tpl = mako_template(header)
307 head = head_mako_tpl.render(helper=helper,
309 _=self.translate_call,
311 **self.parser_instance.localcontext)
313 raise except_osv(_('Webkit render'),
314 exceptions.text_error_template().render())
317 foot_mako_tpl = mako_template(footer)
319 foot = foot_mako_tpl.render(helper=helper,
321 _=self.translate_call,
322 **self.parser_instance.localcontext)
324 msg = exceptions.text_error_template().render()
325 netsvc.Logger().notifyChannel('Webkit render', netsvc.LOG_ERROR, msg)
326 raise except_osv(_('Webkit render'), msg)
327 if report_xml.webkit_debug :
329 deb = head_mako_tpl.render(helper=helper,
331 _debug=tools.ustr(html),
332 _=self.translate_call,
333 **self.parser_instance.localcontext)
335 msg = exceptions.text_error_template().render()
336 netsvc.Logger().notifyChannel('Webkit render', netsvc.LOG_ERROR, msg)
337 raise except_osv(_('Webkit render'), msg)
339 bin = self.get_lib(cursor, uid, company.id)
340 pdf = self.generate_pdf(bin, report_xml, head, foot, [html])
344 def create(self, cursor, uid, ids, data, context=None):
345 """We override the create function in order to handle generator
346 Code taken from report openoffice. Thanks guys :) """
347 pool = pooler.get_pool(cursor.dbname)
348 ir_obj = pool.get('ir.actions.report.xml')
349 report_xml_ids = ir_obj.search(cursor, uid,
350 [('report_name', '=', self.name[7:])], context=context)
353 report_xml = ir_obj.browse(
359 report_xml.report_rml = None
360 report_xml.report_rml_content = None
361 report_xml.report_sxw_content_data = None
362 report_rml.report_sxw_content = None
363 report_rml.report_sxw = None
365 return super(WebKitParser, self).create(cursor, uid, ids, data, context)
366 if report_xml.report_type != 'webkit' :
367 return super(WebKitParser, self).create(cursor, uid, ids, data, context)
368 result = self.create_source_pdf(cursor, uid, ids, data, report_xml, context)