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)