2011-05-17 13:29:00 +04:00
|
|
|
|
# coding: utf8
|
|
|
|
|
|
|
|
|
|
# WeasyPrint converts web documents (HTML, CSS, ...) to PDF.
|
|
|
|
|
# Copyright (C) 2011 Simon Sapin
|
|
|
|
|
#
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
|
#
|
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
|
#
|
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
This module defines the classes for all types of boxes in the CSS formatting
|
|
|
|
|
structure / box model.
|
|
|
|
|
|
|
|
|
|
http://www.w3.org/TR/CSS21/visuren.html
|
|
|
|
|
|
|
|
|
|
Names are the same as in CSS 2.1 with the exception of TextBox. In WeasyPrint,
|
2011-06-29 17:01:36 +04:00
|
|
|
|
any text is in a TextBox. What CSS calls anonymous inline boxes are text boxes
|
2011-05-25 16:59:42 +04:00
|
|
|
|
but not all text boxes are anonymous inline boxes.
|
|
|
|
|
|
|
|
|
|
http://www.w3.org/TR/CSS21/visuren.html#anonymous
|
|
|
|
|
|
2011-07-11 14:47:00 +04:00
|
|
|
|
Abstract classes, should not be instantiated:
|
2011-05-25 16:59:42 +04:00
|
|
|
|
|
|
|
|
|
* Box
|
|
|
|
|
* BlockLevelBox
|
|
|
|
|
* InlineLevelBox
|
|
|
|
|
* BlockContainerBox
|
|
|
|
|
* AnonymousBox
|
|
|
|
|
* ReplacedBox
|
2011-07-11 14:47:00 +04:00
|
|
|
|
* ParentBox
|
2011-07-20 15:29:38 +04:00
|
|
|
|
* AtomicInlineLevelBox
|
2011-05-25 16:59:42 +04:00
|
|
|
|
|
|
|
|
|
Concrete classes:
|
|
|
|
|
|
2011-07-01 20:20:30 +04:00
|
|
|
|
* PageBox
|
2011-05-25 16:59:42 +04:00
|
|
|
|
* BlockBox
|
|
|
|
|
* AnonymousBlockBox
|
|
|
|
|
* InlineBox
|
|
|
|
|
* InlineBlockBox
|
|
|
|
|
* BlockLevelReplacedBox
|
|
|
|
|
* InlineLevelReplacedBox
|
|
|
|
|
* TextBox
|
|
|
|
|
* LineBox
|
|
|
|
|
|
2011-07-01 20:20:30 +04:00
|
|
|
|
Apart from PageBox and LineBox, all concrete box classes have one of the
|
|
|
|
|
following "outside" behavior:
|
2011-05-25 16:59:42 +04:00
|
|
|
|
|
|
|
|
|
* Block-level (inherits from BlockLevelBox)
|
|
|
|
|
* Inline-level (inherits from InlineLevelBox)
|
|
|
|
|
|
|
|
|
|
and one of the following "inside" behavior:
|
|
|
|
|
|
|
|
|
|
* Block container (inherits from BlockContainerBox)
|
2011-07-11 14:47:00 +04:00
|
|
|
|
* Inline content (InlineBox and TextBox)
|
2011-05-25 16:59:42 +04:00
|
|
|
|
* Replaced content (inherits from ReplacedBox)
|
|
|
|
|
|
|
|
|
|
See respective docstrings for details.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
2011-05-17 13:29:00 +04:00
|
|
|
|
|
2011-07-20 13:35:43 +04:00
|
|
|
|
import collections
|
|
|
|
|
|
2011-06-29 17:01:36 +04:00
|
|
|
|
from .. import css
|
2011-05-17 13:29:00 +04:00
|
|
|
|
|
|
|
|
|
|
2011-05-19 17:31:34 +04:00
|
|
|
|
class Box(object):
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
|
|
|
|
Abstract base class for all boxes.
|
|
|
|
|
"""
|
2011-07-20 20:23:54 +04:00
|
|
|
|
def __init__(self, document, element):
|
|
|
|
|
self.document = document
|
2011-05-19 17:31:34 +04:00
|
|
|
|
# Should never be None
|
|
|
|
|
self.element = element
|
|
|
|
|
# No parent yet. Will be set when this box is added to another box’s
|
|
|
|
|
# children. Only the root box should stay without a parent.
|
|
|
|
|
self.parent = None
|
2011-05-23 15:59:47 +04:00
|
|
|
|
self._init_style()
|
2011-07-07 16:27:46 +04:00
|
|
|
|
# When the size is not calculated yet, use None as default value
|
|
|
|
|
self.width = None
|
|
|
|
|
self.height = None
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-05-23 15:59:47 +04:00
|
|
|
|
def _init_style(self):
|
2011-05-19 17:31:34 +04:00
|
|
|
|
# Computed values
|
2011-05-23 15:59:47 +04:00
|
|
|
|
# Copying might not be needed, but let’s be careful with mutable
|
|
|
|
|
# objects.
|
2011-07-20 20:23:54 +04:00
|
|
|
|
self.style = self.document.style_for(self.element).copy()
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-07-20 13:35:43 +04:00
|
|
|
|
def __repr__(self):
|
|
|
|
|
return '<%s %s>' % (type(self).__name__, self.element.tag)
|
|
|
|
|
|
2011-05-23 18:56:12 +04:00
|
|
|
|
def ancestors(self):
|
2011-05-20 03:57:50 +04:00
|
|
|
|
"""Yield parent and recursively yield parent's parents."""
|
|
|
|
|
parent = self
|
|
|
|
|
while parent.parent:
|
|
|
|
|
parent = parent.parent
|
|
|
|
|
yield parent
|
|
|
|
|
|
2011-07-07 16:27:46 +04:00
|
|
|
|
def containing_block_size(self):
|
|
|
|
|
"""``(width, height)`` size of the box's containing block."""
|
|
|
|
|
if isinstance(self.parent, PageBox):
|
|
|
|
|
return self.parent.width, self.parent.height
|
|
|
|
|
elif self.style.position in ('relative', 'static'):
|
|
|
|
|
return self.parent.width, self.parent.height
|
|
|
|
|
elif self.style.position == 'fixed':
|
|
|
|
|
for ancestor in self.ancestors():
|
|
|
|
|
if isinstance(ancestor, PageBox):
|
|
|
|
|
return ancestor.width, ancestor.height
|
|
|
|
|
assert False, 'Page not found'
|
|
|
|
|
elif self.style.position == 'absolute':
|
|
|
|
|
for ancestor in self.ancestors():
|
|
|
|
|
if ancestor.style.position in ('absolute', 'relative', 'fixed'):
|
|
|
|
|
if ancestor.style.display == 'inline':
|
|
|
|
|
# TODO: fix this bad behaviour, see CSS 10.1
|
|
|
|
|
return ancestor.width, ancestor.height
|
|
|
|
|
else:
|
|
|
|
|
return ancestor.width, ancestor.height
|
|
|
|
|
assert False, 'Containing block not found'
|
|
|
|
|
|
2011-07-15 20:07:49 +04:00
|
|
|
|
def copy(self):
|
2011-07-20 13:35:43 +04:00
|
|
|
|
"""Return shallow copy of the box."""
|
|
|
|
|
cls = type(self)
|
|
|
|
|
# Create a new instance without calling __init__: initializing
|
|
|
|
|
# styles may be kinda expensive, no need to do it again.
|
|
|
|
|
new_box = cls.__new__(cls)
|
|
|
|
|
# Copy attributes
|
|
|
|
|
new_box.__dict__.update(self.__dict__)
|
|
|
|
|
return new_box
|
2011-07-15 20:07:49 +04:00
|
|
|
|
|
2011-07-12 19:53:15 +04:00
|
|
|
|
def padding_width(self):
|
2011-07-22 16:53:51 +04:00
|
|
|
|
"""Width of the padding box."""
|
2011-07-12 19:53:15 +04:00
|
|
|
|
return self.width + self.padding_left + self.padding_right
|
2011-05-19 17:31:34 +04:00
|
|
|
|
|
2011-07-12 19:53:15 +04:00
|
|
|
|
def padding_height(self):
|
2011-07-22 16:53:51 +04:00
|
|
|
|
"""Height of the padding box."""
|
2011-07-12 19:53:15 +04:00
|
|
|
|
return self.height + self.padding_top + self.padding_bottom
|
2011-05-25 16:59:42 +04:00
|
|
|
|
|
2011-07-12 19:53:15 +04:00
|
|
|
|
def border_width(self):
|
2011-07-22 16:53:51 +04:00
|
|
|
|
"""Width of the border box."""
|
|
|
|
|
return self.padding_width() + self.border_left_width + \
|
2011-07-12 19:53:15 +04:00
|
|
|
|
self.border_right_width
|
2011-05-25 16:59:42 +04:00
|
|
|
|
|
2011-07-12 19:53:15 +04:00
|
|
|
|
def border_height(self):
|
2011-07-22 16:53:51 +04:00
|
|
|
|
"""Height of the border box."""
|
|
|
|
|
return self.padding_height() + self.border_top_width + \
|
2011-07-12 19:53:15 +04:00
|
|
|
|
self.border_bottom_width
|
2011-07-15 20:07:49 +04:00
|
|
|
|
|
2011-07-22 16:53:51 +04:00
|
|
|
|
def margin_width(self):
|
|
|
|
|
"""Width of the margin box (aka. outer box)."""
|
|
|
|
|
return self.border_width() + self.margin_left + self.margin_right
|
|
|
|
|
|
|
|
|
|
def margin_height(self):
|
|
|
|
|
"""Height of the margin box (aka. outer box)."""
|
|
|
|
|
return self.border_height() + self.margin_top + self.margin_bottom
|
|
|
|
|
|
2011-07-15 20:07:49 +04:00
|
|
|
|
def horizontal_spacing(self):
|
2011-07-22 16:53:51 +04:00
|
|
|
|
"""Sum of all horizontal margins, paddings and borders."""
|
2011-07-13 20:26:39 +04:00
|
|
|
|
return self.margin_left + self.margin_right + \
|
|
|
|
|
self.padding_left + self.padding_right + \
|
|
|
|
|
self.border_left_width + self.border_right_width
|
2011-07-15 20:07:49 +04:00
|
|
|
|
|
|
|
|
|
def vertical_spacing(self):
|
2011-07-22 16:53:51 +04:00
|
|
|
|
"""Sum of all vertical margins, paddings and borders."""
|
2011-07-13 20:26:39 +04:00
|
|
|
|
return self.margin_top + self.margin_bottom + \
|
|
|
|
|
self.padding_top + self.padding_bottom + \
|
|
|
|
|
self.border_top_width + self.border_bottom_width
|
2011-05-25 16:59:42 +04:00
|
|
|
|
|
2011-07-22 16:53:51 +04:00
|
|
|
|
def content_box_x(self):
|
|
|
|
|
"""Absolute horizontal position of the content box."""
|
|
|
|
|
return self.position_x + self.margin_left + self.padding_left + \
|
|
|
|
|
self.border_left_width
|
|
|
|
|
|
|
|
|
|
def content_box_y(self):
|
|
|
|
|
"""Absolute vertical position of the content box."""
|
|
|
|
|
return self.position_y + self.margin_top + self.padding_top + \
|
|
|
|
|
self.border_top_width
|
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
|
2011-07-11 14:47:00 +04:00
|
|
|
|
class PageBox(Box):
|
2011-07-01 20:20:30 +04:00
|
|
|
|
"""
|
|
|
|
|
Initially the whole document will be in a single page box. During layout
|
|
|
|
|
a new page box is created after every page break.
|
|
|
|
|
"""
|
2011-07-20 17:40:40 +04:00
|
|
|
|
def __init__(self, document, root_box, page_number):
|
2011-07-11 14:47:00 +04:00
|
|
|
|
self.root_box = root_box
|
|
|
|
|
self.root_box.parent = self
|
2011-07-01 20:20:30 +04:00
|
|
|
|
# starting at 1 for the first page.
|
|
|
|
|
self.page_number = page_number
|
|
|
|
|
# Page boxes are not linked to any element.
|
2011-07-20 20:23:54 +04:00
|
|
|
|
super(PageBox, self).__init__(document, element=None)
|
2011-07-01 20:20:30 +04:00
|
|
|
|
|
|
|
|
|
def _init_style(self):
|
|
|
|
|
# First page is a right page. TODO: this "should depend on the major
|
|
|
|
|
# writing direction of the document".
|
|
|
|
|
first_is_right = True
|
|
|
|
|
is_right = (self.page_number % 2) == (1 if first_is_right else 0)
|
|
|
|
|
page_type = 'right' if is_right else 'left'
|
|
|
|
|
if self.page_number == 1:
|
|
|
|
|
page_type = 'first_' + page_type
|
2011-07-20 20:23:54 +04:00
|
|
|
|
style = self.document.computed_styles['@page', page_type]
|
2011-07-01 20:20:30 +04:00
|
|
|
|
# Copying might not be needed, but let’s be careful with mutable
|
|
|
|
|
# objects.
|
|
|
|
|
self.style = style.copy()
|
|
|
|
|
|
2011-07-07 19:33:17 +04:00
|
|
|
|
def containing_block_size(self):
|
|
|
|
|
return self.outer_width, self.outer_height
|
|
|
|
|
|
2011-07-01 20:20:30 +04:00
|
|
|
|
|
2011-07-11 14:47:00 +04:00
|
|
|
|
class ParentBox(Box):
|
|
|
|
|
"""
|
|
|
|
|
A box that has children.
|
|
|
|
|
"""
|
2011-07-20 20:23:54 +04:00
|
|
|
|
def __init__(self, document, element):
|
|
|
|
|
super(ParentBox, self).__init__(document, element)
|
2011-07-20 13:35:43 +04:00
|
|
|
|
self.empty()
|
2011-07-11 14:47:00 +04:00
|
|
|
|
|
2011-07-20 13:35:43 +04:00
|
|
|
|
def empty(self):
|
|
|
|
|
"""Initialize or empty the children list."""
|
|
|
|
|
self.children = collections.deque()
|
|
|
|
|
|
|
|
|
|
def add_child(self, child):
|
2011-07-11 14:47:00 +04:00
|
|
|
|
"""
|
|
|
|
|
Add the new child to this box’s children list and set this box as the
|
|
|
|
|
child’s parent.
|
|
|
|
|
"""
|
|
|
|
|
child.parent = self
|
2011-07-20 13:35:43 +04:00
|
|
|
|
self.children.append(child)
|
2011-07-11 14:47:00 +04:00
|
|
|
|
|
|
|
|
|
def descendants(self):
|
|
|
|
|
"""A flat generator for a box, its children and descendants."""
|
|
|
|
|
yield self
|
|
|
|
|
for child in self.children or []:
|
|
|
|
|
if hasattr(child, 'descendants'):
|
|
|
|
|
for grand_child in child.descendants():
|
|
|
|
|
yield grand_child
|
|
|
|
|
else:
|
|
|
|
|
yield child
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BlockLevelBox(Box):
|
|
|
|
|
"""
|
|
|
|
|
A box that participates in an block formatting context.
|
|
|
|
|
|
|
|
|
|
An element with a 'display' value of 'block', 'liste-item' or 'table'
|
|
|
|
|
generates a block-level box.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BlockContainerBox(ParentBox):
|
|
|
|
|
"""
|
|
|
|
|
A box that either contains only block-level boxes or establishes an inline
|
|
|
|
|
formatting context and thus contains only line boxes.
|
|
|
|
|
|
|
|
|
|
A non-replaced element with a 'display' value of 'block', 'list-item',
|
|
|
|
|
'inline-block' or 'table-cell' generates a block container box.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
class BlockBox(BlockContainerBox, BlockLevelBox):
|
|
|
|
|
"""
|
|
|
|
|
A block-level box that is also a block container.
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
A non-replaced element with a 'display' value of 'block', 'list-item'
|
|
|
|
|
generates a block box.
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-19 17:31:34 +04:00
|
|
|
|
|
|
|
|
|
|
2011-05-23 15:59:47 +04:00
|
|
|
|
class AnonymousBox(Box):
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-25 16:59:42 +04:00
|
|
|
|
A box that is not directly generated by an element. Inherits style instead
|
|
|
|
|
of copying them.
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-23 15:59:47 +04:00
|
|
|
|
def _init_style(self):
|
2011-07-20 20:23:54 +04:00
|
|
|
|
parent_style = self.document.style_for(self.element)
|
|
|
|
|
self.style = css.computed_from_cascaded(self.element, {}, parent_style)
|
2011-07-15 20:07:49 +04:00
|
|
|
|
|
2011-07-20 20:23:54 +04:00
|
|
|
|
# These properties are not inherited so they always have their initial
|
|
|
|
|
# value, zero. The used value is zero too.
|
2011-07-13 20:26:39 +04:00
|
|
|
|
self.margin_top = 0
|
|
|
|
|
self.margin_bottom = 0
|
2011-07-20 20:23:54 +04:00
|
|
|
|
self.margin_left = 0
|
|
|
|
|
self.margin_right = 0
|
|
|
|
|
|
2011-07-13 20:26:39 +04:00
|
|
|
|
self.padding_top = 0
|
|
|
|
|
self.padding_bottom = 0
|
2011-07-20 20:23:54 +04:00
|
|
|
|
self.padding_left = 0
|
|
|
|
|
self.padding_right = 0
|
|
|
|
|
|
2011-07-13 20:26:39 +04:00
|
|
|
|
self.border_top_width = 0
|
|
|
|
|
self.border_bottom_width = 0
|
2011-07-20 20:23:54 +04:00
|
|
|
|
self.border_left_width = 0
|
|
|
|
|
self.border_right_width = 0
|
2011-05-23 15:59:47 +04:00
|
|
|
|
|
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
class AnonymousBlockBox(AnonymousBox, BlockBox):
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-25 16:59:42 +04:00
|
|
|
|
Wraps inline-level boxes where block-level boxes are needed.
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
Block containers (eventually) contain either only block-level boxes or only
|
|
|
|
|
inline-level boxes. When they initially contain both, consecutive
|
2011-06-29 16:04:42 +04:00
|
|
|
|
inline-level boxes are wrapped in an anonymous block box by
|
2011-05-25 16:59:42 +04:00
|
|
|
|
``boxes.inline_in_block()``.
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-19 18:03:50 +04:00
|
|
|
|
|
|
|
|
|
|
2011-07-11 14:47:00 +04:00
|
|
|
|
class LineBox(AnonymousBox, ParentBox):
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-25 16:59:42 +04:00
|
|
|
|
Eventually a line in an inline formatting context. Can only contain
|
|
|
|
|
inline-level boxes.
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-05-23 16:31:14 +04:00
|
|
|
|
In early stages of building the box tree a single line box contains many
|
2011-07-01 20:20:30 +04:00
|
|
|
|
consecutive inline boxes. Later, during layout phase, each line boxes will
|
|
|
|
|
be split into multiple line boxes, one for each actual line.
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-19 17:31:34 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InlineLevelBox(Box):
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-25 16:59:42 +04:00
|
|
|
|
A box that participates in an inline formatting context.
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
An inline-level box that is not an inline box (see below) is said to be
|
|
|
|
|
"atomic". Such boxes are inline-blocks, replaced elements and inline tables.
|
|
|
|
|
|
|
|
|
|
An element with a 'display' value of 'inline', 'inline-table', or
|
|
|
|
|
'inline-block' generates an inline-level box.
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-19 17:31:34 +04:00
|
|
|
|
|
|
|
|
|
|
2011-07-11 14:47:00 +04:00
|
|
|
|
class InlineBox(InlineLevelBox, ParentBox):
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-05-25 16:59:42 +04:00
|
|
|
|
A box who participates in an inline formatting context and whose content
|
|
|
|
|
also participates in that inline formatting context.
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
A non-replaced element with a 'display' value of 'inline' generates an
|
|
|
|
|
inline box.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
2011-07-11 14:47:00 +04:00
|
|
|
|
class TextBox(AnonymousBox, InlineLevelBox):
|
2011-05-25 16:59:42 +04:00
|
|
|
|
"""
|
|
|
|
|
A box that contains only text and has no box children.
|
2011-06-29 16:04:42 +04:00
|
|
|
|
|
2011-05-25 16:59:42 +04:00
|
|
|
|
Any text in the document ends up in a text box. What CSS calls "anonymous
|
|
|
|
|
inline boxes" are also text boxes.
|
2011-05-23 16:31:14 +04:00
|
|
|
|
"""
|
2011-07-20 20:23:54 +04:00
|
|
|
|
def __init__(self, document, element, text):
|
|
|
|
|
super(TextBox, self).__init__(document, element)
|
2011-05-19 17:31:34 +04:00
|
|
|
|
self.text = text
|
|
|
|
|
|
|
|
|
|
|
2011-07-20 15:29:38 +04:00
|
|
|
|
class AtomicInlineLevelBox(InlineLevelBox):
|
|
|
|
|
"""
|
|
|
|
|
A box that participates in an inline formatting context (it is
|
|
|
|
|
inline-level) but can not be split for line breaks.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InlineBlockBox(AtomicInlineLevelBox, BlockContainerBox):
|
2011-05-25 16:59:42 +04:00
|
|
|
|
"""
|
|
|
|
|
A box that is both inline-level and a block container: it behaves as
|
|
|
|
|
inline on the outside and as a block on the inside.
|
|
|
|
|
|
2011-07-20 15:29:38 +04:00
|
|
|
|
A non-replaced element with a 'display' value of 'inline-block' generates
|
|
|
|
|
an inline-block box.
|
2011-05-25 16:59:42 +04:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReplacedBox(Box):
|
|
|
|
|
"""
|
|
|
|
|
A box that is replaced, ie. its content is rendered externally and is opaque
|
|
|
|
|
from CSS’s point of view. Example: <img> elements are replaced.
|
|
|
|
|
"""
|
2011-07-20 20:23:54 +04:00
|
|
|
|
def __init__(self, document, element, replacement):
|
|
|
|
|
super(ReplacedBox, self).__init__(document, element)
|
2011-05-25 17:54:46 +04:00
|
|
|
|
self.replacement = replacement
|
2011-05-25 16:59:42 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BlockLevelReplacedBox(ReplacedBox, BlockLevelBox):
|
|
|
|
|
"""
|
|
|
|
|
A box that is both replaced and block-level.
|
|
|
|
|
|
|
|
|
|
A replaced element with a 'display' value of 'block', 'liste-item' or
|
|
|
|
|
'table' generates a block-level replaced box.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
2011-07-20 15:29:38 +04:00
|
|
|
|
class InlineLevelReplacedBox(ReplacedBox, AtomicInlineLevelBox):
|
2011-05-25 16:59:42 +04:00
|
|
|
|
"""
|
|
|
|
|
A box that is both replaced and inline-level.
|
|
|
|
|
|
|
|
|
|
A replaced element with a 'display' value of 'inline', 'inline-table', or
|
|
|
|
|
'inline-block' generates an inline-level replaced box.
|
|
|
|
|
"""
|