fix: report un-optimizable images as a warning, not a traceback
The optimizer is best-effort: any image it cannot process is left unchanged in the output, which remains valid. Previously, an extraction failure (e.g. an exotic colorspace pikepdf cannot transcode) was logged with log.exception, printing a full traceback at ERROR level that alarmed users even though nothing was wrong with the output (issue #846). Trap such failures with a concise warning that the image was left unchanged and the output is still valid, and demote the traceback to debug verbosity for diagnosis.
This commit is contained in:
+38
-12
@@ -197,14 +197,14 @@ def test_optimize_off(resources, outpdf):
|
||||
def test_group3(resources):
|
||||
with pikepdf.open(resources / 'ccitt.pdf') as pdf:
|
||||
im = pdf.pages[0].Resources.XObject['/Im1']
|
||||
assert (
|
||||
opt.extract_image_filter(im, im.objgen[0]) is not None
|
||||
), "Group 4 should be allowed"
|
||||
assert opt.extract_image_filter(im, im.objgen[0]) is not None, (
|
||||
"Group 4 should be allowed"
|
||||
)
|
||||
|
||||
im.DecodeParms['/K'] = 0
|
||||
assert (
|
||||
opt.extract_image_filter(im, im.objgen[0]) is None
|
||||
), "Group 3 should be disallowed"
|
||||
assert opt.extract_image_filter(im, im.objgen[0]) is None, (
|
||||
"Group 3 should be disallowed"
|
||||
)
|
||||
|
||||
|
||||
def test_find_formx(resources):
|
||||
@@ -234,9 +234,7 @@ def test_find_formx_circular_reference(resources, tmp_path, caplog):
|
||||
# entries that all point back to /Form1 itself, creating a fan-out
|
||||
# cycle of branching factor 3.
|
||||
form = pdf.pages[0].obj.Resources.XObject.Form1
|
||||
form.Resources.XObject = Dictionary(
|
||||
{'/Fm0': form, '/Fm1': form, '/Fm2': form}
|
||||
)
|
||||
form.Resources.XObject = Dictionary({'/Fm0': form, '/Fm1': form, '/Fm2': form})
|
||||
pdf.save(out)
|
||||
|
||||
caplog.set_level(logging.WARNING, logger='ocrmypdf.optimize')
|
||||
@@ -244,9 +242,7 @@ def test_find_formx_circular_reference(resources, tmp_path, caplog):
|
||||
opt._find_image_xrefs(pdf)
|
||||
|
||||
n_warnings = sum(
|
||||
1
|
||||
for r in caplog.records
|
||||
if 'Recursion depth exceeded' in r.getMessage()
|
||||
1 for r in caplog.records if 'Recursion depth exceeded' in r.getMessage()
|
||||
)
|
||||
# Without the fix this is in the tens of thousands.
|
||||
assert n_warnings == 0, (
|
||||
@@ -255,6 +251,36 @@ def test_find_formx_circular_reference(resources, tmp_path, caplog):
|
||||
)
|
||||
|
||||
|
||||
def test_extract_images_traps_errors_as_warning(resources, tmp_path, caplog):
|
||||
"""Regression for issue #846.
|
||||
|
||||
The optimizer is best-effort: any image it cannot process can simply be
|
||||
passed through unchanged. When extraction of an image raises (e.g. an
|
||||
exotic colorspace pikepdf cannot transcode), the user should see a concise
|
||||
warning that the image was left unchanged, not an alarming traceback
|
||||
logged at ERROR level.
|
||||
"""
|
||||
import logging
|
||||
from unittest.mock import Mock
|
||||
|
||||
def boom(*, pdf, root, image, xref, options):
|
||||
raise NotImplementedError("synthetic extraction failure")
|
||||
|
||||
caplog.set_level(logging.DEBUG, logger='ocrmypdf.optimize')
|
||||
with pikepdf.open(resources / 'francais.pdf') as pdf:
|
||||
results = list(opt.extract_images(pdf, tmp_path, Mock(), boom))
|
||||
|
||||
# The error is trapped, not propagated, and nothing is extracted.
|
||||
assert results == []
|
||||
# A friendly warning is emitted...
|
||||
assert any(
|
||||
r.levelno == logging.WARNING and 'left unchanged' in r.getMessage()
|
||||
for r in caplog.records
|
||||
)
|
||||
# ...and no traceback is logged at ERROR level or above.
|
||||
assert not any(r.levelno >= logging.ERROR for r in caplog.records)
|
||||
|
||||
|
||||
def test_extract_image_filter_with_pdf_image():
|
||||
image = Dictionary()
|
||||
image.Subtype = Name.Image
|
||||
|
||||
Reference in New Issue
Block a user