diff --git a/ocrmypdf/tesseract.py b/ocrmypdf/tesseract.py index f25d0d3e..020540cc 100644 --- a/ocrmypdf/tesseract.py +++ b/ocrmypdf/tesseract.py @@ -98,6 +98,8 @@ def get_orientation(input_file, language: list, timeout: float, log): return OrientationConfidence(angle=0, confidence=0.0) except CalledProcessError as e: tesseract_log_output(log, e.output, input_file) + if 'Image too large' in e.output: + return OrientationConfidence(0, 0) raise e from e else: osd = {} @@ -148,6 +150,13 @@ def page_timedout(log, input_file): log.warning(prefix + " took too long to OCR - skipping") +def _generate_null_hocr(output_hocr, pageinfo): + with open(output_hocr, 'w', encoding="utf-8") as f: + f.write(HOCR_TEMPLATE.format( + pageinfo['width_pixels'], + pageinfo['height_pixels'])) + + def generate_hocr(input_file, output_hocr, language: list, tessconfig: list, timeout: float, pageinfo_getter, pagesegmode: int, log): @@ -175,13 +184,13 @@ def generate_hocr(input_file, output_hocr, language: list, tessconfig: list, # Temporary workaround to hocrTransform not being able to function if # it does not have a valid hOCR file. page_timedout(log, input_file) - with open(output_hocr, 'w', encoding="utf-8") as f: - pageinfo = pageinfo_getter() - f.write(HOCR_TEMPLATE.format( - pageinfo['width_pixels'], - pageinfo['height_pixels'])) + _generate_null_hocr(output_hocr, pageinfo_getter()) except CalledProcessError as e: tesseract_log_output(log, e.output, input_file) + if 'Image too large' in e.output: + _generate_null_hocr(output_hocr, pageinfo_getter()) + return + raise e from e else: tesseract_log_output(log, stdout, input_file) @@ -242,6 +251,9 @@ def generate_pdf(input_image, skip_pdf, output_pdf, language: list, shutil.copy(skip_pdf, output_pdf) except CalledProcessError as e: tesseract_log_output(log, e.output, input_image) + if 'Image too large' in e.output: + shutil.copy(skip_pdf, output_pdf) + return raise e from e else: tesseract_log_output(log, stdout, input_image) diff --git a/tests/resources/README.rst b/tests/resources/README.rst index da867662..d7ce2f8f 100644 --- a/tests/resources/README.rst +++ b/tests/resources/README.rst @@ -32,6 +32,7 @@ under the terms of the license in LICENSE.rst. - cmyk.pdf (a CMYK image created in Photoshop) - enormous.pdf (a very lage page) - francais.pdf (a page containing French accented characters) +- hugemono.pdf (large monochrome JBIG2 page with pixel dimensions of 35000x35000) - invalid.pdf (a PDF file header followed by EOF marker) - missing_docinfo.pdf (PDF file with no /DocumentInfo section) diff --git a/tests/resources/hugemono.pdf b/tests/resources/hugemono.pdf new file mode 100644 index 00000000..30700b3e Binary files /dev/null and b/tests/resources/hugemono.pdf differ diff --git a/tests/spoof/tesseract_big_image_error.py b/tests/spoof/tesseract_big_image_error.py new file mode 100755 index 00000000..827d6585 --- /dev/null +++ b/tests/spoof/tesseract_big_image_error.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +import sys + + +VERSION_STRING = '''tesseract 3.04.00 + leptonica-1.72 + libjpeg 8d : libpng 1.6.19 : libtiff 4.0.6 : zlib 1.2.5 +SPOOFED: return error claiming image too big +''' + +"""Simulates a Tesseract crash + +It isn't strictly necessary to crash the process and that has unwanted +side effects like triggering core dumps or error reporting, logging and such. +It's enough to dump some text to stderr and return an error code. + +Follows the POSIX? convention of returning 128 + signal number. + +""" + + +def main(): + if sys.argv[1] == '--version': + print(VERSION_STRING, file=sys.stderr) + sys.exit(0) + elif sys.argv[1] == '--list-langs': + print('List of available languages (1):\neng', file=sys.stderr) + sys.exit(0) + elif sys.argv[-1] == 'hocr': + print("Image too large: (33830, 14959)\n" + "Error during processing.", file=sys.stderr) + sys.exit(1) + elif sys.argv[-1] == 'pdf': + print("Image too large: (33830, 14959)\n" + "Error during processing.", file=sys.stderr) + sys.exit(1) + elif sys.argv[-1] == 'stdout': + print("Image too large: (33830, 14959)\n" + "Error during processing.", file=sys.stderr) + sys.exit(1) + else: + print("Spoof doesn't understand arguments", file=sys.stderr) + print(sys.argv, file=sys.stderr) + sys.exit(1) + + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/tests/spoof/tesseract_cache.py b/tests/spoof/tesseract_cache.py index fd3655e0..96485e14 100755 --- a/tests/spoof/tesseract_cache.py +++ b/tests/spoof/tesseract_cache.py @@ -90,6 +90,13 @@ def main(): universal_newlines=True) stdout, stderr = p.communicate() + if p.returncode != 0: + # Do not cache errors or crashes + print("Tesseract error", file=sys.stderr) + print(stdout, end='') + print(stderr, end='', file=sys.stderr) + return p.returncode + with open(cache_name + '.stdout', 'w') as f: f.write(stdout) with open(cache_name + '.stderr', 'w') as f: diff --git a/tests/test_main.py b/tests/test_main.py index 52669dd8..bf3c3d1e 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -113,6 +113,11 @@ def spoof_tesseract_crash(): return spoof('tesseract', 'tesseract_crash.py') +@pytest.fixture +def spoof_tesseract_big_image_error(): + return spoof('tesseract', 'tesseract_big_image_error.py') + + def test_quick(spoof_tesseract_noop): check_ocrmypdf('c02-22.pdf', 'test_quick.pdf', env=spoof_tesseract_noop) @@ -477,3 +482,13 @@ def test_tesseract_crash_autorotate(spoof_tesseract_crash): assert sh.returncode == ExitCode.child_process_error assert not os.path.exists(_outfile('wontwork.pdf')) assert "ERROR" in err + + +@pytest.mark.parametrize('renderer', [ + 'hocr', + 'tesseract', + ]) +def test_tesseract_image_too_big(renderer, spoof_tesseract_big_image_error): + check_ocrmypdf( + 'hugemono.pdf', 'hugemono_%s.pdf' % renderer, '-r', + '--pdf-renderer', renderer, env=spoof_tesseract_big_image_error)