mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-04 07:57:52 +03:00
624 lines
19 KiB
Python
624 lines
19 KiB
Python
"""
|
||
weasyprint.tests.test_counters
|
||
------------------------------
|
||
|
||
Test CSS counters.
|
||
|
||
"""
|
||
|
||
import pytest
|
||
from weasyprint import HTML
|
||
|
||
from .testing_utils import assert_no_logs, assert_tree, parse_all, render_pages
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counters_1():
|
||
assert_tree(parse_all('''
|
||
<style>
|
||
p { counter-increment: p 2 }
|
||
p:before { content: counter(p); }
|
||
p:nth-child(1) { counter-increment: none; }
|
||
p:nth-child(2) { counter-increment: p; }
|
||
</style>
|
||
<p></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p style="counter-reset: p 117 p"></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p style="counter-reset: p -13"></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p style="counter-reset: p 42"></p>
|
||
<p></p>
|
||
<p></p>'''), [
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::before', 'Inline', [
|
||
('p::before', 'Text', counter)])])])
|
||
for counter in '0 1 3 2 4 6 -11 -9 -7 44 46 48'.split()])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counters_2():
|
||
assert_tree(parse_all('''
|
||
<ol style="list-style-position: inside">
|
||
<li></li>
|
||
<li></li>
|
||
<li></li>
|
||
<li><ol>
|
||
<li></li>
|
||
<li style="counter-increment: none"></li>
|
||
<li></li>
|
||
</ol></li>
|
||
<li></li>
|
||
</ol>'''), [
|
||
('ol', 'Block', [
|
||
('li', 'Block', [
|
||
('li', 'Line', [
|
||
('li::marker', 'Inline', [
|
||
('li::marker', 'Text', '1. ')])])]),
|
||
('li', 'Block', [
|
||
('li', 'Line', [
|
||
('li::marker', 'Inline', [
|
||
('li::marker', 'Text', '2. ')])])]),
|
||
('li', 'Block', [
|
||
('li', 'Line', [
|
||
('li::marker', 'Inline', [
|
||
('li::marker', 'Text', '3. ')])])]),
|
||
('li', 'Block', [
|
||
('li', 'Block', [
|
||
('li', 'Line', [
|
||
('li::marker', 'Inline', [
|
||
('li::marker', 'Text', '4. ')])])]),
|
||
('ol', 'Block', [
|
||
('li', 'Block', [
|
||
('li', 'Line', [
|
||
('li::marker', 'Inline', [
|
||
('li::marker', 'Text', '1. ')])])]),
|
||
('li', 'Block', [
|
||
('li', 'Line', [
|
||
('li::marker', 'Inline', [
|
||
('li::marker', 'Text', '1. ')])])]),
|
||
('li', 'Block', [
|
||
('li', 'Line', [
|
||
('li::marker', 'Inline', [
|
||
('li::marker', 'Text', '2. ')])])])])]),
|
||
('li', 'Block', [
|
||
('li', 'Line', [
|
||
('li::marker', 'Inline', [
|
||
('li::marker', 'Text', '5. ')])])])])])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counters_3():
|
||
assert_tree(parse_all('''
|
||
<style>
|
||
p { display: list-item; list-style: inside decimal }
|
||
</style>
|
||
<div>
|
||
<p></p>
|
||
<p></p>
|
||
<p style="counter-reset: list-item 7 list-item -56"></p>
|
||
</div>
|
||
<p></p>'''), [
|
||
('div', 'Block', [
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::marker', 'Inline', [
|
||
('p::marker', 'Text', '1. ')])])]),
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::marker', 'Inline', [
|
||
('p::marker', 'Text', '2. ')])])]),
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::marker', 'Inline', [
|
||
('p::marker', 'Text', '-55. ')])])])]),
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::marker', 'Inline', [
|
||
('p::marker', 'Text', '1. ')])])])])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counters_4():
|
||
assert_tree(parse_all('''
|
||
<style>
|
||
section:before { counter-reset: h; content: '' }
|
||
h1:before { counter-increment: h; content: counters(h, '.') }
|
||
</style>
|
||
<body>
|
||
<section><h1></h1>
|
||
<h1></h1>
|
||
<section><h1></h1>
|
||
<h1></h1>
|
||
</section>
|
||
<h1></h1>
|
||
</section>
|
||
</body>'''), [
|
||
('section', 'Block', [
|
||
('section', 'Block', [
|
||
('section', 'Line', [
|
||
('section::before', 'Inline', [])])]),
|
||
('h1', 'Block', [
|
||
('h1', 'Line', [
|
||
('h1::before', 'Inline', [
|
||
('h1::before', 'Text', '1')])])]),
|
||
('h1', 'Block', [
|
||
('h1', 'Line', [
|
||
('h1::before', 'Inline', [
|
||
('h1::before', 'Text', '2')])])]),
|
||
('section', 'Block', [
|
||
('section', 'Block', [
|
||
('section', 'Line', [
|
||
('section::before', 'Inline', [])])]),
|
||
('h1', 'Block', [
|
||
('h1', 'Line', [
|
||
('h1::before', 'Inline', [
|
||
('h1::before', 'Text', '2.1')])])]),
|
||
('h1', 'Block', [
|
||
('h1', 'Line', [
|
||
('h1::before', 'Inline', [
|
||
('h1::before', 'Text', '2.2')])])])]),
|
||
('h1', 'Block', [
|
||
('h1', 'Line', [
|
||
('h1::before', 'Inline', [
|
||
('h1::before', 'Text', '3')])])])])])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counters_5():
|
||
assert_tree(parse_all('''
|
||
<style>
|
||
p:before { content: counter(c) }
|
||
</style>
|
||
<div>
|
||
<span style="counter-reset: c">
|
||
Scope created now, deleted after the div
|
||
</span>
|
||
</div>
|
||
<p></p>'''), [
|
||
('div', 'Block', [
|
||
('div', 'Line', [
|
||
('span', 'Inline', [
|
||
('span', 'Text',
|
||
'Scope created now, deleted after the div ')])])]),
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::before', 'Inline', [
|
||
('p::before', 'Text', '0')])])])])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counters_6():
|
||
# counter-increment may interfere with display: list-item
|
||
assert_tree(parse_all('''
|
||
<p style="counter-increment: c;
|
||
display: list-item; list-style: inside decimal">'''), [
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::marker', 'Inline', [
|
||
('p::marker', 'Text', '0. ')])])])])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counters_7():
|
||
# Test that counters are case-sensitive
|
||
# See https://github.com/Kozea/WeasyPrint/pull/827
|
||
assert_tree(parse_all('''
|
||
<style>
|
||
p { counter-increment: p 2 }
|
||
p:before { content: counter(p) '.' counter(P); }
|
||
</style>
|
||
<p></p>
|
||
<p style="counter-increment: P 3"></p>
|
||
<p></p>'''), [
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::before', 'Inline', [
|
||
('p::before', 'Text', counter)])])])
|
||
for counter in '2.0 2.3 4.3'.split()])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counters_8():
|
||
assert_tree(parse_all('''
|
||
<style>
|
||
p:before { content: 'a'; display: list-item }
|
||
</style>
|
||
<p></p>
|
||
<p></p>'''), 2 * [
|
||
('p', 'Block', [
|
||
('p::before', 'Block', [
|
||
('p::marker', 'Block', [
|
||
('p::marker', 'Line', [
|
||
('p::marker', 'Text', '• ')])]),
|
||
('p::before', 'Block', [
|
||
('p::before', 'Line', [
|
||
('p::before', 'Text', 'a')])])])])])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_1():
|
||
assert_tree(parse_all('''
|
||
<style>
|
||
body { --var: 'Counter'; counter-reset: p -12 }
|
||
p { counter-increment: p }
|
||
p:nth-child(1):before { content: '-' counter(p, none) '-'; }
|
||
p:nth-child(2):before { content: counter(p, disc); }
|
||
p:nth-child(3):before { content: counter(p, circle); }
|
||
p:nth-child(4):before { content: counter(p, square); }
|
||
p:nth-child(5):before { content: counter(p); }
|
||
p:nth-child(6):before { content: var(--var) ':' counter(p); }
|
||
p:nth-child(7):before { content: counter(p) ':' var(--var); }
|
||
</style>
|
||
<p></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p></p>
|
||
'''), [
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::before', 'Inline', [
|
||
('p::before', 'Text', counter)])])])
|
||
for counter in '-- • ◦ ▪ -7 Counter:-6 -5:Counter'.split()])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_2():
|
||
assert_tree(parse_all('''
|
||
<style>
|
||
p { counter-increment: p }
|
||
p::before { content: counter(p, decimal-leading-zero); }
|
||
</style>
|
||
<p style="counter-reset: p -1987"></p>
|
||
<p></p>
|
||
<p style="counter-reset: p -12"></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p style="counter-reset: p -2"></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p style="counter-reset: p 8"></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p style="counter-reset: p 98"></p>
|
||
<p></p>
|
||
<p></p>
|
||
<p style="counter-reset: p 4134"></p>
|
||
<p></p>
|
||
'''), [
|
||
('p', 'Block', [
|
||
('p', 'Line', [
|
||
('p::before', 'Inline', [
|
||
('p::before', 'Text', counter)])])])
|
||
for counter in '''-1986 -1985 -11 -10 -9 -8 -1 00 01 02 09 10 11
|
||
99 100 101 4135 4136'''.split()])
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_3():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'decimal-leading-zero') for value in [
|
||
-1986, -1985,
|
||
-11, -10, -9, -8,
|
||
-1, 0, 1, 2,
|
||
9, 10, 11,
|
||
99, 100, 101,
|
||
4135, 4136
|
||
]] == '''
|
||
-1986 -1985 -11 -10 -9 -8 -1 00 01 02 09 10 11
|
||
99 100 101 4135 4136
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_4():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'lower-roman') for value in [
|
||
-1986, -1985,
|
||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||
49, 50,
|
||
389, 390,
|
||
3489, 3490, 3491,
|
||
4999, 5000, 5001
|
||
]] == '''
|
||
-1986 -1985 -1 0 i ii iii iv v vi vii viii ix x xi xii
|
||
xlix l ccclxxxix cccxc mmmcdlxxxix mmmcdxc mmmcdxci
|
||
4999 5000 5001
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_5():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'upper-roman') for value in [
|
||
-1986, -1985,
|
||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||
49, 50,
|
||
389, 390,
|
||
3489, 3490, 3491,
|
||
4999, 5000, 5001
|
||
]] == '''
|
||
-1986 -1985 -1 0 I II III IV V VI VII VIII IX X XI XII
|
||
XLIX L CCCLXXXIX CCCXC MMMCDLXXXIX MMMCDXC MMMCDXCI
|
||
4999 5000 5001
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_6():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'lower-alpha') for value in [
|
||
-1986, -1985,
|
||
-1, 0, 1, 2, 3, 4,
|
||
25, 26, 27, 28, 29,
|
||
2002, 2003
|
||
]] == '''
|
||
-1986 -1985 -1 0 a b c d y z aa ab ac bxz bya
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_7():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'upper-alpha') for value in [
|
||
-1986, -1985,
|
||
-1, 0, 1, 2, 3, 4,
|
||
25, 26, 27, 28, 29,
|
||
2002, 2003
|
||
]] == '''
|
||
-1986 -1985 -1 0 A B C D Y Z AA AB AC BXZ BYA
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_8():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'lower-latin') for value in [
|
||
-1986, -1985,
|
||
-1, 0, 1, 2, 3, 4,
|
||
25, 26, 27, 28, 29,
|
||
2002, 2003
|
||
]] == '''
|
||
-1986 -1985 -1 0 a b c d y z aa ab ac bxz bya
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_9():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'upper-latin') for value in [
|
||
-1986, -1985,
|
||
-1, 0, 1, 2, 3, 4,
|
||
25, 26, 27, 28, 29,
|
||
2002, 2003
|
||
]] == '''
|
||
-1986 -1985 -1 0 A B C D Y Z AA AB AC BXZ BYA
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_10():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'georgian') for value in [
|
||
-1986, -1985,
|
||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||
20, 30, 40, 50, 60, 70, 80, 90, 100,
|
||
200, 300, 400, 500, 600, 700, 800, 900, 1000,
|
||
2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000,
|
||
19999, 20000, 20001
|
||
]] == '''
|
||
-1986 -1985 -1 0 ა
|
||
ბ გ დ ე ვ ზ ჱ თ ი ია იბ
|
||
კ ლ მ ნ ჲ ო პ ჟ რ
|
||
ს ტ ჳ ფ ქ ღ ყ შ ჩ
|
||
ც ძ წ ჭ ხ ჴ ჯ ჰ ჵ
|
||
ჵჰშჟთ 20000 20001
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
def test_counter_styles_11():
|
||
render = HTML(string='')._ua_counter_style()[0].render_value
|
||
assert [render(value, 'armenian') for value in [
|
||
-1986, -1985,
|
||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||
20, 30, 40, 50, 60, 70, 80, 90, 100,
|
||
200, 300, 400, 500, 600, 700, 800, 900, 1000,
|
||
2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000,
|
||
9999, 10000, 10001
|
||
]] == '''
|
||
-1986 -1985 -1 0 Ա
|
||
Բ Գ Դ Ե Զ Է Ը Թ Ժ ԺԱ ԺԲ
|
||
Ի Լ Խ Ծ Կ Հ Ձ Ղ Ճ
|
||
Մ Յ Ն Շ Ո Չ Պ Ջ Ռ
|
||
Ս Վ Տ Ր Ց Ւ Փ Ք
|
||
ՔՋՂԹ 10000 10001
|
||
'''.split()
|
||
|
||
|
||
@assert_no_logs
|
||
@pytest.mark.parametrize('arguments, values', (
|
||
('cyclic "a" "b" "c"', ('a ', 'b ', 'c ', 'a ')),
|
||
('symbolic "a" "b"', ('a ', 'b ', 'aa ', 'bb ')),
|
||
('"a" "b"', ('a ', 'b ', 'aa ', 'bb ')),
|
||
('alphabetic "a" "b"', ('a ', 'b ', 'aa ', 'ab ')),
|
||
('fixed "a" "b"', ('a ', 'b ', '3 ', '4 ')),
|
||
('numeric "0" "1" "2"', ('1 ', '2 ', '10 ', '11 ')),
|
||
))
|
||
def test_counter_symbols(arguments, values):
|
||
page, = render_pages('''
|
||
<style>
|
||
ol { list-style-type: symbols(%s) }
|
||
</style>
|
||
<ol>
|
||
<li>abc</li>
|
||
<li>abc</li>
|
||
<li>abc</li>
|
||
<li>abc</li>
|
||
</ol>
|
||
''' % arguments)
|
||
html, = page.children
|
||
body, = html.children
|
||
ol, = body.children
|
||
li_1, li_2, li_3, li_4 = ol.children
|
||
assert li_1.children[0].children[0].children[0].text == values[0]
|
||
assert li_2.children[0].children[0].children[0].text == values[1]
|
||
assert li_3.children[0].children[0].children[0].text == values[2]
|
||
assert li_4.children[0].children[0].children[0].text == values[3]
|
||
|
||
|
||
@assert_no_logs
|
||
@pytest.mark.parametrize('style_type, values', (
|
||
('decimal', ('1. ', '2. ', '3. ', '4. ')),
|
||
('"/"', ('/', '/', '/', '/')),
|
||
))
|
||
def test_list_style_types(style_type, values):
|
||
page, = render_pages('''
|
||
<style>
|
||
ol { list-style-type: %s }
|
||
</style>
|
||
<ol>
|
||
<li>abc</li>
|
||
<li>abc</li>
|
||
<li>abc</li>
|
||
<li>abc</li>
|
||
</ol>
|
||
''' % style_type)
|
||
html, = page.children
|
||
body, = html.children
|
||
ol, = body.children
|
||
li_1, li_2, li_3, li_4 = ol.children
|
||
assert li_1.children[0].children[0].children[0].text == values[0]
|
||
assert li_2.children[0].children[0].children[0].text == values[1]
|
||
assert li_3.children[0].children[0].children[0].text == values[2]
|
||
assert li_4.children[0].children[0].children[0].text == values[3]
|
||
|
||
|
||
def test_counter_set():
|
||
page, = render_pages('''
|
||
<style>
|
||
body { counter-reset: h2 0 h3 4; font-size: 1px }
|
||
article { counter-reset: h2 2 }
|
||
h1 { counter-increment: h1 }
|
||
h1::before { content: counter(h1) }
|
||
h2 { counter-increment: h2; counter-set: h3 3 }
|
||
h2::before { content: counter(h2) }
|
||
h3 { counter-increment: h3 }
|
||
h3::before { content: counter(h3) }
|
||
</style>
|
||
<article>
|
||
<h1></h1>
|
||
</article>
|
||
<article>
|
||
<h2></h2>
|
||
<h3></h3>
|
||
</article>
|
||
<article>
|
||
<h3></h3>
|
||
</article>
|
||
<article>
|
||
<h2></h2>
|
||
</article>
|
||
<article>
|
||
<h3></h3>
|
||
<h3></h3>
|
||
</article>
|
||
<article>
|
||
<h1></h1>
|
||
<h2></h2>
|
||
<h3></h3>
|
||
</article>
|
||
''')
|
||
html, = page.children
|
||
body, = html.children
|
||
art_1, art_2, art_3, art_4, art_5, art_6 = body.children
|
||
|
||
h1, = art_1.children
|
||
assert h1.children[0].children[0].children[0].text == '1'
|
||
|
||
h2, h3, = art_2.children
|
||
assert h2.children[0].children[0].children[0].text == '3'
|
||
assert h3.children[0].children[0].children[0].text == '4'
|
||
|
||
h3, = art_3.children
|
||
assert h3.children[0].children[0].children[0].text == '5'
|
||
|
||
h2, = art_4.children
|
||
assert h2.children[0].children[0].children[0].text == '3'
|
||
|
||
h3_1, h3_2 = art_5.children
|
||
assert h3_1.children[0].children[0].children[0].text == '4'
|
||
assert h3_2.children[0].children[0].children[0].text == '5'
|
||
|
||
h1, h2, h3 = art_6.children
|
||
assert h1.children[0].children[0].children[0].text == '1'
|
||
assert h2.children[0].children[0].children[0].text == '3'
|
||
assert h3.children[0].children[0].children[0].text == '4'
|
||
|
||
|
||
def test_counter_multiple_extends():
|
||
# Inspired by W3C failing test system-extends-invalid
|
||
page, = render_pages('''
|
||
<style>
|
||
@counter-style a {
|
||
system: extends b;
|
||
prefix: a;
|
||
}
|
||
@counter-style b {
|
||
system: extends c;
|
||
suffix: b;
|
||
}
|
||
@counter-style c {
|
||
system: extends b;
|
||
pad: 2 c;
|
||
}
|
||
@counter-style d {
|
||
system: extends d;
|
||
prefix: d;
|
||
}
|
||
@counter-style e {
|
||
system: extends unknown;
|
||
prefix: e;
|
||
}
|
||
@counter-style f {
|
||
system: extends decimal;
|
||
symbols: a;
|
||
}
|
||
@counter-style g {
|
||
system: extends decimal;
|
||
additive-symbols: 1 a;
|
||
}
|
||
</style>
|
||
<ol>
|
||
<li style="list-style-type: a"></li>
|
||
<li style="list-style-type: b"></li>
|
||
<li style="list-style-type: c"></li>
|
||
<li style="list-style-type: d"></li>
|
||
<li style="list-style-type: e"></li>
|
||
<li style="list-style-type: f"></li>
|
||
<li style="list-style-type: g"></li>
|
||
<li style="list-style-type: h"></li>
|
||
</ol>
|
||
''')
|
||
html, = page.children
|
||
body, = html.children
|
||
ol, = body.children
|
||
li_1, li_2, li_3, li_4, li_5, li_6, li_7, li_8 = ol.children
|
||
assert li_1.children[0].children[0].children[0].text == 'a1b'
|
||
assert li_2.children[0].children[0].children[0].text == '2b'
|
||
assert li_3.children[0].children[0].children[0].text == 'c3. '
|
||
assert li_4.children[0].children[0].children[0].text == 'd4. '
|
||
assert li_5.children[0].children[0].children[0].text == 'e5. '
|
||
assert li_6.children[0].children[0].children[0].text == '6. '
|
||
assert li_7.children[0].children[0].children[0].text == '7. '
|
||
assert li_8.children[0].children[0].children[0].text == '8. '
|