From 57771f06a32f4d540590956e20c6a540f8719ecc Mon Sep 17 00:00:00 2001 From: "James R. Barlow" Date: Thu, 16 Apr 2020 15:38:33 -0700 Subject: [PATCH] Refactor xy-pair for resolution to tuple --- src/ocrmypdf/_pipeline.py | 33 +++++++++++++---------------- src/ocrmypdf/exec/ghostscript.py | 13 ++++++------ src/ocrmypdf/pdfinfo/info.py | 36 +++++++++++++------------------- tests/test_ghostscript.py | 6 ++---- tests/test_main.py | 8 +++---- tests/test_optimize.py | 2 +- tests/test_pdfinfo.py | 10 ++++----- tests/test_preprocessing.py | 22 +++++++------------ tests/test_rotation.py | 3 +-- 9 files changed, 57 insertions(+), 76 deletions(-) diff --git a/src/ocrmypdf/_pipeline.py b/src/ocrmypdf/_pipeline.py index 034b831c..842b4f1a 100644 --- a/src/ocrmypdf/_pipeline.py +++ b/src/ocrmypdf/_pipeline.py @@ -201,12 +201,12 @@ def validate_pdfinfo_options(context): def get_page_dpi(pageinfo, options): "Get the DPI when nonsquare DPI is tolerable" xres = max( - pageinfo.xres or VECTOR_PAGE_DPI, + pageinfo.xyres[0] or VECTOR_PAGE_DPI, options.oversample or 0, VECTOR_PAGE_DPI if pageinfo.has_vector else 0, ) yres = max( - pageinfo.yres or VECTOR_PAGE_DPI, + pageinfo.xyres[1] or VECTOR_PAGE_DPI, options.oversample or 0, VECTOR_PAGE_DPI if pageinfo.has_vector else 0, ) @@ -215,8 +215,8 @@ def get_page_dpi(pageinfo, options): def get_page_square_dpi(pageinfo, options): "Get the DPI when we require xres == yres, scaled to physical units" - xres = pageinfo.xres or 0 - yres = pageinfo.yres or 0 + xres = pageinfo.xyres[0] or 0 + yres = pageinfo.xyres[1] or 0 userunit = pageinfo.userunit or 1 return float( max( @@ -232,8 +232,8 @@ 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, + (pageinfo.xyres[0]) or VECTOR_PAGE_DPI, + (pageinfo.xyres[1]) or VECTOR_PAGE_DPI, VECTOR_PAGE_DPI if pageinfo.has_vector else 0, options.oversample or 0, ) @@ -321,9 +321,8 @@ def rasterize_preview(input_file, page_context): ghostscript.rasterize_pdf( input_file, output_file, - xres=canvas_dpi, - yres=canvas_dpi, raster_device='jpeggray', + xyres=(canvas_dpi, canvas_dpi), page_dpi=(page_dpi, page_dpi), pageno=page_context.pageinfo.pageno + 1, ) @@ -436,9 +435,8 @@ def rasterize( ghostscript.rasterize_pdf( input_file, output_file, - xres=canvas_dpi, - yres=canvas_dpi, raster_device=device, + xyres=(canvas_dpi, canvas_dpi), page_dpi=(page_dpi, page_dpi), pageno=pageinfo.pageno + 1, rotation=correction, @@ -488,8 +486,7 @@ def create_ocr_image(image, page_context): # pink = ImageColor.getcolor('#ff0080', im.mode) draw = ImageDraw.ImageDraw(im) - xres, yres = im.info['dpi'] - log.debug('resolution %r %r' % (xres, yres)) + log.debug('resolution %r', im.info['dpi']) if not options.force_ocr: # Do not mask text areas when forcing OCR, because we need to OCR @@ -505,12 +502,12 @@ def create_ocr_image(image, page_context): # without regard whatever resolution is in pageinfo (may differ or # be None) bbox = [float(v) for v in textarea] - xscale, yscale = float(xres) / 72.0, float(yres) / 72.0 + xyscale = tuple(float(coord) / 72.0 for coord in im.info['dpi']) pixcoords = [ - bbox[0] * xscale, - im.height - bbox[3] * yscale, - bbox[2] * xscale, - im.height - bbox[1] * yscale, + bbox[0] * xyscale[0], + im.height - bbox[3] * xyscale[1], + bbox[2] * xyscale[0], + im.height - bbox[1] * xyscale[1], ] pixcoords = [int(round(c)) for c in pixcoords] log.debug('blanking %r', pixcoords) @@ -524,7 +521,7 @@ def create_ocr_image(image, page_context): del draw # Pillow requires integer DPI - dpi = round(xres), round(yres) + dpi = tuple(round(coord) for coord in im.info['dpi']) im.save(output_file, dpi=dpi) return output_file diff --git a/src/ocrmypdf/exec/ghostscript.py b/src/ocrmypdf/exec/ghostscript.py index de4cbda2..d85ce801 100644 --- a/src/ocrmypdf/exec/ghostscript.py +++ b/src/ocrmypdf/exec/ghostscript.py @@ -135,32 +135,31 @@ def extract_text(input_file, pageno=1): def rasterize_pdf( input_file, output_file, - xres, - yres, + *, raster_device, + xyres, pageno=1, page_dpi=None, rotation=None, filter_vector=False, ): - """Rasterize one page of a PDF at resolution (xres, yres) in canvas units. + """Rasterize one page of a PDF at resolution xyres 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 + (xyres[0], xyres[1]) even if those numbers are noninteger. The image's DPI will be overridden with the values in page_dpi. :param input_file: pathlike :param output_file: pathlike - :param xres: resolution at which to rasterize page - :param yres: :param raster_device: + :param xyres: resolution at which to rasterize page :param pageno: page number to rasterize (beginning at page 1) :param page_dpi: resolution tuple (x, y) overriding output image DPI :param rotation: 0, 90, 180, 270: clockwise angle to rotate page :param filter_vector: if True, remove vector graphics objects :return: """ - res = round(xres, 6), round(yres, 6) + res = round(xyres[0], 6), round(xyres[1], 6) if not page_dpi: page_dpi = res diff --git a/src/ocrmypdf/pdfinfo/info.py b/src/ocrmypdf/pdfinfo/info.py index 329661b3..bb9605e1 100644 --- a/src/ocrmypdf/pdfinfo/info.py +++ b/src/ocrmypdf/pdfinfo/info.py @@ -356,12 +356,11 @@ class ImageInfo: return self._enc @property - def xres(self): - return _get_dpi(self._shorthand, (self._width, self._height))[0] - - @property - def yres(self): - return _get_dpi(self._shorthand, (self._width, self._height))[1] + def xyres(self): + return ( + _get_dpi(self._shorthand, (self._width, self._height))[0], + _get_dpi(self._shorthand, (self._width, self._height))[1], + ) def __repr__(self): class_locals = { @@ -371,7 +370,7 @@ class ImageInfo: } return ( "" + "{comp} {bpc} {enc} {xyres}>" ).format(**class_locals) @@ -607,9 +606,9 @@ def _pdf_get_pageinfo(pdf, pageno: int, infile: PathLike, xmltext: str): pageinfo['images'] = [im for im in contentsinfo if isinstance(im, ImageInfo)] if pageinfo['images']: - xres = Decimal(max(image.xres for image in pageinfo['images'])) - yres = Decimal(max(image.yres for image in pageinfo['images'])) - pageinfo['xres'], pageinfo['yres'] = xres, yres + xres = Decimal(max(image.xyres[0] for image in pageinfo['images'])) + yres = Decimal(max(image.xyres[1] for image in pageinfo['images'])) + pageinfo['xyres'] = xres, yres pageinfo['width_pixels'] = int(round(xres * pageinfo['width_inches'])) pageinfo['height_pixels'] = int(round(yres * pageinfo['height_inches'])) @@ -679,11 +678,11 @@ class PageInfo: @property def width_pixels(self): - return int(round(self.width_inches * self.xres)) + return int(round(self.width_inches * self.xyres[0])) @property def height_pixels(self): - return int(round(self.height_inches * self.yres)) + return int(round(self.height_inches * self.xyres[1])) @property def rotation(self): @@ -723,12 +722,8 @@ class PageInfo: ) @property - def xres(self): - return self._pageinfo.get('xres', None) - - @property - def yres(self): - return self._pageinfo.get('yres', None) + def xyres(self): + return self._pageinfo.get('xyres', (0, 0)) @property def userunit(self): @@ -743,14 +738,13 @@ class PageInfo: def __repr__(self): return ( - '' + '' ).format( self.pageno, self.width_inches, self.height_inches, self.rotation, - self.xres, - self.yres, + self.xyres, self.has_text, ) diff --git a/tests/test_ghostscript.py b/tests/test_ghostscript.py index 5bb7b76f..2e1543ea 100644 --- a/tests/test_ghostscript.py +++ b/tests/test_ghostscript.py @@ -76,9 +76,8 @@ def test_rasterize_size(francais, outdir, caplog): rasterize_pdf( path, outdir / 'out.png', - target_size[0] / page_size[0], - target_size[1] / page_size[1], raster_device='pngmono', + xyres=(target_size[0] / page_size[0], target_size[1] / page_size[1]), page_dpi=forced_dpi, ) @@ -99,9 +98,8 @@ def test_rasterize_rotated(francais, outdir, caplog): rasterize_pdf( path, outdir / 'out.png', - target_size[0] / page_size[0], - target_size[1] / page_size[1], raster_device='pngmono', + xyres=(target_size[0] / page_size[0], target_size[1] / page_size[1]), page_dpi=forced_dpi, rotation=90, ) diff --git a/tests/test_main.py b/tests/test_main.py index 2f843536..0583abfc 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -74,8 +74,8 @@ def test_oversample(spoof_tesseract_cache, renderer, resources, outpdf): pdfinfo = PdfInfo(oversampled_pdf) - print(pdfinfo[0].xres) - assert abs(pdfinfo[0].xres - 350) < 1 + print(pdfinfo[0].xyres[0]) + assert abs(pdfinfo[0].xyres[0] - 350) < 1 def test_repeat_ocr(resources, no_outpdf): @@ -393,8 +393,8 @@ def test_very_high_dpi(spoof_tesseract_cache, resources, outpdf): pdfinfo = PdfInfo(outpdf) image = pdfinfo[0].images[0] - assert isclose(image.xres, image.yres) - assert isclose(image.xres, 2400) + assert isclose(image.xyres[0], image.xyres[1]) + assert isclose(image.xyres[0], 2400) def test_overlay(spoof_tesseract_noop, resources, outpdf): diff --git a/tests/test_optimize.py b/tests/test_optimize.py index 6cdc1c71..5f198143 100644 --- a/tests/test_optimize.py +++ b/tests/test_optimize.py @@ -43,7 +43,7 @@ def test_mono_not_inverted(resources, outdir): opt.main(infile, outdir / 'out.pdf', level=3) rasterize_pdf( - outdir / 'out.pdf', outdir / 'im.png', xres=10, yres=10, raster_device='pnggray' + outdir / 'out.pdf', outdir / 'im.png', raster_device='pnggray', xyres=(10, 10) ) with Image.open(fspath(outdir / 'im.png')) as im: diff --git a/tests/test_pdfinfo.py b/tests/test_pdfinfo.py index facf3b6f..40f49c94 100644 --- a/tests/test_pdfinfo.py +++ b/tests/test_pdfinfo.py @@ -85,8 +85,8 @@ def test_single_page_image(outdir): assert pdfimage.color == Colorspace.gray # DPI in a 1"x1" is the image width - assert isclose(pdfimage.xres, 8) - assert isclose(pdfimage.yres, 8) + assert isclose(pdfimage.xyres[0], 8) + assert isclose(pdfimage.xyres[1], 8) def test_single_page_inline_image(outdir): @@ -105,7 +105,7 @@ def test_single_page_inline_image(outdir): info = pdfinfo.PdfInfo(filename) print(info) pdfimage = info[0].images[0] - assert isclose(pdfimage.xres, 8) + assert isclose(pdfimage.xyres[0], 8) assert pdfimage.color == Colorspace.gray assert pdfimage.width == 8 @@ -117,7 +117,7 @@ def test_jpeg(resources, outdir): pdfimage = pdf[0].images[0] assert pdfimage.enc == Encoding.jpeg - assert isclose(pdfimage.xres, 150) + assert isclose(pdfimage.xyres[0], 150) def test_form_xobject(resources): @@ -139,7 +139,7 @@ def test_no_contents(resources): def test_oversized_page(resources): pdf = pdfinfo.PdfInfo(resources / 'poster.pdf') image = pdf[0].images[0] - assert image.width * image.xres > 200, "this is supposed to be oversized" + assert image.width * image.xyres[0] > 200, "this is supposed to be oversized" def test_pickle(resources): diff --git a/tests/test_preprocessing.py b/tests/test_preprocessing.py index 50eb6b56..00e4aeda 100644 --- a/tests/test_preprocessing.py +++ b/tests/test_preprocessing.py @@ -50,12 +50,7 @@ def test_deskew(spoof_tesseract_noop, resources, outdir): deskewed_png = outdir / 'deskewed.png' ghostscript.rasterize_pdf( - deskewed_pdf, - deskewed_png, - xres=150, - yres=150, - raster_device='pngmono', - pageno=1, + deskewed_pdf, deskewed_png, raster_device='pngmono', xyres=(150, 150), pageno=1 ) pix = Pix.open(deskewed_png) @@ -82,7 +77,7 @@ def test_remove_background(spoof_tesseract_noop, resources, outdir): output_png = outdir / 'remove_bg.png' ghostscript.rasterize_pdf( - output_pdf, output_png, xres=100, yres=100, raster_device='png16m', pageno=1 + output_pdf, output_png, raster_device='png16m', xyres=(100, 100), pageno=1 ) # The output image should contain pure white and black @@ -122,7 +117,7 @@ def test_exotic_image( def test_non_square_resolution(renderer, spoof_tesseract_cache, resources, outpdf): # Confirm input image is non-square resolution in_pageinfo = PdfInfo(resources / 'aspect.pdf') - assert in_pageinfo[0].xres != in_pageinfo[0].yres + assert in_pageinfo[0].xyres[0] != in_pageinfo[0].xyres[1] check_ocrmypdf( resources / 'aspect.pdf', @@ -135,8 +130,7 @@ def test_non_square_resolution(renderer, spoof_tesseract_cache, resources, outpd out_pageinfo = PdfInfo(outpdf) # Confirm resolution was kept the same - assert in_pageinfo[0].xres == out_pageinfo[0].xres - assert in_pageinfo[0].yres == out_pageinfo[0].yres + assert in_pageinfo[0].xyres == out_pageinfo[0].xyres @pytest.mark.parametrize('renderer', RENDERERS) @@ -145,7 +139,7 @@ def test_convert_to_square_resolution( ): # Confirm input image is non-square resolution in_pageinfo = PdfInfo(resources / 'aspect.pdf') - assert in_pageinfo[0].xres != in_pageinfo[0].yres + assert in_pageinfo[0].xyres[0] != in_pageinfo[0].xyres[1] # --force-ocr requires means forced conversion to square resolution check_ocrmypdf( @@ -162,7 +156,7 @@ def test_convert_to_square_resolution( in_p0, out_p0 = in_pageinfo[0], out_pageinfo[0] # Resolution show now be equal - assert out_p0.xres == out_p0.yres + assert out_p0.xyres[0] == out_p0.xyres[1] # Page size should match input page size assert isclose(in_p0.width_inches, out_p0.width_inches) @@ -170,7 +164,7 @@ def test_convert_to_square_resolution( # Because we rasterized the page to produce a new image, it should occupy # the entire page - out_im_w = out_p0.images[0].width / out_p0.images[0].xres - out_im_h = out_p0.images[0].height / out_p0.images[0].yres + out_im_w = out_p0.images[0].width / out_p0.images[0].xyres[0] + out_im_h = out_p0.images[0].height / out_p0.images[0].xyres[1] assert isclose(out_p0.width_inches, out_im_w) assert isclose(out_p0.height_inches, out_im_h) diff --git a/tests/test_rotation.py b/tests/test_rotation.py index 796c2bfb..101ee8e7 100644 --- a/tests/test_rotation.py +++ b/tests/test_rotation.py @@ -58,9 +58,8 @@ def check_monochrome_correlation( ghostscript.rasterize_pdf( pdf, png, - xres=100, - yres=100, raster_device='pngmono', + xyres=(100, 100), pageno=pageno, rotation=0, )