diff --git a/ocrmypdf/exec/ghostscript.py b/ocrmypdf/exec/ghostscript.py index 8ed8fe95..db36a092 100644 --- a/ocrmypdf/exec/ghostscript.py +++ b/ocrmypdf/exec/ghostscript.py @@ -7,8 +7,10 @@ from shutil import copy from functools import lru_cache import re import sys +from math import isclose from . import get_program from ..exceptions import SubprocessOutputError +from PIL import Image @lru_cache(maxsize=1) @@ -33,8 +35,22 @@ def _gs_error_reported(stream): return re.search(r'error', stream, flags=re.IGNORECASE) +def _correct_image_size(input_fp, output_file, res, int_res, log): + # If the output size was reduced due to rounding DPI to an + # integer, resize the image to its expected size + input_fp.seek(0) + with Image.open(input_fp) as im: + expected_size = round(res[0] / int_res[0] * im.size[0]), \ + round(res[1] / int_res[1] * im.size[1]) + log.info(im.size) + log.info(expected_size) + im.resize(expected_size).save(output_file) + + def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log, pageno=1): + res = xres, yres + int_res = round(xres), round(yres) with NamedTemporaryFile(delete=True) as tmp: args_gs = [ get_program('gs'), @@ -46,7 +62,7 @@ def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log, '-dFirstPage=%i' % pageno, '-dLastPage=%i' % pageno, '-o', tmp.name, - '-r{0}x{1}'.format(str(round(xres)), str(round(yres))), + '-r{0}x{1}'.format(str(int_res[0]), str(int_res[1])), input_file ] @@ -58,7 +74,13 @@ def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log, log.debug(p.stdout) if p.returncode == 0: - copy(tmp.name, output_file) + if isclose(int_res[0], res[0], abs_tol=0.1) and \ + isclose(int_res[1], res[1], abs_tol=0.1): + copy(tmp.name, output_file) + else: + # If the output size was reduced due to rounding DPI to an + # integer, resize the image to its expected size + _correct_image_size(tmp, output_file, res, int_res, log) else: log.error('Ghostscript rasterizing failed') raise SubprocessOutputError() diff --git a/ocrmypdf/pipeline.py b/ocrmypdf/pipeline.py index f3dfe04c..8d754737 100644 --- a/ocrmypdf/pipeline.py +++ b/ocrmypdf/pipeline.py @@ -445,10 +445,10 @@ def rasterize_with_ghostscript( # Ghostscript respects /UserUnit when rasterizing. Rasterize at # the true DPI but rewrite the output image to the working DPI. ghostscript.rasterize_pdf( - input_file, output_file + '.tmp', xres=true_dpi, yres=true_dpi, + input_file, output_file + '.jpg', xres=true_dpi, yres=true_dpi, raster_device=device, log=log) - with Image.open(output_file + ".tmp") as im: + with Image.open(output_file + ".jpg") as im: im.save(output_file, dpi=(working_dpi, working_dpi)) diff --git a/tests/test_userunit.py b/tests/test_userunit.py index b486873a..14f2fc91 100644 --- a/tests/test_userunit.py +++ b/tests/test_userunit.py @@ -39,3 +39,13 @@ def poster(resources): def test_userunit_ghostscript_fails(poster, no_outpdf): p, out, err = run_ocrmypdf(poster, no_outpdf, '--output-type=pdfa') assert p.returncode == ExitCode.input_file + + +def test_userunit_qpdf_passes(spoof_tesseract_cache, poster, outpdf): + before = PdfInfo(poster) + check_ocrmypdf(poster, outpdf, '--output-type=pdf', + env=spoof_tesseract_cache) + + after = PdfInfo(outpdf) + assert isclose(before[0].width_inches, after[0].width_inches) +