Kozea / WeasyPrint

The awesome document factory
https://weasyprint.org
BSD 3-Clause "New" or "Revised" License
7.21k stars 683 forks source link

[59.0] Including images twice with dpi option fails #1877

Closed cocorossello closed 1 year ago

cocorossello commented 1 year ago

With this html:

<html>

<body>
     <img src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" style="width:200px;"/>
     <img src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"/>
</body>
</html>

Rendering fails if using dpi option: html.write_pdf(optimize_images=True, jpeg_quality=75, dpi=120)

It happens with some images, not all. Everything is ok without dpi option

The error:


weasyprint  | Traceback (most recent call last):
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 2190, in wsgi_app
weasyprint  |     response = self.full_dispatch_request()
weasyprint  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1486, in full_dispatch_request
weasyprint  |     rv = self.handle_user_exception(e)
weasyprint  |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1484, in full_dispatch_request
weasyprint  |     rv = self.dispatch_request()
weasyprint  |          ^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1469, in dispatch_request
weasyprint  |     return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
weasyprint  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/src/app/wsgi.py", line 54, in checkauth
weasyprint  |     return f(*args, **kwargs)
weasyprint  |            ^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/src/app/wsgi.py", line 112, in generate
weasyprint  |     pdf = html.write_pdf(optimize_images=True, jpeg_quality=60, dpi=120)
weasyprint  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/__init__.py", line 252, in write_pdf
weasyprint  |     self.render(font_config, counter_style, **options)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/document.py", line 399, in write_pdf
weasyprint  |     pdf = generate_pdf(self, target, zoom, **options)
weasyprint  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/pdf/__init__.py", line 185, in generate_pdf
weasyprint  |     page.paint(stream, scale=scale)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/document.py", line 103, in paint
weasyprint  |     draw_page(self._page_box, stream)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 68, in draw_page
weasyprint  |     draw_stacking_context(stream, stacking_context)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 176, in draw_stacking_context
weasyprint  |     draw_stacking_context(stream, child_context)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 169, in draw_stacking_context
weasyprint  |     draw_inline_level(
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 983, in draw_inline_level
weasyprint  |     draw_inline_level(
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 989, in draw_inline_level
weasyprint  |     draw_replacedbox(stream, box)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 945, in draw_replacedbox
weasyprint  |     box.replacement.draw(
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/images.py", line 106, in draw
weasyprint  |     image_name = stream.add_image(self, width, height, interpolate, ratio)
weasyprint  |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/pdf/stream.py", line 383, in add_image
weasyprint  |     xobject = image.get_xobject(width, height, interpolate)
weasyprint  |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/images.py", line 160, in get_xobject
weasyprint  |     pillow_image = Image.open(io.BytesIO(self.image_data.data))
weasyprint  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/PIL/Image.py", line 3298, in open
weasyprint  |     raise UnidentifiedImageError(msg)
weasyprint  | PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x7faa3098c5e0>
weasyprint  | 2023-05-11 22:35:37,247 ERROR: Exception on /pdf [POST] [url=/pdf?filename=result.pdf&websessionId=468138647A5B4C78F285A6F50A1B6B40.L000&debugInfo=booking+TTL-83737] [in /usr/local/lib/python3.11/site-packages/flask/app.py:1414]
weasyprint  | Traceback (most recent call last):
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 2190, in wsgi_app
weasyprint  |     response = self.full_dispatch_request()
weasyprint  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1486, in full_dispatch_request
weasyprint  |     rv = self.handle_user_exception(e)
weasyprint  |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1484, in full_dispatch_request
weasyprint  |     rv = self.dispatch_request()
weasyprint  |          ^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1469, in dispatch_request
weasyprint  |     return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
weasyprint  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/src/app/wsgi.py", line 54, in checkauth
weasyprint  |     return f(*args, **kwargs)
weasyprint  |            ^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/src/app/wsgi.py", line 112, in generate
weasyprint  |     pdf = html.write_pdf(optimize_images=True, jpeg_quality=60, dpi=120)
weasyprint  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/__init__.py", line 252, in write_pdf
weasyprint  |     self.render(font_config, counter_style, **options)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/document.py", line 399, in write_pdf
weasyprint  |     pdf = generate_pdf(self, target, zoom, **options)
weasyprint  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/pdf/__init__.py", line 185, in generate_pdf
weasyprint  |     page.paint(stream, scale=scale)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/document.py", line 103, in paint
weasyprint  |     draw_page(self._page_box, stream)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 68, in draw_page
weasyprint  |     draw_stacking_context(stream, stacking_context)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 176, in draw_stacking_context
weasyprint  |     draw_stacking_context(stream, child_context)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 169, in draw_stacking_context
weasyprint  |     draw_inline_level(
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 983, in draw_inline_level
weasyprint  |     draw_inline_level(
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 989, in draw_inline_level
weasyprint  |     draw_replacedbox(stream, box)
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/draw.py", line 945, in draw_replacedbox
weasyprint  |     box.replacement.draw(
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/images.py", line 106, in draw
weasyprint  |     image_name = stream.add_image(self, width, height, interpolate, ratio)
weasyprint  |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/pdf/stream.py", line 383, in add_image
weasyprint  |     xobject = image.get_xobject(width, height, interpolate)
weasyprint  |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/weasyprint/images.py", line 160, in get_xobject
weasyprint  |     pillow_image = Image.open(io.BytesIO(self.image_data.data))
weasyprint  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weasyprint  |   File "/usr/local/lib/python3.11/site-packages/PIL/Image.py", line 3298, in open
weasyprint  |     raise UnidentifiedImageError(msg)
weasyprint  | PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x7faa30a9b5b0>
liZe commented 1 year ago

Hi!

Thanks for the report.

The bug happens when:

It’s related to 93df1a5e, that fixes the same problem, but only when images have the same size.

liZe commented 1 year ago

The problem is fixed by 33892cd1 (and the image is now included only once), but WeasyPrint continues to crash when image-rendering is set to different values for the same PNG.

We’ll also need to include tests afterwards…

liZe commented 1 year ago

New issue (should not happen in real documents) here: #1942