mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-04 07:57:52 +03:00
Image loading: only swallow exception related to fetching or decoding.
This commit is contained in:
parent
05e1cb22b2
commit
d8014d28ad
@ -11,7 +11,7 @@ WeasyPrint |version| depends on:
|
||||
* cairocffi_ ≥ 0.3
|
||||
* tinycss_ = 0.3
|
||||
* cssselect_ ≥ 0.6
|
||||
* CairoSVG_ ≥ 0.4.1
|
||||
* CairoSVG_ ≥ 0.5
|
||||
* Pyphen_
|
||||
* Optional: GDK-PixBuf_ [#]_
|
||||
|
||||
|
2
setup.py
2
setup.py
@ -29,7 +29,7 @@ REQUIREMENTS = [
|
||||
'cssselect>=0.6',
|
||||
'CairoSVG>=0.4.1',
|
||||
'cffi>=0.6',
|
||||
'cairocffi>=0.4',
|
||||
'cairocffi>=0.5',
|
||||
'Pyphen'
|
||||
# C dependencies: Gdk-Pixbuf (optional), Pango, cairo.
|
||||
]
|
||||
|
@ -30,7 +30,7 @@ try:
|
||||
except OSError:
|
||||
pixbuf = None
|
||||
|
||||
from .urls import fetch
|
||||
from .urls import fetch, URLFetchingError
|
||||
from .logger import LOGGER
|
||||
from .compat import xrange
|
||||
|
||||
@ -44,6 +44,20 @@ IMAGE_RENDERING_TO_FILTER = dict(
|
||||
)
|
||||
|
||||
|
||||
class ImageLoadingError(ValueError):
|
||||
"""An error occured when loading an image.
|
||||
|
||||
The image data is probably corrupted or in an invalid format.
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_exception(cls, exception):
|
||||
name = type(exception).__name__
|
||||
value = str(exception)
|
||||
return cls('%s: %s' % (name, value) if value else name)
|
||||
|
||||
|
||||
class RasterImage(object):
|
||||
def __init__(self, image_surface):
|
||||
self.image_surface = image_surface
|
||||
@ -83,11 +97,14 @@ class SVGImage(object):
|
||||
self._svg_data = svg_data
|
||||
|
||||
# TODO: find a way of not doing twice the whole rendering.
|
||||
svg = self._render()
|
||||
try:
|
||||
svg = self._render()
|
||||
except Exception as e:
|
||||
raise ImageLoadingError.from_exception(e)
|
||||
# TODO: support SVG images with none or only one of intrinsic
|
||||
# width, height and ratio.
|
||||
if not (svg.width > 0 and svg.height > 0):
|
||||
raise ValueError(
|
||||
raise ImageLoadingError(
|
||||
'SVG images without an intrinsic size are not supported.')
|
||||
self.intrinsic_width = svg.width
|
||||
self.intrinsic_height = svg.height
|
||||
@ -112,36 +129,46 @@ class SVGImage(object):
|
||||
context.paint()
|
||||
|
||||
|
||||
def get_image_from_uri(cache, url_fetcher, uri, forced_mime_type=None):
|
||||
def get_image_from_uri(cache, url_fetcher, url, forced_mime_type=None):
|
||||
"""Get a cairo Pattern from an image URI."""
|
||||
missing = object()
|
||||
image = cache.get(uri, missing)
|
||||
image = cache.get(url, missing)
|
||||
if image is not missing:
|
||||
return image
|
||||
|
||||
try:
|
||||
with fetch(url_fetcher, uri) as result:
|
||||
with fetch(url_fetcher, url) as result:
|
||||
mime_type = forced_mime_type or result['mime_type']
|
||||
if mime_type == 'image/svg+xml':
|
||||
image = SVGImage(
|
||||
result.get('string') or result['file_obj'].read(), uri)
|
||||
string = (result['string'] if 'string' in result
|
||||
else result['file_obj'].read())
|
||||
image = SVGImage(string, url)
|
||||
elif mime_type == 'image/png':
|
||||
image = RasterImage(cairocffi.ImageSurface.create_from_png(
|
||||
result.get('file_obj') or BytesIO(result.get('string'))))
|
||||
obj = result.get('file_obj') or BytesIO(result.get('string'))
|
||||
try:
|
||||
surface = cairocffi.ImageSurface.create_from_png(obj)
|
||||
except Exception as exc:
|
||||
raise ImageLoadingError.from_exception(exc)
|
||||
image = RasterImage(surface)
|
||||
else:
|
||||
if pixbuf is None:
|
||||
raise OSError(
|
||||
raise ImageLoadingError(
|
||||
'Could not load GDK-Pixbuf. '
|
||||
'PNG and SVG are the only image formats available.')
|
||||
string = result.get('string') or result['file_obj'].read()
|
||||
surface, format_name = pixbuf.decode_to_image_surface(string)
|
||||
string = (result['string'] if 'string' in result
|
||||
else result['file_obj'].read())
|
||||
try:
|
||||
surface, format_name = (
|
||||
pixbuf.decode_to_image_surface(string))
|
||||
except pixbuf.ImageLoadingError as exc:
|
||||
raise ImageLoadingError(str(exc))
|
||||
if format_name == 'jpeg' and CAIRO_HAS_MIME_DATA:
|
||||
surface.set_mime_data('image/jpeg', string)
|
||||
image = RasterImage(surface)
|
||||
except Exception as exc:
|
||||
LOGGER.warn('Error for image at %s : %r', uri, exc)
|
||||
except (URLFetchingError, ImageLoadingError) as exc:
|
||||
LOGGER.warn('Error for image at %s : %r', url, exc)
|
||||
image = None
|
||||
cache[uri] = image
|
||||
cache[url] = image
|
||||
return image
|
||||
|
||||
|
||||
|
@ -267,10 +267,19 @@ def default_url_fetcher(url):
|
||||
raise ValueError('Not an absolute URI: %r' % url)
|
||||
|
||||
|
||||
class URLFetchingError(IOError):
|
||||
"""Some error happened when fetching an URL."""
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def fetch(url_fetcher, url):
|
||||
"""Call an url_fetcher, fill in optional data, and clean up."""
|
||||
result = url_fetcher(url)
|
||||
try:
|
||||
result = url_fetcher(url)
|
||||
except Exception as exc:
|
||||
name = type(exc).__name__
|
||||
value = str(exc)
|
||||
raise URLFetchingError('%s: %s' % (name, value) if value else name)
|
||||
result.setdefault('redirected_url', url)
|
||||
result.setdefault('mime_type', None)
|
||||
if result.get('file_obj'):
|
||||
|
Loading…
Reference in New Issue
Block a user