mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-05 00:21:15 +03:00
Fix the shirk to fit agorithm
This commit is contained in:
parent
e8920cea3f
commit
82db8a421c
@ -14,10 +14,39 @@ from __future__ import division, unicode_literals
|
||||
|
||||
import cairo
|
||||
|
||||
from ..css.values import get_percentage_value
|
||||
from ..formatting_structure import boxes
|
||||
from ..text import TextFragment
|
||||
from .inlines import replaced_box_width
|
||||
from .percentages import resolve_percentages
|
||||
|
||||
|
||||
def variable_and_fixed_widths(box, width=None):
|
||||
"""Return ``(variable_ratio, fixed_width)`` of ``box``.
|
||||
|
||||
``'auto'`` margins are ignored. ``'auto'`` width is not allowed.
|
||||
|
||||
"""
|
||||
if width is None:
|
||||
width = box.style.width
|
||||
|
||||
assert width is not 'auto'
|
||||
|
||||
if isinstance(width, (int, float)):
|
||||
fixed_width = width
|
||||
variable_ratio = 0
|
||||
else:
|
||||
fixed_width = 0
|
||||
variable_ratio = get_percentage_value(width) / 100.
|
||||
|
||||
for value in ('margin_left', 'margin_right',
|
||||
'border_left_width', 'border_right_width',
|
||||
'padding_left', 'padding_right'):
|
||||
style_value = box.style[value]
|
||||
if isinstance(style_value, (int, float)):
|
||||
fixed_width += style_value
|
||||
elif style_value != 'auto':
|
||||
variable_ratio += get_percentage_value(style_value) / 100.
|
||||
|
||||
return variable_ratio, fixed_width
|
||||
|
||||
|
||||
def shrink_to_fit(box):
|
||||
@ -25,84 +54,79 @@ def shrink_to_fit(box):
|
||||
return preferred_width(box), preferred_mimimum_width(box)
|
||||
|
||||
|
||||
def preferred_mimimum_width(box, containing_block=None):
|
||||
def preferred_mimimum_width(box):
|
||||
"""Return the preferred minimum width for ``box``.
|
||||
|
||||
This is the width by breaking at every line-break opportunity.
|
||||
|
||||
"""
|
||||
if isinstance(box, boxes.BlockContainerBox):
|
||||
return block_preferred_minimum_width(box, containing_block)
|
||||
return block_preferred_minimum_width(box)
|
||||
elif isinstance(box, (boxes.InlineBox, boxes.LineBox)):
|
||||
return inline_preferred_minimum_width(box, containing_block)
|
||||
return inline_preferred_minimum_width(box)
|
||||
else:
|
||||
raise TypeError(
|
||||
'Preferred minimum width for %s not handled yet' %
|
||||
type(box).__name__)
|
||||
|
||||
|
||||
def preferred_width(box, containing_block=None):
|
||||
def preferred_width(box):
|
||||
"""Return the preferred width for ``box``.
|
||||
|
||||
This is the width by only breaking at forced line breaks.
|
||||
|
||||
"""
|
||||
if isinstance(box, boxes.BlockContainerBox):
|
||||
return block_preferred_width(box, containing_block)
|
||||
return block_preferred_width(box)
|
||||
elif isinstance(box, (boxes.InlineBox, boxes.LineBox)):
|
||||
return inline_preferred_width(box, containing_block)
|
||||
return inline_preferred_width(box)
|
||||
else:
|
||||
raise TypeError(
|
||||
'Preferred width for %s not handled yet' % type(box).__name__)
|
||||
|
||||
|
||||
def block_preferred_minimum_width(box, containing_block=None):
|
||||
def _block_preferred_width(box, function):
|
||||
"""Helper to create ``block_preferred_*_width.``"""
|
||||
if not isinstance(box.style.width, (int, float)):
|
||||
# % and 'auto' width
|
||||
if box.children:
|
||||
width = max(function(child) for child in box.children)
|
||||
else:
|
||||
width = 0
|
||||
else:
|
||||
width = None
|
||||
|
||||
variable_ratio, fixed_width = variable_and_fixed_widths(box, width)
|
||||
if variable_ratio < 1:
|
||||
return fixed_width / (1 - variable_ratio)
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def block_preferred_minimum_width(box):
|
||||
"""Return the preferred minimum width for a ``BlockBox``."""
|
||||
if box.style['width'] == 'auto':
|
||||
if box.children:
|
||||
return max(
|
||||
preferred_mimimum_width(child, box)
|
||||
for child in box.children)
|
||||
else:
|
||||
return 0
|
||||
elif isinstance(box.style['width'], (int, float)):
|
||||
assert containing_block
|
||||
resolve_percentages(box, containing_block)
|
||||
return box.margin_width()
|
||||
else:
|
||||
# TODO: find a value for % width
|
||||
raise TypeError('Width %s is unknown' % box.style['width'])
|
||||
return _block_preferred_width(box, preferred_mimimum_width)
|
||||
|
||||
|
||||
def block_preferred_width(box, containing_block=None):
|
||||
def block_preferred_width(box):
|
||||
"""Return the preferred width for a ``BlockBox``."""
|
||||
if box.style['width'] == 'auto':
|
||||
if box.children:
|
||||
return max(
|
||||
preferred_width(child, box) for child in box.children)
|
||||
else:
|
||||
return 0
|
||||
elif isinstance(box.style['width'], (int, float)):
|
||||
assert containing_block
|
||||
resolve_percentages(box, containing_block)
|
||||
return box.margin_width()
|
||||
else:
|
||||
# TODO: find a value for % width
|
||||
raise TypeError('Width %s is unknown' % box.style['width'])
|
||||
return _block_preferred_width(box, preferred_width)
|
||||
|
||||
|
||||
def inline_preferred_minimum_width(box, containing_block=None):
|
||||
def inline_preferred_minimum_width(box):
|
||||
"""Return the preferred minimum width for an ``InlineBox``.
|
||||
|
||||
*Warning:* only TextBox and InlineReplacedBox children are supported
|
||||
for now. (No recursive InlineBox childdren.)
|
||||
for now. (No recursive InlineBox children.)
|
||||
|
||||
"""
|
||||
widest_line = 0
|
||||
for child in box.children:
|
||||
if isinstance(child, boxes.AtomicInlineLevelBox):
|
||||
if isinstance(child, boxes.InlineReplacedBox):
|
||||
# Images are on their own line
|
||||
current_line = replaced_preferred_width(child)
|
||||
elif isinstance(child, boxes.InlineBlockBox):
|
||||
current_line = block_preferred_minimum_width(child)
|
||||
else:
|
||||
assert isinstance(child, boxes.TextBox)
|
||||
current_line = max(text_lines_width(child, width=0))
|
||||
@ -110,7 +134,7 @@ def inline_preferred_minimum_width(box, containing_block=None):
|
||||
return widest_line
|
||||
|
||||
|
||||
def inline_preferred_width(box, containing_block=None):
|
||||
def inline_preferred_width(box):
|
||||
"""Return the preferred width for an ``InlineBox``.
|
||||
|
||||
*Warning:* only TextBox and InlineReplacedBox children are supported
|
||||
@ -123,6 +147,8 @@ def inline_preferred_width(box, containing_block=None):
|
||||
if isinstance(child, boxes.InlineReplacedBox):
|
||||
# No line break around images
|
||||
current_line += replaced_preferred_width(child)
|
||||
elif isinstance(child, boxes.InlineBlockBox):
|
||||
current_line += block_preferred_width(child)
|
||||
else:
|
||||
assert isinstance(child, boxes.TextBox)
|
||||
lines = list(text_lines_width(child, width=None))
|
||||
@ -149,8 +175,14 @@ def text_lines_width(box, width):
|
||||
|
||||
def replaced_preferred_width(box):
|
||||
"""Return the preferred (minimum) width for an ``InlineReplacedBox``."""
|
||||
# TODO: get the actual device size. Or do we really care?
|
||||
# TODO: what about percentage widths?
|
||||
resolve_percentages(box, containing_block=(0, 0))
|
||||
replaced_box_width(box, device_size=None)
|
||||
return box.width
|
||||
if isinstance(box, (int, float)):
|
||||
width = box.style.width
|
||||
else:
|
||||
# TODO: handle the images with no intinsic width
|
||||
_, width, _ = box.replacement
|
||||
|
||||
variable_ratio, fixed_width = variable_and_fixed_widths(box, width)
|
||||
if variable_ratio < 1:
|
||||
return fixed_width / (1 - variable_ratio)
|
||||
else:
|
||||
return 0
|
||||
|
Loading…
Reference in New Issue
Block a user