diff --git a/ocrmypdf/pageinfo.py b/ocrmypdf/pageinfo.py index 1984511a..92c01faa 100644 --- a/ocrmypdf/pageinfo.py +++ b/ocrmypdf/pageinfo.py @@ -98,6 +98,22 @@ InlineSettings = namedtuple('InlineSettings', ContentsInfo = namedtuple('ContentsInfo', ['raster_settings', 'inline_images']) +def _normalize_stack(operations): + """Fix runs of qQ's in the stack + + For some reason PyPDF2 converts runs of qqq, QQ, QQQq, etc. into single + operations. Break this silliness up and issue each stack operation + individually so we don't lose count. + + """ + for operands, command in operations: + if re.match(br'Q*q+$', command): # Zero or more Q, one or more q + for char in command: # Split into individual bytes + yield ([], bytes([char])) # Yield individual bytes + else: + yield (operands, command) + + def _interpret_contents(contentstream): """Interpret the PDF content stream @@ -127,14 +143,19 @@ def _interpret_contents(contentstream): image_raster_settings = [] inline_images = [] - for op in operations: + for n, op in enumerate(_normalize_stack(operations)): operands, command = op if command == b'q': stack.append(ctm) if len(stack) > 32: - raise RuntimeError("PDF graphics stack overflow") + raise RuntimeError( + "PDF graphics stack overflow, command %i" % n) elif command == b'Q': - ctm = stack.pop() + try: + ctm = stack.pop() + except IndexError: + raise RuntimeError( + "PDF graphics stack underflow, command %i" % n) elif command == b'cm': ctm = matrix_mult( _matrix_from_shorthand(operands), ctm) diff --git a/tests/resources/README.rst b/tests/resources/README.rst index a8f29346..fd515361 100644 --- a/tests/resources/README.rst +++ b/tests/resources/README.rst @@ -79,6 +79,9 @@ under the terms of the license in LICENSE.rst. * - missing_docinfo.pdf - @jbarlow83 - PDF file with no /DocumentInfo section + * - overlay.pdf + - @maxandersen + - PDF file generated by PDFPen pro that triggered content stream parse errors Assemblies ========== diff --git a/tests/resources/overlay.pdf b/tests/resources/overlay.pdf new file mode 100644 index 00000000..f8a29dbd Binary files /dev/null and b/tests/resources/overlay.pdf differ diff --git a/tests/test_main.py b/tests/test_main.py index 3bced8d9..0abfc392 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -687,3 +687,8 @@ def test_very_high_dpi(spoof_tesseract_cache, resources, outpdf): "Checks for a Decimal quantize error with high DPI, etc" check_ocrmypdf(resources / '2400dpi.pdf', outpdf, env=spoof_tesseract_cache) + + +def test_overlay(spoof_tesseract_noop, resources, outpdf): + check_ocrmypdf(resources / 'overlay.pdf', outpdf, + env=spoof_tesseract_noop)