From 08e9798d74ef41a67ccee4f5beee31c1078dc38a Mon Sep 17 00:00:00 2001 From: "Nicolas (vin)" Date: Tue, 21 Dec 2021 06:41:28 +0000 Subject: [PATCH] [FIX] [base, account_edi_facturx]: Fix pdf export There is an issue when exporting pdf using edi documents created before the PDF/A commit. With the subtype now included, the system would try to use the subtype given by the ir.attachment which would not be formated as expected by the pdf file format. The attachment may get neutered by the ORM, so we may have to force the mimetype when embedding it. This fix in two parts will allow to force a subtype when adding an attachment into a pdf, as well as parse the subtype of ir.attachment to give them the right format. xxx/xxx should become /xxx#2Fxxx opw-2714040 closes odoo/odoo#81898 X-original-commit: 880d7a1c8474a3bee4fd05474788fbd4a63f4ae7 Signed-off-by: Laurent Smet --- .../models/account_edi_format.py | 4 +- odoo/tools/pdf.py | 37 ++++++++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/addons/account_edi_facturx/models/account_edi_format.py b/addons/account_edi_facturx/models/account_edi_format.py index 159ec423d4b..02c0b2da64b 100644 --- a/addons/account_edi_facturx/models/account_edi_format.py +++ b/addons/account_edi_facturx/models/account_edi_format.py @@ -36,7 +36,7 @@ class AccountEdiFormat(models.Model): if not edi_document.attachment_id: return - pdf_writer.embed_odoo_attachment(edi_document.attachment_id) + pdf_writer.embed_odoo_attachment(edi_document.attachment_id, subtype='application/xml') if not pdf_writer.is_pdfa and str2bool(self.env['ir.config_parameter'].sudo().get_param('edi.use_pdfa', 'False')): try: pdf_writer.convert_to_pdfa() @@ -75,7 +75,7 @@ class AccountEdiFormat(models.Model): return self.env['ir.attachment'].create({ 'name': 'factur-x.xml', 'raw': xml_content.encode(), - 'mimetype': '/application#2Fxml' + 'mimetype': 'application/xml' }) def _is_facturx(self, filename, tree): diff --git a/odoo/tools/pdf.py b/odoo/tools/pdf.py index 3f882818cac..7da157575be 100644 --- a/odoo/tools/pdf.py +++ b/odoo/tools/pdf.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import io +import re from datetime import datetime from hashlib import md5 @@ -18,6 +19,8 @@ from odoo.tools.misc import file_open _logger = getLogger(__name__) DEFAULT_PDF_DATETIME_FORMAT = "D:%Y%m%d%H%M%S+00'00'" +REGEX_SUBTYPE_UNFORMATED = re.compile(r'^\w+/[\w-]+$') +REGEX_SUBTYPE_FORMATED = re.compile(r'^/\w+#2F[\w-]+$') # make sure values are unwrapped by calling the specialized __getitem__ @@ -95,16 +98,16 @@ class OdooPdfFileReader(PdfFileReader): try: file_path = self.trailer["/Root"].get("/Names", {}).get("/EmbeddedFiles", {}).get("/Names") + + if not file_path: + return [] + for i in range(0, len(file_path), 2): + attachment = file_path[i+1].getObject() + yield (attachment["/F"], attachment["/EF"]["/F"].getObject().getData()) except Exception: # malformed pdf (i.e. invalid xref page) return [] - if not file_path: - return [] - for i in range(0, len(file_path), 2): - attachment = file_path[i+1].getObject() - yield (attachment["/F"], attachment["/EF"]["/F"].getObject().getData()) - class OdooPdfFileWriter(PdfFileWriter): @@ -117,21 +120,29 @@ class OdooPdfFileWriter(PdfFileWriter): self._reader = None self.is_pdfa = False - def addAttachment(self, name, data, subtype=""): + def addAttachment(self, name, data, subtype=None): """ Add an attachment to the pdf. Supports adding multiple attachment, while respecting PDF/A rules. :param name: The name of the attachement :param data: The data of the attachement :param subtype: The mime-type of the attachement. This is required by PDF/A, but not essential otherwise. - It should take the form of "/xxx%2Fxxx". E.g. for "text/xml": "/text%2Fxml" + It should take the form of "/xxx#2Fxxx". E.g. for "text/xml": "/text#2Fxml" """ - if subtype == 'application/xml': - subtype = '/application#2Fxml' + adapted_subtype = subtype + if subtype: + # If we receive the subtype in an 'unformated' (mimetype) format, we'll try to convert it to a pdf-valid one + if REGEX_SUBTYPE_UNFORMATED.match(subtype): + adapted_subtype = '/' + subtype.replace('/', '#2F') + + if not REGEX_SUBTYPE_FORMATED.match(adapted_subtype): + # The subtype still does not match the correct format, so we will not add it to the document + _logger.warning("Attempt to add an attachment with the incorrect subtype '%s'. The subtype will be ignored.", subtype) + adapted_subtype = '' attachment = self._create_attachment_object({ 'filename': name, 'content': data, - 'subtype': subtype, + 'subtype': adapted_subtype, }) if self._root_object.get('/Names') and self._root_object['/Names'].get('/EmbeddedFiles'): names_array = self._root_object["/Names"]["/EmbeddedFiles"]["/Names"] @@ -163,9 +174,9 @@ class OdooPdfFileWriter(PdfFileWriter): NameObject("/AF"): attachment_array }) - def embed_odoo_attachment(self, attachment): + def embed_odoo_attachment(self, attachment, subtype=None): assert attachment, "embed_odoo_attachment cannot be called without attachment." - self.addAttachment(attachment.name, attachment.raw, attachment.mimetype) + self.addAttachment(attachment.name, attachment.raw, subtype=subtype or attachment.mimetype) def cloneReaderDocumentRoot(self, reader): super().cloneReaderDocumentRoot(reader)