mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-05 08:27:22 +03:00
Merge branch 'master' into box-decoration-break
This commit is contained in:
commit
5dd6e4c2cd
@ -93,7 +93,7 @@ INITIAL_VALUES = {
|
||||
# Color 3 (REC): https://www.w3.org/TR/css3-color/
|
||||
'opacity': 1,
|
||||
|
||||
# Multi-column Layout (CR): https://www.w3.org/TR/css3-multicol/
|
||||
# Multi-column Layout (WD): https://www.w3.org/TR/css-multicol-1/
|
||||
'column_width': 'auto',
|
||||
'column_count': 'auto',
|
||||
'column_gap': Dimension(1, 'em'),
|
||||
@ -103,7 +103,7 @@ INITIAL_VALUES = {
|
||||
'column_fill': 'balance',
|
||||
'column_span': 'none',
|
||||
|
||||
# Fonts 3 (CR): https://www.w3.org/TR/css-fonts-3/
|
||||
# Fonts 3 (REC): https://www.w3.org/TR/css-fonts-3/
|
||||
'font_family': ('serif',), # depends on user agent
|
||||
'font_feature_settings': 'normal',
|
||||
'font_kerning': 'auto',
|
||||
@ -120,11 +120,12 @@ INITIAL_VALUES = {
|
||||
'font_variant_position': 'normal',
|
||||
'font_weight': 400,
|
||||
|
||||
# Fragmentation 3 (CR): https://www.w3.org/TR/css-break-3/
|
||||
# Fragmentation 3/4 (CR/WD): https://www.w3.org/TR/css-break-4/
|
||||
'box_decoration_break': 'slice',
|
||||
'break_after': 'auto',
|
||||
'break_before': 'auto',
|
||||
'break_inside': 'auto',
|
||||
'margin_break': 'auto',
|
||||
'orphans': 2,
|
||||
'widows': 2,
|
||||
|
||||
@ -139,7 +140,7 @@ INITIAL_VALUES = {
|
||||
'image_resolution': 1, # dppx
|
||||
'image_rendering': 'auto',
|
||||
|
||||
# Paged Media 3 (WD): https://www.w3.org/TR/css3-page/
|
||||
# Paged Media 3 (WD): https://www.w3.org/TR/css-page-3/
|
||||
'size': None, # set to A4 in computed_values
|
||||
'page': 'auto',
|
||||
'bleed_left': 'auto',
|
||||
@ -161,11 +162,11 @@ INITIAL_VALUES = {
|
||||
'white_space': 'normal',
|
||||
'word_spacing': 0, # computed value for 'normal'
|
||||
|
||||
# Transforms 1 (WD): https://www.w3.org/TR/css-transforms-1/
|
||||
# Transforms 1 (CR): https://www.w3.org/TR/css-transforms-1/
|
||||
'transform_origin': (Dimension(50, '%'), Dimension(50, '%')),
|
||||
'transform': (), # computed value for 'none'
|
||||
|
||||
# User Interface 3 (CR): https://www.w3.org/TR/css-ui-3/
|
||||
# User Interface 3 (REC): https://www.w3.org/TR/css-ui-3/
|
||||
'box_sizing': 'content-box',
|
||||
'outline_color': 'currentColor', # invert is not supported
|
||||
'outline_style': 'none',
|
||||
|
@ -172,7 +172,7 @@ def comma_separated_list(function):
|
||||
|
||||
|
||||
def get_keyword(token):
|
||||
"""If ``value`` is a keyword, return its name.
|
||||
"""If ``token`` is a keyword, return its lowercase name.
|
||||
|
||||
Otherwise return ``None``.
|
||||
|
||||
@ -181,6 +181,16 @@ def get_keyword(token):
|
||||
return token.lower_value
|
||||
|
||||
|
||||
def get_custom_ident(token):
|
||||
"""If ``token`` is a keyword, return its name.
|
||||
|
||||
Otherwise return ``None``.
|
||||
|
||||
"""
|
||||
if token.type == 'ident':
|
||||
return token.value
|
||||
|
||||
|
||||
def get_single_keyword(tokens):
|
||||
"""If ``values`` is a 1-element list of keywords, return its name.
|
||||
|
||||
@ -432,7 +442,7 @@ def check_counter_function(token, allowed_type=None):
|
||||
ident = args.pop(0)
|
||||
if ident.type != 'ident':
|
||||
return
|
||||
arguments.append(ident.lower_value)
|
||||
arguments.append(ident.value)
|
||||
|
||||
if name == 'counters':
|
||||
string = args.pop(0)
|
||||
@ -626,7 +636,7 @@ def get_target(token, base_url):
|
||||
ident = args.pop(0)
|
||||
if ident.type != 'ident':
|
||||
return
|
||||
values.append(ident.lower_value)
|
||||
values.append(ident.value)
|
||||
|
||||
if name == 'target-counters':
|
||||
string = get_string(args.pop(0))
|
||||
|
@ -17,9 +17,9 @@ from .. import computed_values
|
||||
from ..properties import KNOWN_PROPERTIES, Dimension
|
||||
from ..utils import (
|
||||
InvalidValues, comma_separated_list, get_angle, get_content_list,
|
||||
get_content_list_token, get_image, get_keyword, get_length, get_resolution,
|
||||
get_single_keyword, get_url, parse_2d_position, parse_background_position,
|
||||
parse_function, single_keyword, single_token)
|
||||
get_content_list_token, get_custom_ident, get_image, get_keyword,
|
||||
get_length, get_resolution, get_single_keyword, get_url, parse_2d_position,
|
||||
parse_background_position, parse_function, single_keyword, single_token)
|
||||
|
||||
PREFIX = '-weasy-'
|
||||
PROPRIETARY = set()
|
||||
@ -308,6 +308,13 @@ def box_decoration_break(keyword):
|
||||
return keyword in ('slice', 'clone')
|
||||
|
||||
|
||||
@property()
|
||||
@single_keyword
|
||||
def margin_break(keyword):
|
||||
"""``margin-break`` property validation."""
|
||||
return keyword in ('auto', 'keep', 'discard')
|
||||
|
||||
|
||||
@property(unstable=True)
|
||||
@single_token
|
||||
def page(token):
|
||||
@ -1306,14 +1313,14 @@ def string_set(tokens, base_url):
|
||||
"""Validation for ``string-set``."""
|
||||
# Spec asks for strings after custom keywords, but we allow content-lists
|
||||
if len(tokens) >= 2:
|
||||
var_name = get_keyword(tokens[0])
|
||||
var_name = get_custom_ident(tokens[0])
|
||||
if var_name is None:
|
||||
return
|
||||
parsed_tokens = tuple(
|
||||
get_content_list_token(token, base_url) for token in tokens[1:])
|
||||
if None not in parsed_tokens:
|
||||
return (var_name, parsed_tokens)
|
||||
elif tokens and tokens[0].value == 'none':
|
||||
elif tokens and get_keyword(tokens[0]) == 'none':
|
||||
return 'none'
|
||||
|
||||
|
||||
|
@ -204,6 +204,7 @@ class LayoutContext(object):
|
||||
self.excluded_shapes = None # Not initialized yet
|
||||
self.string_set = defaultdict(lambda: defaultdict(lambda: list()))
|
||||
self.current_page = None
|
||||
self.forced_break = False
|
||||
|
||||
# Cache
|
||||
self.strut_layouts = {}
|
||||
|
@ -41,6 +41,15 @@ def block_level_layout(context, box, max_position_y, skip_stack,
|
||||
if box.margin_bottom == 'auto':
|
||||
box.margin_bottom = 0
|
||||
|
||||
if (context.current_page > 1 and page_is_empty):
|
||||
# TODO: we should take care of cases when this box doesn't have
|
||||
# collapsing margins with the first child of the page, see
|
||||
# test_margin_break_clearance.
|
||||
if box.style['margin_break'] == 'discard':
|
||||
box.margin_top = 0
|
||||
elif box.style['margin_break'] == 'auto' and context.forced_break:
|
||||
box.margin_top = 0
|
||||
|
||||
collapsed_margin = collapse_margin(
|
||||
adjoining_margins + [box.margin_top])
|
||||
box.clearance = get_clearance(context, box, collapsed_margin)
|
||||
@ -438,11 +447,6 @@ def block_container_layout(context, box, max_position_y, skip_stack,
|
||||
page_name = block_level_page_name(last_in_flow_child, child)
|
||||
if page_name or page_break in (
|
||||
'page', 'left', 'right', 'recto', 'verso'):
|
||||
if page_break == 'page':
|
||||
page_break = 'any'
|
||||
elif page_break not in ('left', 'right', 'recto', 'verso'):
|
||||
assert page_name
|
||||
page_break = 'any'
|
||||
page_name = child.page_values()[0]
|
||||
next_page = {'break': page_break, 'page': page_name}
|
||||
resume_at = (index, None)
|
||||
|
@ -39,13 +39,18 @@ def flex_layout(context, box, max_position_y, skip_stack, containing_block,
|
||||
else:
|
||||
axis, cross = 'height', 'width'
|
||||
|
||||
margin_left = 0 if box.margin_left == 'auto' else box.margin_left
|
||||
margin_right = 0 if box.margin_right == 'auto' else box.margin_right
|
||||
margin_top = 0 if box.margin_top == 'auto' else box.margin_top
|
||||
margin_bottom = 0 if box.margin_bottom == 'auto' else box.margin_bottom
|
||||
|
||||
if getattr(box, axis) != 'auto':
|
||||
available_main_space = getattr(box, axis)
|
||||
else:
|
||||
if axis == 'width':
|
||||
available_main_space = (
|
||||
containing_block.width -
|
||||
box.margin_left - box.margin_right -
|
||||
margin_left - margin_right -
|
||||
box.padding_left - box.padding_right -
|
||||
box.border_left_width - box.border_right_width)
|
||||
else:
|
||||
@ -58,7 +63,7 @@ def flex_layout(context, box, max_position_y, skip_stack, containing_block,
|
||||
main_space = min(main_space, containing_block.height)
|
||||
available_main_space = (
|
||||
main_space -
|
||||
box.margin_top - box.margin_bottom -
|
||||
margin_top - margin_bottom -
|
||||
box.padding_top - box.padding_bottom -
|
||||
box.border_top_width - box.border_bottom_width)
|
||||
|
||||
@ -75,24 +80,28 @@ def flex_layout(context, box, max_position_y, skip_stack, containing_block,
|
||||
main_space = min(main_space, containing_block.height)
|
||||
available_cross_space = (
|
||||
main_space -
|
||||
box.margin_top - box.margin_bottom -
|
||||
margin_top - margin_bottom -
|
||||
box.padding_top - box.padding_bottom -
|
||||
box.border_top_width - box.border_bottom_width)
|
||||
else:
|
||||
available_cross_space = (
|
||||
containing_block.width -
|
||||
box.margin_left - box.margin_right -
|
||||
margin_left - margin_right -
|
||||
box.padding_left - box.padding_right -
|
||||
box.border_left_width - box.border_right_width)
|
||||
|
||||
# Step 3
|
||||
children = box.children
|
||||
resolve_percentages(box, containing_block)
|
||||
if box.margin_top == 'auto':
|
||||
box.margin_top = 0
|
||||
if box.margin_bottom == 'auto':
|
||||
box.margin_bottom = 0
|
||||
parent_box = box.copy_with_children(children)
|
||||
resolve_percentages(parent_box, containing_block)
|
||||
if parent_box.margin_top == 'auto':
|
||||
box.margin_top = 0
|
||||
if parent_box.margin_bottom == 'auto':
|
||||
box.margin_bottom = 0
|
||||
if parent_box.margin_left == 'auto':
|
||||
box.margin_left = 0
|
||||
if parent_box.margin_right == 'auto':
|
||||
box.margin_right = 0
|
||||
if isinstance(parent_box, boxes.FlexBox):
|
||||
blocks.block_level_width(parent_box, containing_block)
|
||||
else:
|
||||
@ -441,7 +450,7 @@ def flex_layout(context, box, max_position_y, skip_stack, containing_block,
|
||||
parent_box, device_size, page_is_empty, absolute_boxes,
|
||||
fixed_boxes, adjoining_margins=[]))
|
||||
|
||||
child._baseline = find_in_flow_baseline(new_child)
|
||||
child._baseline = find_in_flow_baseline(new_child) or 0
|
||||
if cross == 'height':
|
||||
child.height = new_child.height
|
||||
# As flex items margins never collapse (with other flex items
|
||||
@ -844,8 +853,9 @@ def flex_layout(context, box, max_position_y, skip_stack, containing_block,
|
||||
if axis == 'width': # and main text direction is horizontal
|
||||
box.baseline = flex_lines[0].lower_baseline if flex_lines else 0
|
||||
else:
|
||||
box.baseline = (
|
||||
find_in_flow_baseline(box.children[0]) if box.children else 0)
|
||||
box.baseline = ((
|
||||
find_in_flow_baseline(box.children[0])
|
||||
if box.children else 0) or 0)
|
||||
|
||||
context.finish_block_formatting_context(box)
|
||||
|
||||
|
@ -853,10 +853,9 @@ def split_inline_box(context, box, position_x, max_x, skip_stack,
|
||||
|
||||
# We have to check whether the child we're breaking
|
||||
# is the one broken by the initial skip stack.
|
||||
broken_child = bool(
|
||||
initial_skip_stack and
|
||||
initial_skip_stack[0] == child_index and
|
||||
initial_skip_stack[1])
|
||||
broken_child = same_broken_child(
|
||||
initial_skip_stack,
|
||||
(child_index, child_resume_at))
|
||||
if broken_child:
|
||||
# As this child has already been broken
|
||||
# following the original skip stack, we have to
|
||||
@ -1286,3 +1285,14 @@ def can_break_inside(box):
|
||||
else:
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def same_broken_child(skip_stack_1, skip_stack_2):
|
||||
"""Check that the skip stacks design the same text box."""
|
||||
while isinstance(skip_stack_1, tuple) and isinstance(skip_stack_2, tuple):
|
||||
if skip_stack_1[1] is None and skip_stack_2[1] is None:
|
||||
return True
|
||||
if skip_stack_1[0] != skip_stack_2[0]:
|
||||
return False
|
||||
skip_stack_1, skip_stack_2 = skip_stack_1[1], skip_stack_2[1]
|
||||
return False
|
||||
|
@ -545,6 +545,7 @@ def make_page(context, root_box, page_type, resume_at, page_number,
|
||||
# See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
|
||||
assert isinstance(root_box, (boxes.BlockBox, boxes.FlexContainerBox))
|
||||
context.create_block_formatting_context()
|
||||
context.current_page = page_number
|
||||
page_is_empty = True
|
||||
adjoining_margins = []
|
||||
positioned_boxes = [] # Mixed absolute and fixed
|
||||
@ -722,16 +723,20 @@ def remake_page(index, context, root_box, html, cascaded_styles,
|
||||
page_state = copy.deepcopy(initial_page_state)
|
||||
next_page_name = initial_next_page['page']
|
||||
first = index == 0
|
||||
# TODO: handle recto/verso and add test
|
||||
blank = ((initial_next_page['break'] == 'left' and right_page) or
|
||||
(initial_next_page['break'] == 'right' and not right_page))
|
||||
if blank:
|
||||
next_page_name = None
|
||||
side = 'right' if right_page else 'left'
|
||||
page_type = PageType(
|
||||
side, blank, first, name=(next_page_name or None))
|
||||
page_type = PageType(side, blank, first, name=(next_page_name or None))
|
||||
set_page_type_computed_styles(
|
||||
page_type, cascaded_styles, computed_styles, html)
|
||||
|
||||
context.forced_break = (
|
||||
initial_next_page['break'] != 'any' or initial_next_page['page'])
|
||||
context.margin_clearance = False
|
||||
|
||||
# make_page wants a page_number of index + 1
|
||||
page_number = index + 1
|
||||
page, resume_at, next_page = make_page(
|
||||
|
@ -656,6 +656,8 @@ def find_in_flow_baseline(box, last=False, baseline_types=(boxes.LineBox,)):
|
||||
Return the absolute Y position for the first (or last) in-flow baseline
|
||||
if any, or None.
|
||||
"""
|
||||
# TODO: synthetize baseline when needed
|
||||
# See https://www.w3.org/TR/css-align-3/#synthesize-baseline
|
||||
if isinstance(box, baseline_types):
|
||||
return box.position_y + box.baseline
|
||||
if isinstance(box, boxes.ParentBox) and not isinstance(
|
||||
|
@ -1065,6 +1065,25 @@ def test_counters_6():
|
||||
('p::marker', 'Text', '0.')])])])
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_counters_7():
|
||||
# Test that counters are case-sensitive
|
||||
# See https://github.com/Kozea/WeasyPrint/pull/827
|
||||
assert_tree(parse_all('''
|
||||
<style>
|
||||
p { counter-increment: p 2 }
|
||||
p:before { content: counter(p) '.' counter(P); }
|
||||
</style>
|
||||
<p></p>
|
||||
<p style="counter-increment: P 3"></p>
|
||||
<p></p>'''), [
|
||||
('p', 'Block', [
|
||||
('p', 'Line', [
|
||||
('p::before', 'Inline', [
|
||||
('p::before', 'Text', counter)])])])
|
||||
for counter in '2.0 2.3 4.3'.split()])
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_counter_styles_1():
|
||||
assert_tree(parse_all('''
|
||||
@ -1477,7 +1496,7 @@ def test_margin_box_string_set_7():
|
||||
# Test regression: https://github.com/Kozea/WeasyPrint/issues/722
|
||||
page_1, = render_pages('''
|
||||
<style>
|
||||
img { string-set: left attr(alt) }
|
||||
img { string-set: left attr(alt) }
|
||||
img + img { string-set: right attr(alt) }
|
||||
@page { @top-left { content: '[' string(left) ']' }
|
||||
@top-right { content: '{' string(right) '}' } }
|
||||
@ -1495,15 +1514,16 @@ def test_margin_box_string_set_7():
|
||||
assert right_text_box.text == '{Cake}'
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_margin_box_string_set_8():
|
||||
# Test regression: https://github.com/Kozea/WeasyPrint/issues/726
|
||||
page_1, page_2, page_3 = render_pages('''
|
||||
<style>
|
||||
@page { @top-left { content: '[' string(left) ']' } }
|
||||
p { page-break-before: always }
|
||||
.initial { -weasy-string-set: left 'initial' }
|
||||
.empty { -weasy-string-set: left '' }
|
||||
.space { -weasy-string-set: left ' ' }
|
||||
.initial { string-set: left 'initial' }
|
||||
.empty { string-set: left '' }
|
||||
.space { string-set: left ' ' }
|
||||
</style>
|
||||
|
||||
<p class="initial">Initial</p>
|
||||
@ -1526,6 +1546,31 @@ def test_margin_box_string_set_8():
|
||||
assert left_text_box.text == '[ ]'
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_margin_box_string_set_9():
|
||||
# Test that named strings are case-sensitive
|
||||
# See https://github.com/Kozea/WeasyPrint/pull/827
|
||||
page_1, = render_pages('''
|
||||
<style>
|
||||
@page {
|
||||
@top-center {
|
||||
content: string(text_header, first)
|
||||
' ' string(TEXT_header, first)
|
||||
}
|
||||
}
|
||||
p { string-set: text_header content() }
|
||||
div { string-set: TEXT_header content() }
|
||||
</style>
|
||||
<p>first assignment</p>
|
||||
<div>second assignment</div>
|
||||
''')
|
||||
|
||||
html, top_center = page_1.children
|
||||
line_box, = top_center.children
|
||||
text_box, = line_box.children
|
||||
assert text_box.text == 'first assignment second assignment'
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_page_counters():
|
||||
"""Test page-based counters."""
|
||||
|
@ -353,3 +353,20 @@ def test_flex_item_min_height():
|
||||
div_3.height ==
|
||||
article.height ==
|
||||
50)
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_flex_auto_margin():
|
||||
# Regression test for https://github.com/Kozea/WeasyPrint/issues/800
|
||||
page, = render_pages('<div style="display: flex; margin: auto">')
|
||||
page, = render_pages(
|
||||
'<div style="display: flex; flex-direction: column; margin: auto">')
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_flex_no_baseline():
|
||||
# Regression test for https://github.com/Kozea/WeasyPrint/issues/765
|
||||
page, = render_pages('''
|
||||
<div class="references" style="display: flex; align-items: baseline;">
|
||||
<div></div>
|
||||
</div>''')
|
||||
|
@ -238,9 +238,9 @@ def test_breaking_linebox_regression_2():
|
||||
def test_breaking_linebox_regression_3():
|
||||
# Regression test #1 for https://github.com/Kozea/WeasyPrint/issues/560
|
||||
page, = parse(
|
||||
'<style>@font-face { src: url(AHEM____.TTF); font-family: ahem }</style>'
|
||||
'<div style="width: 5.5em; font-family: ahem">'
|
||||
'aaaa aaaa a [<span>aaa</span>]')
|
||||
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
||||
'<div style="width: 5.5em; font-family: ahem">'
|
||||
'aaaa aaaa a [<span>aaa</span>]')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
div, = body.children
|
||||
@ -257,9 +257,9 @@ def test_breaking_linebox_regression_3():
|
||||
def test_breaking_linebox_regression_4():
|
||||
# Regression test #2 for https://github.com/Kozea/WeasyPrint/issues/560
|
||||
page, = parse(
|
||||
'<style>@font-face { src: url(AHEM____.TTF); font-family: ahem }</style>'
|
||||
'<div style="width: 5.5em; font-family: ahem">'
|
||||
'aaaa a <span>b c</span>d')
|
||||
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
||||
'<div style="width: 5.5em; font-family: ahem">'
|
||||
'aaaa a <span>b c</span>d')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
div, = body.children
|
||||
@ -275,9 +275,9 @@ def test_breaking_linebox_regression_4():
|
||||
def test_breaking_linebox_regression_5():
|
||||
# Regression test for https://github.com/Kozea/WeasyPrint/issues/580
|
||||
page, = parse(
|
||||
'<style>@font-face { src: url(AHEM____.TTF); font-family: ahem }</style>'
|
||||
'<div style="width: 5.5em; font-family: ahem">'
|
||||
'<span>aaaa aaaa a a a</span><span>bc</span>')
|
||||
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
||||
'<div style="width: 5.5em; font-family: ahem">'
|
||||
'<span>aaaa aaaa a a a</span><span>bc</span>')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
div, = body.children
|
||||
@ -293,9 +293,9 @@ def test_breaking_linebox_regression_5():
|
||||
def test_breaking_linebox_regression_6():
|
||||
# Regression test for https://github.com/Kozea/WeasyPrint/issues/586
|
||||
page, = parse(
|
||||
'<style>@font-face { src: url(AHEM____.TTF); font-family: ahem }</style>'
|
||||
'<div style="width: 5.5em; font-family: ahem">'
|
||||
'a a <span style="white-space: nowrap">/ccc</span>')
|
||||
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
||||
'<div style="width: 5.5em; font-family: ahem">'
|
||||
'a a <span style="white-space: nowrap">/ccc</span>')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
div, = body.children
|
||||
@ -308,9 +308,9 @@ def test_breaking_linebox_regression_6():
|
||||
def test_breaking_linebox_regression_7():
|
||||
# Regression test for https://github.com/Kozea/WeasyPrint/issues/660
|
||||
page, = parse(
|
||||
'<style>@font-face { src: url(AHEM____.TTF); font-family: ahem }</style>'
|
||||
'<div style="width: 3.5em; font-family: ahem">'
|
||||
'<span><span>abc d e</span></span><span>f')
|
||||
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
||||
'<div style="width: 3.5em; font-family: ahem">'
|
||||
'<span><span>abc d e</span></span><span>f')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
div, = body.children
|
||||
@ -321,6 +321,46 @@ def test_breaking_linebox_regression_7():
|
||||
assert line3.children[1].children[0].text == 'f'
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_breaking_linebox_regression_8():
|
||||
# Regression test for https://github.com/Kozea/WeasyPrint/issues/783
|
||||
page, = parse(
|
||||
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
||||
'<p style="font-family: ahem"><span>\n'
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'
|
||||
'bbbbbbbbbbb\n'
|
||||
'<b>cccc</b></span>ddd</p>')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
p, = body.children
|
||||
line1, line2 = p.children
|
||||
assert line1.children[0].children[0].text == (
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbb')
|
||||
assert line2.children[0].children[0].children[0].text == 'cccc'
|
||||
assert line2.children[1].text == 'ddd'
|
||||
|
||||
|
||||
@pytest.mark.xfail
|
||||
@assert_no_logs
|
||||
def test_breaking_linebox_regression_9():
|
||||
# Regression test for https://github.com/Kozea/WeasyPrint/issues/783
|
||||
# TODO: inlines.can_break_inside return False for span but we can break
|
||||
# before the <b> tag. can_break_inside should be fixed.
|
||||
page, = parse(
|
||||
'<style>@font-face {src: url(AHEM____.TTF); font-family: ahem}</style>'
|
||||
'<p style="font-family: ahem"><span>\n'
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbb\n'
|
||||
'<b>cccc</b></span>ddd</p>')
|
||||
html, = page.children
|
||||
body, = html.children
|
||||
p, = body.children
|
||||
line1, line2 = p.children
|
||||
assert line1.children[0].children[0].text == (
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbb')
|
||||
assert line2.children[0].children[0].children[0].text == 'cccc'
|
||||
assert line2.children[1].text == 'ddd'
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_linebox_text():
|
||||
page, = parse('''
|
||||
|
@ -428,6 +428,75 @@ def test_page_breaks_complex_8():
|
||||
assert div_4.height == 20
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
@pytest.mark.parametrize('break_after, margin_break, margin_top', (
|
||||
('page', 'auto', 0),
|
||||
('auto', 'auto', 5),
|
||||
('page', 'keep', 5),
|
||||
('auto', 'keep', 5),
|
||||
('page', 'discard', 0),
|
||||
('auto', 'discard', 0),
|
||||
))
|
||||
def test_margin_break(break_after, margin_break, margin_top):
|
||||
page_1, page_2 = render_pages('''
|
||||
<style>
|
||||
@page { size: 70px; margin: 0 }
|
||||
div { height: 63px; margin: 5px 0 8px;
|
||||
break-after: %s; margin-break: %s }
|
||||
</style>
|
||||
<section>
|
||||
<div></div>
|
||||
</section>
|
||||
<section>
|
||||
<div></div>
|
||||
</section>
|
||||
''' % (break_after, margin_break))
|
||||
html, = page_1.children
|
||||
body, = html.children
|
||||
section, = body.children
|
||||
div, = section.children
|
||||
assert div.margin_top == 5
|
||||
|
||||
html, = page_2.children
|
||||
body, = html.children
|
||||
section, = body.children
|
||||
div, = section.children
|
||||
assert div.margin_top == margin_top
|
||||
|
||||
|
||||
@pytest.mark.xfail
|
||||
@assert_no_logs
|
||||
def test_margin_break_clearance():
|
||||
page_1, page_2 = render_pages('''
|
||||
<style>
|
||||
@page { size: 70px; margin: 0 }
|
||||
div { height: 63px; margin: 5px 0 8px; break-after: page }
|
||||
</style>
|
||||
<section>
|
||||
<div></div>
|
||||
</section>
|
||||
<section>
|
||||
<div style="border-top: 1px solid black">
|
||||
<div></div>
|
||||
</div>
|
||||
</section>
|
||||
''')
|
||||
html, = page_1.children
|
||||
body, = html.children
|
||||
section, = body.children
|
||||
div, = section.children
|
||||
assert div.margin_top == 5
|
||||
|
||||
html, = page_2.children
|
||||
body, = html.children
|
||||
section, = body.children
|
||||
div_1, = section.children
|
||||
assert div_1.margin_top == 0
|
||||
div_2, = div_1.children
|
||||
assert div_2.margin_top == 5
|
||||
assert div_2.content_box_y() == 5
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_page_names_1():
|
||||
pages = render_pages('''
|
||||
|
Loading…
Reference in New Issue
Block a user