diff --git a/ocrmypdf/exec/ghostscript.py b/ocrmypdf/exec/ghostscript.py index b73912f8..d3399710 100644 --- a/ocrmypdf/exec/ghostscript.py +++ b/ocrmypdf/exec/ghostscript.py @@ -35,9 +35,28 @@ def _gs_error_reported(stream): def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log, - pageno=1): + pageno=1, page_dpi=None): + """ + Rasterize one page of a PDF at resolution (xres, yres) in canvas units. + + The image is sized to match the integer pixels dimensions implied by + (xres, yres) even if those numbers are noninteger. The image's DPI will + be overridden with the values in page_dpi. + + :param input_file: + :param output_file: + :param xres: resolution at which to rasterize page + :param yres: + :param raster_device: + :param log: + :param pageno: page number to rasterize + :param page_dpi: resolution tuple (x, y) overriding output image DPI + :return: + """ res = xres, yres int_res = round(xres), round(yres) + if not page_dpi: + page_dpi = res with NamedTemporaryFile(delete=True) as tmp: args_gs = [ get_program('gs'), @@ -64,20 +83,19 @@ def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log, log.error('Ghostscript rasterizing failed') raise SubprocessOutputError() - tmp.seek(0) - # Ghostscript only accepts integers for output resolution # if the resolution happens to be fractional, then the discrepancy # would change the size of the output page, especially if the DPI # is quite low. Resize the image to the expected size + tmp.seek(0) with Image.open(tmp) as im: expected_size = round(im.size[0] / int_res[0] * res[0]), \ round(im.size[1] / int_res[1] * res[1]) - if expected_size != im.size: + if expected_size != im.size or page_dpi != (xres, yres): log.debug( "Ghostscript: resize output image {} -> {}".format( im.size, expected_size)) - im.resize(expected_size).save(output_file) + im.resize(expected_size).save(output_file, dpi=page_dpi) else: copy(tmp.name, output_file) diff --git a/ocrmypdf/pdfinfo.py b/ocrmypdf/pdfinfo.py index 8d498769..f0f4a460 100644 --- a/ocrmypdf/pdfinfo.py +++ b/ocrmypdf/pdfinfo.py @@ -695,7 +695,7 @@ class PdfInfo: @property def has_userunit(self): - return any(page.userunit for page in self.pages) + return any(page.userunit != 1.0 for page in self.pages) def __getitem__(self, item): return self._pages[item] diff --git a/ocrmypdf/pipeline.py b/ocrmypdf/pipeline.py index 8d754737..3678da9d 100644 --- a/ocrmypdf/pipeline.py +++ b/ocrmypdf/pipeline.py @@ -215,15 +215,18 @@ def get_page_dpi(pageinfo, options): def get_page_square_dpi(pageinfo, options): - "Get the working DPI when we require xres == yres" + "Get the DPI when we require xres == yres, scaled to physical units" + xres = pageinfo.xres or 0 + yres = pageinfo.yres or 0 + userunit = pageinfo.userunit or 1 return float(max( - (pageinfo.xres * pageinfo.userunit) or VECTOR_PAGE_DPI, - (pageinfo.yres * pageinfo.userunit) or VECTOR_PAGE_DPI, + (xres * userunit) or VECTOR_PAGE_DPI, + (yres * userunit) or VECTOR_PAGE_DPI, options.oversample or 0)) -def get_page_true_square_dpi(pageinfo, options): - "Get the true DPI when we require xres == yres, and scaled in userunits" +def get_canvas_square_dpi(pageinfo, options): + """Get the DPI when we require xres == yres, in Postscript units""" return float(max( (pageinfo.xres) or VECTOR_PAGE_DPI, (pageinfo.yres) or VECTOR_PAGE_DPI, @@ -433,23 +436,12 @@ def rasterize_with_ghostscript( # Produce the page image with square resolution or else deskew and OCR # will not work properly. - true_dpi = get_page_true_square_dpi(pageinfo, options) - working_dpi = get_page_square_dpi(pageinfo, options) + canvas_dpi = get_canvas_square_dpi(pageinfo, options) + page_dpi = get_page_square_dpi(pageinfo, options) - if true_dpi == working_dpi: - dpi = true_dpi - ghostscript.rasterize_pdf( - input_file, output_file, xres=dpi, yres=dpi, - raster_device=device, log=log) - else: - # 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 + '.jpg', xres=true_dpi, yres=true_dpi, - raster_device=device, log=log) - - with Image.open(output_file + ".jpg") as im: - im.save(output_file, dpi=(working_dpi, working_dpi)) + ghostscript.rasterize_pdf( + input_file, output_file, xres=canvas_dpi, yres=canvas_dpi, + raster_device=device, log=log, page_dpi=(page_dpi, page_dpi)) def preprocess_remove_background(