1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-04 16:07:57 +03:00
WeasyPrint/tests/test_text.py

1235 lines
37 KiB
Python
Raw Normal View History

"""
weasyprint.tests.test_text
--------------------------
2011-07-07 12:08:22 +04:00
Test the text layout.
2011-07-07 12:08:22 +04:00
2011-08-24 14:39:34 +04:00
"""
2011-07-07 12:08:22 +04:00
2018-03-20 00:35:15 +03:00
import pytest
2020-12-06 22:19:59 +03:00
from weasyprint.css.properties import INITIAL_VALUES
from weasyprint.text.line_break import split_first_line
2018-03-20 00:35:15 +03:00
2021-04-19 18:15:53 +03:00
from .testing_utils import MONO_FONTS, SANS_FONTS, assert_no_logs, render_pages
def make_text(text, width=None, **style):
2018-01-13 19:41:08 +03:00
"""Wrapper for split_first_line() creating a style dict."""
new_style = dict(INITIAL_VALUES)
2018-03-30 01:13:36 +03:00
new_style['font_family'] = MONO_FONTS.split(',')
new_style.update(style)
return split_first_line(
2018-01-13 19:41:08 +03:00
text, new_style, context=None, max_width=width,
justification_spacing=0)
2011-07-07 12:08:22 +04:00
2011-08-24 14:39:34 +04:00
2012-02-20 16:04:35 +04:00
@assert_no_logs
2011-07-07 12:08:22 +04:00
def test_line_content():
for width, remaining in [(100, 'text for test'),
(45, 'is a text for test')]:
text = 'This is a text for test'
_, length, resume_index, _, _, _ = make_text(
2018-03-30 01:13:36 +03:00
text, width, font_family=SANS_FONTS.split(','), font_size=19)
assert text[resume_index:] == remaining
assert length + 1 == resume_index # +1 for the removed trailing space
2011-07-07 12:08:22 +04:00
2012-02-20 16:04:35 +04:00
@assert_no_logs
def test_line_with_any_width():
_, _, _, width_1, _, _ = make_text('some text')
_, _, _, width_2, _, _ = make_text('some text some text')
assert width_1 < width_2
2011-08-24 14:39:34 +04:00
2012-02-20 16:04:35 +04:00
@assert_no_logs
def test_line_breaking():
2019-04-12 17:27:33 +03:00
string = 'Thïs is a text for test'
2011-07-11 13:30:32 +04:00
# These two tests do not really rely on installed fonts
_, _, resume_index, _, _, _ = make_text(string, 90, font_size=1)
assert resume_index is None
2011-07-19 13:38:54 +04:00
_, _, resume_index, _, _, _ = make_text(string, 90, font_size=100)
assert string.encode('utf-8')[resume_index:].decode('utf-8') == (
'is a text for test')
2011-07-07 12:08:22 +04:00
_, _, resume_index, _, _, _ = make_text(
2018-03-30 01:13:36 +03:00
string, 100, font_family=SANS_FONTS.split(','), font_size=19)
assert string.encode('utf-8')[resume_index:].decode('utf-8') == (
'text for test')
@assert_no_logs
def test_line_breaking_rtl():
string = 'لوريم ايبسوم دولا'
# These two tests do not really rely on installed fonts
_, _, resume_index, _, _, _ = make_text(string, 90, font_size=1)
assert resume_index is None
_, _, resume_index, _, _, _ = make_text(string, 90, font_size=100)
assert string.encode('utf-8')[resume_index:].decode('utf-8') == (
'ايبسوم دولا')
2011-08-24 14:39:34 +04:00
2012-02-20 16:04:35 +04:00
@assert_no_logs
2011-07-07 12:08:22 +04:00
def test_text_dimension():
string = 'This is a text for test. This is a test for text.py'
_, _, _, width_1, height_1, _ = make_text(string, 200, font_size=12)
2011-09-27 17:19:31 +04:00
_, _, _, width_2, height_2, _ = make_text(string, 200, font_size=20)
assert width_1 * height_1 < width_2 * height_2
2011-07-19 13:38:54 +04:00
2012-02-20 16:04:35 +04:00
@assert_no_logs
def test_text_font_size_zero():
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>
p { font-size: 0; }
</style>
<p>test font size zero</p>
''')
2018-03-14 03:32:01 +03:00
html, = page.children
body, = html.children
paragraph, = body.children
2013-04-12 22:09:41 +04:00
line, = paragraph.children
2011-10-19 17:14:43 +04:00
# zero-sized text boxes are removed
2013-04-12 22:09:41 +04:00
assert not line.children
assert line.height == 0
assert paragraph.height == 0
@assert_no_logs
def test_text_font_size_very_small():
# Test regression: https://github.com/Kozea/WeasyPrint/issues/1499
page, = render_pages('''
<style>
p { font-size: 0.00000001px }
</style>
<p>test font size zero</p>
''')
html, = page.children
body, = html.children
paragraph, = body.children
line, = paragraph.children
assert line.height < 0.001
assert paragraph.height < 0.001
@assert_no_logs
def test_text_spaced_inlines():
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<p>start <i><b>bi1</b> <b>bi2</b></i> <b>b1</b> end</p>
''')
2018-03-14 03:32:01 +03:00
html, = page.children
body, = html.children
paragraph, = body.children
line, = paragraph.children
start, i, space, b, end = line.children
assert start.text == 'start '
assert space.text == ' '
assert space.width > 0
assert end.text == ' end'
bi1, space, bi2 = i.children
bi1, = bi1.children
bi2, = bi2.children
assert bi1.text == 'bi1'
assert space.text == ' '
assert space.width > 0
assert bi2.text == 'bi2'
b1, = b.children
assert b1.text == 'b1'
2016-08-20 23:58:43 +03:00
@assert_no_logs
def test_text_align_left():
2018-03-20 00:35:15 +03:00
# <--------------------> page, body
# +-----+
# +---+ |
# | | |
# +---+-----+
# ^ ^ ^ ^
# x=0 x=40 x=100 x=200
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>
@page { size: 200px }
</style>
<body>
<img src="pattern.png" style="width: 40px"
><img src="pattern.png" style="width: 60px">''')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
line, = body.children
img_1, img_2 = line.children
# initial value for text-align: left (in ltr text)
assert img_1.position_x == 0
assert img_2.position_x == 40
@assert_no_logs
def test_text_align_right():
2018-03-20 00:35:15 +03:00
# <--------------------> page, body
# +-----+
# +---+ |
# | | |
# +---+-----+
# ^ ^ ^ ^
# x=0 x=100 x=200
# x=140
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>
@page { size: 200px }
body { text-align: right }
</style>
<body>
<img src="pattern.png" style="width: 40px"
><img src="pattern.png" style="width: 60px">''')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
line, = body.children
img_1, img_2 = line.children
assert img_1.position_x == 100 # 200 - 60 - 40
assert img_2.position_x == 140 # 200 - 60
@assert_no_logs
def test_text_align_center():
2018-03-20 00:35:15 +03:00
# <--------------------> page, body
# +-----+
# +---+ |
# | | |
# +---+-----+
# ^ ^ ^ ^
# x= x=50 x=150
# x=90
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>
@page { size: 200px }
body { text-align: center }
</style>
<body>
<img src="pattern.png" style="width: 40px"
><img src="pattern.png" style="width: 60px">''')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
line, = body.children
img_1, img_2 = line.children
assert img_1.position_x == 50
assert img_2.position_x == 90
@assert_no_logs
def test_text_align_justify():
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>
@page { size: 300px 1000px }
body { text-align: justify }
</style>
<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: 100px"
></strong><img src="pattern.png" style="width: 290px"
><!-- Last image will be on its own line. -->''')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
paragraph, = body.children
line_1, line_2 = paragraph.children
image_1, space_1, strong = line_1.children
image_2, space_2, image_3, space_3, image_4 = strong.children
image_5, = line_2.children
assert space_1.text == ' '
assert space_2.text == ' '
assert space_3.text == ' '
assert image_1.position_x == 0
assert space_1.position_x == 40
assert strong.position_x == 70
assert image_2.position_x == 70
assert space_2.position_x == 130
assert image_3.position_x == 160
assert space_3.position_x == 170
assert image_4.position_x == 200
assert strong.width == 230
assert image_5.position_x == 0
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_text_align_justify_all():
page, = render_pages('''
<style>
@page { size: 300px 1000px }
body { text-align: justify-all }
</style>
<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: 100px"
></strong><img src="pattern.png" style="width: 200px">
<img src="pattern.png" style="width: 10px">''')
html, = page.children
body, = html.children
paragraph, = body.children
line_1, line_2 = paragraph.children
image_1, space_1, strong = line_1.children
image_2, space_2, image_3, space_3, image_4 = strong.children
image_5, space_4, image_6 = line_2.children
assert space_1.text == ' '
assert space_2.text == ' '
assert space_3.text == ' '
assert space_4.text == ' '
assert image_1.position_x == 0
assert space_1.position_x == 40
assert strong.position_x == 70
assert image_2.position_x == 70
assert space_2.position_x == 130
assert image_3.position_x == 160
assert space_3.position_x == 170
assert image_4.position_x == 200
assert strong.width == 230
assert image_5.position_x == 0
assert space_4.position_x == 200
assert image_6.position_x == 290
@assert_no_logs
def test_text_align_all_last():
page, = render_pages('''
<style>
@page { size: 300px 1000px }
body { text-align-all: justify; text-align-last: right }
</style>
<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: 100px"
></strong><img src="pattern.png" style="width: 200px"
><img src="pattern.png" style="width: 10px">''')
html, = page.children
body, = html.children
paragraph, = body.children
line_1, line_2 = paragraph.children
image_1, space_1, strong = line_1.children
image_2, space_2, image_3, space_3, image_4 = strong.children
image_5, image_6 = line_2.children
assert space_1.text == ' '
assert space_2.text == ' '
assert space_3.text == ' '
assert image_1.position_x == 0
assert space_1.position_x == 40
assert strong.position_x == 70
assert image_2.position_x == 70
assert space_2.position_x == 130
assert image_3.position_x == 160
assert space_3.position_x == 170
assert image_4.position_x == 200
assert strong.width == 230
assert image_5.position_x == 90
assert image_6.position_x == 290
@assert_no_logs
def test_text_align_not_enough_space():
page, = render_pages('''
<style>
p { text-align: center; width: 0 }
span { display: inline-block }
</style>
<p><span>aaaaaaaaaaaaaaaaaaaaaaaaaa</span></p>''')
html, = page.children
body, = html.children
paragraph, = body.children
span, = paragraph.children
assert span.position_x == 0
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_text_align_justify_no_space():
2016-08-20 23:58:43 +03:00
# single-word line (zero spaces)
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>
body { text-align: justify; width: 50px }
</style>
<p>Supercalifragilisticexpialidocious bar</p>
2016-08-20 23:58:43 +03:00
''')
html, = page.children
body, = html.children
paragraph, = body.children
line_1, line_2 = paragraph.children
text, = line_1.children
assert text.position_x == 0
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_text_align_justify_text_indent():
# text-indent
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>
@page { size: 300px 1000px }
body { text-align: justify }
p { text-indent: 3px }
</style>
<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: 100px"
></strong><img src="pattern.png" style="width: 290px"
><!-- Last image will be on its own line. -->''')
html, = page.children
body, = html.children
paragraph, = body.children
line_1, line_2 = paragraph.children
image_1, space_1, strong = line_1.children
image_2, space_2, image_3, space_3, image_4 = strong.children
image_5, = line_2.children
assert space_1.text == ' '
assert space_2.text == ' '
assert space_3.text == ' '
assert image_1.position_x == 3
assert space_1.position_x == 43
assert strong.position_x == 72
assert image_2.position_x == 72
assert space_2.position_x == 132
assert image_3.position_x == 161
assert space_3.position_x == 171
assert image_4.position_x == 200
assert strong.width == 228
assert image_5.position_x == 0
2016-08-20 23:58:43 +03:00
@assert_no_logs
def test_text_align_justify_no_break_between_children():
# Test justification when line break happens between two inline children
# that must stay together.
# Test regression: https://github.com/Kozea/WeasyPrint/issues/637
page, = render_pages('''
<style>
@font-face {src: url(weasyprint.otf); font-family: weasyprint}
p { text-align: justify; font-family: weasyprint; width: 7em }
</style>
<p>
<span>a</span>
<span>b</span>
<span>bla</span><span>,</span>
<span>b</span>
</p>
''')
html, = page.children
body, = html.children
paragraph, = body.children
line_1, line_2 = paragraph.children
span_1, space_1, span_2, space_2 = line_1.children
assert span_1.position_x == 0
assert span_2.position_x == 6 * 16 # 1 character + 5 spaces
assert line_1.width == 7 * 16 # 7em
span_1, span_2, space_1, span_3, space_2 = line_2.children
assert span_1.position_x == 0
assert span_2.position_x == 3 * 16 # 3 characters
assert span_3.position_x == 5 * 16 # (3 + 1) characters + 1 space
2016-08-20 23:58:43 +03:00
@assert_no_logs
def test_word_spacing():
# keep the empty <style> as a regression test: element.text is None
# (Not a string.)
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style></style>
<body><strong>Lorem ipsum dolor<em>sit amet</em></strong>''')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
line, = body.children
strong_1, = line.children
# TODO: Pango gives only half of word-spacing to a space at the end
# of a TextBox. Is this what we want?
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>strong { word-spacing: 11px }</style>
<body><strong>Lorem ipsum dolor<em>sit amet</em></strong>''')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
line, = body.children
strong_2, = line.children
assert strong_2.width - strong_1.width == 33
@assert_no_logs
2018-03-20 00:35:15 +03:00
def test_letter_spacing_1():
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2016-08-20 23:58:43 +03:00
<body><strong>Supercalifragilisticexpialidocious</strong>''')
html, = page.children
body, = html.children
line, = body.children
strong_1, = line.children
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2016-08-20 23:58:43 +03:00
<style>strong { letter-spacing: 11px }</style>
<body><strong>Supercalifragilisticexpialidocious</strong>''')
html, = page.children
body, = html.children
line, = body.children
strong_2, = line.children
assert strong_2.width - strong_1.width == 34 * 11
# an embedded tag should not affect the single-line letter spacing
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<style>strong { letter-spacing: 11px }</style>'
'<body><strong>Supercali<span>fragilistic</span>expialidocious'
'</strong>')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
line, = body.children
strong_3, = line.children
assert strong_3.width == strong_2.width
# duplicate wrapped lines should also have same overall width
# Note work-around for word-wrap bug (issue #163) by marking word
# as an inline-block
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<style>'
' strong {'
' letter-spacing: 11px;'
2021-01-21 14:42:25 +03:00
f' max-width: {strong_3.width * 1.5}px'
2018-03-14 03:32:01 +03:00
'}'
' span { display: inline-block }'
'</style>'
'<body><strong>'
' <span>Supercali<i>fragilistic</i>expialidocious</span> '
' <span>Supercali<i>fragilistic</i>expialidocious</span>'
'</strong>')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
line1, line2 = body.children
assert line1.children[0].width == line2.children[0].width
assert line1.children[0].width == strong_2.width
@pytest.mark.parametrize('spacing', ('word-spacing', 'letter-spacing'))
@assert_no_logs
def test_spacing_ex(spacing):
# Test regression on ex units in spacing properties
render_pages(f'<div style="{spacing}: 2ex">abc def')
2018-10-26 19:49:19 +03:00
@pytest.mark.parametrize('indent', ('12px', '6%'))
2016-08-20 23:58:43 +03:00
@assert_no_logs
2018-10-26 19:49:19 +03:00
def test_text_indent(indent):
page, = render_pages('''
<style>
@page { size: 220px }
body { margin: 10px; text-indent: %(indent)s }
</style>
<p>Some text that is long enough that it take at least three line,
but maybe more.
''' % {'indent': indent})
html, = page.children
body, = html.children
paragraph, = body.children
lines = paragraph.children
text_1, = lines[0].children
text_2, = lines[1].children
text_3, = lines[2].children
assert text_1.position_x == 22 # 10px margin-left + 12px indent
assert text_2.position_x == 10 # No indent
assert text_3.position_x == 10 # No indent
2016-08-20 23:58:43 +03:00
@assert_no_logs
def test_text_indent_inline():
# Test regression: https://github.com/Kozea/WeasyPrint/issues/1000
page, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
p { display: inline-block; text-indent: 1em;
font-family: weasyprint }
</style>
<p><span>text
''')
html, = page.children
body, = html.children
paragraph, = body.children
line, = paragraph.children
assert line.width == (4 + 1) * 16
@pytest.mark.parametrize('indent', ('12px', '6%'))
@assert_no_logs
def test_text_indent_multipage(indent):
# Test regression: https://github.com/Kozea/WeasyPrint/issues/706
pages = render_pages('''
<style>
@page { size: 220px 1.5em; margin: 0 }
body { margin: 10px; text-indent: %(indent)s }
</style>
<p>Some text that is long enough that it take at least three line,
but maybe more.
''' % {'indent': indent})
page = pages.pop(0)
html, = page.children
body, = html.children
paragraph, = body.children
line, = paragraph.children
text, = line.children
assert text.position_x == 22 # 10px margin-left + 12px indent
page = pages.pop(0)
html, = page.children
body, = html.children
paragraph, = body.children
line, = paragraph.children
text, = line.children
assert text.position_x == 10 # No indent
2016-08-20 23:58:43 +03:00
@assert_no_logs
2018-03-20 00:35:15 +03:00
def test_hyphenate_character_1():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 5em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-character: \'!\'" lang=fr>'
2016-08-20 23:58:43 +03:00
'hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) > 1
assert lines[0].children[0].text.endswith('!')
full_text = ''.join(line.children[0].text for line in lines)
assert full_text.replace('!', '') == 'hyphénation'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_hyphenate_character_2():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 5em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-character: \'à\'" lang=fr>'
2016-08-20 23:58:43 +03:00
'hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) > 1
assert lines[0].children[0].text.endswith('à')
full_text = ''.join(line.children[0].text for line in lines)
assert full_text.replace('à', '') == 'hyphénation'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_hyphenate_character_3():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 5em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-character: \'ù ù\'" lang=fr>'
2016-08-20 23:58:43 +03:00
'hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) > 1
assert lines[0].children[0].text.endswith('ù ù')
full_text = ''.join(line.children[0].text for line in lines)
assert full_text.replace(' ', '').replace('ù', '') == 'hyphénation'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_hyphenate_character_4():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 5em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-character: \'\'" lang=fr>'
2016-08-20 23:58:43 +03:00
'hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) > 1
full_text = ''.join(line.children[0].text for line in lines)
assert full_text == 'hyphénation'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_hyphenate_character_5():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 5em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-character: \'———\'" lang=fr>'
2016-08-20 23:58:43 +03:00
'hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) > 1
assert lines[0].children[0].text.endswith('———')
full_text = ''.join(line.children[0].text for line in lines)
assert full_text.replace('', '') == 'hyphénation'
@assert_no_logs
2018-03-20 00:35:15 +03:00
def test_hyphenate_manual_1():
2016-08-20 23:58:43 +03:00
for i in range(1, len('hyphénation')):
for hyphenate_character in ('!', 'ù ù'):
word = 'hyphénation'[:i] + '\u00ad' + 'hyphénation'[i:]
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 5em; font-family: weasyprint">'
2020-05-30 16:48:24 +03:00
'<style>@font-face {'
' src: url(weasyprint.otf); font-family: weasyprint}</style>'
'<body style="hyphens: manual;'
2020-05-30 16:48:24 +03:00
f' hyphenate-character: \'{hyphenate_character}\'"'
f' lang=fr>{word}')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
lines = body.children
assert len(lines) == 2
assert lines[0].children[0].text.endswith(hyphenate_character)
full_text = ''.join(
child.text for line in lines for child in line.children)
assert full_text.replace(hyphenate_character, '') == word
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_hyphenate_manual_2():
2016-08-20 23:58:43 +03:00
for i in range(1, len('hy phénation')):
for hyphenate_character in ('!', 'ù ù'):
word = 'hy phénation'[:i] + '\u00ad' + 'hy phénation'[i:]
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 5em; font-family: weasyprint">'
2020-05-30 16:48:24 +03:00
'<style>@font-face {'
' src: url(weasyprint.otf); font-family: weasyprint}</style>'
'<body style="hyphens: manual;'
2020-05-30 16:48:24 +03:00
f' hyphenate-character: \'{hyphenate_character}\'"'
f' lang=fr>{word}')
2016-08-20 23:58:43 +03:00
html, = page.children
body, = html.children
lines = body.children
assert len(lines) in (2, 3)
full_text = ''.join(
child.text for line in lines for child in line.children)
full_text = full_text.replace(hyphenate_character, '')
if lines[0].children[0].text.endswith(hyphenate_character):
assert full_text == word
else:
assert lines[0].children[0].text.endswith('y')
if len(lines) == 3:
assert lines[1].children[0].text.endswith(
hyphenate_character)
@assert_no_logs
def test_hyphenate_manual_3():
# Automatic hyphenation opportunities within a word must be ignored if the
# word contains a conditional hyphen, in favor of the conditional
# hyphen(s).
page, = render_pages(
'<html style="width: 0.1em" lang="en">'
'<body style="hyphens: auto">in&shy;lighten&shy;lighten&shy;in')
html, = page.children
body, = html.children
line_1, line_2, line_3, line_4 = body.children
assert line_1.children[0].text == 'in\xad'
assert line_2.children[0].text == 'lighten\xad'
assert line_3.children[0].text == 'lighten\xad'
assert line_4.children[0].text == 'in'
2016-08-20 23:58:43 +03:00
@assert_no_logs
2018-03-20 00:35:15 +03:00
def test_hyphenate_limit_zone_1():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 12em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-limit-zone: 0" lang=fr>'
2016-08-20 23:58:43 +03:00
'mmmmm hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) == 2
assert lines[0].children[0].text.endswith('')
full_text = ''.join(line.children[0].text for line in lines)
assert full_text.replace('', '') == 'mmmmm hyphénation'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_hyphenate_limit_zone_2():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 12em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-limit-zone: 9em" lang=fr>'
2016-08-20 23:58:43 +03:00
'mmmmm hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) > 1
assert lines[0].children[0].text.endswith('mm')
full_text = ''.join(line.children[0].text for line in lines)
assert full_text == 'mmmmmhyphénation'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_hyphenate_limit_zone_3():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 12em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-limit-zone: 5%" lang=fr>'
2016-08-20 23:58:43 +03:00
'mmmmm hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) == 2
assert lines[0].children[0].text.endswith('')
full_text = ''.join(line.children[0].text for line in lines)
assert full_text.replace('', '') == 'mmmmm hyphénation'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_hyphenate_limit_zone_4():
2018-03-14 03:32:01 +03:00
page, = render_pages(
'<html style="width: 12em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
'hyphenate-limit-zone: 95%" lang=fr>'
2016-08-20 23:58:43 +03:00
'mmmmm hyphénation')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) > 1
assert lines[0].children[0].text.endswith('mm')
full_text = ''.join(line.children[0].text for line in lines)
assert full_text == 'mmmmmhyphénation'
@assert_no_logs
2018-03-20 00:35:15 +03:00
@pytest.mark.parametrize('css, result', (
('auto', 2),
('auto auto 0', 2),
('0 0 0', 2),
('4 4 auto', 1),
('6 2 4', 2),
('auto 1 auto', 2),
('7 auto auto', 1),
('6 auto auto', 2),
('5 2', 2),
('3', 2),
('2 4 6', 1),
('auto 4', 1),
('auto 2', 2),
))
def test_hyphenate_limit_chars(css, result):
2020-05-30 16:48:24 +03:00
page, = render_pages(
'<html style="width: 1em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
2018-03-20 00:35:15 +03:00
'<body style="hyphens: auto;'
2020-05-30 16:48:24 +03:00
f'hyphenate-limit-chars: {css}" lang=en>'
'hyphen')
2018-03-20 00:35:15 +03:00
html, = page.children
body, = html.children
lines = body.children
assert len(lines) == result
2016-08-20 23:58:43 +03:00
@assert_no_logs
@pytest.mark.parametrize('css', (
# light·en
'3 3 3', # 'en' is shorter than 3
'3 6 2', # 'light' is shorter than 6
'8', # 'lighten' is shorter than 8
))
def test_hyphenate_limit_chars_punctuation(css):
# See https://github.com/Kozea/WeasyPrint/issues/109
page, = render_pages(
'<html style="width: 1em; font-family: weasyprint">'
'<style>'
' @font-face {src: url(weasyprint.otf); font-family: weasyprint}'
'</style>'
'<body style="hyphens: auto;'
2020-05-30 16:48:24 +03:00
f'hyphenate-limit-chars: {css}" lang=en>'
'..lighten..')
html, = page.children
body, = html.children
lines = body.children
assert len(lines) == 1
2018-03-20 00:35:15 +03:00
@assert_no_logs
@pytest.mark.parametrize('wrap, text, test, full_text', (
('break-word', 'aaaaaaaa', lambda a: a > 1, 'aaaaaaaa'),
('normal', 'aaaaaaaa', lambda a: a == 1, 'aaaaaaaa'),
('break-word', 'hyphenations', lambda a: a > 3,
'hy\u2010phen\u2010ations'),
('break-word', "A splitted word. An hyphenated word.",
lambda a: a > 8, "Asplittedword.Anhy\u2010phen\u2010atedword."),
))
def test_overflow_wrap(wrap, text, test, full_text):
page, = render_pages('''
<style>
@font-face {src: url(weasyprint.otf); font-family: weasyprint}
body {width: 80px; overflow: hidden; font-family: weasyprint; }
2018-03-20 00:35:15 +03:00
span {overflow-wrap: %s; white-space: normal; }
</style>
<body style="hyphens: auto;" lang="en">
<span>%s
''' % (wrap, text))
html, = page.children
body, = html.children
lines = []
for line in body.children:
box, = line.children
textBox, = box.children
lines.append(textBox.text)
lines_full_text = ''.join(line for line in lines)
assert test(len(lines))
assert full_text == lines_full_text
2016-08-20 23:58:43 +03:00
2018-03-20 00:35:15 +03:00
def white_space_lines(width, space):
page, = render_pages('''
<style>
2020-05-30 16:48:24 +03:00
body { font-size: 100px; width: %dpx }
2018-03-20 00:35:15 +03:00
span { white-space: %s }
</style>
<body><span>This + \n is text''' % (width, space))
html, = page.children
body, = html.children
return body.children
2016-08-20 23:58:43 +03:00
@assert_no_logs
2018-03-20 00:35:15 +03:00
def test_white_space_1():
line1, line2, line3, line4 = white_space_lines(1, 'normal')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This'
box2, = line2.children
text2, = box2.children
assert text2.text == '+'
box3, = line3.children
text3, = box3.children
assert text3.text == 'is'
box4, = line4.children
text4, = box4.children
assert text4.text == 'text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_2():
line1, line2 = white_space_lines(1, 'pre')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This + '
box2, = line2.children
text2, = box2.children
assert text2.text == ' is text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_3():
line1, = white_space_lines(1, 'nowrap')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This + is text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_4():
line1, line2, line3, line4, line5 = white_space_lines(1, 'pre-wrap')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This '
box2, = line2.children
text2, = box2.children
assert text2.text == '+ '
box3, = line3.children
text3, = box3.children
assert text3.text == ' '
box4, = line4.children
text4, = box4.children
assert text4.text == 'is '
box5, = line5.children
text5, = box5.children
assert text5.text == 'text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_5():
line1, line2, line3, line4 = white_space_lines(1, 'pre-line')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This'
box2, = line2.children
text2, = box2.children
assert text2.text == '+'
box3, = line3.children
text3, = box3.children
assert text3.text == 'is'
box4, = line4.children
text4, = box4.children
assert text4.text == 'text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_6():
line1, = white_space_lines(1000000, 'normal')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This + is text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_7():
line1, line2 = white_space_lines(1000000, 'pre')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This + '
box2, = line2.children
text2, = box2.children
assert text2.text == ' is text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_8():
line1, = white_space_lines(1000000, 'nowrap')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This + is text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_9():
line1, line2 = white_space_lines(1000000, 'pre-wrap')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This + '
box2, = line2.children
text2, = box2.children
assert text2.text == ' is text'
2018-03-20 00:35:15 +03:00
@assert_no_logs
def test_white_space_10():
line1, line2 = white_space_lines(1000000, 'pre-line')
2016-08-20 23:58:43 +03:00
box1, = line1.children
text1, = box1.children
assert text1.text == 'This +'
box2, = line2.children
text2, = box2.children
assert text2.text == 'is text'
2016-08-21 00:31:12 +03:00
@assert_no_logs
def test_white_space_11():
# Test regression: https://github.com/Kozea/WeasyPrint/issues/813
page, = render_pages('''
<style>
pre { width: 0 }
</style>
<body><pre>This<br/>is text''')
html, = page.children
body, = html.children
pre, = body.children
line1, line2 = pre.children
text1, box = line1.children
assert text1.text == 'This'
assert box.element_tag == 'br'
text2, = line2.children
assert text2.text == 'is text'
@assert_no_logs
def test_white_space_12():
# Test regression: https://github.com/Kozea/WeasyPrint/issues/813
page, = render_pages('''
<style>
pre { width: 0 }
</style>
<body><pre>This is <span>lol</span> text''')
html, = page.children
body, = html.children
pre, = body.children
line1, = pre.children
text1, span, text2 = line1.children
assert text1.text == 'This is '
assert span.element_tag == 'span'
assert text2.text == ' text'
2016-08-21 00:31:12 +03:00
@assert_no_logs
2018-03-20 00:35:15 +03:00
@pytest.mark.parametrize('value, width', (
(8, 144), # (2 + (8 - 1)) * 16
(4, 80), # (2 + (4 - 1)) * 16
('3em', 64), # (2 + (3 - 1)) * 16
('25px', 41), # 2 * 16 + 25 - 1 * 16
# (0, 32), # See Layout.set_tabs
))
def test_tab_size(value, width):
page, = render_pages('''
<style>
@font-face {src: url(weasyprint.otf); font-family: weasyprint}
pre { tab-size: %s; font-family: weasyprint }
2018-03-20 00:35:15 +03:00
</style>
<pre>a&#9;a</pre>
''' % value)
html, = page.children
body, = html.children
paragraph, = body.children
line, = paragraph.children
assert line.width == width
2016-08-21 01:47:13 +03:00
@assert_no_logs
def test_text_transform():
2018-03-14 03:32:01 +03:00
page, = render_pages('''
2018-03-20 00:35:15 +03:00
<style>
p { text-transform: capitalize }
p+p { text-transform: uppercase }
p+p+p { text-transform: lowercase }
p+p+p+p { text-transform: full-width }
p+p+p+p+p { text-transform: none }
</style>
2016-08-21 01:47:13 +03:00
<p> lO1</p><p> lO1</p><p> lO1</p><p> lO1</p><p> lO1</p>
''')
2018-03-14 03:32:01 +03:00
html, = page.children
body, = html.children
p1, p2, p3, p4, p5 = body.children
2016-08-21 01:47:13 +03:00
line1, = p1.children
text1, = line1.children
assert text1.text == 'Hé Lo1'
line2, = p2.children
text2, = line2.children
assert text2.text == 'HÉ LO1'
line3, = p3.children
text3, = line3.children
assert text3.text == 'hé lo1'
line4, = p4.children
text4, = line4.children
assert text4.text == '\uff48é\u3000\uff4c\uff2f\uff11'
line5, = p5.children
text5, = line5.children
assert text5.text == 'hé lO1'
@assert_no_logs
def test_text_floating_pre_line():
# Test regression: https://github.com/Kozea/WeasyPrint/issues/610
page, = render_pages('''
<div style="float: left; white-space: pre-line">This is
oh this end </div>
''')
2021-01-23 20:50:52 +03:00
@assert_no_logs
@pytest.mark.parametrize(
'leader, content', (
('dotted', '.'),
('solid', '_'),
('space', ' '),
('" .-"', ' .-'),
)
)
def test_leader_content(leader, content):
page, = render_pages('''
<style>div::after { content: leader(%s) }</style>
<div></div>
''' % leader)
html, = page.children
body, = html.children
div, = body.children
line, = div.children
after, = line.children
inline, = after.children
assert inline.children[0].text == content
2021-03-11 17:48:31 +03:00
2021-02-14 22:01:43 +03:00
@pytest.mark.xfail
2021-02-14 20:58:50 +03:00
@assert_no_logs
def test_max_lines():
page, = render_pages('''
<style>
@page {size: 10px 10px;}
@font-face {src: url(weasyprint.otf); font-family: weasyprint}
p {
font-family: weasyprint;
font-size: 2px;
max-lines: 2;
}
</style>
<p>
abcd efgh ijkl
</p>
''')
html, = page.children
body, = html.children
2021-02-14 22:01:43 +03:00
p1, p2 = body.children
line1, line2 = p1.children
line3, = p2.children
2021-02-14 20:58:50 +03:00
text1, = line1.children
text2, = line2.children
2021-02-14 22:01:43 +03:00
text3, = line3.children
2021-02-14 20:58:50 +03:00
assert text1.text == 'abcd'
assert text2.text == 'efgh'
2021-02-14 22:01:43 +03:00
assert text3.text == 'ijkl'
2021-02-14 20:58:50 +03:00
@assert_no_logs
def test_continue():
page, = render_pages('''
<style>
@page {size: 10px 4px;}
@font-face {src: url(weasyprint.otf); font-family: weasyprint}
div {
continue: discard;
font-family: weasyprint;
font-size: 2px;
}
</style>
<div>
abcd efgh ijkl
</div>
''')
html, = page.children
body, = html.children
p, = body.children
line1, line2 = p.children
text1, = line1.children
text2, = line2.children
assert text1.text == 'abcd'
2021-03-11 17:55:10 +03:00
assert text2.text == 'efgh'