Skip to content

Commit

Permalink
Support images in <embed> or <object>
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonSapin committed Feb 29, 2012
1 parent e6cee2f commit 476732b
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 28 deletions.
12 changes: 8 additions & 4 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
Version 0.6, released on 2012-XX-XX
===================================

* *Backward incompatible*: completely change the API. See the documentation:
http://weasyprint.org/using/#as-a-python-library
* *Backward incompatible*: completely change the Python API.
See the documentation: http://weasyprint.org/using/#as-a-python-library
* *Backward incompatible*: Proper margin collapsing.
This changes how blocks are rendered: adjoining margins "collapse"
(their maximum is used) instead of accumulating.
* Support images in ``embed`` or ``object`` elements.
* Switch to pystacia instead of PIL for raster images
* Add compatibility with CPython 2.6 and 3.2 (was only 2.7 previously)
* Proper margin collapsing
* Add compatibility with CPython 2.6 and 3.2. (Previously only 2.7
was supported)
* Many bug fixes


Expand Down
4 changes: 2 additions & 2 deletions weasyprint/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ def pages(self):
"""
return layout.layout_document(self, self.formatting_structure)

def get_image_from_uri(self, uri):
def get_image_from_uri(self, uri, type_=None):
"""
Same as ``weasy.images.get_image_from_uri`` but cache results
"""
missing = object()
surface = self._image_cache.get(uri, missing)
if surface is missing:
surface = images.get_image_from_uri(uri)
surface = images.get_image_from_uri(uri, type_)
self._image_cache[uri] = surface
return surface

Expand Down
56 changes: 47 additions & 9 deletions weasyprint/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,9 @@ def make_text_box(element, box, text):
box.style, [text_box])


# TODO: also support images (including SVG) in <embed> and <object> elements?
@handler('img')
def handle_img(document, element, box):
"""Handle ``<img>`` tags, return either an image or the alt-text.
"""Handle ``<img>`` elements, return either an image or the alt-text.
See: http://www.w3.org/TR/html5/embedded-content-1.html#the-img-element
Expand Down Expand Up @@ -135,18 +134,54 @@ def handle_img(document, element, box):
return []


@handler('embed')
def handle_embed(document, element, box):
"""Handle ``<embed>`` elements, return either an image or nothing.
See: http://www.w3.org/TR/html5/the-iframe-element.html#the-embed-element
"""
src = get_url_attribute(element, 'src')
type_ = element.get('type', '').strip()
if src:
image = document.get_image_from_uri(src, type_)
if image is not None:
return [make_replaced_box(element, box, image)]
# No fallback.
return []


@handler('object')
def handle_object(document, element, box):
"""Handle ``<object>`` elements, return either an image or the fallback
content.
See: http://www.w3.org/TR/html5/the-iframe-element.html#the-object-element
"""
data = get_url_attribute(element, 'data')
type_ = element.get('type', '').strip()
if data:
image = document.get_image_from_uri(data, type_)
if image is not None:
return [make_replaced_box(element, box, image)]
# The element’s children are the fallback.
return [box]


def integer_attribute(element, box, name, minimum=1):
"""Read an integer attribute from the HTML element and set it on the box.
"""
value = element.get(name, '').strip()
try:
value = int(value)
except ValueError:
pass
else:
if value >= minimum:
setattr(box, name, value)
if value:
try:
value = int(value)
except ValueError:
pass
else:
if value >= minimum:
setattr(box, name, value)


@handler('colgroup')
Expand All @@ -157,6 +192,9 @@ def handle_colgroup(_document, element, box):
box.span = None # sum of the children’s spans
else:
integer_attribute(element, box, 'span')
box.children = (
boxes.TableColumnBox.anonymous_from(box, [])
for _i in xrange(box.span))
return [box]


Expand Down
7 changes: 5 additions & 2 deletions weasyprint/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,18 @@ def fallback_handler(file_like, uri):
return png_handler(BytesIO(png_bytes), uri)


def get_image_from_uri(uri):
def get_image_from_uri(uri, type_=None):
"""Get a :class:`cairo.Surface`` from an image URI."""
try:
file_like, mime_type, _charset = urlopen(uri)
except (IOError, ValueError) as exc:
LOGGER.warn('Error while fetching an image from %s : %r', uri, exc)
return None

handler = FORMAT_HANDLERS.get(mime_type, fallback_handler)
if not type_:
type_ = mime_type # Use eg. the HTTP header
#else: the type was forced by eg. a 'type' attribute on <embed>
handler = FORMAT_HANDLERS.get(type_, fallback_handler)
exception = None
try:
image = handler(file_like, uri)
Expand Down
1 change: 0 additions & 1 deletion weasyprint/layout/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,6 @@ def block_level_height(document, box, max_position_y, skip_stack,
# TODO: See corner cases in
# http://www.w3.org/TR/CSS21/visudet.html#normal-block
if new_box.height == 'auto':
print(box, position_y, new_box.content_box_y())
new_box.height = position_y - new_box.content_box_y()

if resume_at is not None:
Expand Down
26 changes: 20 additions & 6 deletions weasyprint/tests/test_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,11 +842,24 @@ def test_whitespace_processing():
def test_with_images():
"""Test that width, height and ratio of images are respected."""
# Try a few image formats
for url in ['pattern.png', 'pattern.gif', 'blue.jpg', 'pattern.svg',
"data:image/svg+xml,<svg width='4' height='4'></svg>",
"data:image/svg+xml,<svg width='4px' height='4px'></svg>",
]:
page, = parse('<img src="%s">' % url)
for html in [
'<img src="%s">' % url for url in [
'pattern.png', 'pattern.gif', 'blue.jpg', 'pattern.svg',
"data:image/svg+xml,<svg width='4' height='4'></svg>",
"data:image/svg+xml,<svg width='4px' height='4px'></svg>",
]
] + [
'<embed src=pattern.png>',
'<embed src=pattern.svg>',
'<embed src=really-a-png.svg type=image/png>',
'<embed src=really-a-svg.png type=image/svg+xml>',

'<object data=pattern.png>',
'<object data=pattern.svg>',
'<object data=really-a-png.svg type=image/png>',
'<object data=really-a-svg.png type=image/svg+xml>',
]:
page, = parse(html)
html, = page.children
body, = html.children
line, = body.children
Expand Down Expand Up @@ -877,11 +890,12 @@ def test_with_images():
'data:image/png,Not a PNG',
'data:image/jpeg,Not a JPEG',
'data:image/svg+xml,<svg>invalid xml',
'really-a-svg.png',
]:
with capture_logs() as logs:
page, = parse("<p><img src='%s' alt='invalid image'>" % url)
assert len(logs) == 1
if url.startswith('data:'):
if url.startswith('data:') or 'really' in url:
assert 'WARNING: Error while parsing an image' in logs[0]
else:
assert 'WARNING: Error while fetching an image' in logs[0]
Expand Down
9 changes: 5 additions & 4 deletions weasyprint/tests/w3_test_suite/templates/run_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
iframe {
border: 2px inset;
padding: 0;
width: 660px;
height: 660px;
box-sizing: border-box
width: 640px;
height: 640px;
box-sizing: content-box
}
.render {
display: table-cell;
Expand All @@ -23,7 +23,8 @@ <h2>{{ test.title }}</h2>

<div class=render>
<h3>WeasyPrint</h3>
<iframe src="{{ url_for('render', test_id=test.test_id) }}"></iframe>
<iframe src="{{ url_for('render', test_id=test.test_id) }}"
marginwidth=0 marginheight=0></iframe>
</div>

<div class=render>
Expand Down

0 comments on commit 476732b

Please sign in to comment.