2022-03-25 13:47:27 +03:00
|
|
|
|
"""Test that the "before layout" box tree is correctly constructed."""
|
2011-08-24 13:36:10 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
import pytest
|
2020-12-06 22:19:59 +03:00
|
|
|
|
from weasyprint.css import PageType, get_all_computed_styles
|
|
|
|
|
from weasyprint.formatting_structure import boxes, build
|
2021-09-13 16:37:29 +03:00
|
|
|
|
from weasyprint.layout.page import set_page_type_computed_styles
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
2021-04-19 18:15:53 +03:00
|
|
|
|
from .testing_utils import (
|
|
|
|
|
FakeHTML, assert_no_logs, assert_tree, capture_logs, parse, parse_all,
|
|
|
|
|
render_pages)
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _get_grid(html):
|
|
|
|
|
html = parse_all(html)
|
|
|
|
|
body, = html.children
|
|
|
|
|
table_wrapper, = body.children
|
|
|
|
|
table, = table_wrapper.children
|
|
|
|
|
return tuple(
|
|
|
|
|
[[(style, width, color) if width else None
|
|
|
|
|
for _score, (style, width, color) in column]
|
|
|
|
|
for column in grid]
|
|
|
|
|
for grid in table.collapsed_border_grid)
|
2011-09-27 13:44:35 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-05-19 17:31:34 +04:00
|
|
|
|
def test_box_tree():
|
2011-11-14 17:12:13 +04:00
|
|
|
|
assert_tree(parse('<p>'), [('p', 'Block', [])])
|
2018-03-19 00:23:39 +03:00
|
|
|
|
assert_tree(parse('''
|
|
|
|
|
<style>
|
|
|
|
|
span { display: inline-block }
|
|
|
|
|
</style>
|
|
|
|
|
<p>Hello <em>World <img src="pattern.png"><span>L</span></em>!</p>'''), [
|
|
|
|
|
('p', 'Block', [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Text', 'Hello '),
|
|
|
|
|
('em', 'Inline', [
|
|
|
|
|
('em', 'Text', 'World '),
|
2011-12-05 17:24:43 +04:00
|
|
|
|
('img', 'InlineReplaced', '<replaced>'),
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('span', 'InlineBlock', [
|
2013-04-11 20:43:05 +04:00
|
|
|
|
('span', 'Text', 'L')])]),
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Text', '!')])])
|
2011-05-19 17:31:34 +04:00
|
|
|
|
|
2011-05-19 18:03:50 +04:00
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-08-16 14:00:59 +04:00
|
|
|
|
def test_html_entities():
|
|
|
|
|
for quote in ['"', '"', '"', '"']:
|
2012-02-21 17:13:34 +04:00
|
|
|
|
assert_tree(parse('<p>{0}abc{1}'.format(quote, quote)), [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Text', '"abc"')])])
|
2011-08-16 14:00:59 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2018-03-19 00:23:39 +03:00
|
|
|
|
def test_inline_in_block_1():
|
2011-05-23 18:56:12 +04:00
|
|
|
|
source = '<div>Hello, <em>World</em>!\n<p>Lipsum.</p></div>'
|
2011-05-19 20:22:06 +04:00
|
|
|
|
expected = [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('div', 'Block', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('div', 'Block', [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('div', 'Line', [
|
|
|
|
|
('div', 'Text', 'Hello, '),
|
|
|
|
|
('em', 'Inline', [
|
|
|
|
|
('em', 'Text', 'World')]),
|
2021-10-13 10:48:39 +03:00
|
|
|
|
('div', 'Text', '! ')])]),
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('p', 'Text', 'Lipsum.')])])])]
|
2011-05-19 20:22:06 +04:00
|
|
|
|
box = parse(source)
|
2012-04-02 16:45:44 +04:00
|
|
|
|
box = build.inline_in_block(box)
|
|
|
|
|
assert_tree(box, expected)
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_inline_in_block_2():
|
2012-04-02 16:45:44 +04:00
|
|
|
|
source = '<div><p>Lipsum.</p>Hello, <em>World</em>!\n</div>'
|
|
|
|
|
expected = [
|
|
|
|
|
('div', 'Block', [
|
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('p', 'Text', 'Lipsum.')])]),
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('div', 'Block', [
|
2012-04-02 16:45:44 +04:00
|
|
|
|
('div', 'Line', [
|
|
|
|
|
('div', 'Text', 'Hello, '),
|
|
|
|
|
('em', 'Inline', [
|
|
|
|
|
('em', 'Text', 'World')]),
|
2021-10-13 10:48:39 +03:00
|
|
|
|
('div', 'Text', '! ')])])])]
|
2012-04-02 16:45:44 +04:00
|
|
|
|
box = parse(source)
|
2011-07-20 13:35:43 +04:00
|
|
|
|
box = build.inline_in_block(box)
|
2011-05-23 18:14:51 +04:00
|
|
|
|
assert_tree(box, expected)
|
2011-05-19 20:22:06 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_inline_in_block_3():
|
2012-06-01 19:08:54 +04:00
|
|
|
|
# Absolutes are left in the lines to get their static position later.
|
|
|
|
|
source = '''<p>Hello <em style="position:absolute;
|
|
|
|
|
display: block">World</em>!</p>'''
|
2012-05-09 16:14:42 +04:00
|
|
|
|
expected = [
|
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('p', 'Text', 'Hello '),
|
|
|
|
|
('em', 'Block', [
|
|
|
|
|
('em', 'Line', [
|
|
|
|
|
('em', 'Text', 'World')])]),
|
|
|
|
|
('p', 'Text', '!')])])]
|
2012-06-01 19:08:54 +04:00
|
|
|
|
box = parse(source)
|
|
|
|
|
box = build.inline_in_block(box)
|
|
|
|
|
assert_tree(box, expected)
|
|
|
|
|
box = build.block_in_inline(box)
|
|
|
|
|
assert_tree(box, expected)
|
2012-05-09 16:14:42 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_inline_in_block_4():
|
2012-06-15 19:59:15 +04:00
|
|
|
|
# Floats are pull to the top of their containing blocks
|
2012-06-01 19:08:54 +04:00
|
|
|
|
source = '<p>Hello <em style="float: left">World</em>!</p>'
|
2012-06-15 19:59:15 +04:00
|
|
|
|
box = parse(source)
|
|
|
|
|
box = build.inline_in_block(box)
|
|
|
|
|
box = build.block_in_inline(box)
|
|
|
|
|
assert_tree(box, [
|
2012-06-01 19:08:54 +04:00
|
|
|
|
('p', 'Block', [
|
2012-06-22 13:40:15 +04:00
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('p', 'Text', 'Hello '),
|
|
|
|
|
('em', 'Block', [
|
|
|
|
|
('em', 'Line', [
|
|
|
|
|
('em', 'Text', 'World')])]),
|
|
|
|
|
('p', 'Text', '!')])])])
|
2012-05-09 16:14:42 +04:00
|
|
|
|
|
2011-05-19 18:03:50 +04:00
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-05-19 20:58:39 +04:00
|
|
|
|
def test_block_in_inline():
|
2011-05-23 15:59:47 +04:00
|
|
|
|
box = parse('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
p { display: inline-block; }
|
|
|
|
|
span, i { display: block; }
|
|
|
|
|
</style>
|
|
|
|
|
<p>Lorem <em>ipsum <strong>dolor <span>sit</span>
|
|
|
|
|
<span>amet,</span></strong><span><em>conse<i>''')
|
2011-07-20 13:35:43 +04:00
|
|
|
|
box = build.inline_in_block(box)
|
2011-05-23 18:14:51 +04:00
|
|
|
|
assert_tree(box, [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('body', 'Line', [
|
|
|
|
|
('p', 'InlineBlock', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('p', 'Text', 'Lorem '),
|
|
|
|
|
('em', 'Inline', [
|
|
|
|
|
('em', 'Text', 'ipsum '),
|
|
|
|
|
('strong', 'Inline', [
|
|
|
|
|
('strong', 'Text', 'dolor '),
|
|
|
|
|
('span', 'Block', [ # This block is "pulled up"
|
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('span', 'Text', 'sit')])]),
|
2021-10-13 10:48:39 +03:00
|
|
|
|
('strong', 'Text', ' '),
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('span', 'Block', [ # This block is "pulled up"
|
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('span', 'Text', 'amet,')])])]),
|
|
|
|
|
('span', 'Block', [ # This block is "pulled up"
|
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('em', 'Inline', [
|
2012-12-26 19:43:10 +04:00
|
|
|
|
('em', 'Text', 'conse'),
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('i', 'Block', [])])])])])])])])])
|
2011-05-25 17:55:08 +04:00
|
|
|
|
|
2011-07-20 13:35:43 +04:00
|
|
|
|
box = build.block_in_inline(box)
|
2011-05-25 17:55:08 +04:00
|
|
|
|
assert_tree(box, [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('body', 'Line', [
|
|
|
|
|
('p', 'InlineBlock', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('p', 'Block', [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('p', 'Text', 'Lorem '),
|
|
|
|
|
('em', 'Inline', [
|
|
|
|
|
('em', 'Text', 'ipsum '),
|
|
|
|
|
('strong', 'Inline', [
|
|
|
|
|
('strong', 'Text', 'dolor ')])])])]),
|
|
|
|
|
('span', 'Block', [
|
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('span', 'Text', 'sit')])]),
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('p', 'Block', [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('em', 'Inline', [
|
|
|
|
|
('strong', 'Inline', [
|
2021-10-13 10:48:39 +03:00
|
|
|
|
('strong', 'Text', ' ')])])])]),
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('span', 'Block', [
|
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('span', 'Text', 'amet,')])]),
|
|
|
|
|
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('p', 'Block', [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('em', 'Inline', [
|
|
|
|
|
('strong', 'Inline', [])])])]),
|
|
|
|
|
('span', 'Block', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('span', 'Block', [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('em', 'Inline', [
|
2012-12-26 19:43:10 +04:00
|
|
|
|
('em', 'Text', 'conse')])])]),
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('i', 'Block', []),
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('span', 'Block', [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('em', 'Inline', [])])])]),
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('p', 'Block', [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('em', 'Inline', [])])])])])])
|
2011-05-19 18:03:50 +04:00
|
|
|
|
|
2011-05-23 15:59:47 +04:00
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-05-23 15:59:47 +04:00
|
|
|
|
def test_styles():
|
|
|
|
|
box = parse('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
span { display: block; }
|
|
|
|
|
* { margin: 42px }
|
|
|
|
|
html { color: blue }
|
|
|
|
|
</style>
|
|
|
|
|
<p>Lorem <em>ipsum <strong>dolor <span>sit</span>
|
|
|
|
|
<span>amet,</span></strong><span>consectetur</span></em></p>''')
|
2011-07-20 13:35:43 +04:00
|
|
|
|
box = build.inline_in_block(box)
|
|
|
|
|
box = build.block_in_inline(box)
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-10-08 16:41:12 +04:00
|
|
|
|
descendants = list(box.descendants())
|
|
|
|
|
assert len(descendants) == 31
|
|
|
|
|
assert descendants[0] == box
|
|
|
|
|
|
|
|
|
|
for child in descendants:
|
2011-05-23 15:59:47 +04:00
|
|
|
|
# All boxes inherit the color
|
2018-01-13 19:05:23 +03:00
|
|
|
|
assert child.style['color'] == (0, 0, 1, 1) # blue
|
2011-05-23 15:59:47 +04:00
|
|
|
|
# Only non-anonymous boxes have margins
|
2018-01-13 19:05:23 +03:00
|
|
|
|
assert child.style['margin_top'] in ((0, 'px'), (42, 'px'))
|
2011-05-23 15:59:47 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-05-23 19:37:37 +04:00
|
|
|
|
def test_whitespace():
|
|
|
|
|
# TODO: test more cases
|
|
|
|
|
# http://www.w3.org/TR/CSS21/text.html#white-space-model
|
2011-11-14 20:42:15 +04:00
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<p>Lorem \t\r\n ipsum\t<strong> dolor
|
|
|
|
|
<img src=pattern.png> sit
|
|
|
|
|
<span style="position: absolute"></span> <em> amet </em>
|
|
|
|
|
consectetur</strong>.</p>
|
|
|
|
|
<pre>\t foo\n</pre>
|
|
|
|
|
<pre style="white-space: pre-wrap">\t foo\n</pre>
|
|
|
|
|
<pre style="white-space: pre-line">\t foo\n</pre>
|
2011-11-14 20:42:15 +04:00
|
|
|
|
'''), [
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('p', 'Text', 'Lorem ipsum '),
|
|
|
|
|
('strong', 'Inline', [
|
2011-11-16 20:34:02 +04:00
|
|
|
|
('strong', 'Text', 'dolor '),
|
2011-12-05 17:24:43 +04:00
|
|
|
|
('img', 'InlineReplaced', '<replaced>'),
|
2012-06-28 15:05:22 +04:00
|
|
|
|
('strong', 'Text', ' sit '),
|
|
|
|
|
('span', 'Block', []),
|
|
|
|
|
('em', 'Inline', [
|
|
|
|
|
('em', 'Text', 'amet ')]),
|
|
|
|
|
('strong', 'Text', 'consectetur')]),
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('p', 'Text', '.')])]),
|
|
|
|
|
('pre', 'Block', [
|
|
|
|
|
('pre', 'Line', [
|
2011-05-23 19:37:37 +04:00
|
|
|
|
# pre
|
2016-05-08 17:44:57 +03:00
|
|
|
|
('pre', 'Text', '\t foo\n')])]),
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('pre', 'Block', [
|
|
|
|
|
('pre', 'Line', [
|
2011-05-23 19:37:37 +04:00
|
|
|
|
# pre-wrap
|
2016-05-08 17:44:57 +03:00
|
|
|
|
('pre', 'Text', '\t foo\n')])]),
|
2011-11-14 17:12:13 +04:00
|
|
|
|
('pre', 'Block', [
|
|
|
|
|
('pre', 'Line', [
|
2011-05-23 19:37:37 +04:00
|
|
|
|
# pre-line
|
2012-06-28 15:05:22 +04:00
|
|
|
|
('pre', 'Text', ' foo\n')])])])
|
2011-07-05 13:32:16 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2018-03-19 00:23:39 +03:00
|
|
|
|
@pytest.mark.parametrize('page_type, top, right, bottom, left', (
|
2019-04-02 13:39:00 +03:00
|
|
|
|
(PageType(side='left', first=True, index=0, blank=None, name=None),
|
2019-04-01 19:45:59 +03:00
|
|
|
|
20, 3, 3, 10),
|
2019-04-02 13:39:00 +03:00
|
|
|
|
(PageType(side='right', first=True, index=0, blank=None, name=None),
|
2019-04-01 19:45:59 +03:00
|
|
|
|
20, 10, 3, 3),
|
2019-04-02 13:39:00 +03:00
|
|
|
|
(PageType(side='left', first=None, index=1, blank=None, name=None),
|
2019-04-01 19:45:59 +03:00
|
|
|
|
10, 3, 3, 10),
|
2019-04-02 13:39:00 +03:00
|
|
|
|
(PageType(side='right', first=None, index=1, blank=None, name=None),
|
2018-03-19 00:23:39 +03:00
|
|
|
|
10, 10, 3, 3),
|
2019-04-02 17:36:56 +03:00
|
|
|
|
(PageType(side='right', first=None, index=1, blank=None, name='name'),
|
|
|
|
|
5, 10, 3, 15),
|
|
|
|
|
(PageType(side='right', first=None, index=2, blank=None, name='name'),
|
|
|
|
|
5, 10, 1, 15),
|
2019-04-02 17:57:45 +03:00
|
|
|
|
(PageType(side='right', first=None, index=8, blank=None, name='name'),
|
|
|
|
|
5, 10, 2, 15),
|
2018-03-19 00:23:39 +03:00
|
|
|
|
))
|
|
|
|
|
def test_page_style(page_type, top, right, bottom, left):
|
2017-07-18 00:40:59 +03:00
|
|
|
|
document = FakeHTML(string='''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page { margin: 3px }
|
2019-04-02 17:36:56 +03:00
|
|
|
|
@page name { margin-left: 15px; margin-top: 5px }
|
|
|
|
|
@page :nth(3) { margin-bottom: 1px }
|
2019-04-02 17:57:45 +03:00
|
|
|
|
@page :nth(5n+4) { margin-bottom: 2px }
|
2018-03-19 00:23:39 +03:00
|
|
|
|
@page :first { margin-top: 20px }
|
|
|
|
|
@page :right { margin-right: 10px; margin-top: 10px }
|
|
|
|
|
@page :left { margin-left: 10px; margin-top: 10px }
|
|
|
|
|
</style>
|
2017-07-18 00:40:59 +03:00
|
|
|
|
''')
|
2018-08-17 11:30:51 +03:00
|
|
|
|
style_for = get_all_computed_styles(document)
|
2011-08-24 13:36:10 +04:00
|
|
|
|
|
2019-04-02 13:00:47 +03:00
|
|
|
|
# Force the generation of the style for this page type as it's generally
|
|
|
|
|
# only done during the rendering.
|
|
|
|
|
set_page_type_computed_styles(page_type, document, style_for)
|
2017-07-18 00:40:59 +03:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
style = style_for(page_type)
|
|
|
|
|
assert style['margin_top'] == (top, 'px')
|
|
|
|
|
assert style['margin_right'] == (right, 'px')
|
|
|
|
|
assert style['margin_bottom'] == (bottom, 'px')
|
|
|
|
|
assert style['margin_left'] == (left, 'px')
|
2011-10-21 13:36:01 +04:00
|
|
|
|
|
|
|
|
|
|
2012-05-24 18:06:58 +04:00
|
|
|
|
@assert_no_logs
|
2018-03-19 00:23:39 +03:00
|
|
|
|
def test_images_1():
|
2012-05-24 18:06:58 +04:00
|
|
|
|
with capture_logs() as logs:
|
|
|
|
|
result = parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<p><img src=pattern.png
|
|
|
|
|
/><img alt="No src"
|
|
|
|
|
/><img src=inexistent.jpg alt="Inexistent src" /></p>
|
2012-05-24 18:06:58 +04:00
|
|
|
|
''')
|
|
|
|
|
assert len(logs) == 1
|
2017-07-25 14:59:56 +03:00
|
|
|
|
assert 'ERROR: Failed to load image' in logs[0]
|
2012-05-24 18:06:58 +04:00
|
|
|
|
assert 'inexistent.jpg' in logs[0]
|
|
|
|
|
assert_tree(result, [
|
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('img', 'InlineReplaced', '<replaced>'),
|
|
|
|
|
('img', 'Inline', [
|
|
|
|
|
('img', 'Text', 'No src')]),
|
|
|
|
|
('img', 'Inline', [
|
2013-04-11 14:08:53 +04:00
|
|
|
|
('img', 'Text', 'Inexistent src')])])])])
|
2012-05-24 18:06:58 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_images_2():
|
2012-05-24 18:06:58 +04:00
|
|
|
|
with capture_logs() as logs:
|
|
|
|
|
result = parse_all('<p><img src=pattern.png alt="No base_url">',
|
|
|
|
|
base_url=None)
|
|
|
|
|
assert len(logs) == 1
|
2017-07-25 14:59:56 +03:00
|
|
|
|
assert 'ERROR: Relative URI reference without a base URI' in logs[0]
|
2012-05-24 18:06:58 +04:00
|
|
|
|
assert_tree(result, [
|
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('img', 'Inline', [
|
|
|
|
|
('img', 'Text', 'No base_url')])])])])
|
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2018-03-19 00:23:39 +03:00
|
|
|
|
def test_tables_1():
|
2011-11-14 20:42:15 +04:00
|
|
|
|
# Rules in http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
2011-11-14 20:42:15 +04:00
|
|
|
|
# Rule 1.3
|
2011-11-15 21:22:00 +04:00
|
|
|
|
# Also table model: http://www.w3.org/TR/CSS21/tables.html#model
|
2011-11-14 20:42:15 +04:00
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<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>
|
2011-11-14 20:42:15 +04:00
|
|
|
|
'''), [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-table', 'Block', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-caption', 'TableCaption', [
|
|
|
|
|
('x-caption', 'Line', [
|
|
|
|
|
('x-caption', 'Text', 'top caption')])]),
|
|
|
|
|
('x-table', 'Table', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-table', 'TableColumnGroup', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-col', 'TableColumn', [])]),
|
|
|
|
|
('x-thead', 'TableRowGroup', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-thead', 'TableRow', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-th', 'TableCell', [])])]),
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-table', 'TableRowGroup', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('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', []),
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-table', 'TableRowGroup', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-tr', 'TableRow', [
|
|
|
|
|
('x-td', 'TableCell', [
|
|
|
|
|
('x-td', 'Line', [
|
|
|
|
|
('x-td', 'Text', 'baz')])])])]),
|
|
|
|
|
('x-tfoot', 'TableRowGroup', [])]),
|
|
|
|
|
('x-caption', 'TableCaption', [])])])
|
2011-11-14 17:29:40 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_tables_2():
|
2011-11-14 20:42:15 +04:00
|
|
|
|
# Rules 1.4 and 3.1
|
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<span style="display: table-cell">foo</span>
|
|
|
|
|
<span style="display: table-cell">bar</span>
|
2011-11-14 20:42:15 +04:00
|
|
|
|
'''), [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('body', 'Block', [
|
|
|
|
|
('body', 'Table', [
|
|
|
|
|
('body', 'TableRowGroup', [
|
|
|
|
|
('body', 'TableRow', [
|
2011-11-17 20:05:55 +04:00
|
|
|
|
('span', 'TableCell', [
|
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('span', 'Text', 'foo')])]),
|
|
|
|
|
('span', 'TableCell', [
|
|
|
|
|
('span', 'Line', [
|
|
|
|
|
('span', 'Text', 'bar')])])])])])])])
|
2011-11-14 20:42:15 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_tables_3():
|
2011-11-14 20:42:15 +04:00
|
|
|
|
# http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
|
|
|
|
|
# Rules 1.1 and 1.2
|
2011-12-14 21:33:55 +04:00
|
|
|
|
# Rule XXX (not in the spec): column groups have at least one column child
|
2011-11-14 20:42:15 +04:00
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<span style="display: table-column-group">
|
|
|
|
|
1
|
|
|
|
|
<em style="display: table-column">
|
|
|
|
|
2
|
|
|
|
|
<strong>3</strong>
|
|
|
|
|
</em>
|
|
|
|
|
<strong>4</strong>
|
|
|
|
|
</span>
|
|
|
|
|
<ins style="display: table-column-group"></ins>
|
2011-11-14 20:42:15 +04:00
|
|
|
|
'''), [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('body', 'Block', [
|
|
|
|
|
('body', 'Table', [
|
2011-11-15 21:22:00 +04:00
|
|
|
|
('span', 'TableColumnGroup', [
|
2011-12-14 21:33:55 +04:00
|
|
|
|
('em', 'TableColumn', [])]),
|
|
|
|
|
('ins', 'TableColumnGroup', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('ins', 'TableColumn', [])])])])])
|
2011-11-14 20:42:15 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_tables_4():
|
2011-11-14 20:42:15 +04:00
|
|
|
|
# Rules 2.1 then 2.3
|
2013-07-22 03:10:25 +04:00
|
|
|
|
assert_tree(parse_all('<x-table>foo <div></div></x-table>'), [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-table', 'Block', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-table', 'Table', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-table', 'TableRowGroup', [
|
|
|
|
|
('x-table', 'TableRow', [
|
|
|
|
|
('x-table', 'TableCell', [
|
|
|
|
|
('x-table', 'Block', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-table', 'Line', [
|
|
|
|
|
('x-table', 'Text', 'foo ')])]),
|
2011-11-17 20:05:55 +04:00
|
|
|
|
('div', 'Block', [])])])])])])])
|
2011-11-14 20:42:15 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_tables_5():
|
2011-11-14 20:42:15 +04:00
|
|
|
|
# Rule 2.2
|
2013-07-22 03:10:25 +04:00
|
|
|
|
assert_tree(parse_all('<x-thead style="display: table-header-group">'
|
|
|
|
|
'<div></div><x-td></x-td></x-thead>'), [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('body', 'Block', [
|
|
|
|
|
('body', 'Table', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-thead', 'TableRowGroup', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-thead', 'TableRow', [
|
|
|
|
|
('x-thead', 'TableCell', [
|
2011-11-15 21:22:00 +04:00
|
|
|
|
('div', 'Block', [])]),
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-td', 'TableCell', [])])])])])])
|
2011-11-14 20:42:15 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_tables_6():
|
2013-07-22 03:10:25 +04:00
|
|
|
|
# Rule 3.2
|
|
|
|
|
assert_tree(parse_all('<span><x-tr></x-tr></span>'), [
|
|
|
|
|
('body', 'Line', [
|
|
|
|
|
('span', 'Inline', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('span', 'InlineBlock', [
|
|
|
|
|
('span', 'InlineTable', [
|
|
|
|
|
('span', 'TableRowGroup', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-tr', 'TableRow', [])])])])])])])
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_tables_7():
|
2013-07-22 03:10:25 +04:00
|
|
|
|
# Rule 3.1
|
|
|
|
|
# Also, rule 1.3 does not apply: whitespace before and after is preserved
|
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<span>
|
|
|
|
|
<em style="display: table-cell"></em>
|
|
|
|
|
<em style="display: table-cell"></em>
|
|
|
|
|
</span>
|
2013-07-22 03:10:25 +04:00
|
|
|
|
'''), [
|
|
|
|
|
('body', 'Line', [
|
|
|
|
|
('span', 'Inline', [
|
|
|
|
|
# Whitespace is preserved in table handling, then collapsed
|
|
|
|
|
# into a single space.
|
|
|
|
|
('span', 'Text', ' '),
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('span', 'InlineBlock', [
|
|
|
|
|
('span', 'InlineTable', [
|
|
|
|
|
('span', 'TableRowGroup', [
|
|
|
|
|
('span', 'TableRow', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('em', 'TableCell', []),
|
|
|
|
|
('em', 'TableCell', [])])])])]),
|
|
|
|
|
('span', 'Text', ' ')])])])
|
2011-11-14 20:42:15 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_tables_8():
|
2011-11-14 20:42:15 +04:00
|
|
|
|
# Rule 3.2
|
2013-07-22 03:10:25 +04:00
|
|
|
|
assert_tree(parse_all('<x-tr></x-tr>\t<x-tr></x-tr>'), [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('body', 'Block', [
|
|
|
|
|
('body', 'Table', [
|
|
|
|
|
('body', 'TableRowGroup', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-tr', 'TableRow', []),
|
|
|
|
|
('x-tr', 'TableRow', [])])])])])
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_tables_9():
|
2013-07-22 03:10:25 +04:00
|
|
|
|
assert_tree(parse_all('<x-col></x-col>\n<x-colgroup></x-colgroup>'), [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('body', 'Block', [
|
|
|
|
|
('body', 'Table', [
|
|
|
|
|
('body', 'TableColumnGroup', [
|
2013-07-22 03:10:25 +04:00
|
|
|
|
('x-col', 'TableColumn', [])]),
|
|
|
|
|
('x-colgroup', 'TableColumnGroup', [
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('x-colgroup', 'TableColumn', [])])])])])
|
2011-11-15 21:58:43 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-11-15 21:58:43 +04:00
|
|
|
|
def test_table_style():
|
|
|
|
|
html = parse_all('<table style="margin: 1px; padding: 2px"></table>')
|
|
|
|
|
body, = html.children
|
|
|
|
|
wrapper, = body.children
|
|
|
|
|
table, = wrapper.children
|
|
|
|
|
assert isinstance(wrapper, boxes.BlockBox)
|
|
|
|
|
assert isinstance(table, boxes.TableBox)
|
2018-01-13 19:05:23 +03:00
|
|
|
|
assert wrapper.style['margin_top'] == (1, 'px')
|
|
|
|
|
assert wrapper.style['padding_top'] == (0, 'px')
|
|
|
|
|
assert table.style['margin_top'] == (0, 'px')
|
|
|
|
|
assert table.style['padding_top'] == (2, 'px')
|
2011-11-17 18:39:30 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-11-22 16:06:50 +04:00
|
|
|
|
def test_column_style():
|
2011-11-17 18:39:30 +04:00
|
|
|
|
html = parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<table>
|
|
|
|
|
<col span=3 style="width: 10px"></col>
|
|
|
|
|
<col span=2></col>
|
|
|
|
|
</table>
|
2011-11-17 18:39:30 +04:00
|
|
|
|
''')
|
|
|
|
|
body, = html.children
|
|
|
|
|
wrapper, = body.children
|
|
|
|
|
table, = wrapper.children
|
2011-11-17 20:05:55 +04:00
|
|
|
|
colgroup, = table.column_groups
|
2018-01-13 19:05:23 +03:00
|
|
|
|
widths = [col.style['width'] for col in colgroup.children]
|
2012-04-03 16:59:06 +04:00
|
|
|
|
assert widths == [(10, 'px'), (10, 'px'), (10, 'px'), 'auto', 'auto']
|
2011-11-17 20:05:55 +04:00
|
|
|
|
assert [col.grid_x for col in colgroup.children] == [0, 1, 2, 3, 4]
|
2011-11-17 18:39:30 +04:00
|
|
|
|
# copies, not the same box object
|
2011-11-17 20:05:55 +04:00
|
|
|
|
assert colgroup.children[0] is not colgroup.children[1]
|
2011-11-17 19:13:00 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-11-17 19:13:00 +04:00
|
|
|
|
def test_nested_grid_x():
|
|
|
|
|
html = parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<table>
|
|
|
|
|
<col span=2></col>
|
|
|
|
|
<colgroup span=2></colgroup>
|
|
|
|
|
<colgroup>
|
|
|
|
|
<col></col>
|
|
|
|
|
<col span=2></col>
|
|
|
|
|
</colgroup>
|
|
|
|
|
<col></col>
|
|
|
|
|
</table>
|
2011-11-17 19:13:00 +04:00
|
|
|
|
''')
|
|
|
|
|
body, = html.children
|
|
|
|
|
wrapper, = body.children
|
|
|
|
|
table, = wrapper.children
|
2011-11-17 20:05:55 +04:00
|
|
|
|
grid = [(colgroup.grid_x, [col.grid_x for col in colgroup.children])
|
2011-11-22 16:06:50 +04:00
|
|
|
|
for colgroup in table.column_groups]
|
2011-12-14 21:33:55 +04:00
|
|
|
|
assert grid == [(0, [0, 1]), (2, [2, 3]), (4, [4, 5, 6]), (7, [7])]
|
2011-11-21 16:51:22 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2018-03-19 00:23:39 +03:00
|
|
|
|
def test_colspan_rowspan_1():
|
|
|
|
|
# +---+---+---+
|
|
|
|
|
# | A | B | C | X
|
|
|
|
|
# +---+---+---+
|
|
|
|
|
# | D | E | X
|
|
|
|
|
# +---+---+ +---+
|
|
|
|
|
# | F ...| | | <-- overlap
|
|
|
|
|
# +---+---+---+ +
|
|
|
|
|
# | H | X X | G |
|
|
|
|
|
# +---+---+ + +
|
|
|
|
|
# | I | J | X | |
|
|
|
|
|
# +---+---+ +---+
|
|
|
|
|
|
|
|
|
|
# X: empty cells
|
2011-11-21 16:51:22 +04:00
|
|
|
|
html = parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>A <td>B <td>C
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>D <td colspan=2 rowspan=2>E
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td colspan=2>F <td rowspan=0>G
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>H
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>I <td>J
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
2011-11-21 16:51:22 +04:00
|
|
|
|
''')
|
|
|
|
|
body, = html.children
|
|
|
|
|
wrapper, = body.children
|
|
|
|
|
table, = wrapper.children
|
|
|
|
|
group, = table.children
|
|
|
|
|
assert [[c.grid_x for c in row.children] for row in group.children] == [
|
|
|
|
|
[0, 1, 2],
|
|
|
|
|
[0, 1],
|
2017-03-25 02:24:27 +03:00
|
|
|
|
[0, 3],
|
2011-11-21 16:51:22 +04:00
|
|
|
|
[0],
|
|
|
|
|
[0, 1],
|
|
|
|
|
]
|
|
|
|
|
assert [[c.colspan for c in row.children] for row in group.children] == [
|
|
|
|
|
[1, 1, 1],
|
|
|
|
|
[1, 2],
|
2017-03-25 02:24:27 +03:00
|
|
|
|
[2, 1],
|
2011-11-21 16:51:22 +04:00
|
|
|
|
[1],
|
|
|
|
|
[1, 1],
|
|
|
|
|
]
|
|
|
|
|
assert [[c.rowspan for c in row.children] for row in group.children] == [
|
|
|
|
|
[1, 1, 1],
|
|
|
|
|
[1, 2],
|
2017-03-25 02:24:27 +03:00
|
|
|
|
[1, 3],
|
2011-11-21 16:51:22 +04:00
|
|
|
|
[1],
|
|
|
|
|
[1, 1],
|
|
|
|
|
]
|
2011-12-02 19:51:41 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_colspan_rowspan_2():
|
2012-06-02 11:02:01 +04:00
|
|
|
|
# A cell box cannot extend beyond the last row box of a table.
|
|
|
|
|
html = parse_all('''
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td rowspan=5></td>
|
|
|
|
|
<td></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
''')
|
|
|
|
|
body, = html.children
|
|
|
|
|
wrapper, = body.children
|
|
|
|
|
table, = wrapper.children
|
|
|
|
|
group, = table.children
|
|
|
|
|
assert [[c.grid_x for c in row.children] for row in group.children] == [
|
|
|
|
|
[0, 1],
|
|
|
|
|
[1],
|
|
|
|
|
]
|
|
|
|
|
assert [[c.colspan for c in row.children] for row in group.children] == [
|
|
|
|
|
[1, 1],
|
|
|
|
|
[1],
|
|
|
|
|
]
|
|
|
|
|
assert [[c.rowspan for c in row.children] for row in group.children] == [
|
|
|
|
|
[2, 1], # Not 5
|
|
|
|
|
[1],
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2018-03-19 00:23:39 +03:00
|
|
|
|
def test_before_after_1():
|
2011-12-28 18:34:30 +04:00
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
p:before { content: normal }
|
|
|
|
|
div:before { content: none }
|
|
|
|
|
section::before { color: black }
|
|
|
|
|
</style>
|
|
|
|
|
<p></p>
|
|
|
|
|
<div></div>
|
|
|
|
|
<section></section>
|
2011-12-28 18:34:30 +04:00
|
|
|
|
'''), [
|
|
|
|
|
# No content in pseudo-element, no box generated
|
|
|
|
|
('p', 'Block', []),
|
|
|
|
|
('div', 'Block', []),
|
|
|
|
|
('section', 'Block', [])])
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_before_after_2():
|
2011-12-02 19:51:41 +04:00
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
p:before { content: 'a' 'b' }
|
|
|
|
|
p::after { content: 'd' 'e' }
|
|
|
|
|
</style>
|
|
|
|
|
<p> c </p>
|
2011-12-02 19:51:41 +04:00
|
|
|
|
'''), [
|
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('p::before', 'Inline', [
|
|
|
|
|
('p::before', 'Text', 'ab')]),
|
2011-12-02 19:51:41 +04:00
|
|
|
|
('p', 'Text', ' c '),
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('p::after', 'Inline', [
|
|
|
|
|
('p::after', 'Text', 'de')])])])])
|
2011-12-02 19:51:41 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_before_after_3():
|
2011-12-02 19:51:41 +04:00
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
a[href]:before { content: '[' attr(href) '] ' }
|
|
|
|
|
</style>
|
|
|
|
|
<p><a href="some url">some text</a></p>
|
2011-12-02 19:51:41 +04:00
|
|
|
|
'''), [
|
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('a', 'Inline', [
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('a::before', 'Inline', [
|
|
|
|
|
('a::before', 'Text', '[some url] ')]),
|
2011-12-02 19:51:41 +04:00
|
|
|
|
('a', 'Text', 'some text')])])])])
|
2011-12-02 21:02:58 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_before_after_4():
|
2012-02-17 21:49:58 +04:00
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
body { quotes: '«' '»' '“' '”' }
|
|
|
|
|
q:before { content: open-quote ' '}
|
|
|
|
|
q:after { content: ' ' close-quote }
|
|
|
|
|
</style>
|
|
|
|
|
<p><q>Lorem ipsum <q>dolor</q> sit amet</q></p>
|
2011-12-02 21:02:58 +04:00
|
|
|
|
'''), [
|
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
|
|
|
|
('q', 'Inline', [
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('q::before', 'Inline', [
|
|
|
|
|
('q::before', 'Text', '« ')]),
|
2011-12-02 21:02:58 +04:00
|
|
|
|
('q', 'Text', 'Lorem ipsum '),
|
|
|
|
|
('q', 'Inline', [
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('q::before', 'Inline', [
|
|
|
|
|
('q::before', 'Text', '“ ')]),
|
2011-12-02 21:02:58 +04:00
|
|
|
|
('q', 'Text', 'dolor'),
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('q::after', 'Inline', [
|
|
|
|
|
('q::after', 'Text', ' ”')])]),
|
2011-12-02 21:02:58 +04:00
|
|
|
|
('q', 'Text', ' sit amet'),
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('q::after', 'Inline', [
|
|
|
|
|
('q::after', 'Text', ' »')])])])])])
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_before_after_5():
|
2013-04-08 12:02:37 +04:00
|
|
|
|
with capture_logs() as logs:
|
|
|
|
|
assert_tree(parse_all('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
p:before {
|
|
|
|
|
content: 'a' url(pattern.png) 'b';
|
|
|
|
|
|
|
|
|
|
/* Invalid, ignored in favor of the one above.
|
|
|
|
|
Regression test: this used to crash: */
|
|
|
|
|
content: some-function(nested-function(something));
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<p>c</p>
|
2013-04-08 12:02:37 +04:00
|
|
|
|
'''), [
|
|
|
|
|
('p', 'Block', [
|
|
|
|
|
('p', 'Line', [
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('p::before', 'Inline', [
|
|
|
|
|
('p::before', 'Text', 'a'),
|
2018-01-13 18:34:14 +03:00
|
|
|
|
('p::before', 'InlineReplaced', '<replaced>'),
|
2016-12-04 18:43:49 +03:00
|
|
|
|
('p::before', 'Text', 'b')]),
|
2013-04-08 12:02:37 +04:00
|
|
|
|
('p', 'Text', 'c')])])])
|
|
|
|
|
assert len(logs) == 1
|
|
|
|
|
assert 'nested-function(' in logs[0]
|
|
|
|
|
assert 'invalid value' in logs[0]
|
2011-12-07 20:09:59 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-12-28 18:34:30 +04:00
|
|
|
|
def test_margin_boxes():
|
2012-09-25 18:01:47 +04:00
|
|
|
|
page_1, page_2 = render_pages('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
/* Make the page content area only 10px high and wide,
|
|
|
|
|
so every word in <p> end up on a page of its own. */
|
|
|
|
|
size: 30px;
|
|
|
|
|
margin: 10px;
|
|
|
|
|
@top-center { content: "Title" }
|
|
|
|
|
}
|
|
|
|
|
@page :first {
|
|
|
|
|
@bottom-left { content: "foo" }
|
|
|
|
|
@bottom-left-corner { content: "baz" }
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<p>lorem ipsum
|
2011-12-28 18:34:30 +04:00
|
|
|
|
''')
|
|
|
|
|
assert page_1.children[0].element_tag == 'html'
|
|
|
|
|
assert page_2.children[0].element_tag == 'html'
|
|
|
|
|
|
|
|
|
|
margin_boxes_1 = [box.at_keyword for box in page_1.children[1:]]
|
|
|
|
|
margin_boxes_2 = [box.at_keyword for box in page_2.children[1:]]
|
2012-07-02 20:15:57 +04:00
|
|
|
|
assert margin_boxes_1 == ['@top-center', '@bottom-left',
|
|
|
|
|
'@bottom-left-corner']
|
2011-12-28 18:34:30 +04:00
|
|
|
|
assert margin_boxes_2 == ['@top-center']
|
|
|
|
|
|
|
|
|
|
html, top_center = page_2.children
|
|
|
|
|
line_box, = top_center.children
|
|
|
|
|
text_box, = line_box.children
|
|
|
|
|
assert text_box.text == 'Title'
|
2012-01-17 18:40:26 +04:00
|
|
|
|
|
2015-03-21 07:50:22 +03:00
|
|
|
|
|
2015-03-09 06:02:51 +03:00
|
|
|
|
@assert_no_logs
|
2018-03-19 00:23:39 +03:00
|
|
|
|
def test_margin_box_string_set_1():
|
2015-03-21 07:08:12 +03:00
|
|
|
|
# Test that both pages get string in the `bottom-center` margin box
|
2015-03-19 05:46:51 +03:00
|
|
|
|
page_1, page_2 = render_pages('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
@bottom-center { content: string(text_header) }
|
|
|
|
|
}
|
|
|
|
|
p {
|
|
|
|
|
string-set: text_header content();
|
|
|
|
|
}
|
|
|
|
|
.page {
|
|
|
|
|
page-break-before: always;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<p>first assignment</p>
|
|
|
|
|
<div class="page"></div>
|
2015-03-19 05:46:51 +03:00
|
|
|
|
''')
|
|
|
|
|
|
2015-03-21 07:08:12 +03:00
|
|
|
|
html, bottom_center = page_2.children
|
|
|
|
|
line_box, = bottom_center.children
|
2015-03-19 05:46:51 +03:00
|
|
|
|
text_box, = line_box.children
|
|
|
|
|
assert text_box.text == 'first assignment'
|
|
|
|
|
|
2015-03-21 07:08:12 +03:00
|
|
|
|
html, bottom_center = page_1.children
|
|
|
|
|
line_box, = bottom_center.children
|
2015-03-09 06:02:51 +03:00
|
|
|
|
text_box, = line_box.children
|
|
|
|
|
assert text_box.text == 'first assignment'
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_margin_box_string_set_2():
|
2015-03-22 01:54:14 +03:00
|
|
|
|
def simple_string_set_test(content_val, extra_style=""):
|
|
|
|
|
page_1, = render_pages('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
@top-center { content: string(text_header) }
|
|
|
|
|
}
|
|
|
|
|
p {
|
|
|
|
|
string-set: text_header content(%(content_val)s);
|
|
|
|
|
}
|
|
|
|
|
%(extra_style)s
|
|
|
|
|
</style>
|
|
|
|
|
<p>first assignment</p>
|
2015-03-22 07:30:38 +03:00
|
|
|
|
''' % dict(content_val=content_val, extra_style=extra_style))
|
2015-03-22 01:54:14 +03:00
|
|
|
|
|
|
|
|
|
html, top_center = page_1.children
|
|
|
|
|
line_box, = top_center.children
|
|
|
|
|
text_box, = line_box.children
|
2015-03-22 02:24:13 +03:00
|
|
|
|
if content_val in ('before', 'after'):
|
2015-04-30 22:54:19 +03:00
|
|
|
|
assert text_box.text == 'pseudo'
|
2015-03-22 01:54:14 +03:00
|
|
|
|
else:
|
|
|
|
|
assert text_box.text == 'first assignment'
|
|
|
|
|
|
|
|
|
|
# Test each accepted value of `content()` as an arguemnt to `string-set`
|
|
|
|
|
for value in ('', 'text', 'before', 'after'):
|
2015-03-22 02:24:13 +03:00
|
|
|
|
if value in ('before', 'after'):
|
2020-05-30 16:48:24 +03:00
|
|
|
|
extra_style = 'p:%s{content: "pseudo"}' % value
|
2015-03-22 01:54:14 +03:00
|
|
|
|
simple_string_set_test(value, extra_style)
|
|
|
|
|
else:
|
|
|
|
|
simple_string_set_test(value)
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_margin_box_string_set_3():
|
2015-03-21 07:08:12 +03:00
|
|
|
|
# Test `first` (default value) ie. use the first assignment on the page
|
2015-03-20 06:03:57 +03:00
|
|
|
|
page_1, = render_pages('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
@top-center { content: string(text_header, first) }
|
|
|
|
|
}
|
|
|
|
|
p {
|
|
|
|
|
string-set: text_header content();
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<p>first assignment</p>
|
|
|
|
|
<p>Second assignment</p>
|
2015-03-09 06:02:51 +03:00
|
|
|
|
''')
|
|
|
|
|
|
2015-03-20 06:03:57 +03:00
|
|
|
|
html, top_center = page_1.children
|
2015-03-09 06:02:51 +03:00
|
|
|
|
line_box, = top_center.children
|
|
|
|
|
text_box, = line_box.children
|
2015-03-20 06:03:57 +03:00
|
|
|
|
assert text_box.text == 'first assignment'
|
2015-03-09 06:02:51 +03:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_margin_box_string_set_4():
|
2015-03-20 06:03:57 +03:00
|
|
|
|
# test `first-except` ie. exclude from page on which value is assigned
|
2015-03-09 06:02:51 +03:00
|
|
|
|
page_1, page_2 = render_pages('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
@top-center { content: string(header_nofirst, first-except) }
|
|
|
|
|
}
|
|
|
|
|
p{
|
|
|
|
|
string-set: header_nofirst content();
|
|
|
|
|
}
|
|
|
|
|
.page{
|
|
|
|
|
page-break-before: always;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<p>first_excepted</p>
|
|
|
|
|
<div class="page"></div>
|
2015-03-09 06:02:51 +03:00
|
|
|
|
''')
|
|
|
|
|
html, top_center = page_1.children
|
|
|
|
|
assert len(top_center.children) == 0
|
|
|
|
|
|
|
|
|
|
html, top_center = page_2.children
|
|
|
|
|
line_box, = top_center.children
|
|
|
|
|
text_box, = line_box.children
|
2015-05-01 21:28:42 +03:00
|
|
|
|
assert text_box.text == 'first_excepted'
|
2015-03-09 06:02:51 +03:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_margin_box_string_set_5():
|
2015-03-21 07:50:22 +03:00
|
|
|
|
# Test `last` ie. use the most-recent assignment
|
2015-03-09 06:02:51 +03:00
|
|
|
|
page_1, = render_pages('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
@top-center { content: string(header_last, last) }
|
|
|
|
|
}
|
|
|
|
|
p {
|
|
|
|
|
string-set: header_last content();
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<p>String set</p>
|
|
|
|
|
<p>Second assignment</p>
|
2015-03-09 06:02:51 +03:00
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
html, top_center = page_1.children[:2]
|
|
|
|
|
line_box, = top_center.children
|
|
|
|
|
|
|
|
|
|
text_box, = line_box.children
|
2015-05-01 21:28:42 +03:00
|
|
|
|
assert text_box.text == 'Second assignment'
|
2012-01-17 18:40:26 +04:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_margin_box_string_set_6():
|
2015-04-30 22:54:19 +03:00
|
|
|
|
# Test multiple complex string-set values
|
|
|
|
|
page_1, = render_pages('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
@top-center { content: string(text_header, first) }
|
|
|
|
|
@bottom-center { content: string(text_footer, last) }
|
|
|
|
|
}
|
|
|
|
|
html { counter-reset: a }
|
|
|
|
|
body { counter-increment: a }
|
|
|
|
|
ul { counter-reset: b }
|
|
|
|
|
li {
|
|
|
|
|
counter-increment: b;
|
|
|
|
|
string-set:
|
|
|
|
|
text_header content(before) "-" content() "-" content(after)
|
|
|
|
|
counter(a, upper-roman) '.' counters(b, '|'),
|
|
|
|
|
text_footer content(before) '-' attr(class)
|
|
|
|
|
counters(b, '|') "/" counter(a, upper-roman);
|
|
|
|
|
}
|
|
|
|
|
li:before { content: 'before!' }
|
|
|
|
|
li:after { content: 'after!' }
|
|
|
|
|
li:last-child:before { content: 'before!last' }
|
|
|
|
|
li:last-child:after { content: 'after!last' }
|
|
|
|
|
</style>
|
|
|
|
|
<ul>
|
|
|
|
|
<li class="firstclass">first
|
|
|
|
|
<li>
|
|
|
|
|
<ul>
|
|
|
|
|
<li class="secondclass">second
|
2015-04-30 22:54:19 +03:00
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
html, top_center, bottom_center = page_1.children
|
|
|
|
|
top_line_box, = top_center.children
|
|
|
|
|
top_text_box, = top_line_box.children
|
2015-05-01 21:28:42 +03:00
|
|
|
|
assert top_text_box.text == 'before!-first-after!I.1'
|
2015-04-30 22:54:19 +03:00
|
|
|
|
bottom_line_box, = bottom_center.children
|
|
|
|
|
bottom_text_box, = bottom_line_box.children
|
2015-05-01 21:28:42 +03:00
|
|
|
|
assert bottom_text_box.text == 'before!last-secondclass2|1/I'
|
2015-04-30 22:54:19 +03:00
|
|
|
|
|
2015-03-21 07:50:22 +03:00
|
|
|
|
|
2018-11-02 16:32:14 +03:00
|
|
|
|
def test_margin_box_string_set_7():
|
|
|
|
|
# Test regression: https://github.com/Kozea/WeasyPrint/issues/722
|
|
|
|
|
page_1, = render_pages('''
|
|
|
|
|
<style>
|
2019-03-18 15:48:59 +03:00
|
|
|
|
img { string-set: left attr(alt) }
|
2018-11-02 16:32:14 +03:00
|
|
|
|
img + img { string-set: right attr(alt) }
|
|
|
|
|
@page { @top-left { content: '[' string(left) ']' }
|
|
|
|
|
@top-right { content: '{' string(right) '}' } }
|
|
|
|
|
</style>
|
|
|
|
|
<img src=pattern.png alt="Chocolate">
|
|
|
|
|
<img src=no_such_file.png alt="Cake">
|
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
html, top_left, top_right = page_1.children
|
|
|
|
|
left_line_box, = top_left.children
|
|
|
|
|
left_text_box, = left_line_box.children
|
|
|
|
|
assert left_text_box.text == '[Chocolate]'
|
|
|
|
|
right_line_box, = top_right.children
|
|
|
|
|
right_text_box, = right_line_box.children
|
|
|
|
|
assert right_text_box.text == '{Cake}'
|
|
|
|
|
|
|
|
|
|
|
2019-03-18 15:48:59 +03:00
|
|
|
|
@assert_no_logs
|
2018-11-02 19:47:21 +03:00
|
|
|
|
def test_margin_box_string_set_8():
|
|
|
|
|
# Test regression: https://github.com/Kozea/WeasyPrint/issues/726
|
|
|
|
|
page_1, page_2, page_3 = render_pages('''
|
|
|
|
|
<style>
|
|
|
|
|
@page { @top-left { content: '[' string(left) ']' } }
|
|
|
|
|
p { page-break-before: always }
|
2019-03-18 15:48:59 +03:00
|
|
|
|
.initial { string-set: left 'initial' }
|
|
|
|
|
.empty { string-set: left '' }
|
|
|
|
|
.space { string-set: left ' ' }
|
2018-11-02 19:47:21 +03:00
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<p class="initial">Initial</p>
|
|
|
|
|
<p class="empty">Empty</p>
|
|
|
|
|
<p class="space">Space</p>
|
|
|
|
|
''')
|
|
|
|
|
html, top_left = page_1.children
|
|
|
|
|
left_line_box, = top_left.children
|
|
|
|
|
left_text_box, = left_line_box.children
|
|
|
|
|
assert left_text_box.text == '[initial]'
|
|
|
|
|
|
|
|
|
|
html, top_left = page_2.children
|
|
|
|
|
left_line_box, = top_left.children
|
|
|
|
|
left_text_box, = left_line_box.children
|
|
|
|
|
assert left_text_box.text == '[]'
|
|
|
|
|
|
|
|
|
|
html, top_left = page_3.children
|
|
|
|
|
left_line_box, = top_left.children
|
|
|
|
|
left_text_box, = left_line_box.children
|
|
|
|
|
assert left_text_box.text == '[ ]'
|
|
|
|
|
|
|
|
|
|
|
2019-03-18 16:05:55 +03:00
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_margin_box_string_set_9():
|
|
|
|
|
# Test that named strings are case-sensitive
|
|
|
|
|
# See https://github.com/Kozea/WeasyPrint/pull/827
|
|
|
|
|
page_1, = render_pages('''
|
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
@top-center {
|
|
|
|
|
content: string(text_header, first)
|
|
|
|
|
' ' string(TEXT_header, first)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p { string-set: text_header content() }
|
|
|
|
|
div { string-set: TEXT_header content() }
|
|
|
|
|
</style>
|
|
|
|
|
<p>first assignment</p>
|
|
|
|
|
<div>second assignment</div>
|
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
html, top_center = page_1.children
|
|
|
|
|
line_box, = top_center.children
|
|
|
|
|
text_box, = line_box.children
|
|
|
|
|
assert text_box.text == 'first assignment second assignment'
|
|
|
|
|
|
|
|
|
|
|
2022-04-18 14:08:32 +03:00
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_margin_box_string_set_10():
|
|
|
|
|
page_1, page_2, page_3, page_4 = render_pages('''
|
|
|
|
|
<style>
|
|
|
|
|
@page { @top-left { content: '[' string(p, start) ']' } }
|
|
|
|
|
p { string-set: p content(); page-break-after: always }
|
|
|
|
|
</style>
|
|
|
|
|
<article></article>
|
|
|
|
|
<p>1</p>
|
|
|
|
|
<article></article>
|
|
|
|
|
<p>2</p>
|
|
|
|
|
<p>3</p>
|
|
|
|
|
<article></article>
|
|
|
|
|
''')
|
|
|
|
|
html, top_left = page_1.children
|
|
|
|
|
left_line_box, = top_left.children
|
|
|
|
|
left_text_box, = left_line_box.children
|
|
|
|
|
assert left_text_box.text == '[]'
|
|
|
|
|
|
|
|
|
|
html, top_left = page_2.children
|
|
|
|
|
left_line_box, = top_left.children
|
|
|
|
|
left_text_box, = left_line_box.children
|
|
|
|
|
assert left_text_box.text == '[1]'
|
|
|
|
|
|
|
|
|
|
html, top_left = page_3.children
|
|
|
|
|
left_line_box, = top_left.children
|
|
|
|
|
left_text_box, = left_line_box.children
|
|
|
|
|
assert left_text_box.text == '[3]'
|
|
|
|
|
|
|
|
|
|
html, top_left = page_4.children
|
|
|
|
|
left_line_box, = top_left.children
|
|
|
|
|
left_text_box, = left_line_box.children
|
|
|
|
|
assert left_text_box.text == '[3]'
|
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2012-01-17 18:40:26 +04:00
|
|
|
|
def test_page_counters():
|
|
|
|
|
"""Test page-based counters."""
|
2012-09-25 18:01:47 +04:00
|
|
|
|
pages = render_pages('''
|
2018-03-19 00:23:39 +03:00
|
|
|
|
<style>
|
|
|
|
|
@page {
|
|
|
|
|
/* Make the page content area only 10px high and wide,
|
|
|
|
|
so every word in <p> end up on a page of its own. */
|
|
|
|
|
size: 30px;
|
|
|
|
|
margin: 10px;
|
|
|
|
|
@bottom-center {
|
|
|
|
|
content: "Page " counter(page) " of " counter(pages) ".";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<p>lorem ipsum dolor
|
2012-01-17 18:40:26 +04:00
|
|
|
|
''')
|
2012-09-25 18:01:47 +04:00
|
|
|
|
for page_number, page in enumerate(pages, 1):
|
2012-01-17 18:40:26 +04:00
|
|
|
|
html, bottom_center = page.children
|
|
|
|
|
line_box, = bottom_center.children
|
|
|
|
|
text_box, = line_box.children
|
2012-02-21 17:13:34 +04:00
|
|
|
|
assert text_box.text == 'Page {0} of 3.'.format(page_number)
|
2012-07-11 16:11:26 +04:00
|
|
|
|
|
2015-03-21 07:50:22 +03:00
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
black = (0, 0, 0, 1)
|
|
|
|
|
red = (1, 0, 0, 1)
|
|
|
|
|
green = (0, 1, 0, 1) # lime in CSS
|
|
|
|
|
blue = (0, 0, 1, 1)
|
|
|
|
|
yellow = (1, 1, 0, 1)
|
|
|
|
|
black_3 = ('solid', 3, black)
|
|
|
|
|
red_1 = ('solid', 1, red)
|
|
|
|
|
yellow_5 = ('solid', 5, yellow)
|
|
|
|
|
green_5 = ('solid', 5, green)
|
|
|
|
|
dashed_blue_5 = ('dashed', 5, blue)
|
|
|
|
|
|
|
|
|
|
|
2012-07-11 16:11:26 +04:00
|
|
|
|
@assert_no_logs
|
2018-03-19 00:23:39 +03:00
|
|
|
|
def test_border_collapse_1():
|
2012-07-11 16:11:26 +04:00
|
|
|
|
html = parse_all('<table></table>')
|
|
|
|
|
body, = html.children
|
|
|
|
|
table_wrapper, = body.children
|
|
|
|
|
table, = table_wrapper.children
|
|
|
|
|
assert isinstance(table, boxes.TableBox)
|
|
|
|
|
assert not hasattr(table, 'collapsed_border_grid')
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
grid = _get_grid('<table style="border-collapse: collapse"></table>')
|
2012-07-11 16:11:26 +04:00
|
|
|
|
assert grid == ([], [])
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_border_collapse_2():
|
|
|
|
|
vertical_borders, horizontal_borders = _get_grid('''
|
|
|
|
|
<style>td { border: 1px solid red }</style>
|
|
|
|
|
<table style="border-collapse: collapse; border: 3px solid black">
|
|
|
|
|
<tr> <td>A</td> <td>B</td> </tr>
|
|
|
|
|
<tr> <td>C</td> <td>D</td> </tr>
|
|
|
|
|
</table>
|
2012-07-11 16:11:26 +04:00
|
|
|
|
''')
|
|
|
|
|
assert vertical_borders == [
|
|
|
|
|
[black_3, red_1, black_3],
|
|
|
|
|
[black_3, red_1, black_3],
|
|
|
|
|
]
|
|
|
|
|
assert horizontal_borders == [
|
|
|
|
|
[black_3, black_3],
|
|
|
|
|
[red_1, red_1],
|
|
|
|
|
[black_3, black_3],
|
|
|
|
|
]
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_border_collapse_3():
|
2012-07-11 16:11:26 +04:00
|
|
|
|
# hidden vs. none
|
2018-03-19 00:23:39 +03:00
|
|
|
|
vertical_borders, horizontal_borders = _get_grid('''
|
|
|
|
|
<style>table, td { border: 3px solid }</style>
|
|
|
|
|
<table style="border-collapse: collapse">
|
|
|
|
|
<tr> <td>A</td> <td style="border-style: hidden">B</td> </tr>
|
|
|
|
|
<tr> <td>C</td> <td style="border-style: none">D</td> </tr>
|
|
|
|
|
</table>
|
2012-07-11 16:11:26 +04:00
|
|
|
|
''')
|
|
|
|
|
assert vertical_borders == [
|
|
|
|
|
[black_3, None, None],
|
|
|
|
|
[black_3, black_3, black_3],
|
|
|
|
|
]
|
|
|
|
|
assert horizontal_borders == [
|
|
|
|
|
[black_3, None],
|
|
|
|
|
[black_3, None],
|
|
|
|
|
[black_3, black_3],
|
|
|
|
|
]
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_border_collapse_4():
|
|
|
|
|
vertical_borders, horizontal_borders = _get_grid('''
|
|
|
|
|
<style>td { border: 1px solid red }</style>
|
|
|
|
|
<table style="border-collapse: collapse; border: 5px solid yellow">
|
|
|
|
|
<col style="border: 3px solid black" />
|
|
|
|
|
<tr> <td></td> <td></td> <td></td> </tr>
|
|
|
|
|
<tr> <td></td> <td style="border: 5px dashed blue"></td>
|
|
|
|
|
<td style="border: 5px solid lime"></td> </tr>
|
|
|
|
|
<tr> <td></td> <td></td> <td></td> </tr>
|
|
|
|
|
<tr> <td></td> <td></td> <td></td> </tr>
|
|
|
|
|
</table>
|
2012-07-11 16:11:26 +04:00
|
|
|
|
''')
|
|
|
|
|
assert vertical_borders == [
|
|
|
|
|
[yellow_5, black_3, red_1, yellow_5],
|
|
|
|
|
[yellow_5, dashed_blue_5, green_5, green_5],
|
|
|
|
|
[yellow_5, black_3, red_1, yellow_5],
|
|
|
|
|
[yellow_5, black_3, red_1, yellow_5],
|
|
|
|
|
]
|
|
|
|
|
assert horizontal_borders == [
|
|
|
|
|
[yellow_5, yellow_5, yellow_5],
|
|
|
|
|
[red_1, dashed_blue_5, green_5],
|
|
|
|
|
[red_1, dashed_blue_5, green_5],
|
|
|
|
|
[red_1, red_1, red_1],
|
|
|
|
|
[yellow_5, yellow_5, yellow_5],
|
|
|
|
|
]
|
|
|
|
|
|
2018-03-19 00:23:39 +03:00
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
def test_border_collapse_5():
|
2012-07-11 16:11:26 +04:00
|
|
|
|
# rowspan and colspan
|
2018-03-19 00:23:39 +03:00
|
|
|
|
vertical_borders, horizontal_borders = _get_grid('''
|
2012-07-11 16:11:26 +04:00
|
|
|
|
<style>col, tr { border: 3px solid }</style>
|
|
|
|
|
<table style="border-collapse: collapse">
|
|
|
|
|
<col /><col /><col />
|
|
|
|
|
<tr> <td rowspan=2></td> <td></td> <td></td> </tr>
|
|
|
|
|
<tr> <td colspan=2></td> </tr>
|
|
|
|
|
</table>
|
|
|
|
|
''')
|
|
|
|
|
assert vertical_borders == [
|
|
|
|
|
[black_3, black_3, black_3, black_3],
|
|
|
|
|
[black_3, black_3, None, black_3],
|
|
|
|
|
]
|
|
|
|
|
assert horizontal_borders == [
|
|
|
|
|
[black_3, black_3, black_3],
|
|
|
|
|
[None, black_3, black_3],
|
|
|
|
|
[black_3, black_3, black_3],
|
|
|
|
|
]
|
2019-07-18 12:01:16 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@assert_no_logs
|
|
|
|
|
@pytest.mark.parametrize('html', (
|
|
|
|
|
'<html style="display: none">',
|
|
|
|
|
'<html style="display: none">abc',
|
|
|
|
|
'<html style="display: none"><p>abc',
|
|
|
|
|
'<body style="display: none"><p>abc',
|
|
|
|
|
))
|
|
|
|
|
def test_display_none_root(html):
|
|
|
|
|
box = parse_all(html)
|
2021-08-23 22:54:28 +03:00
|
|
|
|
assert box.style['display'] == ('block', 'flow')
|
2019-07-18 12:01:16 +03:00
|
|
|
|
assert not box.children
|