Fix Ghostscript rasterizing of UserUnit pages and related sizing issues

This commit is contained in:
James R. Barlow
2017-05-29 12:14:10 -07:00
parent facdd13879
commit 1d57bcc99e
3 changed files with 37 additions and 27 deletions
+23 -5
View File
@@ -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)
+1 -1
View File
@@ -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]
+13 -21
View File
@@ -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(