mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-04 16:07:57 +03:00
Merge branch 'master' into border3
This commit is contained in:
commit
d4493e5707
@ -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
|
||||
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
|
||||
|
||||
For example, if you have only one top-level ``<h1>`` and do not wish to
|
||||
|
@ -8,6 +8,7 @@ WeasyPrint |version| depends on:
|
||||
* Pango_
|
||||
* CFFI_ ≥ 0.5
|
||||
* lxml_
|
||||
* html5lib ≥ 1.0b3
|
||||
* cairocffi_ ≥ 0.3
|
||||
* tinycss_ = 0.3
|
||||
* cssselect_ ≥ 0.6
|
||||
|
7
setup.py
7
setup.py
@ -12,11 +12,13 @@
|
||||
|
||||
import re
|
||||
import sys
|
||||
import codecs
|
||||
from os import path
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
VERSION = re.search("VERSION = '([^']+)'", open(
|
||||
path.join(path.dirname(__file__), 'weasyprint', '__init__.py')
|
||||
VERSION = re.search("VERSION = '([^']+)'", codecs.open(
|
||||
path.join(path.dirname(__file__), 'weasyprint', '__init__.py'),
|
||||
encoding="utf-8",
|
||||
).read().strip()).group(1)
|
||||
|
||||
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 = [
|
||||
# XXX: Keep this in sync with docs/install.rst
|
||||
'lxml',
|
||||
'html5lib>=1.0b3',
|
||||
'tinycss==0.3',
|
||||
'cssselect>=0.6',
|
||||
'CairoSVG>=0.4.1',
|
||||
|
@ -27,10 +27,11 @@ __all__ = ['HTML', 'CSS', 'Document', 'Page', 'default_url_fetcher',
|
||||
|
||||
|
||||
import contextlib
|
||||
import lxml.etree
|
||||
import html5lib
|
||||
|
||||
from .urls import (fetch, default_url_fetcher, path2url, ensure_url,
|
||||
url_is_absolute)
|
||||
from .compat import unicode
|
||||
from .logger import LOGGER
|
||||
# Some import are at the end of the file (after the CSS class) is defined
|
||||
# to work around circular imports.
|
||||
@ -81,16 +82,14 @@ class HTML(object):
|
||||
if source_type == 'tree':
|
||||
result = source
|
||||
else:
|
||||
if source_type == 'string':
|
||||
parse = lxml.etree.fromstring
|
||||
else:
|
||||
parse = lxml.etree.parse
|
||||
if not encoding:
|
||||
encoding = protocol_encoding
|
||||
parser = lxml.etree.HTMLParser(encoding=encoding)
|
||||
result = parse(source, parser=parser)
|
||||
if result is None:
|
||||
raise ValueError('Error while parsing HTML')
|
||||
if isinstance(source, unicode):
|
||||
encoding = None
|
||||
result = html5lib.parse(
|
||||
source, treebuilder='lxml', encoding=encoding,
|
||||
namespaceHTMLElements=False)
|
||||
assert result
|
||||
base_url = find_base_url(result, base_url)
|
||||
if hasattr(result, 'getroot'):
|
||||
result.docinfo.URL = base_url
|
||||
|
@ -74,7 +74,7 @@ def main(argv=None, stdout=None, stdin=None):
|
||||
prog='weasyprint', description='Renders web pages to PDF or PNG.')
|
||||
parser.add_argument('--version', action='version',
|
||||
version='WeasyPrint version %s' % VERSION,
|
||||
help='Print WeasyPrint’s version number and exit.')
|
||||
help="Print WeasyPrint's version number and exit.")
|
||||
parser.add_argument('-e', '--encoding',
|
||||
help='Character encoding of the input')
|
||||
parser.add_argument('-f', '--format', choices=['pdf', 'png'],
|
||||
|
@ -10,16 +10,16 @@ br:before { content: '\A'; white-space: pre-line }
|
||||
ol { list-style-type: decimal }
|
||||
ol, ul { counter-reset: list-item }
|
||||
|
||||
table { display: table;
|
||||
table, x-table { display: table;
|
||||
box-sizing: border-box }
|
||||
tr { display: table-row }
|
||||
thead { display: table-header-group }
|
||||
tbody { display: table-row-group }
|
||||
tfoot { display: table-footer-group }
|
||||
col { display: table-column }
|
||||
colgroup { display: table-column-group }
|
||||
td, th { display: table-cell }
|
||||
caption { display: table-caption }
|
||||
tr, x-tr { display: table-row }
|
||||
thead, x-thead { display: table-header-group }
|
||||
tbody, x-tbody { display: table-row-group }
|
||||
tfoot, x-tfoot { display: table-footer-group }
|
||||
col, x-col { display: table-column }
|
||||
colgroup, x-colgroup { display: table-column-group }
|
||||
td, th, x-td, x-th { display: table-cell }
|
||||
caption, x-caption { display: table-caption }
|
||||
|
||||
*[lang] { -weasy-lang: attr(lang); }
|
||||
a[href] { -weasy-link: attr(href); }
|
||||
|
@ -167,7 +167,11 @@ def table_layout(context, table, max_position_y, skip_stack,
|
||||
row_bottom_y = max(
|
||||
cell.position_y + cell.border_height()
|
||||
for cell in ending_cells)
|
||||
row.height = row_bottom_y - row.position_y
|
||||
if row.height == 'auto':
|
||||
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:
|
||||
row_bottom_y = row.position_y
|
||||
row.height = 0
|
||||
@ -574,7 +578,8 @@ def find_in_flow_baseline(box, last=False, baseline_types=(boxes.LineBox,)):
|
||||
"""
|
||||
if isinstance(box, baseline_types):
|
||||
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
|
||||
for child in children:
|
||||
if child.is_in_normal_flow():
|
||||
|
@ -13,6 +13,8 @@
|
||||
from __future__ import division, unicode_literals
|
||||
|
||||
import functools
|
||||
import pprint
|
||||
import difflib
|
||||
|
||||
from .testing_utils import (
|
||||
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().
|
||||
|
||||
"""
|
||||
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):
|
||||
@ -251,10 +258,10 @@ def test_block_in_inline():
|
||||
box = parse('''
|
||||
<style>
|
||||
p { display: inline-block; }
|
||||
span { display: block; }
|
||||
span, i { display: block; }
|
||||
</style>
|
||||
<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)
|
||||
assert_tree(box, [
|
||||
('body', 'Line', [
|
||||
@ -277,7 +284,7 @@ def test_block_in_inline():
|
||||
('span', 'Line', [
|
||||
('em', 'Inline', [
|
||||
('em', 'Text', 'conse'),
|
||||
('div', 'Block', [])])])])])])])])])
|
||||
('i', 'Block', [])])])])])])])])])
|
||||
|
||||
box = build.block_in_inline(box)
|
||||
assert_tree(box, [
|
||||
@ -312,7 +319,7 @@ def test_block_in_inline():
|
||||
('span', 'Line', [
|
||||
('em', 'Inline', [
|
||||
('em', 'Text', 'conse')])])]),
|
||||
('div', 'Block', []),
|
||||
('i', 'Block', []),
|
||||
('span', 'AnonBlock', [
|
||||
('span', 'Line', [
|
||||
('em', 'Inline', [])])])]),
|
||||
@ -481,48 +488,48 @@ def test_tables():
|
||||
# Rule 1.3
|
||||
# Also table model: http://www.w3.org/TR/CSS21/tables.html#model
|
||||
assert_tree(parse_all('''
|
||||
<table>
|
||||
<tr>
|
||||
<th>foo</th>
|
||||
<th>bar</th>
|
||||
</tr>
|
||||
<tfoot></tfoot>
|
||||
<thead><th></th></thead>
|
||||
<caption style="caption-side: bottom"></caption>
|
||||
<thead></thead>
|
||||
<col></col>
|
||||
<caption>top caption</caption>
|
||||
<tr>
|
||||
<td>baz</td>
|
||||
</tr>
|
||||
</table>
|
||||
<x-table>
|
||||
<x-tr>
|
||||
<x-th>foo</x-th>
|
||||
<x-th>bar</x-th>
|
||||
</x-tr>
|
||||
<x-tfoot></x-tfoot>
|
||||
<x-thead><x-th></x-th></x-thead>
|
||||
<x-caption style="caption-side: bottom"></x-caption>
|
||||
<x-thead></x-thead>
|
||||
<x-col></x-col>
|
||||
<x-caption>top caption</x-caption>
|
||||
<x-tr>
|
||||
<x-td>baz</x-td>
|
||||
</x-tr>
|
||||
</x-table>
|
||||
'''), [
|
||||
('table', 'AnonBlock', [
|
||||
('caption', 'TableCaption', [
|
||||
('caption', 'Line', [
|
||||
('caption', 'Text', 'top caption')])]),
|
||||
('table', 'Table', [
|
||||
('table', 'AnonTableColumnGroup', [
|
||||
('col', 'TableColumn', [])]),
|
||||
('thead', 'TableRowGroup', [
|
||||
('thead', 'AnonTableRow', [
|
||||
('th', 'TableCell', [])])]),
|
||||
('table', 'AnonTableRowGroup', [
|
||||
('tr', 'TableRow', [
|
||||
('th', 'TableCell', [
|
||||
('th', 'Line', [
|
||||
('th', 'Text', 'foo')])]),
|
||||
('th', 'TableCell', [
|
||||
('th', 'Line', [
|
||||
('th', 'Text', 'bar')])])])]),
|
||||
('thead', 'TableRowGroup', []),
|
||||
('table', 'AnonTableRowGroup', [
|
||||
('tr', 'TableRow', [
|
||||
('td', 'TableCell', [
|
||||
('td', 'Line', [
|
||||
('td', 'Text', 'baz')])])])]),
|
||||
('tfoot', 'TableRowGroup', [])]),
|
||||
('caption', 'TableCaption', [])])])
|
||||
('x-table', 'AnonBlock', [
|
||||
('x-caption', 'TableCaption', [
|
||||
('x-caption', 'Line', [
|
||||
('x-caption', 'Text', 'top caption')])]),
|
||||
('x-table', 'Table', [
|
||||
('x-table', 'AnonTableColumnGroup', [
|
||||
('x-col', 'TableColumn', [])]),
|
||||
('x-thead', 'TableRowGroup', [
|
||||
('x-thead', 'AnonTableRow', [
|
||||
('x-th', 'TableCell', [])])]),
|
||||
('x-table', 'AnonTableRowGroup', [
|
||||
('x-tr', 'TableRow', [
|
||||
('x-th', 'TableCell', [
|
||||
('x-th', 'Line', [
|
||||
('x-th', 'Text', 'foo')])]),
|
||||
('x-th', 'TableCell', [
|
||||
('x-th', 'Line', [
|
||||
('x-th', 'Text', 'bar')])])])]),
|
||||
('x-thead', 'TableRowGroup', []),
|
||||
('x-table', 'AnonTableRowGroup', [
|
||||
('x-tr', 'TableRow', [
|
||||
('x-td', 'TableCell', [
|
||||
('x-td', 'Line', [
|
||||
('x-td', 'Text', 'baz')])])])]),
|
||||
('x-tfoot', 'TableRowGroup', [])]),
|
||||
('x-caption', 'TableCaption', [])])])
|
||||
|
||||
# Rules 1.4 and 3.1
|
||||
assert_tree(parse_all('''
|
||||
@ -562,72 +569,74 @@ def test_tables():
|
||||
('ins', 'AnonTableColumn', [])])])])])
|
||||
|
||||
# Rules 2.1 then 2.3
|
||||
assert_tree(parse_all('<table>foo <div></div></table>'), [
|
||||
('table', 'AnonBlock', [
|
||||
('table', 'Table', [
|
||||
('table', 'AnonTableRowGroup', [
|
||||
('table', 'AnonTableRow', [
|
||||
('table', 'AnonTableCell', [
|
||||
('table', 'AnonBlock', [
|
||||
('table', 'Line', [
|
||||
('table', 'Text', 'foo ')])]),
|
||||
assert_tree(parse_all('<x-table>foo <div></div></x-table>'), [
|
||||
('x-table', 'AnonBlock', [
|
||||
('x-table', 'Table', [
|
||||
('x-table', 'AnonTableRowGroup', [
|
||||
('x-table', 'AnonTableRow', [
|
||||
('x-table', 'AnonTableCell', [
|
||||
('x-table', 'AnonBlock', [
|
||||
('x-table', 'Line', [
|
||||
('x-table', 'Text', 'foo ')])]),
|
||||
('div', 'Block', [])])])])])])])
|
||||
|
||||
# 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', 'AnonTable', [
|
||||
('thead', 'TableRowGroup', [
|
||||
('thead', 'AnonTableRow', [
|
||||
('thead', 'AnonTableCell', [
|
||||
('x-thead', 'TableRowGroup', [
|
||||
('x-thead', 'AnonTableRow', [
|
||||
('x-thead', 'AnonTableCell', [
|
||||
('div', 'Block', [])]),
|
||||
('td', 'TableCell', [])])])])])])
|
||||
('x-td', 'TableCell', [])])])])])])
|
||||
|
||||
# TODO: re-enable this once we support inline-table
|
||||
# # Rule 3.2
|
||||
# assert_tree(parse_all('<span><tr></tr></span>'), [
|
||||
# ('body', 'Line', [
|
||||
# ('span', 'Inline', [
|
||||
# ('span', 'AnonInlineBlock', [
|
||||
# ('span', 'AnonInlineTable', [
|
||||
# ('span', 'AnonTableRowGroup', [
|
||||
# ('tr', 'TableRow', [])])])])])])])
|
||||
# Rule 3.2
|
||||
assert_tree(parse_all('<span><x-tr></x-tr></span>'), [
|
||||
('body', 'Line', [
|
||||
('span', 'Inline', [
|
||||
('span', 'AnonInlineBlock', [
|
||||
('span', 'AnonInlineTable', [
|
||||
('span', 'AnonTableRowGroup', [
|
||||
('x-tr', 'TableRow', [])])])])])])])
|
||||
|
||||
# # Rule 3.1
|
||||
# # Also, rule 1.3 does not apply: whitespace before and after is preserved
|
||||
# assert_tree(parse_all('''
|
||||
# <span>
|
||||
# <em style="display: table-cell"></em>
|
||||
# <em style="display: table-cell"></em>
|
||||
# </span>
|
||||
# '''), [
|
||||
# ('body', 'Line', [
|
||||
# ('span', 'Inline', [
|
||||
# # Whitespace is preserved in table handling, then collapsed
|
||||
# # into a single space.
|
||||
# ('span', 'Text', ' '),
|
||||
# ('span', 'AnonInlineBlock', [
|
||||
# ('span', 'AnonInlineTable', [
|
||||
# ('span', 'AnonTableRowGroup', [
|
||||
# ('span', 'AnonTableRow', [
|
||||
# ('em', 'TableCell', []),
|
||||
# ('em', 'TableCell', [])])])])]),
|
||||
# ('span', 'Text', ' ')])])])
|
||||
# Rule 3.1
|
||||
# Also, rule 1.3 does not apply: whitespace before and after is preserved
|
||||
assert_tree(parse_all('''
|
||||
<span>
|
||||
<em style="display: table-cell"></em>
|
||||
<em style="display: table-cell"></em>
|
||||
</span>
|
||||
'''), [
|
||||
('body', 'Line', [
|
||||
('span', 'Inline', [
|
||||
# Whitespace is preserved in table handling, then collapsed
|
||||
# into a single space.
|
||||
('span', 'Text', ' '),
|
||||
('span', 'AnonInlineBlock', [
|
||||
('span', 'AnonInlineTable', [
|
||||
('span', 'AnonTableRowGroup', [
|
||||
('span', 'AnonTableRow', [
|
||||
('em', 'TableCell', []),
|
||||
('em', 'TableCell', [])])])])]),
|
||||
('span', 'Text', ' ')])])])
|
||||
|
||||
# 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', 'AnonTable', [
|
||||
('body', 'AnonTableRowGroup', [
|
||||
('tr', 'TableRow', []),
|
||||
('tr', 'TableRow', [])])])])])
|
||||
assert_tree(parse_all('<col></col>\n<colgroup></colgroup>'), [
|
||||
('x-tr', 'TableRow', []),
|
||||
('x-tr', 'TableRow', [])])])])])
|
||||
|
||||
assert_tree(parse_all('<x-col></x-col>\n<x-colgroup></x-colgroup>'), [
|
||||
('body', 'AnonBlock', [
|
||||
('body', 'AnonTable', [
|
||||
('body', 'AnonTableColumnGroup', [
|
||||
('col', 'TableColumn', [])]),
|
||||
('colgroup', 'TableColumnGroup', [
|
||||
('colgroup', 'AnonTableColumn', [])])])])])
|
||||
('x-col', 'TableColumn', [])]),
|
||||
('x-colgroup', 'TableColumnGroup', [
|
||||
('x-colgroup', 'AnonTableColumn', [])])])])])
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
|
@ -24,6 +24,7 @@ import pytest
|
||||
|
||||
from ..compat import xrange, izip, ints_from_bytes
|
||||
from ..urls import ensure_url
|
||||
from ..html import HTML_HANDLERS
|
||||
from .. import HTML
|
||||
from .testing_utils import (
|
||||
resource_filename, TestHTML, FONTS, assert_no_logs, capture_logs)
|
||||
@ -1459,37 +1460,42 @@ def test_visibility():
|
||||
@assert_no_logs
|
||||
@requires_cairo_1_12
|
||||
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 = '''
|
||||
<style>
|
||||
@page { size: 28px; background: #fff }
|
||||
table { margin: 1px; padding: 1px; border-spacing: 1px;
|
||||
border: 1px solid transparent }
|
||||
td { width: 2px; height: 2px; padding: 1px;
|
||||
border: 1px solid transparent }
|
||||
x-table { margin: 1px; padding: 1px; border-spacing: 1px;
|
||||
border: 1px solid transparent }
|
||||
x-td { width: 2px; height: 2px; padding: 1px;
|
||||
border: 1px solid transparent }
|
||||
%(extra_css)s
|
||||
</style>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col>
|
||||
<col>
|
||||
</colgroup>
|
||||
<col>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td rowspan=2></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<x-table>
|
||||
<x-colgroup>
|
||||
<x-col></x-col>
|
||||
<x-col></x-col>
|
||||
</x-colgroup>
|
||||
<x-col></x-col>
|
||||
<x-tbody>
|
||||
<x-tr>
|
||||
<x-td></x-td>
|
||||
<x-td rowspan=2></x-td>
|
||||
<x-td></x-td>
|
||||
</x-tr>
|
||||
<x-tr>
|
||||
<x-td colspan=2></x-td>
|
||||
<x-td></x-td>
|
||||
</x-tr>
|
||||
</x-tbody>
|
||||
<x-tr>
|
||||
<x-td></x-td>
|
||||
<x-td></x-td>
|
||||
</x-tr>
|
||||
</x-table>
|
||||
'''
|
||||
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
|
||||
@ -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+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], source % {'extra_css': '''
|
||||
table { border-color: #00f; table-layout: fixed }
|
||||
td { border-color: rgba(255, 0, 0, 0.5) }
|
||||
x-table { border-color: #00f; table-layout: fixed }
|
||||
x-td { border-color: rgba(255, 0, 0, 0.5) }
|
||||
'''})
|
||||
|
||||
assert_pixels('table_collapsed_borders', 28, 28, [
|
||||
@ -1560,9 +1566,9 @@ def test_tables():
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], source % {'extra_css': '''
|
||||
table { border: 2px solid #00f; table-layout: fixed;
|
||||
border-collapse: collapse }
|
||||
td { border-color: #ff7f7f }
|
||||
x-table { border: 2px solid #00f; table-layout: fixed;
|
||||
border-collapse: collapse }
|
||||
x-td { border-color: #ff7f7f }
|
||||
'''})
|
||||
|
||||
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+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], source % {'extra_css': '''
|
||||
table { border: solid #00f; border-width: 8px 2px;
|
||||
table-layout: fixed; border-collapse: collapse }
|
||||
td { border-color: #ff7f7f }
|
||||
x-table { border: solid #00f; border-width: 8px 2px;
|
||||
table-layout: fixed; border-collapse: collapse }
|
||||
x-td { border-color: #ff7f7f }
|
||||
@page { size: 28px 26px; margin: 1px;
|
||||
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+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], source % {'extra_css': '''
|
||||
table { border-color: #00f; table-layout: fixed }
|
||||
td { background: rgba(255, 0, 0, 0.5) }
|
||||
x-table { border-color: #00f; table-layout: fixed }
|
||||
x-td { background: rgba(255, 0, 0, 0.5) }
|
||||
'''})
|
||||
|
||||
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+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], source % {'extra_css': '''
|
||||
table { border-color: #00f; table-layout: fixed }
|
||||
colgroup { background: rgba(255, 0, 0, 0.5) }
|
||||
col { background: rgba(0, 255, 0, 0.5) }
|
||||
x-table { border-color: #00f; table-layout: fixed }
|
||||
x-colgroup { background: rgba(255, 0, 0, 0.5) }
|
||||
x-col { background: rgba(0, 255, 0, 0.5) }
|
||||
'''})
|
||||
|
||||
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+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], source % {'extra_css': '''
|
||||
table { border-color: #00f; table-layout: fixed }
|
||||
tbody { background: rgba(255, 0, 0, 0.5) }
|
||||
tr { background: rgba(0, 255, 0, 0.5) }
|
||||
x-table { border-color: #00f; table-layout: fixed }
|
||||
x-tbody { background: rgba(255, 0, 0, 0.5) }
|
||||
x-tr { background: rgba(0, 255, 0, 0.5) }
|
||||
'''})
|
||||
|
||||
r = as_pixel(b'\xff\x00\x00\xff')
|
||||
@ -1842,7 +1848,7 @@ def test_before_after():
|
||||
body { margin: 0; background: #fff }
|
||||
a[href]:before { content: '[' attr(href) '] ' }
|
||||
</style>
|
||||
<p><a href="some url">some content</p>
|
||||
<p><a href="some url">some content</a></p>
|
||||
'''),
|
||||
('pseudo_before_reference', '''
|
||||
<style>
|
||||
|
@ -1581,7 +1581,7 @@ def test_page_breaks():
|
||||
body { margin: 0 }
|
||||
div { height: 30px }
|
||||
</style>
|
||||
<div/><div/><div/><div/><div/>
|
||||
<div></div><div></div><div></div><div></div><div></div>
|
||||
''')
|
||||
page_divs = []
|
||||
for page in pages:
|
||||
@ -2773,10 +2773,10 @@ def test_text_align_justify():
|
||||
@page { size: 300px 1000px }
|
||||
body { text-align: justify }
|
||||
</style>
|
||||
<p><img src="pattern.png" style="width: 40px"> 
|
||||
<p><img src="pattern.png" style="width: 40px">
|
||||
<strong>
|
||||
<img src="pattern.png" style="width: 60px"> 
|
||||
<img src="pattern.png" style="width: 10px"> 
|
||||
<img src="pattern.png" style="width: 60px">
|
||||
<img src="pattern.png" style="width: 10px">
|
||||
<img src="pattern.png" style="width: 100px"
|
||||
></strong><img src="pattern.png" style="width: 290px"
|
||||
><!-- Last image will be on its own line. -->''')
|
||||
@ -2825,8 +2825,7 @@ def test_word_spacing():
|
||||
# (Not a string.)
|
||||
page, = parse('''
|
||||
<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
|
||||
body, = html.children
|
||||
line, = body.children
|
||||
@ -2837,8 +2836,7 @@ def test_word_spacing():
|
||||
# of a TextBox. Is this what we want?
|
||||
page, = parse('''
|
||||
<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
|
||||
body, = html.children
|
||||
line, = body.children
|
||||
@ -2850,8 +2848,7 @@ def test_word_spacing():
|
||||
def test_letter_spacing():
|
||||
"""Test letter-spacing."""
|
||||
page, = parse('''
|
||||
<body><strong>Supercalifragilisticexpialidocious</strong></body>
|
||||
''')
|
||||
<body><strong>Supercalifragilisticexpialidocious</strong>''')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
line, = body.children
|
||||
@ -2860,8 +2857,7 @@ def test_letter_spacing():
|
||||
|
||||
page, = parse('''
|
||||
<style>strong { letter-spacing: 11px }</style>
|
||||
<body><strong>Supercalifragilisticexpialidocious</strong></body>
|
||||
''')
|
||||
<body><strong>Supercalifragilisticexpialidocious</strong>''')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
line, = body.children
|
||||
@ -3021,8 +3017,8 @@ def test_table_column_width():
|
||||
with capture_logs() as logs:
|
||||
page, = parse(source)
|
||||
assert len(logs) == 1
|
||||
assert logs[0] == ('WARNING: This table row has more columns than '
|
||||
'the table, ignored 1 cells: (<TableCellBox td 25>,)')
|
||||
assert logs[0].startswith('WARNING: This table row has more columns than '
|
||||
'the table, ignored 1 cell')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
wrapper, = body.children
|
||||
@ -3145,8 +3141,8 @@ def test_table_row_height():
|
||||
<tr>
|
||||
<td rowspan=0 style="height: 420px; vertical-align: top"></td>
|
||||
<td>X<br>X<br>X</td>
|
||||
<td><table style="margin-top: 20px;
|
||||
border-spacing: 0">X</table></td>
|
||||
<td><table style="margin-top: 20px; border-spacing: 0">
|
||||
<tr><td>X</td></tr></table></td>
|
||||
<td style="vertical-align: top">X</td>
|
||||
<td style="vertical-align: middle">X</td>
|
||||
<td style="vertical-align: bottom">X</td>
|
||||
|
@ -21,14 +21,10 @@ def to_lists(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):
|
||||
return (
|
||||
serialize_box(context.box),
|
||||
[serialize_box(b) for b in context.blocks_and_cells],
|
||||
context.box.element_tag,
|
||||
[b.element_tag for b in context.blocks_and_cells],
|
||||
[serialize_stacking(c) for c in context.zero_z_contexts],
|
||||
)
|
||||
|
||||
@ -39,14 +35,14 @@ def test_nested():
|
||||
<p id=lorem></p>
|
||||
<div style="position: relative">
|
||||
<p id=lipsum></p>
|
||||
</p>
|
||||
</div>
|
||||
''')
|
||||
assert to_lists(page) == (
|
||||
'html 1',
|
||||
['body 1', 'p 1'],
|
||||
'html',
|
||||
['body', 'p'],
|
||||
[(
|
||||
'div 2',
|
||||
['p 3'],
|
||||
'div',
|
||||
['p'],
|
||||
[])])
|
||||
|
||||
page, = parse('''\
|
||||
@ -55,10 +51,10 @@ def test_nested():
|
||||
</div>
|
||||
''')
|
||||
assert to_lists(page) == (
|
||||
'html 1',
|
||||
['body 1'],
|
||||
[('div 1', [], []), # In this order
|
||||
('p 2', [], [])])
|
||||
'html',
|
||||
['body'],
|
||||
[('div', [], []), # In this order
|
||||
('p', [], [])])
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
|
Loading…
Reference in New Issue
Block a user