Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support of column breaks #1619

Merged
merged 5 commits into from
Apr 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ The ``column-gap``, ``column-rule-color``, ``column-rule-style`` and
are supported.

The ``break-before``, ``break-after`` and ``break-inside`` properties are
**not** supported.
supported.

The ``column-span`` property is supported for direct children of columns.

Expand Down
223 changes: 208 additions & 15 deletions tests/layout/test_column.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def test_column_span_1():
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
body { margin: 0; font-family: weasyprint; line-height: 1 }
div { columns: 2; width: 10em; column-gap: 0 }
div { columns: 2; width: 10em; column-gap: 0; orphans: 1; widows: 1 }
section { column-span: all; margin: 1em 0 }
</style>

Expand Down Expand Up @@ -158,10 +158,10 @@ def test_columns_multipage():
assert len(columns) == 2
assert len(columns[0].children) == 2
assert len(columns[1].children) == 2
columns[0].children[0].children[0].text == 'a'
columns[0].children[1].children[0].text == 'b'
columns[1].children[0].children[0].text == 'c'
columns[1].children[1].children[0].text == 'd'
assert columns[0].children[0].children[0].text == 'a'
assert columns[0].children[1].children[0].text == 'b'
assert columns[1].children[0].children[0].text == 'c'
assert columns[1].children[1].children[0].text == 'd'

html, = page2.children
body, = html.children
Expand All @@ -170,13 +170,13 @@ def test_columns_multipage():
assert len(columns) == 2
assert len(columns[0].children) == 2
assert len(columns[1].children) == 1
columns[0].children[0].children[0].text == 'e'
columns[0].children[1].children[0].text == 'f'
columns[1].children[0].children[0].text == 'g'
assert columns[0].children[0].children[0].text == 'e'
assert columns[0].children[1].children[0].text == 'f'
assert columns[1].children[0].children[0].text == 'g'


@assert_no_logs
def test_column_breaks():
def test_columns_breaks():
page1, page2 = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
Expand All @@ -195,16 +195,209 @@ def test_column_breaks():
assert len(columns) == 2
assert len(columns[0].children) == 1
assert len(columns[1].children) == 1
columns[0].children[0].children[0].children[0].text == 'a'
columns[1].children[0].children[0].children[0].text == 'b'
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[1].children[0].children[0].children[0].text == 'b'

html, = page2.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 1
assert len(columns[0].children) == 1
columns[0].children[0].children[0].children[0].text == 'c'
assert columns[0].children[0].children[0].children[0].text == 'c'


@assert_no_logs
def test_columns_break_after_column_1():
page1, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 2; column-gap: 1px }
body { margin: 0; font-family: weasyprint;
font-size: 1px; line-height: 1px; orphans: 1; widows: 1 }
@page { margin: 0; size: 3px 10px }
section { break-after: column }
</style>
<div>a b <section>c</section> d</div>
''')
html, = page1.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 2
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[0].children[0].children[1].children[0].text == 'b'
assert columns[0].children[1].children[0].children[0].text == 'c'
assert columns[1].children[0].children[0].children[0].text == 'd'


@assert_no_logs
def test_columns_break_after_column_2():
page1, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 2; column-gap: 1px }
body { margin: 0; font-family: weasyprint;
font-size: 1px; line-height: 1px; orphans: 1; widows: 1 }
@page { margin: 0; size: 3px 10px }
section { break-after: column }
</style>
<div><section>a</section> b c d</div>
''')
html, = page1.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 2
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[1].children[0].children[0].children[0].text == 'b'
assert columns[1].children[0].children[1].children[0].text == 'c'
assert columns[1].children[0].children[2].children[0].text == 'd'


@assert_no_logs
def test_columns_break_after_avoid_column():
page1, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 2; column-gap: 1px }
body { margin: 0; font-family: weasyprint;
font-size: 1px; line-height: 1px; orphans: 1; widows: 1 }
@page { margin: 0; size: 3px 10px }
section { break-after: avoid-column }
</style>
<div>a <section>b</section> c d</div>
''')
html, = page1.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 2
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[0].children[1].children[0].children[0].text == 'b'
assert columns[0].children[2].children[0].children[0].text == 'c'
assert columns[1].children[0].children[0].children[0].text == 'd'


@assert_no_logs
def test_columns_break_before_column_1():
page1, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 2; column-gap: 1px }
body { margin: 0; font-family: weasyprint;
font-size: 1px; line-height: 1px; orphans: 1; widows: 1 }
@page { margin: 0; size: 3px 10px }
section { break-before: column }
</style>
<div>a b c <section>d</section></div>
''')
html, = page1.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 2
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[0].children[0].children[1].children[0].text == 'b'
assert columns[0].children[0].children[2].children[0].text == 'c'
assert columns[1].children[0].children[0].children[0].text == 'd'


@assert_no_logs
def test_columns_break_before_column_2():
page1, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 2; column-gap: 1px }
body { margin: 0; font-family: weasyprint;
font-size: 1px; line-height: 1px; orphans: 1; widows: 1 }
@page { margin: 0; size: 3px 10px }
section { break-before: column }
</style>
<div>a <section>b</section> c d</div>
''')
html, = page1.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 2
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[1].children[0].children[0].children[0].text == 'b'
assert columns[1].children[1].children[0].children[0].text == 'c'
assert columns[1].children[1].children[1].children[0].text == 'd'


@assert_no_logs
def test_columns_break_before_avoid_column():
page1, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 2; column-gap: 1px }
body { margin: 0; font-family: weasyprint;
font-size: 1px; line-height: 1px; orphans: 1; widows: 1 }
@page { margin: 0; size: 3px 10px }
section { break-before: avoid-column }
</style>
<div>a b <section>c</section> d</div>
''')
html, = page1.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 2
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[0].children[0].children[1].children[0].text == 'b'
assert columns[0].children[1].children[0].children[0].text == 'c'
assert columns[1].children[0].children[0].children[0].text == 'd'


@pytest.mark.xfail
@assert_no_logs
def test_columns_break_inside_column_1():
page1, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 2; column-gap: 1px }
body { margin: 0; font-family: weasyprint;
font-size: 1px; line-height: 1px; orphans: 1; widows: 1 }
@page { margin: 0; size: 3px 10px }
section { break-inside: avoid-column }
</style>
<div><section>a b c</section> d</div>
''')
html, = page1.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 2
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[0].children[0].children[1].children[0].text == 'b'
assert columns[0].children[0].children[2].children[0].text == 'c'
assert columns[1].children[0].children[0].children[0].text == 'd'


@assert_no_logs
def test_columns_break_inside_column_2():
page1, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 2; column-gap: 1px }
body { margin: 0; font-family: weasyprint;
font-size: 1px; line-height: 1px; orphans: 1; widows: 1 }
@page { margin: 0; size: 3px 10px }
section { break-inside: avoid-column }
</style>
<div>a <section>b c d</section></div>
''')
html, = page1.children
body, = html.children
div, = body.children
columns = div.children
assert len(columns) == 2
assert columns[0].children[0].children[0].children[0].text == 'a'
assert columns[1].children[0].children[0].children[0].text == 'b'
assert columns[1].children[0].children[1].children[0].text == 'c'
assert columns[1].children[0].children[2].children[0].text == 'd'


@assert_no_logs
Expand Down Expand Up @@ -256,7 +449,7 @@ def test_columns_fixed_height(prop):
page, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 4; column-gap: 0; %s: 10px }
div { columns: 4; column-gap: 0; %s: 10px; orphans: 1; widows: 1 }
body { margin: 0; font-family: weasyprint; line-height: 1px }
@page { margin: 0; size: 4px 50px; font-size: 1px }
</style>
Expand All @@ -279,7 +472,7 @@ def test_columns_padding():
page, = render_pages('''
<style>
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
div { columns: 4; column-gap: 0; padding: 1px }
div { columns: 4; column-gap: 0; padding: 1px; orphans: 1; widows: 1 }
body { margin: 0; font-family: weasyprint; line-height: 1px }
@page { margin: 0; size: 6px 50px; font-size: 1px }
</style>
Expand Down Expand Up @@ -307,7 +500,7 @@ def test_columns_relative():
@font-face { src: url(weasyprint.otf); font-family: weasyprint }
article { position: absolute; top: 3px }
div { columns: 4; column-gap: 0; position: relative;
top: 1px; left: 2px }
top: 1px; left: 2px; orphans: 1; widows: 1 }
body { margin: 0; font-family: weasyprint; line-height: 1px }
@page { margin: 0; size: 4px 50px; font-size: 1px }
</style>
Expand Down
2 changes: 0 additions & 2 deletions weasyprint/css/computed_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,6 @@ def length_tuple(style, name, values):
@register_computer('break-before')
def break_before_after(style, name, value):
"""Compute the ``break-before`` and ``break-after`` properties."""
# 'always' is defined as an alias to 'page' in multi-column
# https://www.w3.org/TR/css3-multicol/#column-breaks
if value == 'always':
return 'page'
else:
Expand Down
2 changes: 0 additions & 2 deletions weasyprint/css/validation/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,6 @@ def border_style(keyword):
@single_keyword
def break_before_after(keyword):
"""``break-before`` and ``break-after`` properties validation."""
# 'always' is defined as an alias to 'page' in multi-column
# https://www.w3.org/TR/css3-multicol/#column-breaks
return keyword in ('auto', 'avoid', 'avoid-page', 'page', 'left', 'right',
'recto', 'verso', 'avoid-column', 'column', 'always')

Expand Down
1 change: 1 addition & 0 deletions weasyprint/layout/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ def __init__(self, style_for, get_image_from_uri, font_config,
self.current_page = None
self.forced_break = False
self.broken_out_of_flow = []
self.in_column = False

# Cache
self.strut_layouts = {}
Expand Down
Loading