2012-03-22 02:19:27 +04:00
|
|
|
|
"""
|
|
|
|
|
weasyprint.tests.test_text
|
|
|
|
|
--------------------------
|
2011-07-07 12:08:22 +04:00
|
|
|
|
|
2012-03-22 02:19:27 +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
|
|
|
|
|
|
2019-03-01 13:38:03 +03:00
|
|
|
|
from ..css.properties import INITIAL_VALUES
|
|
|
|
|
from ..text import split_first_line
|
2019-03-10 21:34:16 +03:00
|
|
|
|
from .test_boxes import render_pages
|
|
|
|
|
from .testing_utils import MONO_FONTS, SANS_FONTS, assert_no_logs
|
2012-07-28 21:23:47 +04:00
|
|
|
|
|
|
|
|
|
|
2012-06-21 17:10:17 +04:00
|
|
|
|
def make_text(text, width=None, **style):
|
2018-01-13 19:41:08 +03:00
|
|
|
|
"""Wrapper for split_first_line() creating a style dict."""
|
2017-07-28 14:35:06 +03:00
|
|
|
|
new_style = dict(INITIAL_VALUES)
|
2018-03-30 01:13:36 +03:00
|
|
|
|
new_style['font_family'] = MONO_FONTS.split(',')
|
2016-11-01 04:32:06 +03:00
|
|
|
|
new_style.update(style)
|
2012-11-24 19:14:49 +04:00
|
|
|
|
return split_first_line(
|
2018-01-13 19:41:08 +03:00
|
|
|
|
text, new_style, context=None, max_width=width,
|
2017-10-21 17:56:25 +03:00
|
|
|
|
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():
|
2012-07-28 21:23:47 +04:00
|
|
|
|
for width, remaining in [(100, 'text for test'),
|
2011-10-13 20:23:49 +04:00
|
|
|
|
(45, 'is a text for test')]:
|
2011-09-27 16:48:52 +04:00
|
|
|
|
text = 'This is a text for test'
|
2012-06-21 17:10:17 +04:00
|
|
|
|
_, length, resume_at, _, _, _ = make_text(
|
2018-03-30 01:13:36 +03:00
|
|
|
|
text, width, font_family=SANS_FONTS.split(','), font_size=19)
|
2011-10-08 20:31:15 +04:00
|
|
|
|
assert text[resume_at:] == remaining
|
2012-11-23 02:52:50 +04:00
|
|
|
|
assert length + 1 == resume_at # +1 is for the removed trailing space
|
2011-07-07 12:08:22 +04:00
|
|
|
|
|
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-07-12 20:03:22 +04:00
|
|
|
|
def test_line_with_any_width():
|
2012-06-21 17:10:17 +04:00
|
|
|
|
_, _, _, width_1, _, _ = make_text('some text')
|
|
|
|
|
_, _, _, width_2, _, _ = make_text('some text some text')
|
|
|
|
|
assert width_1 < width_2
|
2011-07-12 20:03: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 18:40:27 +04:00
|
|
|
|
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
|
|
|
|
|
2011-09-29 20:36:55 +04:00
|
|
|
|
# These two tests do not really rely on installed fonts
|
2012-06-21 17:10:17 +04:00
|
|
|
|
_, _, resume_at, _, _, _ = make_text(string, 90, font_size=1)
|
2011-10-08 20:31:15 +04:00
|
|
|
|
assert resume_at is None
|
2011-07-19 13:38:54 +04:00
|
|
|
|
|
2012-06-21 17:10:17 +04:00
|
|
|
|
_, _, resume_at, _, _, _ = make_text(string, 90, font_size=100)
|
2019-04-12 17:04:25 +03:00
|
|
|
|
assert string.encode('utf-8')[resume_at:].decode('utf-8') == (
|
|
|
|
|
'is a text for test')
|
2011-07-07 12:08:22 +04:00
|
|
|
|
|
2018-03-30 01:13:36 +03:00
|
|
|
|
_, _, resume_at, _, _, _ = make_text(
|
|
|
|
|
string, 100, font_family=SANS_FONTS.split(','), font_size=19)
|
2019-04-12 17:04:25 +03:00
|
|
|
|
assert string.encode('utf-8')[resume_at:].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_at, _, _, _ = make_text(string, 90, font_size=1)
|
|
|
|
|
assert resume_at is None
|
|
|
|
|
|
|
|
|
|
_, _, resume_at, _, _, _ = make_text(string, 90, font_size=100)
|
|
|
|
|
assert string.encode('utf-8')[resume_at:].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():
|
2012-02-17 21:49:58 +04:00
|
|
|
|
string = 'This is a text for test. This is a test for text.py'
|
2012-06-21 17:10:17 +04:00
|
|
|
|
_, _, _, width_1, height_1, _ = make_text(string, 200, font_size=12)
|
2011-09-27 17:19:31 +04:00
|
|
|
|
|
2012-06-21 17:10:17 +04:00
|
|
|
|
_, _, _, width_2, height_2, _ = make_text(string, 200, font_size=20)
|
2011-10-08 20:31:15 +04:00
|
|
|
|
assert width_1 * height_1 < width_2 * height_2
|
2011-07-19 13:38:54 +04:00
|
|
|
|
|
2011-08-26 15:56:00 +04:00
|
|
|
|
|
2012-02-20 16:04:35 +04:00
|
|
|
|
@assert_no_logs
|
2011-08-26 15:56:00 +04:00
|
|
|
|
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>
|
2011-08-26 15:56:00 +04:00
|
|
|
|
''')
|
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
|
2011-10-05 15:01:44 +04:00
|
|
|
|
assert paragraph.height == 0
|
2013-03-13 16:49:51 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@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>
|
2013-03-13 16:49:51 +04:00
|
|
|
|
''')
|
2018-03-14 03:32:01 +03:00
|
|
|
|
html, = page.children
|
|
|
|
|
body, = html.children
|
|
|
|
|
paragraph, = body.children
|
2013-03-13 16:49:51 +04:00
|
|
|
|
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
|
|
|
|
|
2019-05-17 17:18:56 +03:00
|
|
|
|
@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():
|
2018-01-21 18:08:29 +03:00
|
|
|
|
# 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. -->''')
|
2018-01-21 18:08:29 +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 == 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
|
|
|
|
|
2019-01-22 02:28:37 +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(AHEM____.TTF); font-family: ahem}
|
|
|
|
|
p { text-align: justify; font-family: ahem; 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;'
|
|
|
|
|
' max-width: ' + str(strong_3.width * 1.5) + 'px'
|
|
|
|
|
'}'
|
|
|
|
|
' 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
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
2020-03-17 01:55:39 +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(AHEM____.TTF); font-family: ahem }
|
|
|
|
|
p { display: inline-block; text-indent: 1em; font-family: ahem }
|
|
|
|
|
</style>
|
|
|
|
|
<p><span>text
|
|
|
|
|
''')
|
|
|
|
|
html, = page.children
|
|
|
|
|
body, = html.children
|
|
|
|
|
paragraph, = body.children
|
|
|
|
|
line, = paragraph.children
|
|
|
|
|
assert line.width == (4 + 1) * 16
|
|
|
|
|
|
|
|
|
|
|
2018-10-26 19:49:40 +03:00
|
|
|
|
@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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 5em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 5em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 5em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 5em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 5em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 5em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF);'
|
|
|
|
|
' font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<body style="hyphens: manual;'
|
|
|
|
|
'hyphenate-character: \'%s\'"'
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'lang=fr>%s' % (hyphenate_character, word))
|
|
|
|
|
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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 5em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF);'
|
|
|
|
|
' font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<body style="hyphens: manual;'
|
|
|
|
|
'hyphenate-character: \'%s\'"'
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'lang=fr>%s' % (hyphenate_character, word))
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2019-01-02 19:45:56 +03:00
|
|
|
|
@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­lighten­lighten­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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 12em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 12em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 12em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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(
|
2016-08-20 23:58:43 +03:00
|
|
|
|
'<html style="width: 12em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2017-08-17 08:42:42 +03:00
|
|
|
|
'<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):
|
|
|
|
|
page, = render_pages((
|
|
|
|
|
'<html style="width: 1em; font-family: ahem">'
|
2018-03-27 01:44:33 +03:00
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
2018-03-20 00:35:15 +03:00
|
|
|
|
'<body style="hyphens: auto;'
|
|
|
|
|
'hyphenate-limit-chars: %s" lang=en>'
|
|
|
|
|
'hyphen') % css)
|
|
|
|
|
html, = page.children
|
|
|
|
|
body, = html.children
|
|
|
|
|
lines = body.children
|
|
|
|
|
assert len(lines) == result
|
|
|
|
|
|
2016-08-20 23:58:43 +03:00
|
|
|
|
|
2019-01-02 19:45:56 +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: ahem">'
|
|
|
|
|
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
|
|
|
|
'<body style="hyphens: auto;'
|
|
|
|
|
'hyphenate-limit-chars: %s" lang=en>'
|
|
|
|
|
'..lighten..' % css)
|
|
|
|
|
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>
|
2018-03-27 01:44:33 +03:00
|
|
|
|
@font-face {src: url(AHEM____.TTF); font-family: ahem}
|
2018-03-20 00:35:15 +03:00
|
|
|
|
body {width: 80px; overflow: hidden; font-family: ahem; }
|
|
|
|
|
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>
|
|
|
|
|
body { font-size: 100px; width: %ipx }
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
2019-02-28 18:36:46 +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>
|
2018-03-27 01:44:33 +03:00
|
|
|
|
@font-face {src: url(AHEM____.TTF); font-family: ahem}
|
2018-03-20 00:35:15 +03:00
|
|
|
|
pre { tab-size: %s; font-family: ahem }
|
|
|
|
|
</style>
|
|
|
|
|
<pre>a	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>hé lO1</p><p>hé lO1</p><p>hé lO1</p><p>hé lO1</p><p>hé 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'
|
2019-03-01 18:03:04 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@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>
|
|
|
|
|
''')
|