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.

235 lines
10 KiB

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