You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
11 KiB

11 years ago
  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 time
  34. import logging
  35. from mako import exceptions
  36. from openerp.osv.osv import except_osv
  37. from openerp.tools.translate import _
  38. from openerp import addons
  39. from openerp import pooler
  40. from openerp import tools
  41. from openerp.addons.report_webkit import webkit_report
  42. from openerp.addons.report_webkit.report_helper import WebKitHelper
  43. _logger = logging.getLogger('financial.reports.webkit')
  44. # Class used only as a workaround to bug:
  45. # http://code.google.com/p/wkhtmltopdf/issues/detail?id=656
  46. # html headers and footers do not work on big files (hundreds of pages) so we replace them by
  47. # text headers and footers passed as arguments to wkhtmltopdf
  48. # this class has to be removed once the bug is fixed
  49. # in your report class, to print headers and footers as text, you have to add them in the localcontext with a key 'additional_args'
  50. # for instance:
  51. # header_report_name = _('PARTNER LEDGER')
  52. # footer_date_time = self.formatLang(str(datetime.today()), date_time=True)
  53. # self.localcontext.update({
  54. # 'additional_args': [
  55. # ('--header-font-name', 'Helvetica'),
  56. # ('--footer-font-name', 'Helvetica'),
  57. # ('--header-font-size', '10'),
  58. # ('--footer-font-size', '7'),
  59. # ('--header-left', header_report_name),
  60. # ('--footer-left', footer_date_time),
  61. # ('--footer-right', ' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
  62. # ('--footer-line',),
  63. # ],
  64. # })
  65. # redefine mako_template as this is overriden by jinja since saas-1
  66. # from openerp.addons.report_webkit.webkit_report import mako_template
  67. from mako.template import Template
  68. from mako.lookup import TemplateLookup
  69. def mako_template(text):
  70. """Build a Mako template.
  71. This template uses UTF-8 encoding
  72. """
  73. tmp_lookup = TemplateLookup() #we need it in order to allow inclusion and inheritance
  74. return Template(text, input_encoding='utf-8', output_encoding='utf-8', lookup=tmp_lookup)
  75. class HeaderFooterTextWebKitParser(webkit_report.WebKitParser):
  76. def generate_pdf(self, comm_path, report_xml, header, footer, html_list, webkit_header=False):
  77. """Call webkit in order to generate pdf"""
  78. if not webkit_header:
  79. webkit_header = report_xml.webkit_header
  80. tmp_dir = tempfile.gettempdir()
  81. out_filename = tempfile.mktemp(suffix=".pdf", prefix="webkit.tmp.")
  82. file_to_del = [out_filename]
  83. if comm_path:
  84. command = [comm_path]
  85. else:
  86. command = ['wkhtmltopdf']
  87. command.append('--quiet')
  88. # default to UTF-8 encoding. Use <meta charset="latin-1"> to override.
  89. command.extend(['--encoding', 'utf-8'])
  90. if webkit_header.margin_top:
  91. command.extend(['--margin-top', str(webkit_header.margin_top).replace(',', '.')])
  92. if webkit_header.margin_bottom:
  93. command.extend(['--margin-bottom', str(webkit_header.margin_bottom).replace(',', '.')])
  94. if webkit_header.margin_left:
  95. command.extend(['--margin-left', str(webkit_header.margin_left).replace(',', '.')])
  96. if webkit_header.margin_right:
  97. command.extend(['--margin-right', str(webkit_header.margin_right).replace(',', '.')])
  98. if webkit_header.orientation:
  99. command.extend(['--orientation', str(webkit_header.orientation).replace(',', '.')])
  100. if webkit_header.format:
  101. command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
  102. if self.parser_instance.localcontext.get('additional_args', False):
  103. for arg in self.parser_instance.localcontext['additional_args']:
  104. command.extend(arg)
  105. count = 0
  106. for html in html_list:
  107. html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
  108. count += 1
  109. html_file.write(html)
  110. html_file.close()
  111. file_to_del.append(html_file.name)
  112. command.append(html_file.name)
  113. command.append(out_filename)
  114. stderr_fd, stderr_path = tempfile.mkstemp(text=True)
  115. file_to_del.append(stderr_path)
  116. try:
  117. status = subprocess.call(command, stderr=stderr_fd)
  118. os.close(stderr_fd) # ensure flush before reading
  119. stderr_fd = None # avoid closing again in finally block
  120. fobj = open(stderr_path, 'r')
  121. error_message = fobj.read()
  122. fobj.close()
  123. if not error_message:
  124. error_message = _('No diagnosis message was provided')
  125. else:
  126. error_message = _('The following diagnosis message was provided:\n') + error_message
  127. if status:
  128. raise except_osv(_('Webkit error' ),
  129. _("The command 'wkhtmltopdf' failed with error code = %s. Message: %s") % (status, error_message))
  130. pdf_file = open(out_filename, 'rb')
  131. pdf = pdf_file.read()
  132. pdf_file.close()
  133. finally:
  134. if stderr_fd is not None:
  135. os.close(stderr_fd)
  136. for f_to_del in file_to_del:
  137. try:
  138. os.unlink(f_to_del)
  139. except (OSError, IOError), exc:
  140. _logger.error('cannot remove file %s: %s', f_to_del, exc)
  141. return pdf
  142. # override needed to keep the attachments' storing procedure
  143. def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
  144. """generate the PDF"""
  145. if context is None:
  146. context={}
  147. htmls = []
  148. if report_xml.report_type != 'webkit':
  149. return super(HeaderFooterTextWebKitParser,self).create_single_pdf(cursor, uid, ids, data, report_xml, context=context)
  150. self.parser_instance = self.parser(cursor,
  151. uid,
  152. self.name2,
  153. context=context)
  154. self.pool = pooler.get_pool(cursor.dbname)
  155. objs = self.getObjects(cursor, uid, ids, context)
  156. self.parser_instance.set_context(objs, data, ids, report_xml.report_type)
  157. template = False
  158. if report_xml.report_file:
  159. path = addons.get_module_resource(*report_xml.report_file.split(os.path.sep))
  160. if os.path.exists(path):
  161. template = file(path).read()
  162. if not template and report_xml.report_webkit_data:
  163. template = report_xml.report_webkit_data
  164. if not template:
  165. raise except_osv(_('Error!'), _('Webkit Report template not found !'))
  166. header = report_xml.webkit_header.html
  167. footer = report_xml.webkit_header.footer_html
  168. if not header and report_xml.header:
  169. raise except_osv(
  170. _('No header defined for this Webkit report!'),
  171. _('Please set a header in company settings')
  172. )
  173. css = report_xml.webkit_header.css
  174. if not css:
  175. css = ''
  176. user = self.pool.get('res.users').browse(cursor, uid, uid)
  177. #default_filters=['unicode', 'entity'] can be used to set global filter
  178. body_mako_tpl = mako_template(template)
  179. helper = WebKitHelper(cursor, uid, report_xml.id, context)
  180. if report_xml.precise_mode:
  181. for obj in objs:
  182. self.parser_instance.localcontext['objects'] = [obj]
  183. try:
  184. html = body_mako_tpl.render(helper=helper,
  185. css=css,
  186. _=self.translate_call,
  187. **self.parser_instance.localcontext)
  188. htmls.append(html)
  189. except Exception, e:
  190. msg = exceptions.text_error_template().render()
  191. _logger.error(msg)
  192. raise except_osv(_('Webkit render'), msg)
  193. else:
  194. try:
  195. html = body_mako_tpl.render(helper=helper,
  196. css=css,
  197. _=self.translate_call,
  198. **self.parser_instance.localcontext)
  199. htmls.append(html)
  200. except Exception, e:
  201. msg = exceptions.text_error_template().render()
  202. _logger.error(msg)
  203. raise except_osv(_('Webkit render'), msg)
  204. # NO html footer and header because we write them as text with wkhtmltopdf
  205. head = foot = False
  206. if report_xml.webkit_debug:
  207. try:
  208. deb = body_mako_tpl.render(helper=helper,
  209. css=css,
  210. _debug=tools.ustr("\n".join(htmls)),
  211. _=self.translate_call,
  212. **self.parser_instance.localcontext)
  213. except Exception, e:
  214. msg = exceptions.text_error_template().render()
  215. _logger.error(msg)
  216. raise except_osv(_('Webkit render'), msg)
  217. return (deb, 'html')
  218. bin = self.get_lib(cursor, uid)
  219. pdf = self.generate_pdf(bin, report_xml, head, foot, htmls)
  220. return (pdf, 'pdf')