1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-05 00:21:15 +03:00

Merge branch 'master' into border3

This commit is contained in:
Guillaume Ayoub 2013-12-10 11:02:36 +01:00
commit d4493e5707
11 changed files with 208 additions and 193 deletions

View File

@ -164,7 +164,7 @@ PDF bookmarks are controlled as described in `CSS Generated Content for
Paged Media`_. This module is experimental_: the properties need to be Paged Media`_. This module is experimental_: the properties need to be
prefixed: use ``-weasy-bookmark-level`` and ``-weasy-bookmark-level``. prefixed: use ``-weasy-bookmark-level`` and ``-weasy-bookmark-level``.
.. _CSS Generated Content for Paged Media: http://dev.w3.org/csswg/css3-gcpm/#bookmarks .. _CSS Generated Content for Paged Media: https://dvcs.w3.org/hg/csswg/raw-file/f7490857b4eb/css-gcpm/Overview.html#bookmarks
.. _experimental: http://www.w3.org/TR/css-2010/#experimental .. _experimental: http://www.w3.org/TR/css-2010/#experimental
For example, if you have only one top-level ``<h1>`` and do not wish to For example, if you have only one top-level ``<h1>`` and do not wish to

View File

@ -8,6 +8,7 @@ WeasyPrint |version| depends on:
* Pango_ * Pango_
* CFFI_ ≥ 0.5 * CFFI_ ≥ 0.5
* lxml_ * lxml_
* html5lib ≥ 1.0b3
* cairocffi_ ≥ 0.3 * cairocffi_ ≥ 0.3
* tinycss_ = 0.3 * tinycss_ = 0.3
* cssselect_ ≥ 0.6 * cssselect_ ≥ 0.6

View File

@ -12,11 +12,13 @@
import re import re
import sys import sys
import codecs
from os import path from os import path
from setuptools import setup, find_packages from setuptools import setup, find_packages
VERSION = re.search("VERSION = '([^']+)'", open( VERSION = re.search("VERSION = '([^']+)'", codecs.open(
path.join(path.dirname(__file__), 'weasyprint', '__init__.py') path.join(path.dirname(__file__), 'weasyprint', '__init__.py'),
encoding="utf-8",
).read().strip()).group(1) ).read().strip()).group(1)
LONG_DESCRIPTION = open(path.join(path.dirname(__file__), 'README')).read() LONG_DESCRIPTION = open(path.join(path.dirname(__file__), 'README')).read()
@ -25,6 +27,7 @@ LONG_DESCRIPTION = open(path.join(path.dirname(__file__), 'README')).read()
REQUIREMENTS = [ REQUIREMENTS = [
# XXX: Keep this in sync with docs/install.rst # XXX: Keep this in sync with docs/install.rst
'lxml', 'lxml',
'html5lib>=1.0b3',
'tinycss==0.3', 'tinycss==0.3',
'cssselect>=0.6', 'cssselect>=0.6',
'CairoSVG>=0.4.1', 'CairoSVG>=0.4.1',

View File

@ -27,10 +27,11 @@ __all__ = ['HTML', 'CSS', 'Document', 'Page', 'default_url_fetcher',
import contextlib import contextlib
import lxml.etree import html5lib
from .urls import (fetch, default_url_fetcher, path2url, ensure_url, from .urls import (fetch, default_url_fetcher, path2url, ensure_url,
url_is_absolute) url_is_absolute)
from .compat import unicode
from .logger import LOGGER from .logger import LOGGER
# Some import are at the end of the file (after the CSS class) is defined # Some import are at the end of the file (after the CSS class) is defined
# to work around circular imports. # to work around circular imports.
@ -81,16 +82,14 @@ class HTML(object):
if source_type == 'tree': if source_type == 'tree':
result = source result = source
else: else:
if source_type == 'string':
parse = lxml.etree.fromstring
else:
parse = lxml.etree.parse
if not encoding: if not encoding:
encoding = protocol_encoding encoding = protocol_encoding
parser = lxml.etree.HTMLParser(encoding=encoding) if isinstance(source, unicode):
result = parse(source, parser=parser) encoding = None
if result is None: result = html5lib.parse(
raise ValueError('Error while parsing HTML') source, treebuilder='lxml', encoding=encoding,
namespaceHTMLElements=False)
assert result
base_url = find_base_url(result, base_url) base_url = find_base_url(result, base_url)
if hasattr(result, 'getroot'): if hasattr(result, 'getroot'):
result.docinfo.URL = base_url result.docinfo.URL = base_url

View File

@ -74,7 +74,7 @@ def main(argv=None, stdout=None, stdin=None):
prog='weasyprint', description='Renders web pages to PDF or PNG.') prog='weasyprint', description='Renders web pages to PDF or PNG.')
parser.add_argument('--version', action='version', parser.add_argument('--version', action='version',
version='WeasyPrint version %s' % VERSION, version='WeasyPrint version %s' % VERSION,
help='Print WeasyPrints version number and exit.') help="Print WeasyPrint's version number and exit.")
parser.add_argument('-e', '--encoding', parser.add_argument('-e', '--encoding',
help='Character encoding of the input') help='Character encoding of the input')
parser.add_argument('-f', '--format', choices=['pdf', 'png'], parser.add_argument('-f', '--format', choices=['pdf', 'png'],

View File

@ -10,16 +10,16 @@ br:before { content: '\A'; white-space: pre-line }
ol { list-style-type: decimal } ol { list-style-type: decimal }
ol, ul { counter-reset: list-item } ol, ul { counter-reset: list-item }
table { display: table; table, x-table { display: table;
box-sizing: border-box } box-sizing: border-box }
tr { display: table-row } tr, x-tr { display: table-row }
thead { display: table-header-group } thead, x-thead { display: table-header-group }
tbody { display: table-row-group } tbody, x-tbody { display: table-row-group }
tfoot { display: table-footer-group } tfoot, x-tfoot { display: table-footer-group }
col { display: table-column } col, x-col { display: table-column }
colgroup { display: table-column-group } colgroup, x-colgroup { display: table-column-group }
td, th { display: table-cell } td, th, x-td, x-th { display: table-cell }
caption { display: table-caption } caption, x-caption { display: table-caption }
*[lang] { -weasy-lang: attr(lang); } *[lang] { -weasy-lang: attr(lang); }
a[href] { -weasy-link: attr(href); } a[href] { -weasy-link: attr(href); }

View File

@ -167,7 +167,11 @@ def table_layout(context, table, max_position_y, skip_stack,
row_bottom_y = max( row_bottom_y = max(
cell.position_y + cell.border_height() cell.position_y + cell.border_height()
for cell in ending_cells) for cell in ending_cells)
if row.height == 'auto':
row.height = row_bottom_y - row.position_y row.height = row_bottom_y - row.position_y
else:
row.height = max(row.height, max(
row_cell.height for row_cell in ending_cells))
else: else:
row_bottom_y = row.position_y row_bottom_y = row.position_y
row.height = 0 row.height = 0
@ -574,7 +578,8 @@ def find_in_flow_baseline(box, last=False, baseline_types=(boxes.LineBox,)):
""" """
if isinstance(box, baseline_types): if isinstance(box, baseline_types):
return box.position_y + box.baseline return box.position_y + box.baseline
if isinstance(box, boxes.ParentBox): if isinstance(box, boxes.ParentBox) and not isinstance(
box, boxes.TableCaptionBox):
children = reversed(box.children) if last else box.children children = reversed(box.children) if last else box.children
for child in children: for child in children:
if child.is_in_normal_flow(): if child.is_in_normal_flow():

View File

@ -13,6 +13,8 @@
from __future__ import division, unicode_literals from __future__ import division, unicode_literals
import functools import functools
import pprint
import difflib
from .testing_utils import ( from .testing_utils import (
resource_filename, TestHTML, assert_no_logs, capture_logs) resource_filename, TestHTML, assert_no_logs, capture_logs)
@ -116,7 +118,12 @@ def assert_tree(box, expected):
expected: a list of serialized <body> children as returned by to_lists(). expected: a list of serialized <body> children as returned by to_lists().
""" """
assert to_lists(box) == expected lists = to_lists(box)
if lists != expected:
print(''.join(difflib.unified_diff(
*[pprint.pformat(v).splitlines(True) for v in [lists, expected]],
n=9999)))
assert lists == expected
def sanity_checks(box): def sanity_checks(box):
@ -251,10 +258,10 @@ def test_block_in_inline():
box = parse(''' box = parse('''
<style> <style>
p { display: inline-block; } p { display: inline-block; }
span { display: block; } span, i { display: block; }
</style> </style>
<p>Lorem <em>ipsum <strong>dolor <span>sit</span> <p>Lorem <em>ipsum <strong>dolor <span>sit</span>
<span>amet,</span></strong><span><em>conse<div/></em></span></em></p>''') <span>amet,</span></strong><span><em>conse<i></i></em></span></em></p>''')
box = build.inline_in_block(box) box = build.inline_in_block(box)
assert_tree(box, [ assert_tree(box, [
('body', 'Line', [ ('body', 'Line', [
@ -277,7 +284,7 @@ def test_block_in_inline():
('span', 'Line', [ ('span', 'Line', [
('em', 'Inline', [ ('em', 'Inline', [
('em', 'Text', 'conse'), ('em', 'Text', 'conse'),
('div', 'Block', [])])])])])])])])]) ('i', 'Block', [])])])])])])])])])
box = build.block_in_inline(box) box = build.block_in_inline(box)
assert_tree(box, [ assert_tree(box, [
@ -312,7 +319,7 @@ def test_block_in_inline():
('span', 'Line', [ ('span', 'Line', [
('em', 'Inline', [ ('em', 'Inline', [
('em', 'Text', 'conse')])])]), ('em', 'Text', 'conse')])])]),
('div', 'Block', []), ('i', 'Block', []),
('span', 'AnonBlock', [ ('span', 'AnonBlock', [
('span', 'Line', [ ('span', 'Line', [
('em', 'Inline', [])])])]), ('em', 'Inline', [])])])]),
@ -481,48 +488,48 @@ def test_tables():
# Rule 1.3 # Rule 1.3
# Also table model: http://www.w3.org/TR/CSS21/tables.html#model # Also table model: http://www.w3.org/TR/CSS21/tables.html#model
assert_tree(parse_all(''' assert_tree(parse_all('''
<table> <x-table>
<tr> <x-tr>
<th>foo</th> <x-th>foo</x-th>
<th>bar</th> <x-th>bar</x-th>
</tr> </x-tr>
<tfoot></tfoot> <x-tfoot></x-tfoot>
<thead><th></th></thead> <x-thead><x-th></x-th></x-thead>
<caption style="caption-side: bottom"></caption> <x-caption style="caption-side: bottom"></x-caption>
<thead></thead> <x-thead></x-thead>
<col></col> <x-col></x-col>
<caption>top caption</caption> <x-caption>top caption</x-caption>
<tr> <x-tr>
<td>baz</td> <x-td>baz</x-td>
</tr> </x-tr>
</table> </x-table>
'''), [ '''), [
('table', 'AnonBlock', [ ('x-table', 'AnonBlock', [
('caption', 'TableCaption', [ ('x-caption', 'TableCaption', [
('caption', 'Line', [ ('x-caption', 'Line', [
('caption', 'Text', 'top caption')])]), ('x-caption', 'Text', 'top caption')])]),
('table', 'Table', [ ('x-table', 'Table', [
('table', 'AnonTableColumnGroup', [ ('x-table', 'AnonTableColumnGroup', [
('col', 'TableColumn', [])]), ('x-col', 'TableColumn', [])]),
('thead', 'TableRowGroup', [ ('x-thead', 'TableRowGroup', [
('thead', 'AnonTableRow', [ ('x-thead', 'AnonTableRow', [
('th', 'TableCell', [])])]), ('x-th', 'TableCell', [])])]),
('table', 'AnonTableRowGroup', [ ('x-table', 'AnonTableRowGroup', [
('tr', 'TableRow', [ ('x-tr', 'TableRow', [
('th', 'TableCell', [ ('x-th', 'TableCell', [
('th', 'Line', [ ('x-th', 'Line', [
('th', 'Text', 'foo')])]), ('x-th', 'Text', 'foo')])]),
('th', 'TableCell', [ ('x-th', 'TableCell', [
('th', 'Line', [ ('x-th', 'Line', [
('th', 'Text', 'bar')])])])]), ('x-th', 'Text', 'bar')])])])]),
('thead', 'TableRowGroup', []), ('x-thead', 'TableRowGroup', []),
('table', 'AnonTableRowGroup', [ ('x-table', 'AnonTableRowGroup', [
('tr', 'TableRow', [ ('x-tr', 'TableRow', [
('td', 'TableCell', [ ('x-td', 'TableCell', [
('td', 'Line', [ ('x-td', 'Line', [
('td', 'Text', 'baz')])])])]), ('x-td', 'Text', 'baz')])])])]),
('tfoot', 'TableRowGroup', [])]), ('x-tfoot', 'TableRowGroup', [])]),
('caption', 'TableCaption', [])])]) ('x-caption', 'TableCaption', [])])])
# Rules 1.4 and 3.1 # Rules 1.4 and 3.1
assert_tree(parse_all(''' assert_tree(parse_all('''
@ -562,72 +569,74 @@ def test_tables():
('ins', 'AnonTableColumn', [])])])])]) ('ins', 'AnonTableColumn', [])])])])])
# Rules 2.1 then 2.3 # Rules 2.1 then 2.3
assert_tree(parse_all('<table>foo <div></div></table>'), [ assert_tree(parse_all('<x-table>foo <div></div></x-table>'), [
('table', 'AnonBlock', [ ('x-table', 'AnonBlock', [
('table', 'Table', [ ('x-table', 'Table', [
('table', 'AnonTableRowGroup', [ ('x-table', 'AnonTableRowGroup', [
('table', 'AnonTableRow', [ ('x-table', 'AnonTableRow', [
('table', 'AnonTableCell', [ ('x-table', 'AnonTableCell', [
('table', 'AnonBlock', [ ('x-table', 'AnonBlock', [
('table', 'Line', [ ('x-table', 'Line', [
('table', 'Text', 'foo ')])]), ('x-table', 'Text', 'foo ')])]),
('div', 'Block', [])])])])])])]) ('div', 'Block', [])])])])])])])
# Rule 2.2 # Rule 2.2
assert_tree(parse_all('<thead><div></div><td></td></thead>'), [ assert_tree(parse_all('<x-thead style="display: table-header-group">'
'<div></div><x-td></x-td></x-thead>'), [
('body', 'AnonBlock', [ ('body', 'AnonBlock', [
('body', 'AnonTable', [ ('body', 'AnonTable', [
('thead', 'TableRowGroup', [ ('x-thead', 'TableRowGroup', [
('thead', 'AnonTableRow', [ ('x-thead', 'AnonTableRow', [
('thead', 'AnonTableCell', [ ('x-thead', 'AnonTableCell', [
('div', 'Block', [])]), ('div', 'Block', [])]),
('td', 'TableCell', [])])])])])]) ('x-td', 'TableCell', [])])])])])])
# TODO: re-enable this once we support inline-table # TODO: re-enable this once we support inline-table
# # Rule 3.2 # Rule 3.2
# assert_tree(parse_all('<span><tr></tr></span>'), [ assert_tree(parse_all('<span><x-tr></x-tr></span>'), [
# ('body', 'Line', [ ('body', 'Line', [
# ('span', 'Inline', [ ('span', 'Inline', [
# ('span', 'AnonInlineBlock', [ ('span', 'AnonInlineBlock', [
# ('span', 'AnonInlineTable', [ ('span', 'AnonInlineTable', [
# ('span', 'AnonTableRowGroup', [ ('span', 'AnonTableRowGroup', [
# ('tr', 'TableRow', [])])])])])])]) ('x-tr', 'TableRow', [])])])])])])])
# # Rule 3.1 # Rule 3.1
# # Also, rule 1.3 does not apply: whitespace before and after is preserved # Also, rule 1.3 does not apply: whitespace before and after is preserved
# assert_tree(parse_all(''' assert_tree(parse_all('''
# <span> <span>
# <em style="display: table-cell"></em> <em style="display: table-cell"></em>
# <em style="display: table-cell"></em> <em style="display: table-cell"></em>
# </span> </span>
# '''), [ '''), [
# ('body', 'Line', [ ('body', 'Line', [
# ('span', 'Inline', [ ('span', 'Inline', [
# # Whitespace is preserved in table handling, then collapsed # Whitespace is preserved in table handling, then collapsed
# # into a single space. # into a single space.
# ('span', 'Text', ' '), ('span', 'Text', ' '),
# ('span', 'AnonInlineBlock', [ ('span', 'AnonInlineBlock', [
# ('span', 'AnonInlineTable', [ ('span', 'AnonInlineTable', [
# ('span', 'AnonTableRowGroup', [ ('span', 'AnonTableRowGroup', [
# ('span', 'AnonTableRow', [ ('span', 'AnonTableRow', [
# ('em', 'TableCell', []), ('em', 'TableCell', []),
# ('em', 'TableCell', [])])])])]), ('em', 'TableCell', [])])])])]),
# ('span', 'Text', ' ')])])]) ('span', 'Text', ' ')])])])
# Rule 3.2 # Rule 3.2
assert_tree(parse_all('<tr></tr>\t<tr></tr>'), [ assert_tree(parse_all('<x-tr></x-tr>\t<x-tr></x-tr>'), [
('body', 'AnonBlock', [ ('body', 'AnonBlock', [
('body', 'AnonTable', [ ('body', 'AnonTable', [
('body', 'AnonTableRowGroup', [ ('body', 'AnonTableRowGroup', [
('tr', 'TableRow', []), ('x-tr', 'TableRow', []),
('tr', 'TableRow', [])])])])]) ('x-tr', 'TableRow', [])])])])])
assert_tree(parse_all('<col></col>\n<colgroup></colgroup>'), [
assert_tree(parse_all('<x-col></x-col>\n<x-colgroup></x-colgroup>'), [
('body', 'AnonBlock', [ ('body', 'AnonBlock', [
('body', 'AnonTable', [ ('body', 'AnonTable', [
('body', 'AnonTableColumnGroup', [ ('body', 'AnonTableColumnGroup', [
('col', 'TableColumn', [])]), ('x-col', 'TableColumn', [])]),
('colgroup', 'TableColumnGroup', [ ('x-colgroup', 'TableColumnGroup', [
('colgroup', 'AnonTableColumn', [])])])])]) ('x-colgroup', 'AnonTableColumn', [])])])])])
@assert_no_logs @assert_no_logs

View File

@ -24,6 +24,7 @@ import pytest
from ..compat import xrange, izip, ints_from_bytes from ..compat import xrange, izip, ints_from_bytes
from ..urls import ensure_url from ..urls import ensure_url
from ..html import HTML_HANDLERS
from .. import HTML from .. import HTML
from .testing_utils import ( from .testing_utils import (
resource_filename, TestHTML, FONTS, assert_no_logs, capture_logs) resource_filename, TestHTML, FONTS, assert_no_logs, capture_logs)
@ -1459,37 +1460,42 @@ def test_visibility():
@assert_no_logs @assert_no_logs
@requires_cairo_1_12 @requires_cairo_1_12
def test_tables(): def test_tables():
# TODO: refactor colspan/rowspan into CSS:
# td, th { column-span: attr(colspan integer) }
HTML_HANDLERS['x-td'] = HTML_HANDLERS['td']
HTML_HANDLERS['x-th'] = HTML_HANDLERS['th']
source = ''' source = '''
<style> <style>
@page { size: 28px; background: #fff } @page { size: 28px; background: #fff }
table { margin: 1px; padding: 1px; border-spacing: 1px; x-table { margin: 1px; padding: 1px; border-spacing: 1px;
border: 1px solid transparent } border: 1px solid transparent }
td { width: 2px; height: 2px; padding: 1px; x-td { width: 2px; height: 2px; padding: 1px;
border: 1px solid transparent } border: 1px solid transparent }
%(extra_css)s %(extra_css)s
</style> </style>
<table> <x-table>
<colgroup> <x-colgroup>
<col> <x-col></x-col>
<col> <x-col></x-col>
</colgroup> </x-colgroup>
<col> <x-col></x-col>
<tbody> <x-tbody>
<tr> <x-tr>
<td></td> <x-td></x-td>
<td rowspan=2></td> <x-td rowspan=2></x-td>
<td></td> <x-td></x-td>
</tr> </x-tr>
<tr> <x-tr>
<td colspan=2></td> <x-td colspan=2></x-td>
<td></td> <x-td></x-td>
</tr> </x-tr>
</tbody> </x-tbody>
<tr> <x-tr>
<td></td> <x-td></x-td>
<td></td> <x-td></x-td>
</tr> </x-tr>
</table> </x-table>
''' '''
r = as_pixel(b'\xff\x7f\x7f\xff') # rgba(255, 0, 0, 0.5) above #fff r = as_pixel(b'\xff\x7f\x7f\xff') # rgba(255, 0, 0, 0.5) above #fff
R = as_pixel(b'\xff\x3f\x3f\xff') # r above r above #fff R = as_pixel(b'\xff\x3f\x3f\xff') # r above r above #fff
@ -1526,8 +1532,8 @@ def test_tables():
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, _+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_,
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, _+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
], source % {'extra_css': ''' ], source % {'extra_css': '''
table { border-color: #00f; table-layout: fixed } x-table { border-color: #00f; table-layout: fixed }
td { border-color: rgba(255, 0, 0, 0.5) } x-td { border-color: rgba(255, 0, 0, 0.5) }
'''}) '''})
assert_pixels('table_collapsed_borders', 28, 28, [ assert_pixels('table_collapsed_borders', 28, 28, [
@ -1560,9 +1566,9 @@ def test_tables():
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, _+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, _+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
], source % {'extra_css': ''' ], source % {'extra_css': '''
table { border: 2px solid #00f; table-layout: fixed; x-table { border: 2px solid #00f; table-layout: fixed;
border-collapse: collapse } border-collapse: collapse }
td { border-color: #ff7f7f } x-td { border-color: #ff7f7f }
'''}) '''})
assert_pixels('table_collapsed_borders_paged', 28, 52, [ assert_pixels('table_collapsed_borders_paged', 28, 52, [
@ -1619,9 +1625,9 @@ def test_tables():
_+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+_, _+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+g+_,
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, _+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
], source % {'extra_css': ''' ], source % {'extra_css': '''
table { border: solid #00f; border-width: 8px 2px; x-table { border: solid #00f; border-width: 8px 2px;
table-layout: fixed; border-collapse: collapse } table-layout: fixed; border-collapse: collapse }
td { border-color: #ff7f7f } x-td { border-color: #ff7f7f }
@page { size: 28px 26px; margin: 1px; @page { size: 28px 26px; margin: 1px;
border: 1px solid rgba(0, 255, 0, 0.5); } border: 1px solid rgba(0, 255, 0, 0.5); }
'''}) '''})
@ -1656,8 +1662,8 @@ def test_tables():
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, _+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_,
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, _+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
], source % {'extra_css': ''' ], source % {'extra_css': '''
table { border-color: #00f; table-layout: fixed } x-table { border-color: #00f; table-layout: fixed }
td { background: rgba(255, 0, 0, 0.5) } x-td { background: rgba(255, 0, 0, 0.5) }
'''}) '''})
assert_pixels('table_column_backgrounds', 28, 28, [ assert_pixels('table_column_backgrounds', 28, 28, [
@ -1690,9 +1696,9 @@ def test_tables():
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, _+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_,
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, _+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
], source % {'extra_css': ''' ], source % {'extra_css': '''
table { border-color: #00f; table-layout: fixed } x-table { border-color: #00f; table-layout: fixed }
colgroup { background: rgba(255, 0, 0, 0.5) } x-colgroup { background: rgba(255, 0, 0, 0.5) }
col { background: rgba(0, 255, 0, 0.5) } x-col { background: rgba(0, 255, 0, 0.5) }
'''}) '''})
assert_pixels('table_row_backgrounds', 28, 28, [ assert_pixels('table_row_backgrounds', 28, 28, [
@ -1725,9 +1731,9 @@ def test_tables():
_+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_, _+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+B+_,
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_, _+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
], source % {'extra_css': ''' ], source % {'extra_css': '''
table { border-color: #00f; table-layout: fixed } x-table { border-color: #00f; table-layout: fixed }
tbody { background: rgba(255, 0, 0, 0.5) } x-tbody { background: rgba(255, 0, 0, 0.5) }
tr { background: rgba(0, 255, 0, 0.5) } x-tr { background: rgba(0, 255, 0, 0.5) }
'''}) '''})
r = as_pixel(b'\xff\x00\x00\xff') r = as_pixel(b'\xff\x00\x00\xff')
@ -1842,7 +1848,7 @@ def test_before_after():
body { margin: 0; background: #fff } body { margin: 0; background: #fff }
a[href]:before { content: '[' attr(href) '] ' } a[href]:before { content: '[' attr(href) '] ' }
</style> </style>
<p><a href="some url">some content</p> <p><a href="some url">some content</a></p>
'''), '''),
('pseudo_before_reference', ''' ('pseudo_before_reference', '''
<style> <style>

View File

@ -1581,7 +1581,7 @@ def test_page_breaks():
body { margin: 0 } body { margin: 0 }
div { height: 30px } div { height: 30px }
</style> </style>
<div/><div/><div/><div/><div/> <div></div><div></div><div></div><div></div><div></div>
''') ''')
page_divs = [] page_divs = []
for page in pages: for page in pages:
@ -2773,10 +2773,10 @@ def test_text_align_justify():
@page { size: 300px 1000px } @page { size: 300px 1000px }
body { text-align: justify } body { text-align: justify }
</style> </style>
<p><img src="pattern.png" style="width: 40px"> &#20; <p><img src="pattern.png" style="width: 40px">
<strong> <strong>
<img src="pattern.png" style="width: 60px"> &#20; <img src="pattern.png" style="width: 60px">
<img src="pattern.png" style="width: 10px"> &#20; <img src="pattern.png" style="width: 10px">
<img src="pattern.png" style="width: 100px" <img src="pattern.png" style="width: 100px"
></strong><img src="pattern.png" style="width: 290px" ></strong><img src="pattern.png" style="width: 290px"
><!-- Last image will be on its own line. -->''') ><!-- Last image will be on its own line. -->''')
@ -2825,8 +2825,7 @@ def test_word_spacing():
# (Not a string.) # (Not a string.)
page, = parse(''' page, = parse('''
<style></style> <style></style>
<body><strong>Lorem ipsum dolor<em>sit amet</em></strong></body> <body><strong>Lorem ipsum dolor<em>sit amet</em></strong>''')
''')
html, = page.children html, = page.children
body, = html.children body, = html.children
line, = body.children line, = body.children
@ -2837,8 +2836,7 @@ def test_word_spacing():
# of a TextBox. Is this what we want? # of a TextBox. Is this what we want?
page, = parse(''' page, = parse('''
<style>strong { word-spacing: 11px }</style> <style>strong { word-spacing: 11px }</style>
<body><strong>Lorem ipsum dolor<em>sit amet</em></strong></body> <body><strong>Lorem ipsum dolor<em>sit amet</em></strong>''')
''')
html, = page.children html, = page.children
body, = html.children body, = html.children
line, = body.children line, = body.children
@ -2850,8 +2848,7 @@ def test_word_spacing():
def test_letter_spacing(): def test_letter_spacing():
"""Test letter-spacing.""" """Test letter-spacing."""
page, = parse(''' page, = parse('''
<body><strong>Supercalifragilisticexpialidocious</strong></body> <body><strong>Supercalifragilisticexpialidocious</strong>''')
''')
html, = page.children html, = page.children
body, = html.children body, = html.children
line, = body.children line, = body.children
@ -2860,8 +2857,7 @@ def test_letter_spacing():
page, = parse(''' page, = parse('''
<style>strong { letter-spacing: 11px }</style> <style>strong { letter-spacing: 11px }</style>
<body><strong>Supercalifragilisticexpialidocious</strong></body> <body><strong>Supercalifragilisticexpialidocious</strong>''')
''')
html, = page.children html, = page.children
body, = html.children body, = html.children
line, = body.children line, = body.children
@ -3021,8 +3017,8 @@ def test_table_column_width():
with capture_logs() as logs: with capture_logs() as logs:
page, = parse(source) page, = parse(source)
assert len(logs) == 1 assert len(logs) == 1
assert logs[0] == ('WARNING: This table row has more columns than ' assert logs[0].startswith('WARNING: This table row has more columns than '
'the table, ignored 1 cells: (<TableCellBox td 25>,)') 'the table, ignored 1 cell')
html, = page.children html, = page.children
body, = html.children body, = html.children
wrapper, = body.children wrapper, = body.children
@ -3145,8 +3141,8 @@ def test_table_row_height():
<tr> <tr>
<td rowspan=0 style="height: 420px; vertical-align: top"></td> <td rowspan=0 style="height: 420px; vertical-align: top"></td>
<td>X<br>X<br>X</td> <td>X<br>X<br>X</td>
<td><table style="margin-top: 20px; <td><table style="margin-top: 20px; border-spacing: 0">
border-spacing: 0">X</table></td> <tr><td>X</td></tr></table></td>
<td style="vertical-align: top">X</td> <td style="vertical-align: top">X</td>
<td style="vertical-align: middle">X</td> <td style="vertical-align: middle">X</td>
<td style="vertical-align: bottom">X</td> <td style="vertical-align: bottom">X</td>

View File

@ -21,14 +21,10 @@ def to_lists(page):
return serialize_stacking(StackingContext.from_box(html, page)) return serialize_stacking(StackingContext.from_box(html, page))
def serialize_box(box):
return '%s %s' % (box.element_tag, box.sourceline)
def serialize_stacking(context): def serialize_stacking(context):
return ( return (
serialize_box(context.box), context.box.element_tag,
[serialize_box(b) for b in context.blocks_and_cells], [b.element_tag for b in context.blocks_and_cells],
[serialize_stacking(c) for c in context.zero_z_contexts], [serialize_stacking(c) for c in context.zero_z_contexts],
) )
@ -39,14 +35,14 @@ def test_nested():
<p id=lorem></p> <p id=lorem></p>
<div style="position: relative"> <div style="position: relative">
<p id=lipsum></p> <p id=lipsum></p>
</p> </div>
''') ''')
assert to_lists(page) == ( assert to_lists(page) == (
'html 1', 'html',
['body 1', 'p 1'], ['body', 'p'],
[( [(
'div 2', 'div',
['p 3'], ['p'],
[])]) [])])
page, = parse('''\ page, = parse('''\
@ -55,10 +51,10 @@ def test_nested():
</div> </div>
''') ''')
assert to_lists(page) == ( assert to_lists(page) == (
'html 1', 'html',
['body 1'], ['body'],
[('div 1', [], []), # In this order [('div', [], []), # In this order
('p 2', [], [])]) ('p', [], [])])
@assert_no_logs @assert_no_logs