[FIX] tools.pdf: do not crash when PDF has no attachments
In the PDF, `DictionaryObject` can be wrapped in `IndirectObject`. For nested dictionaries, using `get` on a `DictionaryObject` will unwrap the result if it's an `IndirectObject`, but not `__getitem__` so when trying to futher call `get` on the result will cause an error: we need to unwrap the object first. Instead, we patch `DictionaryObject.get()` for it to unwrap the object in case it's an `IndirectObject`. This is a follow-up commit of fece5ab1bf2fef043b131f1bd0886f0841116103 closes odoo/odoo#53269 X-original-commit: 189a0b28ec7fdb7472f936450cfd7b4bfd6912ed Signed-off-by: Olivier Dony (odo) <odo@openerp.com> Signed-off-by: bfr-o <bfr-o@users.noreply.github.com>
This commit is contained in:
@@ -45,3 +45,4 @@ from . import test_tests_tags
|
||||
from . import test_base_document_layout
|
||||
from . import test_form_create
|
||||
from . import test_cloc
|
||||
from . import test_pdf
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
%PDF-1.3
|
||||
%µ¶
|
||||
|
||||
1 0 obj
|
||||
<</Type/Catalog/Pages 2 0 R/Names 5 0 R>>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<</Type/Pages/Kids[3 0 R]/Count 1>>
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
<</Type/Page/Parent 2 0 R/Resources<</Font<</F1<</Type/Font/Subtype/Type1/BaseFont/Arial>>>>>>/Contents 4 0 R>>
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<</Length 44>>
|
||||
stream
|
||||
BT
|
||||
/F1 100 Tf
|
||||
10 400 Td
|
||||
(!Odoo Test!) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<</Foo/Bar>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 6
|
||||
0000000000 00001 f
|
||||
0000000016 00000 n
|
||||
0000000074 00000 n
|
||||
0000000126 00000 n
|
||||
0000000254 00000 n
|
||||
0000000346 00000 n
|
||||
|
||||
trailer
|
||||
<</Size 6/Root 1 0 R>>
|
||||
startxref
|
||||
376
|
||||
%%EOF
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tools import pdf
|
||||
from odoo.modules.module import get_module_resource
|
||||
import io
|
||||
|
||||
|
||||
class TestPdf(TransactionCase):
|
||||
""" Tests on pdf. """
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
file_path = get_module_resource('base', 'tests', 'minimal.pdf')
|
||||
self.file = open(file_path, 'rb').read()
|
||||
self.minimal_reader_buffer = io.BytesIO(self.file)
|
||||
self.minimal_pdf_reader = pdf.OdooPdfFileReader(self.minimal_reader_buffer)
|
||||
|
||||
def test_odoo_pdf_file_reader(self):
|
||||
attachments = list(self.minimal_pdf_reader.getAttachments())
|
||||
self.assertEqual(len(attachments), 0)
|
||||
|
||||
pdf_writer = pdf.PdfFileWriter()
|
||||
pdf_writer.cloneReaderDocumentRoot(self.minimal_pdf_reader)
|
||||
pdf_writer.addAttachment('test_attachment.txt', b'My awesome attachment')
|
||||
|
||||
attachments = list(self.minimal_pdf_reader.getAttachments())
|
||||
self.assertEqual(len(attachments), 1)
|
||||
|
||||
def test_odoo_pdf_file_writer(self):
|
||||
attachments = list(self.minimal_pdf_reader.getAttachments())
|
||||
self.assertEqual(len(attachments), 0)
|
||||
|
||||
pdf_writer = pdf.OdooPdfFileWriter()
|
||||
pdf_writer.cloneReaderDocumentRoot(self.minimal_pdf_reader)
|
||||
|
||||
pdf_writer.addAttachment('test_attachment.txt', b'My awesome attachment')
|
||||
attachments = list(self.minimal_pdf_reader.getAttachments())
|
||||
self.assertEqual(len(attachments), 1)
|
||||
|
||||
pdf_writer.addAttachment('another_attachment.txt', b'My awesome OTHER attachment')
|
||||
attachments = list(self.minimal_pdf_reader.getAttachments())
|
||||
self.assertEqual(len(attachments), 2)
|
||||
|
||||
def test_merge_pdf(self):
|
||||
self.assertEqual(self.minimal_pdf_reader.getNumPages(), 1)
|
||||
page = self.minimal_pdf_reader.getPage(0)
|
||||
|
||||
merged_pdf = pdf.merge_pdf([self.file, self.file])
|
||||
merged_reader_buffer = io.BytesIO(merged_pdf)
|
||||
merged_pdf_reader = pdf.OdooPdfFileReader(merged_reader_buffer)
|
||||
self.assertEqual(merged_pdf_reader.getNumPages(), 2)
|
||||
merged_reader_buffer.close()
|
||||
|
||||
def test_branded_file_writer(self):
|
||||
# It's not easy to create a PDF with PyPDF2, so instead we copy minimal.pdf with our custom pdf writer
|
||||
pdf_writer = pdf.PdfFileWriter() # BrandedFileWriter
|
||||
pdf_writer.cloneReaderDocumentRoot(self.minimal_pdf_reader)
|
||||
writer_buffer = io.BytesIO()
|
||||
pdf_writer.write(writer_buffer)
|
||||
branded_content = writer_buffer.getvalue()
|
||||
writer_buffer.close()
|
||||
|
||||
# Read the metadata of the newly created pdf.
|
||||
reader_buffer = io.BytesIO(branded_content)
|
||||
pdf_reader = pdf.PdfFileReader(reader_buffer)
|
||||
pdf_info = pdf_reader.getDocumentInfo()
|
||||
self.assertEqual(pdf_info['/Producer'], 'Odoo')
|
||||
self.assertEqual(pdf_info['/Creator'], 'Odoo')
|
||||
reader_buffer.close()
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.minimal_reader_buffer.close()
|
||||
+15
-2
@@ -12,6 +12,17 @@ import hashlib
|
||||
DEFAULT_PDF_DATETIME_FORMAT = "D:%Y%m%d%H%M%S+00'00'"
|
||||
|
||||
|
||||
# make sure values are unwrapped by calling the specialized __getitem__
|
||||
def _unwrapping_get(self, key, default=None):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
|
||||
DictionaryObject.get = _unwrapping_get
|
||||
|
||||
|
||||
class BrandedFileWriter(PdfFileWriter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -25,7 +36,8 @@ PdfFileWriter = BrandedFileWriter
|
||||
|
||||
|
||||
def merge_pdf(pdf_data):
|
||||
''' Merge a collection of PDF documents in one
|
||||
''' Merge a collection of PDF documents in one.
|
||||
Note that the attachments are not merged.
|
||||
:param list pdf_data: a list of PDF datastrings
|
||||
:return: a unique merged PDF datastring
|
||||
'''
|
||||
@@ -40,7 +52,8 @@ def merge_pdf(pdf_data):
|
||||
|
||||
|
||||
def rotate_pdf(pdf):
|
||||
''' Rotate clockwise PDF (90°)
|
||||
''' Rotate clockwise PDF (90°) into a new PDF.
|
||||
Note that the attachments are not copied.
|
||||
:param pdf: a PDF to rotate
|
||||
:return: a PDF rotated
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user