Handle non-UTF-8 DocumentInfo keys in repair_docinfo_nuls (closes #1540)
Some PDFs use a /Name dictionary key in /DocumentInfo whose bytes are not valid UTF-8/PDFDocEncoding, e.g. a Latin-1 /Saks#e5r. Older pikepdf raised UnicodeDecodeError while iterating such a block, crashing the pipeline during PDF/A conversion. repair_docinfo_nuls is documented to log and continue on a malformed DocumentInfo block, so catch UnicodeDecodeError alongside TypeError. Add a mock-based unit test that drives the decode-error branch (current pikepdf surrogate-escapes instead of raising) and an end-to-end test over the reporter's file, committed as docinfo_latin1_key.pdf.
This commit is contained in:
@@ -76,6 +76,9 @@ the copyright holder(s) and license(s) applicable to these resources.
|
||||
* - missing_docinfo.pdf
|
||||
- synthetic
|
||||
- PDF file with no /DocumentInfo section
|
||||
* - docinfo_latin1_key.pdf
|
||||
- synthetic
|
||||
- PDF whose /DocumentInfo dictionary has a /Name key with Latin-1 bytes (/Saks#e5r) that is not valid UTF-8
|
||||
* - overlay.pdf
|
||||
- synthetic
|
||||
- PDF file generated by PDFPen pro that triggered content stream parse errors
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
%PDF-1.3
|
||||
%¿÷¢þ
|
||||
1 0 obj
|
||||
<< /Pages 3 0 R /Type /Catalog >>
|
||||
endobj
|
||||
2 0 obj
|
||||
<< /Author (Geomatikk AS) /Beskrivelse () /Creator (OCRmyPDF 16.10.0 / EasyOCR-PDF 1.7.2) /CreatorVersion (6.36.0.918) /Dokumentidplanreg () /Enhetsnavn () /Hyperlink (1) /Opprinnelse () /Producer (pikepdf 9.5.2) /RegistrationDate (N/A) /Saksansvarlig#20enhet () /Saksbehandler () /Saksnr () /Saks#e5r () /Status () >>
|
||||
endobj
|
||||
3 0 obj
|
||||
<< /Count 1 /Kids [ 4 0 R ] /Type /Pages >>
|
||||
endobj
|
||||
4 0 obj
|
||||
<< /Contents 5 0 R /MediaBox [ 0 0 612 792 ] /Parent 3 0 R /Resources << >> /Type /Page >>
|
||||
endobj
|
||||
5 0 obj
|
||||
<< /Length 0 /Filter /FlateDecode >>
|
||||
stream
|
||||
|
||||
endstream
|
||||
endobj
|
||||
xref
|
||||
0 6
|
||||
0000000000 65535 f
|
||||
0000000015 00000 n
|
||||
0000000064 00000 n
|
||||
0000000398 00000 n
|
||||
0000000457 00000 n
|
||||
0000000563 00000 n
|
||||
trailer << /Info 2 0 R /Root 1 0 R /Size 6 /ID [<c5231b8cfab9c82526c0da7475add5da><c5231b8cfab9c82526c0da7475add5da>] >>
|
||||
startxref
|
||||
633
|
||||
%%EOF
|
||||
+28
-1
@@ -6,13 +6,14 @@ from __future__ import annotations
|
||||
import datetime as dt
|
||||
import warnings
|
||||
from shutil import copyfile
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pikepdf
|
||||
import pytest
|
||||
from pikepdf.models.metadata import decode_pdf_date
|
||||
|
||||
from ocrmypdf._jobcontext import PdfContext
|
||||
from ocrmypdf._metadata import metadata_fixup
|
||||
from ocrmypdf._metadata import metadata_fixup, repair_docinfo_nuls
|
||||
from ocrmypdf._pipeline import convert_to_pdfa
|
||||
from ocrmypdf.api import setup_plugin_infrastructure
|
||||
from ocrmypdf.cli import get_options_and_plugins
|
||||
@@ -43,6 +44,32 @@ def test_preserve_docinfo(output_type, resources, outpdf):
|
||||
assert pdfa_info['output'] == output_type
|
||||
|
||||
|
||||
def test_repair_docinfo_nuls_undecodable_key(caplog):
|
||||
"""A DocumentInfo key with bytes that don't decode must not crash.
|
||||
|
||||
Some PDFs use a /Name dictionary key in DocumentInfo whose bytes are not
|
||||
valid PDFDocEncoding/UTF-8 (e.g. Latin-1 ``/Saks#e5r``). Older pikepdf
|
||||
raised UnicodeDecodeError while iterating such a dictionary. The repair
|
||||
must log and continue rather than propagate the exception. See #1540.
|
||||
"""
|
||||
pdf = MagicMock()
|
||||
pdf.docinfo.items.side_effect = UnicodeDecodeError(
|
||||
'utf-8', b'Saks\xe5r', 4, 5, 'invalid continuation byte'
|
||||
)
|
||||
# Make isinstance(pdf.docinfo, Dictionary) succeed so we reach the loop.
|
||||
with patch('ocrmypdf._metadata.Dictionary', MagicMock):
|
||||
result = repair_docinfo_nuls(pdf)
|
||||
assert result is False
|
||||
assert 'malformed DocumentInfo' in caplog.text
|
||||
|
||||
|
||||
def test_repair_docinfo_nuls_undecodable_key_real_file(resources):
|
||||
"""Opening a real file with a Latin-1 DocumentInfo key must not crash."""
|
||||
with pikepdf.open(resources / 'docinfo_latin1_key.pdf') as pdf:
|
||||
# Should return without raising regardless of pikepdf's decode behavior.
|
||||
repair_docinfo_nuls(pdf)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("output_type", ['pdfa', 'pdf'])
|
||||
def test_override_metadata(output_type, resources, outpdf, caplog):
|
||||
input_file = resources / 'c02-22.pdf'
|
||||
|
||||
Reference in New Issue
Block a user