1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-05 08:27:22 +03:00

Replace "*-content width" by "preferred * width"

This commit is contained in:
Guillaume Ayoub 2016-01-09 15:16:29 +01:00
parent b617122af0
commit 4898ad980b
4 changed files with 183 additions and 171 deletions

View File

@ -17,7 +17,7 @@ from .float import avoid_collisions, float_layout
from .replaced import image_marker_layout from .replaced import image_marker_layout
from .min_max import handle_min_max_width, handle_min_max_height from .min_max import handle_min_max_width, handle_min_max_height
from .percentages import resolve_percentages, resolve_one_percentage from .percentages import resolve_percentages, resolve_one_percentage
from .preferred import (shrink_to_fit, inline_preferred_minimum_width, from .preferred import (shrink_to_fit, inline_min_content_width,
trailing_whitespace_size) trailing_whitespace_size)
from .tables import find_in_flow_baseline, table_wrapper_width from .tables import find_in_flow_baseline, table_wrapper_width
from ..text import split_first_line from ..text import split_first_line
@ -72,7 +72,7 @@ def get_next_linebox(context, linebox, position_y, skip_stack,
if skip_stack == 'continue': if skip_stack == 'continue':
return None, None return None, None
linebox.width = inline_preferred_minimum_width( linebox.width = inline_min_content_width(
context, linebox, skip_stack=skip_stack, first_line=True) context, linebox, skip_stack=skip_stack, first_line=True)
linebox.height, _ = strut_layout(linebox.style, context.enable_hinting) linebox.height, _ = strut_layout(linebox.style, context.enable_hinting)

View File

@ -16,7 +16,7 @@ from ..formatting_structure import boxes, build
from .absolute import absolute_layout from .absolute import absolute_layout
from .blocks import block_level_layout, block_container_layout from .blocks import block_level_layout, block_container_layout
from .percentages import resolve_percentages from .percentages import resolve_percentages
from .preferred import preferred_minimum_width, preferred_width from .preferred import min_content_width, max_content_width
from .min_max import handle_min_max_width, handle_min_max_height from .min_max import handle_min_max_width, handle_min_max_height
@ -30,17 +30,18 @@ class OrientedBox(object):
return self.sugar + self.inner return self.sugar + self.inner
@property @property
def outer_minimum(self): def outer_min_content_size(self):
return self.sugar + ( return self.sugar + (
self.minimum if self.inner == 'auto' else self.inner) self.min_content_size if self.inner == 'auto' else self.inner)
@property @property
def outer_preferred(self): def outer_max_content_size(self):
return self.sugar + ( return self.sugar + (
self.preferred if self.inner == 'auto' else self.inner) self.max_content_size if self.inner == 'auto' else self.inner)
def shrink_to_fit(self, available): def shrink_to_fit(self, available):
self.inner = min(max(self.minimum, available), self.preferred) self.inner = min(
max(self.min_content_size, available), self.max_content_size)
class VerticalBox(OrientedBox): class VerticalBox(OrientedBox):
@ -62,13 +63,13 @@ class VerticalBox(OrientedBox):
box.margin_top = self.margin_a box.margin_top = self.margin_a
box.margin_bottom = self.margin_b box.margin_bottom = self.margin_b
# TODO: preferred (minimum) height??? # TODO: Define what are the min-content and max-content heights
@property @property
def minimum(self): def min_content_size(self):
return 0 return 0
@property @property
def preferred(self): def max_content_size(self):
return 1e6 return 1e6
@ -82,8 +83,8 @@ class HorizontalBox(OrientedBox):
self.padding_plus_border = ( self.padding_plus_border = (
box.padding_left + box.padding_right + box.padding_left + box.padding_right +
box.border_left_width + box.border_right_width) box.border_left_width + box.border_right_width)
self._minimum = None self._min_content_size = None
self._preferred = None self._max_content_size = None
def restore_box_attributes(self): def restore_box_attributes(self):
box = self.box box = self.box
@ -92,18 +93,18 @@ class HorizontalBox(OrientedBox):
box.margin_right = self.margin_b box.margin_right = self.margin_b
@property @property
def minimum(self): def min_content_size(self):
if self._minimum is None: if self._min_content_size is None:
self._minimum = preferred_minimum_width( self._min_content_size = min_content_width(
self.context, self.box, outer=False) self.context, self.box, outer=False)
return self._minimum return self._min_content_size
@property @property
def preferred(self): def max_content_size(self):
if self._preferred is None: if self._max_content_size is None:
self._preferred = preferred_width( self._max_content_size = max_content_width(
self.context, self.box, outer=False) self.context, self.box, outer=False)
return self._preferred return self._max_content_size
def compute_fixed_dimension(context, box, outer, vertical, top_or_left): def compute_fixed_dimension(context, box, outer, vertical, top_or_left):
@ -209,24 +210,28 @@ def compute_variable_dimension(context, side_boxes, vertical, outer_sum):
if box_b.box.is_generated: if box_b.box.is_generated:
if box_b.inner == 'auto': if box_b.inner == 'auto':
ac_preferred = 2 * max( ac_max_content_size = 2 * max(
box_a.outer_preferred, box_c.outer_preferred) box_a.outer_max_content_size, box_c.outer_max_content_size)
if outer_sum >= box_b.outer_preferred + ac_preferred: if outer_sum >= (
box_b.inner = box_b.preferred box_b.outer_max_content_size + ac_max_content_size):
box_b.inner = box_b.max_content_size
else: else:
ac_minimum = 2 * max(box_a.outer_minimum, box_c.outer_minimum) ac_min_content_size = 2 * max(
box_b.inner = box_b.minimum box_a.outer_min_content_size,
available = outer_sum - box_b.outer - ac_minimum box_c.outer_min_content_size)
box_b.inner = box_b.min_content_size
available = outer_sum - box_b.outer - ac_min_content_size
if available > 0: if available > 0:
weight_ac = ac_preferred - ac_minimum weight_ac = ac_max_content_size - ac_min_content_size
weight_b = box_b.preferred - box_b.minimum weight_b = (
box_b.max_content_size - box_b.min_content_size)
weight_sum = weight_ac + weight_b weight_sum = weight_ac + weight_b
# By definition of preferred and minimum, weights can # By definition of max_content_size and min_content_size,
# not be negative. weight_sum == 0 implies that # weights can not be negative. weight_sum == 0 implies that
# preferred == minimum for each box, in which case the # max_content_size == min_content_size for each box, in
# sum can not be both <= and > outer_sum # which case the sum can not be both <= and > outer_sum
# Therefore, one of the last two 'if' statements would # Therefore, one of the last two 'if' statements would not
# not have lead us here. # have lead us here.
assert weight_sum > 0 assert weight_sum > 0
box_b.inner += available * weight_b / weight_sum box_b.inner += available * weight_b / weight_sum
if box_a.inner == 'auto': if box_a.inner == 'auto':
@ -237,23 +242,27 @@ def compute_variable_dimension(context, side_boxes, vertical, outer_sum):
# Non-generated boxes get zero for every box-model property # Non-generated boxes get zero for every box-model property
assert box_b.inner == 0 assert box_b.inner == 0
if box_a.inner == box_c.inner == 'auto': if box_a.inner == box_c.inner == 'auto':
if box_a.outer_preferred + box_c.outer_preferred <= outer_sum: if outer_sum >= (
box_a.inner = box_a.preferred box_a.outer_max_content_size +
box_c.inner = box_c.preferred box_c.outer_max_content_size):
box_a.inner = box_a.max_content_size
box_c.inner = box_c.max_content_size
else: else:
box_a.inner = box_a.minimum box_a.inner = box_a.min_content_size
box_c.inner = box_c.minimum box_c.inner = box_c.min_content_size
available = outer_sum - box_a.outer - box_c.outer available = outer_sum - box_a.outer - box_c.outer
if available > 0: if available > 0:
weight_a = box_a.preferred - box_a.minimum weight_a = (
weight_c = box_c.preferred - box_c.minimum box_a.max_content_size - box_a.min_content_size)
weight_c = (
box_c.max_content_size - box_c.min_content_size)
weight_sum = weight_a + weight_c weight_sum = weight_a + weight_c
# By definition of preferred and minimum, weights can # By definition of max_content_size and min_content_size,
# not be negative. weight_sum == 0 implies that # weights can not be negative. weight_sum == 0 implies that
# preferred == minimum for each box, in which case the # max_content_size == min_content_size for each box, in
# sum can not be both <= and > outer_sum # which case the sum can not be both <= and > outer_sum
# Therefore, one of the last two 'if' statements would # Therefore, one of the last two 'if' statements would not
# not have lead us here. # have lead us here.
assert weight_sum > 0 assert weight_sum > 0
box_a.inner += available * weight_a / weight_sum box_a.inner += available * weight_a / weight_sum
box_c.inner += available * weight_c / weight_sum box_c.inner += available * weight_c / weight_sum

View File

@ -3,9 +3,13 @@
weasyprint.layout.preferred weasyprint.layout.preferred
--------------------------- ---------------------------
Preferred and minimum preferred width, aka. the shrink-to-fit algorithm. Preferred and minimum preferred width, aka. max-content and min-content
width, aka. the shrink-to-fit algorithm.
:copyright: Copyright 2011-2014 Simon Sapin and contributors, see AUTHORS. Terms used (max-content width, min-content width) are defined in David
Baron's unofficial draft (http://dbaron.org/css/intrinsic/).
:copyright: Copyright 2011-2016 Simon Sapin and contributors, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -30,55 +34,56 @@ def shrink_to_fit(context, box, available_width):
""" """
return min( return min(
max( max(
preferred_minimum_width(context, box, outer=False), min_content_width(context, box, outer=False),
available_width), available_width),
preferred_width(context, box, outer=False)) max_content_width(context, box, outer=False))
def preferred_minimum_width(context, box, outer=True): def min_content_width(context, box, outer=True):
"""Return the preferred minimum width for ``box``. """Return the min-content width for ``box``.
This is the width by breaking at every line-break opportunity. This is the width by breaking at every line-break opportunity.
""" """
if isinstance(box, boxes.BlockContainerBox): if isinstance(box, boxes.BlockContainerBox):
if box.is_table_wrapper: if box.is_table_wrapper:
return table_preferred_minimum_width(context, box, outer) return outer_table_min_content_width(context, box, outer)
else: else:
return block_preferred_minimum_width(context, box, outer) return block_min_content_width(context, box, outer)
elif isinstance(box, (boxes.InlineBox, boxes.LineBox)): elif isinstance(box, (boxes.InlineBox, boxes.LineBox)):
return inline_preferred_minimum_width( return inline_min_content_width(
context, box, outer, is_line_start=True) context, box, outer, is_line_start=True)
elif isinstance(box, boxes.ReplacedBox): elif isinstance(box, boxes.ReplacedBox):
return replaced_preferred_width(box, outer) return replaced_max_content_width(box, outer)
else: else:
raise TypeError( raise TypeError(
'Preferred minimum width for %s not handled yet' % 'min-content width for %s not handled yet' %
type(box).__name__) type(box).__name__)
def preferred_width(context, box, outer=True): def max_content_width(context, box, outer=True):
"""Return the preferred width for ``box``. """Return the max-content width for ``box``.
This is the width by only breaking at forced line breaks. This is the width by only breaking at forced line breaks.
""" """
if isinstance(box, boxes.BlockContainerBox): if isinstance(box, boxes.BlockContainerBox):
if box.is_table_wrapper: if box.is_table_wrapper:
return table_preferred_width(context, box, outer) return outer_table_max_content_width(context, box, outer)
else: else:
return block_preferred_width(context, box, outer) return block_max_content_width(context, box, outer)
elif isinstance(box, (boxes.InlineBox, boxes.LineBox)): elif isinstance(box, (boxes.InlineBox, boxes.LineBox)):
return inline_preferred_width(context, box, outer, is_line_start=True) return inline_max_content_width(
context, box, outer, is_line_start=True)
elif isinstance(box, boxes.ReplacedBox): elif isinstance(box, boxes.ReplacedBox):
return replaced_preferred_width(box, outer) return replaced_max_content_width(box, outer)
else: else:
raise TypeError( raise TypeError(
'Preferred width for %s not handled yet' % type(box).__name__) 'max-content width for %s not handled yet' % type(box).__name__)
def _block_preferred_width(context, box, function, outer): def _block_content_width(context, box, function, outer):
"""Helper to create ``block_preferred_*_width.``""" """Helper to create ``block_*_content_width.``"""
width = box.style.width width = box.style.width
if width == 'auto' or width.unit == '%': if width == 'auto' or width.unit == '%':
# "percentages on the following properties are treated instead as # "percentages on the following properties are treated instead as
@ -138,47 +143,47 @@ def adjust(box, outer, width, left=True, right=True):
return 0 return 0
def block_preferred_minimum_width(context, box, outer=True): def block_min_content_width(context, box, outer=True):
"""Return the preferred minimum width for a ``BlockBox``.""" """Return the min-content width for a ``BlockBox``."""
return _block_preferred_width( return _block_content_width(
context, box, preferred_minimum_width, outer) context, box, min_content_width, outer)
def block_preferred_width(context, box, outer=True): def block_max_content_width(context, box, outer=True):
"""Return the preferred width for a ``BlockBox``.""" """Return the max-content width for a ``BlockBox``."""
return _block_preferred_width(context, box, preferred_width, outer) return _block_content_width(context, box, max_content_width, outer)
def table_cell_preferred_minimum_width(context, box, table, def table_cell_min_content_width(context, box, table,
resolved_table_width, outer=True): resolved_table_width, outer=True):
"""Return the preferred minimum width for a ``TableCellBox``.""" """Return the min-content width for a ``TableCellBox``."""
# Try to solve the cell's width if it is a percentage # Try to solve the cell's width if it is a percentage
width = box.style.width width = box.style.width
if (resolved_table_width and table.width != 'auto' and if (resolved_table_width and table.width != 'auto' and
width != 'auto' and width.unit == '%'): width != 'auto' and width.unit == '%'):
return width.value / 100. * table.width return width.value / 100. * table.width
# Else return standard block's preferred minimum width # Else return standard block's min-content width
return _block_preferred_width( return _block_content_width(
context, box, preferred_minimum_width, outer) context, box, min_content_width, outer)
def table_cell_preferred_width(context, box, table, resolved_table_width, def table_cell_max_content_width(context, box, table, resolved_table_width,
outer=True): outer=True):
"""Return the preferred width for a ``TableCellBox``.""" """Return the max-content width for a ``TableCellBox``."""
# Try to solve the cell's width if it is a percentage # Try to solve the cell's width if it is a percentage
width = box.style.width width = box.style.width
if (resolved_table_width and table.width != 'auto' and if (resolved_table_width and table.width != 'auto' and
width != 'auto' and width.unit == '%'): width != 'auto' and width.unit == '%'):
return width.value / 100. * table.width return width.value / 100. * table.width
# Else return standard block's preferred width # Else return standard block's max-content width
return _block_preferred_width(context, box, preferred_width, outer) return _block_content_width(context, box, max_content_width, outer)
def inline_preferred_minimum_width(context, box, outer=True, skip_stack=None, def inline_min_content_width(context, box, outer=True, skip_stack=None,
first_line=False, is_line_start=False): first_line=False, is_line_start=False):
"""Return the preferred minimum width for an ``InlineBox``. """Return the min-content width for an ``InlineBox``.
The width is calculated from the lines from ``skip_stack``. If The width is calculated from the lines from ``skip_stack``. If
``first_line`` is ``True``, only the first line minimum width is ``first_line`` is ``True``, only the first line minimum width is
@ -196,8 +201,8 @@ def inline_preferred_minimum_width(context, box, outer=True, skip_stack=None,
return adjust(box, outer, max(widths)) return adjust(box, outer, max(widths))
def inline_preferred_width(context, box, outer=True, is_line_start=False): def inline_max_content_width(context, box, outer=True, is_line_start=False):
"""Return the preferred width for an ``InlineBox``.""" """Return the max-content width for an ``InlineBox``."""
widths = list( widths = list(
inline_line_widths(context, box, outer, is_line_start, minimum=False)) inline_line_widths(context, box, outer, is_line_start, minimum=False))
widths[-1] -= trailing_whitespace_size(context, box) widths[-1] -= trailing_whitespace_size(context, box)
@ -247,9 +252,9 @@ def inline_line_widths(context, box, outer, is_line_start, minimum,
# "By default, there is a break opportunity # "By default, there is a break opportunity
# both before and after any inline object." # both before and after any inline object."
if minimum: if minimum:
lines = [0, preferred_width(context, child), 0] lines = [0, max_content_width(context, child), 0]
else: else:
lines = [preferred_width(context, child)] lines = [max_content_width(context, child)]
# The first text line goes on the current line # The first text line goes on the current line
current_line += lines[0] current_line += lines[0]
if len(lines) > 1: if len(lines) > 1:
@ -269,14 +274,14 @@ TABLE_CACHE = weakref.WeakKeyDictionary()
def table_and_columns_preferred_widths(context, box, outer=True, def table_and_columns_preferred_widths(context, box, outer=True,
resolved_table_width=False): resolved_table_width=False):
"""Return preferred widths for the table and its columns. """Return max-content widths for the table and its columns.
If ``resolved_table_width`` is ``True``, the resolved width (instead of the If ``resolved_table_width`` is ``True``, the resolved width (instead of the
one given in ``box.style``) is used to get the preferred widths. one given in ``box.style``) is used to get the max-content widths.
The tuple returned is The tuple returned is
``(table_preferred_minimum_width, table_preferred_width, ``(table_min_content_width, table_max_content_width,
column_preferred_minimum_widths, column_preferred_widths)`` column_min_content_widths, column_max_content_widths)``
http://www.w3.org/TR/CSS21/tables.html#auto-table-layout http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
@ -320,25 +325,25 @@ def table_and_columns_preferred_widths(context, box, outer=True,
colspan_cells.append(cell) colspan_cells.append(cell)
# Point #1 # Point #1
column_preferred_widths = [[0] * nb_rows for i in range(nb_columns)] column_max_content_widths = [[0] * nb_rows for i in range(nb_columns)]
column_preferred_minimum_widths = [ column_min_content_widths = [
[0] * nb_rows for i in range(nb_columns)] [0] * nb_rows for i in range(nb_columns)]
for i, row in enumerate(grid): for i, row in enumerate(grid):
for j, cell in enumerate(row): for j, cell in enumerate(row):
if cell: if cell:
column_preferred_widths[j][i] = \ column_max_content_widths[j][i] = \
table_cell_preferred_width( table_cell_max_content_width(
context, cell, table, resolved_table_width) context, cell, table, resolved_table_width)
column_preferred_minimum_widths[j][i] = \ column_min_content_widths[j][i] = \
table_cell_preferred_minimum_width( table_cell_min_content_width(
context, cell, table, resolved_table_width) context, cell, table, resolved_table_width)
column_preferred_widths = [ column_max_content_widths = [
max(widths) if widths else 0 max(widths) if widths else 0
for widths in column_preferred_widths] for widths in column_max_content_widths]
column_preferred_minimum_widths = [ column_min_content_widths = [
max(widths) if widths else 0 max(widths) if widths else 0
for widths in column_preferred_minimum_widths] for widths in column_min_content_widths]
# Point #2 # Point #2
column_groups_widths = [] column_groups_widths = []
@ -351,8 +356,8 @@ def table_and_columns_preferred_widths(context, box, outer=True,
column_widths[column.grid_x] = column.style.width column_widths[column.grid_x] = column.style.width
if column_widths: if column_widths:
for widths in (column_preferred_widths, for widths in (column_max_content_widths,
column_preferred_minimum_widths): column_min_content_widths):
for i, width in enumerate(widths): for i, width in enumerate(widths):
column_width = column_widths[i] column_width = column_widths[i]
if column_width and column_width != 'auto': if column_width and column_width != 'auto':
@ -369,26 +374,26 @@ def table_and_columns_preferred_widths(context, box, outer=True,
column_slice = slice(cell.grid_x, cell.grid_x + cell.colspan) column_slice = slice(cell.grid_x, cell.grid_x + cell.colspan)
cell_width = ( cell_width = (
table_cell_preferred_width( table_cell_max_content_width(
context, cell, table, resolved_table_width) - context, cell, table, resolved_table_width) -
border_spacing_x * (cell.colspan - 1)) border_spacing_x * (cell.colspan - 1))
columns_width = sum(column_preferred_widths[column_slice]) columns_width = sum(column_max_content_widths[column_slice])
if cell_width > columns_width: if cell_width > columns_width:
added_space = (cell_width - columns_width) / cell.colspan added_space = (cell_width - columns_width) / cell.colspan
for i in range(cell.grid_x, cell.grid_x + cell.colspan): for i in range(cell.grid_x, cell.grid_x + cell.colspan):
column_preferred_widths[i] += added_space column_max_content_widths[i] += added_space
cell_minimum_width = ( cell_minimum_width = (
table_cell_preferred_minimum_width( table_cell_min_content_width(
context, cell, table, resolved_table_width) - context, cell, table, resolved_table_width) -
border_spacing_x * (cell.colspan - 1)) border_spacing_x * (cell.colspan - 1))
columns_minimum_width = sum( columns_minimum_width = sum(
column_preferred_minimum_widths[column_slice]) column_min_content_widths[column_slice])
if cell_minimum_width > columns_minimum_width: if cell_minimum_width > columns_minimum_width:
added_space = ( added_space = (
(cell_minimum_width - columns_minimum_width) / cell.colspan) (cell_minimum_width - columns_minimum_width) / cell.colspan)
for i in range(cell.grid_x, cell.grid_x + cell.colspan): for i in range(cell.grid_x, cell.grid_x + cell.colspan):
column_preferred_minimum_widths[i] += added_space column_min_content_widths[i] += added_space
# Point #4 # Point #4
for column_group, column_group_width in column_groups_widths: for column_group, column_group_width in column_groups_widths:
@ -397,7 +402,7 @@ def table_and_columns_preferred_widths(context, box, outer=True,
column_indexes = [ column_indexes = [
column.grid_x for column in column_group.children] column.grid_x for column in column_group.children]
columns_width = sum( columns_width = sum(
column_preferred_minimum_widths[index] column_min_content_widths[index]
for index in column_indexes) for index in column_indexes)
if column_group_width.unit == '%': if column_group_width.unit == '%':
column_group_width = ( column_group_width = (
@ -408,73 +413,75 @@ def table_and_columns_preferred_widths(context, box, outer=True,
added_space = ( added_space = (
(column_group_width - columns_width) / len(column_indexes)) (column_group_width - columns_width) / len(column_indexes))
for i in column_indexes: for i in column_indexes:
column_preferred_minimum_widths[i] += added_space column_min_content_widths[i] += added_space
# The spec seems to say that the colgroup's width is just a # The spec seems to say that the colgroup's width is just a
# hint for column group's columns minimum width, but if the # hint for column group's columns minimum width, but if the
# sum of the preferred maximum width of the colums is lower # sum of the preferred maximum width of the colums is lower
# or greater than the colgroup's one, then the columns # or greater than the colgroup's one, then the columns
# don't follow the hint. These lines make the preferred # don't follow the hint. These lines make the preferred
# width equal or greater than the minimum preferred width. # width equal or greater than the minimum max-content
if (column_preferred_widths[i] < # width.
column_preferred_minimum_widths[i]): if (column_max_content_widths[i] <
column_preferred_widths[i] = \ column_min_content_widths[i]):
column_preferred_minimum_widths[i] column_max_content_widths[i] = \
column_min_content_widths[i]
total_border_spacing = (nb_columns + 1) * border_spacing_x total_border_spacing = (nb_columns + 1) * border_spacing_x
table_preferred_minimum_width = ( table_min_content_width = (
sum(column_preferred_minimum_widths) + total_border_spacing) sum(column_min_content_widths) + total_border_spacing)
table_preferred_width = sum(column_preferred_widths) + total_border_spacing table_max_content_width = (
sum(column_max_content_widths) + total_border_spacing)
captions = [child for child in box.children captions = [child for child in box.children
if child is not table and not child.is_absolutely_positioned()] if child is not table and not child.is_absolutely_positioned()]
if captions: if captions:
caption_width = max( caption_width = max(
preferred_minimum_width(context, caption) for caption in captions) min_content_width(context, caption) for caption in captions)
else: else:
caption_width = 0 caption_width = 0
if table.style.width != 'auto': if table.style.width != 'auto':
# Take care of the table width # Take care of the table width
if resolved_table_width: if resolved_table_width:
if table.width > table_preferred_minimum_width: if table.width > table_min_content_width:
table_preferred_minimum_width = table.width table_min_content_width = table.width
else: else:
if (table.style.width.unit != '%' and if (table.style.width.unit != '%' and
table.style.width.value > table_preferred_minimum_width): table.style.width.value > table_min_content_width):
table_preferred_minimum_width = table.style.width.value table_min_content_width = table.style.width.value
if table_preferred_minimum_width < caption_width: if table_min_content_width < caption_width:
table_preferred_minimum_width = caption_width table_min_content_width = caption_width
if table_preferred_minimum_width > table_preferred_width: if table_min_content_width > table_max_content_width:
table_preferred_width = table_preferred_minimum_width table_max_content_width = table_min_content_width
result = ( result = (
table_preferred_minimum_width, table_preferred_width, table_min_content_width, table_max_content_width,
column_preferred_minimum_widths, column_preferred_widths) column_min_content_widths, column_max_content_widths)
TABLE_CACHE[table] = result TABLE_CACHE[table] = result
return result return result
def table_preferred_minimum_width(context, box, outer=True): def outer_table_min_content_width(context, box, outer=True):
"""Return the preferred minimum width for a ``TableBox``. wrapper""" """Return the min-content width for a ``TableBox``. wrapper"""
resolved_table_width = box.style.width != 'auto' resolved_table_width = box.style.width != 'auto'
minimum_width, _, _, _ = table_and_columns_preferred_widths( minimum_width, _, _, _ = table_and_columns_preferred_widths(
context, box, resolved_table_width) context, box, resolved_table_width)
return adjust(box, outer, minimum_width) return adjust(box, outer, minimum_width)
def table_preferred_width(context, box, outer=True): def outer_table_max_content_width(context, box, outer=True):
"""Return the preferred width for a ``TableBox`` wrapper.""" """Return the max-content width for a ``TableBox`` wrapper."""
resolved_table_width = box.style.width != 'auto' resolved_table_width = box.style.width != 'auto'
_, width, _, _ = table_and_columns_preferred_widths( _, width, _, _ = table_and_columns_preferred_widths(
context, box, resolved_table_width) context, box, resolved_table_width)
return adjust(box, outer, width) return adjust(box, outer, width)
def replaced_preferred_width(box, outer=True): def replaced_max_content_width(box, outer=True):
"""Return the preferred minimum width for an ``InlineReplacedBox``.""" """Return the min-content width for an ``InlineReplacedBox``."""
width = box.style.width width = box.style.width
if width == 'auto' or width.unit == '%': if width == 'auto' or width.unit == '%':
height = box.style.height height = box.style.height

View File

@ -5,7 +5,7 @@
Layout for tables and internal table boxes. Layout for tables and internal table boxes.
:copyright: Copyright 2011-2014 Simon Sapin and contributors, see AUTHORS. :copyright: Copyright 2011-2016 Simon Sapin and contributors, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -23,11 +23,7 @@ from .preferred import table_and_columns_preferred_widths
def table_layout(context, table, max_position_y, skip_stack, def table_layout(context, table, max_position_y, skip_stack,
containing_block, device_size, page_is_empty, absolute_boxes, containing_block, device_size, page_is_empty, absolute_boxes,
fixed_boxes): fixed_boxes):
"""Layout for a table box. """Layout for a table box."""
For now only the fixed layout and separate border model are supported.
"""
# Avoid a circular import # Avoid a circular import
from .blocks import block_container_layout from .blocks import block_container_layout
@ -501,8 +497,8 @@ def auto_table_layout(context, box, containing_block):
""" """
table = box.get_wrapped_table() table = box.get_wrapped_table()
(table_preferred_minimum_width, table_preferred_width, (table_min_content_width, table_max_content_width,
column_preferred_minimum_widths, column_preferred_widths) = \ column_min_content_widths, column_max_content_widths) = \
table_and_columns_preferred_widths( table_and_columns_preferred_widths(
context, box, resolved_table_width=table.width != 'auto') context, box, resolved_table_width=table.width != 'auto')
@ -512,7 +508,7 @@ def auto_table_layout(context, box, containing_block):
border_spacing_x = 0 border_spacing_x = 0
all_border_spacing = ( all_border_spacing = (
border_spacing_x * (len(column_preferred_widths) + 1)) border_spacing_x * (len(column_max_content_widths) + 1))
margins = 0 margins = 0
if box.margin_left != 'auto': if box.margin_left != 'auto':
@ -523,33 +519,33 @@ def auto_table_layout(context, box, containing_block):
cb_width, cb_height = containing_block cb_width, cb_height = containing_block
available_width = cb_width - margins available_width = cb_width - margins
if table.width == 'auto': if table.width == 'auto':
if available_width < table_preferred_minimum_width: if available_width < table_min_content_width:
table.width = table_preferred_minimum_width table.width = table_min_content_width
table.column_widths = column_preferred_minimum_widths table.column_widths = column_min_content_widths
elif available_width < table_preferred_width: elif available_width < table_max_content_width:
table.width = available_width table.width = available_width
table.column_widths = column_preferred_minimum_widths table.column_widths = column_min_content_widths
else: else:
table.width = table_preferred_width table.width = table_max_content_width
table.column_widths = column_preferred_widths table.column_widths = column_max_content_widths
else: else:
if table.width < table_preferred_minimum_width: if table.width < table_min_content_width:
table.width = table_preferred_minimum_width table.width = table_min_content_width
table.column_widths = column_preferred_minimum_widths table.column_widths = column_min_content_widths
elif table.width < table_preferred_width: elif table.width < table_max_content_width:
table.column_widths = column_preferred_minimum_widths table.column_widths = column_min_content_widths
else: else:
table.column_widths = column_preferred_widths table.column_widths = column_max_content_widths
lost_width = table.width - sum(table.column_widths) - all_border_spacing lost_width = table.width - sum(table.column_widths) - all_border_spacing
if lost_width > 0: if lost_width > 0:
sum_column_preferred_widths = sum(column_preferred_widths) sum_column_max_content_widths = sum(column_max_content_widths)
if sum_column_preferred_widths: if sum_column_max_content_widths:
table.column_widths = [ table.column_widths = [
(column_width + lost_width * preferred_column_width / (column_width + lost_width * max_content_column_width /
sum_column_preferred_widths) sum_column_max_content_widths)
for (preferred_column_width, column_width) for (max_content_column_width, column_width)
in zip(column_preferred_widths, table.column_widths)] in zip(column_max_content_widths, table.column_widths)]
else: else:
table.column_widths = [ table.column_widths = [
column_width + lost_width / len(table.column_widths) column_width + lost_width / len(table.column_widths)