diff --git a/trytond/ir/action.py b/trytond/ir/action.py index 05131d0ea3..87398ea0bf 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -168,6 +168,17 @@ class ActionKeyword(OSV): ActionKeyword() +class ActionReportOutputFormat(OSV): + "Output formats for reports" + _name = 'ir.action.report.outputformat' + _description = "Report Output Format" + format = fields.char('Internal Format Name', required=True, + readonly=True, help="Used as file extension, too.") + name = fields.char('Name', required=True, translate=True) + +ActionReportOutputFormat() + + class ActionReport(OSV): "Action report" _name = 'ir.action.report' @@ -187,6 +198,8 @@ class ActionReport(OSV): style_content = fields.Function('get_style_content', type='binary', string='Style') direct_print = fields.Boolean('Direct Print') + output_format = fields.Many2One('ir.action.report.outputformat', + 'Output format', required=True) def __init__(self): super(ActionReport, self).__init__() @@ -204,6 +217,16 @@ class ActionReport(OSV): def default_direct_print(self, cursor, user, context=None): return False + def default_output_format(self, cursor, user, context=None): + format_obj = self.pool.get(ActionReportOutputFormat._name) + formats = format_obj.search(cursor, user, [ + ('format', '=', 'odt'), + ], limit=1, context=context) + if formats: + return format_obj.name_get(cursor, user, formats[0], + context=context)[0] + return False + def get_report_content(self, cursor, user, ids, name, arg, context=None): res = {} for report in self.browse(cursor, user, ids, context=context): diff --git a/trytond/ir/action.xml b/trytond/ir/action.xml index cc86850160..c3d5dc0ab6 100644 --- a/trytond/ir/action.xml +++ b/trytond/ir/action.xml @@ -59,6 +59,7 @@ + ir.action.report form @@ -75,6 +76,8 @@ + + + + odt + ODF Textdocuments (.odt) + + + pdf + PDF Document + + ir.action.act_window form diff --git a/trytond/report/report.py b/trytond/report/report.py index 386d052c09..99f7a59b67 100644 --- a/trytond/report/report.py +++ b/trytond/report/report.py @@ -282,7 +282,62 @@ class Report(object): content_z.close() data = content_io.getvalue() content_io.close() - return ('odt', data) + output_format = report.output_format.format + if output_format == 'pdf': + data = self.convert_pdf(data) + return (output_format, data) + + + def convert_pdf(self, data): + """ + Convert report to PDF using OpenOffice.org. + This requires OpenOffice.org, pyuno and openoffice-python to + be installed. + """ + import tempfile + try: + import unohelper # installs import-hook + import openoffice.interact + import openoffice.officehelper as officehelper + from openoffice.streams import OutputStream + from com.sun.star.beans import PropertyValue + except ImportError, exception: + raise ExceptOSV('ImportError', str(exception)) + try: + # connect to OOo + desktop = openoffice.interact.Desktop() + except officehelper.BootstrapException: + raise ExceptOSV('Error', "Can't connect to (bootstrap) OpenOffice.org") + + res_data = None + # Create temporary file (with name) and write data there. + # We can not use NamedTemporaryFile here, since this would be + # deleted as soon as we close it to allow OOo reading. + #TODO use an input stream here + fd_odt, odt_name = tempfile.mkstemp() + fh_odt = os.fdopen(fd_odt, 'wb+') + try: + fh_odt.write(data) + del data # save memory + fh_odt.close() + doc = desktop.openFile(odt_name, hidden=False) + # Export as PDF + buffer = StringIO.StringIO() + out_props = ( + PropertyValue("FilterName", 0, "writer_pdf_Export", 0), + PropertyValue("Overwrite", 0, True, 0), + PropertyValue("OutputStream", 0, OutputStream(buffer), 0), + ) + doc.storeToURL("private:stream", out_props) + res_data = buffer.getvalue() + del buffer + doc.dispose() + finally: + fh_odt.close() + os.remove(odt_name) + if not res_data: + ExceptOSV('Error', 'Error converting to PDF') + return res_data def _parse_node(self, cursor, user, node, localcontext, context, node_context = None):