mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-11-09 14:05:30 +03:00
Fix "pages" counter in non-root absolute boxes
These boxes were not available through the "descendants" function, because they are represented by placeholders. They are also not available in the list of absolute boxes of the page, because they’re not relative to the root page. The new "placeholders" parameter of "descendants" goes through placeholders and gets all the absolute children. Putting "descendants" and "children" in all boxes, not just parents, avoids useless checks and special cases. Fix #2029.
This commit is contained in:
parent
e018bf3fbb
commit
c6468e5395
@ -62,10 +62,9 @@ def test_breaking_linebox():
|
||||
for child in line.children:
|
||||
assert child.element_tag in ('em', 'p')
|
||||
assert child.style['font_size'] == 13
|
||||
if isinstance(child, boxes.ParentBox):
|
||||
for child_child in child.children:
|
||||
assert child.element_tag in ('em', 'strong', 'span')
|
||||
assert child.style['font_size'] == 13
|
||||
for child_child in child.children:
|
||||
assert child.element_tag in ('em', 'strong', 'span')
|
||||
assert child.style['font_size'] == 13
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
|
@ -167,7 +167,7 @@ def test_target_absolute():
|
||||
content: target-counter('#h', page);
|
||||
}
|
||||
div {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
<div><a id="span">link</a></div>
|
||||
@ -182,3 +182,32 @@ def test_target_absolute():
|
||||
text_box, after = inline.children
|
||||
assert text_box.text == 'link'
|
||||
assert after.children[0].text == '1'
|
||||
|
||||
|
||||
@assert_no_logs
|
||||
def test_target_absolute_non_root():
|
||||
document = FakeHTML(string='''
|
||||
<style>
|
||||
a::after {
|
||||
content: target-counter('#h', page);
|
||||
}
|
||||
section {
|
||||
position: relative;
|
||||
}
|
||||
div {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
<section><div><a id="span">link</a></div></section>
|
||||
<h1 id="h">abc</h1>
|
||||
''')
|
||||
page, = document.render().pages
|
||||
html, = page._page_box.children
|
||||
body, = html.children
|
||||
section, h1 = body.children
|
||||
div, = section.children
|
||||
line, = div.children
|
||||
inline, = line.children
|
||||
text_box, after = inline.children
|
||||
assert text_box.text == 'link'
|
||||
assert after.children[0].text == '1'
|
||||
|
@ -810,10 +810,9 @@ def draw_outlines(stream, box):
|
||||
stream, outline_box, 4 * (width,), style,
|
||||
styled_color(style, color, side))
|
||||
|
||||
if isinstance(box, boxes.ParentBox):
|
||||
for child in box.children:
|
||||
if isinstance(child, boxes.Box):
|
||||
draw_outlines(stream, child)
|
||||
for child in box.children:
|
||||
if isinstance(child, boxes.Box):
|
||||
draw_outlines(stream, child)
|
||||
|
||||
|
||||
def draw_table(stream, table):
|
||||
|
@ -82,13 +82,23 @@ class Box:
|
||||
|
||||
# Default, overriden on some subclasses
|
||||
def all_children(self):
|
||||
return ()
|
||||
return self.children
|
||||
|
||||
def descendants(self, placeholders=False):
|
||||
"""A flat generator for a box, its children and descendants."""
|
||||
yield self
|
||||
for child in self.children:
|
||||
if placeholders or isinstance(child, Box):
|
||||
yield from child.descendants(placeholders)
|
||||
else:
|
||||
yield child
|
||||
|
||||
def __init__(self, element_tag, style, element):
|
||||
self.element_tag = element_tag
|
||||
self.element = element
|
||||
self.style = style
|
||||
self.remove_decoration_sides = set()
|
||||
self.children = []
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{type(self).__name__} {self.element_tag}>'
|
||||
@ -332,9 +342,6 @@ class ParentBox(Box):
|
||||
super().__init__(element_tag, style, element)
|
||||
self.children = tuple(children)
|
||||
|
||||
def all_children(self):
|
||||
return self.children
|
||||
|
||||
def _reset_spacing(self, side):
|
||||
"""Set to 0 the margin, padding and border of ``side``."""
|
||||
self.remove_decoration_sides.add(side)
|
||||
@ -353,7 +360,7 @@ class ParentBox(Box):
|
||||
def copy_with_children(self, new_children):
|
||||
"""Create a new equivalent box with given ``new_children``."""
|
||||
new_box = self.copy()
|
||||
new_box.children = list(new_children)
|
||||
new_box.children = new_children
|
||||
|
||||
# Clear and reset removed decorations as we don't want to keep the
|
||||
# previous data, for example when a box is split between two pages.
|
||||
@ -363,19 +370,9 @@ class ParentBox(Box):
|
||||
|
||||
def deepcopy(self):
|
||||
result = self.copy()
|
||||
result.children = tuple(child.deepcopy() for child in self.children)
|
||||
result.children = list(child.deepcopy() for child in self.children)
|
||||
return result
|
||||
|
||||
def descendants(self):
|
||||
"""A flat generator for a box, its children and descendants."""
|
||||
yield self
|
||||
for child in self.children:
|
||||
if isinstance(child, ParentBox):
|
||||
for grand_child in child.descendants():
|
||||
yield grand_child
|
||||
else:
|
||||
yield child
|
||||
|
||||
def get_wrapped_table(self):
|
||||
"""Get the table wrapped by the box."""
|
||||
assert self.is_table_wrapper
|
||||
|
@ -1231,7 +1231,7 @@ def process_whitespace(box, following_collapsible_space=False):
|
||||
|
||||
box.text = text
|
||||
|
||||
elif isinstance(box, boxes.ParentBox):
|
||||
else:
|
||||
for child in box.children:
|
||||
if isinstance(child, (boxes.TextBox, boxes.InlineBox)):
|
||||
child_collapsible_space = process_whitespace(
|
||||
@ -1257,7 +1257,7 @@ def process_text_transform(box):
|
||||
if box.style['hyphens'] == 'none':
|
||||
box.text = box.text.replace('\u00AD', '') # U+00AD is soft hyphen
|
||||
|
||||
elif isinstance(box, boxes.ParentBox) and not box.is_running():
|
||||
elif not box.is_running():
|
||||
for child in box.children:
|
||||
if isinstance(child, (boxes.TextBox, boxes.InlineBox)):
|
||||
process_text_transform(child)
|
||||
@ -1316,7 +1316,7 @@ def inline_in_block(box):
|
||||
]
|
||||
|
||||
"""
|
||||
if not isinstance(box, boxes.ParentBox) or box.is_running():
|
||||
if not box.children or box.is_running():
|
||||
return box
|
||||
|
||||
box_children = list(box.children)
|
||||
@ -1451,7 +1451,7 @@ def block_in_inline(box):
|
||||
]
|
||||
|
||||
"""
|
||||
if not isinstance(box, boxes.ParentBox) or box.is_running():
|
||||
if not box.children or box.is_running():
|
||||
return box
|
||||
|
||||
new_children = []
|
||||
|
@ -316,10 +316,9 @@ class LayoutContext:
|
||||
for (string_name, _) in element.style['string_set']:
|
||||
if string_name == name:
|
||||
return first_string
|
||||
if isinstance(element, boxes.ParentBox):
|
||||
if element.children:
|
||||
element = element.children[0]
|
||||
continue
|
||||
if element.children:
|
||||
element = element.children[0]
|
||||
continue
|
||||
break
|
||||
elif keyword == 'last':
|
||||
return last_string
|
||||
|
@ -917,7 +917,7 @@ def block_level_page_break(sibling_before, sibling_after):
|
||||
box = sibling_before
|
||||
while isinstance(box, block_parallel_box_types):
|
||||
values.append(box.style['break_after'])
|
||||
if not (isinstance(box, boxes.ParentBox) and box.children):
|
||||
if not box.children:
|
||||
break
|
||||
box = box.children[-1]
|
||||
values.reverse() # Have them in tree order
|
||||
@ -925,7 +925,7 @@ def block_level_page_break(sibling_before, sibling_after):
|
||||
box = sibling_after
|
||||
while isinstance(box, block_parallel_box_types):
|
||||
values.append(box.style['break_before'])
|
||||
if not (isinstance(box, boxes.ParentBox) and box.children):
|
||||
if not box.children:
|
||||
break
|
||||
box = box.children[0]
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
"""Layout for pages and CSS3 margin boxes."""
|
||||
|
||||
import copy
|
||||
from itertools import chain
|
||||
from math import inf
|
||||
|
||||
from ..css import PageType, computed_from_cascaded
|
||||
@ -666,9 +665,6 @@ def make_page(context, root_box, page_type, resume_at, page_number,
|
||||
context.finish_block_formatting_context(root_box)
|
||||
|
||||
page.children = [root_box, footnote_area]
|
||||
descendants = chain(page.descendants(), *(
|
||||
child.descendants() if hasattr(child, 'descendants') else (child,)
|
||||
for child in positioned_boxes))
|
||||
|
||||
# Update page counter values
|
||||
_standardize_page_based_counters(style, None)
|
||||
@ -692,7 +688,7 @@ def make_page(context, root_box, page_type, resume_at, page_number,
|
||||
cached_anchors.extend(x_remake_state.get('anchors', []))
|
||||
cached_lookups.extend(x_remake_state.get('content_lookups', []))
|
||||
|
||||
for child in descendants:
|
||||
for child in page.descendants(placeholders=True):
|
||||
# Cache target's page counters
|
||||
anchor = child.style['anchor']
|
||||
if anchor and anchor not in cached_anchors:
|
||||
|
@ -845,14 +845,14 @@ def find_in_flow_baseline(box, last=False, baseline_types=(boxes.LineBox,)):
|
||||
# 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(
|
||||
box, boxes.TableCaptionBox):
|
||||
children = reversed(box.children) if last else box.children
|
||||
for child in children:
|
||||
if child.is_in_normal_flow():
|
||||
result = find_in_flow_baseline(child, last, baseline_types)
|
||||
if result is not None:
|
||||
return result
|
||||
elif isinstance(box, boxes.TableCaptionBox):
|
||||
return
|
||||
children = reversed(box.children) if last else box.children
|
||||
for child in children:
|
||||
if child.is_in_normal_flow():
|
||||
result = find_in_flow_baseline(child, last, baseline_types)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
|
||||
def distribute_excess_width(context, grid, excess_width, column_widths,
|
||||
|
Loading…
Reference in New Issue
Block a user