Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def resolve_percentages(box, containing_block, main_flex_direction=None):
"""Set used values as attributes of the box object."""
if isinstance(containing_block, boxes.Box):
# cb is short for containing block
cb_width = containing_block.width
cb_height = containing_block.height
else:
cb_width, cb_height = containing_block
if isinstance(box, boxes.PageBox):
maybe_height = cb_height
else:
maybe_height = cb_width
resolve_one_percentage(box, 'margin_left', cb_width)
resolve_one_percentage(box, 'margin_right', cb_width)
resolve_one_percentage(box, 'margin_top', maybe_height)
resolve_one_percentage(box, 'margin_bottom', maybe_height)
resolve_one_percentage(box, 'padding_left', cb_width)
resolve_one_percentage(box, 'padding_right', cb_width)
resolve_one_percentage(box, 'padding_top', maybe_height)
"""
import copy
import re
import unicodedata
import tinycss2.color3
from .. import html
from ..css import computed_values, properties
from ..logger import LOGGER
from . import boxes, counters
# Maps values of the ``display`` CSS property to box types.
BOX_TYPE_FROM_DISPLAY = {
'block': boxes.BlockBox,
'list-item': boxes.BlockBox,
'inline': boxes.InlineBox,
'inline-block': boxes.InlineBlockBox,
'table': boxes.TableBox,
'inline-table': boxes.InlineTableBox,
'table-row': boxes.TableRowBox,
'table-row-group': boxes.TableRowGroupBox,
'table-header-group': boxes.TableRowGroupBox,
'table-footer-group': boxes.TableRowGroupBox,
'table-column': boxes.TableColumnBox,
'table-column-group': boxes.TableColumnGroupBox,
'table-cell': boxes.TableCellBox,
'table-caption': boxes.TableCaptionBox,
'flex': boxes.FlexBox,
'inline-flex': boxes.InlineFlexBox,
}
first_letter_style, [])
text_box = boxes.TextBox(
'%s::first-letter' % box.element_tag,
letter_box.style.inherit_from(), first_letter)
letter_box.children = (text_box,)
box.children = (letter_box,) + tuple(box.children)
else:
letter_box = boxes.BlockBox(
'%s::first-letter' % box.element_tag,
first_letter_style, [])
letter_box.first_letter_style = None
line_box = boxes.LineBox(
'%s::first-letter' % box.element_tag,
letter_box.style.inherit_from(), [])
letter_box.children = (line_box,)
text_box = boxes.TextBox(
'%s::first-letter' % box.element_tag,
letter_box.style.inherit_from(), first_letter)
line_box.children = (text_box,)
box.children = (letter_box,) + tuple(box.children)
if skip_stack and child_skip_stack:
skip_stack = (skip_stack[0], (
child_skip_stack[0] + 1, child_skip_stack[1]))
elif isinstance(child, boxes.ParentBox):
if skip_stack:
child_skip_stack = skip_stack[1]
else:
child_skip_stack = None
child_skip_stack = first_letter_to_box(
child, child_skip_stack, first_letter_style)
if skip_stack:
skip_stack = (skip_stack[0], child_skip_stack)
# XXX: how does border-radius work on pages?
clipped_boxes = [box.rounded_border_box()]
elif isinstance(box, boxes.TableRowGroupBox):
clipped_boxes = []
total_height = 0
for row in box.children:
if row.children:
clipped_boxes += [
cell.rounded_border_box() for cell in row.children]
total_height = max(total_height, max(
cell.border_box_y() + cell.border_height()
for cell in row.children))
painting_area = [
box.border_box_x(), box.border_box_y(),
box.border_box_x() + box.border_width(), total_height]
elif isinstance(box, boxes.TableRowBox):
if box.children:
clipped_boxes = [
cell.rounded_border_box() for cell in box.children]
height = max(
cell.border_height() for cell in box.children)
painting_area = [
box.border_box_x(), box.border_box_y(),
box.border_box_x() + box.border_width(),
box.border_box_y() + height]
elif isinstance(box, (boxes.TableColumnGroupBox, boxes.TableColumnBox)):
cells = box.get_cells()
if cells:
clipped_boxes = [cell.rounded_border_box() for cell in cells]
max_x = max(
cell.border_box_x() + cell.border_width()
for cell in cells)
def atomic_box(context, box, position_x, skip_stack, containing_block,
absolute_boxes, fixed_boxes):
"""Compute the width and the height of the atomic ``box``."""
if isinstance(box, boxes.ReplacedBox):
box = box.copy()
inline_replaced_box_layout(box, containing_block)
box.baseline = box.margin_height()
elif isinstance(box, boxes.InlineBlockBox):
if box.is_table_wrapper:
table_wrapper_width(
context, box,
(containing_block.width, containing_block.height))
box = inline_block_box_layout(
context, box, position_x, skip_stack, containing_block,
absolute_boxes, fixed_boxes)
else: # pragma: no cover
raise TypeError('Layout for %s not handled yet' % type(box).__name__)
return box
def add_word_spacing(context, box, justification_spacing, x_advance):
if isinstance(box, boxes.TextBox):
box.justification_spacing = justification_spacing
box.position_x += x_advance
nb_spaces = count_spaces(box)
if nb_spaces > 0:
layout, _, resume_at, width, _, _ = split_first_line(
box.text, box.style, context, float('inf'),
box.justification_spacing)
assert resume_at is None
# XXX new_box.width - box.width is always 0???
# x_advance += new_box.width - box.width
x_advance += justification_spacing * nb_spaces
box.width = width
box.pango_layout = layout
elif isinstance(box, (boxes.LineBox, boxes.InlineBox)):
box.position_x += x_advance
previous_x_advance = x_advance
for child in box.children:
if child.is_in_normal_flow():
x_advance = add_word_spacing(
context, child, justification_spacing, x_advance)
box.width += x_advance - previous_x_advance
else:
# Atomic inline-level box
box.translate(x_advance, 0)
return x_advance
line_placeholders, waiting_floats, line_children):
"""Fit as much content as possible from an inline-level box in a width.
Return ``(new_box, resume_at, preserved_line_break, first_letter,
last_letter)``. ``resume_at`` is ``None`` if all of the content
fits. Otherwise it can be passed as a ``skip_stack`` parameter to resume
where we left off.
``new_box`` is non-empty (unless the box is empty) and as big as possible
while being narrower than ``available_width``, if possible (may overflow
is no split is possible.)
"""
resolve_percentages(box, containing_block)
float_widths = {'left': 0, 'right': 0}
if isinstance(box, boxes.TextBox):
box.position_x = position_x
if skip_stack is None:
skip = 0
else:
skip, skip_stack = skip_stack
skip = skip or 0
assert skip_stack is None
new_box, skip, preserved_line_break = split_text_box(
context, box, max_x - position_x, skip)
if skip is None:
resume_at = None
else:
resume_at = (skip, None)
if box.text:
but grid_x is an explicit attribute on cells, columns and column group.
http://www.w3.org/TR/CSS21/tables.html#model
http://www.w3.org/TR/CSS21/tables.html#table-layout
"""
# Group table children by type
columns = []
rows = []
all_captions = []
by_type = {
boxes.TableColumnBox: columns,
boxes.TableColumnGroupBox: columns,
boxes.TableRowBox: rows,
boxes.TableRowGroupBox: rows,
boxes.TableCaptionBox: all_captions,
}
for child in children:
by_type[type(child)].append(child)
# Split top and bottom captions
captions = {'top': [], 'bottom': []}
for caption in all_captions:
captions[caption.style['caption_side']].append(caption)
# Assign X positions on the grid to column boxes
column_groups = list(wrap_improper(
box, columns, boxes.TableColumnGroupBox))
grid_x = 0
for group in column_groups:
group.grid_x = grid_x
if group.children:
def set_canvas_background(page):
"""Set a ``canvas_background`` attribute on the PageBox,
with style for the canvas background, taken from the root elememt
or a child of the root element.
See http://www.w3.org/TR/CSS21/colors.html#background
"""
assert not isinstance(page.children[0], boxes.MarginBox)
root_box = page.children[0]
chosen_box = root_box
if root_box.element_tag.lower() == 'html' and root_box.background is None:
for child in root_box.children:
if child.element_tag.lower() == 'body':
chosen_box = child
break
if chosen_box.background:
painting_area = box_rectangle(page, 'padding-box')
page.canvas_background = chosen_box.background._replace(
# TODO: shouldn’t background-clip be considered here?
layers=[
l._replace(painting_area=painting_area)
for l in chosen_box.background.layers])
chosen_box.background = None
def inline_line_widths(context, box, outer, is_line_start, minimum,
skip_stack=None, first_line=False):
current_line = 0
if skip_stack is None:
skip = 0
else:
skip, skip_stack = skip_stack
for index, child in box.enumerate_skip(skip):
if child.is_absolutely_positioned():
continue # Skip
if isinstance(child, boxes.InlineBox):
lines = inline_line_widths(
context, child, outer, is_line_start, minimum, skip_stack,
first_line)
if first_line:
lines = [next(lines)]
else:
lines = list(lines)
if len(lines) == 1:
lines[0] = adjust(child, outer, lines[0])
else:
lines[0] = adjust(child, outer, lines[0], right=False)
lines[-1] = adjust(child, outer, lines[-1], left=False)
elif isinstance(child, boxes.TextBox):
space_collapse = child.style.white_space in (
'normal', 'nowrap', 'pre-line')
if skip_stack is None: