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.

234 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. 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.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. file_to_del = [out_filename]
  74. if comm_path:
  75. command = [comm_path]
  76. else:
  77. command = ['wkhtmltopdf']
  78. command.append('--quiet')
  79. # default to UTF-8 encoding. Use <meta charset="latin-1"> to override.
  80. command.extend(['--encoding', 'utf-8'])
  81. if webkit_header.margin_top:
  82. command.extend(['--margin-top', str(webkit_header.margin_top).replace(',', '.')])
  83. if webkit_header.margin_bottom:
  84. command.extend(['--margin-bottom', str(webkit_header.margin_bottom).replace(',', '.')])
  85. if webkit_header.margin_left:
  86. command.extend(['--margin-left', str(webkit_header.margin_left).replace(',', '.')])
  87. if webkit_header.margin_right:
  88. command.extend(['--margin-right', str(webkit_header.margin_right).replace(',', '.')])
  89. if webkit_header.orientation:
  90. command.extend(['--orientation', str(webkit_header.orientation).replace(',', '.')])
  91. if webkit_header.format:
  92. command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
  93. if self.parser_instance.localcontext.get('additional_args', False):
  94. for arg in self.parser_instance.localcontext['additional_args']:
  95. command.extend(arg)
  96. count = 0
  97. for html in html_list:
  98. html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
  99. count += 1
  100. html_file.write(html)
  101. html_file.close()
  102. file_to_del.append(html_file.name)
  103. command.append(html_file.name)
  104. command.append(out_filename)
  105. stderr_fd, stderr_path = tempfile.mkstemp(text=True)
  106. file_to_del.append(stderr_path)
  107. try:
  108. status = subprocess.call(command, stderr=stderr_fd)
  109. os.close(stderr_fd) # ensure flush before reading
  110. stderr_fd = None # avoid closing again in finally block
  111. fobj = open(stderr_path, 'r')
  112. error_message = fobj.read()
  113. fobj.close()
  114. if not error_message:
  115. error_message = _('No diagnosis message was provided')
  116. else:
  117. error_message = _('The following diagnosis message was provided:\n') + error_message
  118. if status:
  119. raise except_osv(_('Webkit error' ),
  120. _("The command 'wkhtmltopdf' failed with error code = %s. Message: %s") % (status, error_message))
  121. pdf_file = open(out_filename, 'rb')
  122. pdf = pdf_file.read()
  123. pdf_file.close()
  124. finally:
  125. if stderr_fd is not None:
  126. os.close(stderr_fd)
  127. for f_to_del in file_to_del:
  128. try:
  129. os.unlink(f_to_del)
  130. except (OSError, IOError), exc:
  131. _logger.error('cannot remove file %s: %s', f_to_del, exc)
  132. return pdf
  133. # override needed to keep the attachments' storing procedure
  134. def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
  135. """generate the PDF"""
  136. if context is None:
  137. context={}
  138. htmls = []
  139. if report_xml.report_type != 'webkit':
  140. return super(HeaderFooterTextWebKitParser,self).create_single_pdf(cursor, uid, ids, data, report_xml, context=context)
  141. self.parser_instance = self.parser(cursor,
  142. uid,
  143. self.name2,
  144. context=context)
  145. self.pool = pooler.get_pool(cursor.dbname)
  146. objs = self.getObjects(cursor, uid, ids, context)
  147. self.parser_instance.set_context(objs, data, ids, report_xml.report_type)
  148. template = False
  149. if report_xml.report_file:
  150. path = addons.get_module_resource(*report_xml.report_file.split(os.path.sep))
  151. if os.path.exists(path):
  152. template = file(path).read()
  153. if not template and report_xml.report_webkit_data:
  154. template = report_xml.report_webkit_data
  155. if not template:
  156. raise except_osv(_('Error!'), _('Webkit Report template not found !'))
  157. header = report_xml.webkit_header.html
  158. footer = report_xml.webkit_header.footer_html
  159. if not header and report_xml.header:
  160. raise except_osv(
  161. _('No header defined for this Webkit report!'),
  162. _('Please set a header in company settings')
  163. )
  164. css = report_xml.webkit_header.css
  165. if not css:
  166. css = ''
  167. user = self.pool.get('res.users').browse(cursor, uid, uid)
  168. #default_filters=['unicode', 'entity'] can be used to set global filter
  169. body_mako_tpl = mako_template(template)
  170. helper = WebKitHelper(cursor, uid, report_xml.id, context)
  171. if report_xml.precise_mode:
  172. for obj in objs:
  173. self.parser_instance.localcontext['objects'] = [obj]
  174. try:
  175. html = body_mako_tpl.render(helper=helper,
  176. css=css,
  177. _=self.translate_call,
  178. **self.parser_instance.localcontext)
  179. htmls.append(html)
  180. except Exception, e:
  181. msg = exceptions.text_error_template().render()
  182. _logger.error(msg)
  183. raise except_osv(_('Webkit render'), msg)
  184. else:
  185. try:
  186. html = body_mako_tpl.render(helper=helper,
  187. css=css,
  188. _=self.translate_call,
  189. **self.parser_instance.localcontext)
  190. htmls.append(html)
  191. except Exception, e:
  192. msg = exceptions.text_error_template().render()
  193. _logger.error(msg)
  194. raise except_osv(_('Webkit render'), msg)
  195. # NO html footer and header because we write them as text with wkhtmltopdf
  196. head = foot = False
  197. if report_xml.webkit_debug:
  198. try:
  199. deb = body_mako_tpl.render(helper=helper,
  200. css=css,
  201. _debug=tools.ustr("\n".join(htmls)),
  202. _=self.translate_call,
  203. **self.parser_instance.localcontext)
  204. except Exception, e:
  205. msg = exceptions.text_error_template().render()
  206. _logger.error(msg)
  207. raise except_osv(_('Webkit render'), msg)
  208. return (deb, 'html')
  209. bin = self.get_lib(cursor, uid)
  210. pdf = self.generate_pdf(bin, report_xml, head, foot, htmls)
  211. return (pdf, 'pdf')