Fix ZeroDivisionError on files containing images drawn at scale 0
Fixes #761
This commit is contained in:
@@ -15,7 +15,7 @@ from collections.abc import Iterable
|
||||
from contextlib import suppress
|
||||
from functools import wraps
|
||||
from io import StringIO
|
||||
from math import isclose
|
||||
from math import isclose, isfinite
|
||||
from pathlib import Path
|
||||
from typing import Any, Sequence
|
||||
|
||||
@@ -39,6 +39,10 @@ class Resolution(namedtuple('Resolution', ('x', 'y'))):
|
||||
def is_square(self) -> bool:
|
||||
return isclose(self.x, self.y, rel_tol=1e-3)
|
||||
|
||||
@property
|
||||
def is_finite(self) -> bool:
|
||||
return isfinite(self.x) and isfinite(self.y)
|
||||
|
||||
def take_max(self, vals, yvals=None):
|
||||
if yvals is not None:
|
||||
return Resolution(max(self.x, *vals), max(self.y, *yvals))
|
||||
|
||||
@@ -14,7 +14,7 @@ from contextlib import ExitStack
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
from functools import partial
|
||||
from math import hypot, isclose
|
||||
from math import hypot, isclose, ulp
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import Container, Iterator, Optional, Tuple, Union
|
||||
@@ -260,8 +260,9 @@ def _get_dpi(ctm_shorthand, image_size) -> Resolution:
|
||||
image_drawn_height = hypot(c, d)
|
||||
|
||||
# The scale of the image is pixels per unit of default user space (1/72")
|
||||
scale_w = image_size[0] / image_drawn_width
|
||||
scale_h = image_size[1] / image_drawn_height
|
||||
# use ulp to turn divide by zero into infinity
|
||||
scale_w = image_size[0] / (image_drawn_width + ulp(0))
|
||||
scale_h = image_size[1] / (image_drawn_height + ulp(0))
|
||||
|
||||
# DPI = scale * 72
|
||||
dpi_w = scale_w * 72.0
|
||||
@@ -365,6 +366,10 @@ class ImageInfo:
|
||||
def enc(self):
|
||||
return self._enc
|
||||
|
||||
@property
|
||||
def renderable(self):
|
||||
return self.dpi.is_finite and self.width >= 0 and self.height >= 0
|
||||
|
||||
@property
|
||||
def dpi(self):
|
||||
return _get_dpi(self._shorthand, (self._width, self._height))
|
||||
@@ -734,7 +739,9 @@ class PageInfo:
|
||||
|
||||
self._dpi = None
|
||||
if self._images:
|
||||
dpi = Resolution(0.0, 0.0).take_max(image.dpi for image in self._images)
|
||||
dpi = Resolution(0.0, 0.0).take_max(
|
||||
image.dpi for image in self._images if image.renderable
|
||||
)
|
||||
self._dpi = dpi
|
||||
self._width_pixels = int(round(dpi.x * float(self._width_inches)))
|
||||
self._height_pixels = int(round(dpi.y * float(self._height_inches)))
|
||||
|
||||
@@ -17,6 +17,7 @@ from reportlab.pdfgen.canvas import Canvas
|
||||
|
||||
from ocrmypdf import pdfinfo
|
||||
from ocrmypdf.exceptions import InputFileError
|
||||
from ocrmypdf.helpers import Resolution
|
||||
from ocrmypdf.pdfinfo import Colorspace, Encoding
|
||||
from ocrmypdf.pdfinfo.layout import PDFPage
|
||||
|
||||
@@ -194,3 +195,23 @@ def test_pages_issue700(monkeypatch, resources):
|
||||
progbar=False,
|
||||
max_workers=1,
|
||||
)
|
||||
|
||||
|
||||
def test_image_scale0(resources, outpdf):
|
||||
with pikepdf.open(resources / 'cmyk.pdf') as cmyk:
|
||||
xobj = pikepdf.Page(cmyk.pages[0]).as_form_xobject()
|
||||
|
||||
p = pikepdf.Pdf.new()
|
||||
p.add_blank_page(page_size=(72, 72))
|
||||
objname = pikepdf.Page(p.pages[0]).add_resource(
|
||||
p.copy_foreign(xobj), pikepdf.Name.XObject, pikepdf.Name.Im0
|
||||
)
|
||||
print(objname)
|
||||
p.pages[0].Contents = pikepdf.Stream(
|
||||
p, b"q 0 0 0 0 0 0 cm %s Do Q" % bytes(objname)
|
||||
)
|
||||
p.save(outpdf)
|
||||
|
||||
pi = pdfinfo.PdfInfo(outpdf, detailed_analysis=True, progbar=False, max_workers=1)
|
||||
assert not pi.pages[0]._images[0].dpi.is_finite
|
||||
assert pi.pages[0].dpi == Resolution(0, 0)
|
||||
|
||||
Reference in New Issue
Block a user