Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php// $Header: /cvsroot/html2ps/box.block.php,v 1.56 2007/01/24 18:55:43 Konstantin Exp $/*** @package HTML2PS* @subpackage Document** Class defined in this file handles the layout of block HTML elements*//*** @package HTML2PS* @subpackage Document** The BlockBox class describes the layout and behavior of HTML element having* 'display: block' CSS property.** @link http://www.w3.org/TR/CSS21/visuren.html#block-box CSS 2.1 Block-level elements and block boxes*/class BlockBox extends GenericContainerBox {/*** Create empty block element*/function BlockBox() {$this->GenericContainerBox();}/*** Create new block element and automatically fill in its contents using* parsed HTML data** @param mixed $root the HTML element corresponding to the element being created** @return BlockBox new BlockBox object (with contents filled)** @see GenericContainerBox::create_content()*/function &create(&$root, &$pipeline) {$box = new BlockBox();$box->readCSS($pipeline->get_current_css_state());$box->create_content($root, $pipeline);return $box;}/*** Create new block element and automatically initialize its contents* with the given text string** @param string $content The text string to be put inside the block box** @return BlockBox new BlockBox object (with contents filled)** @see InlineBox* @see InlineBox::create_from_text()*/function &create_from_text($content, &$pipeline) {$box = new BlockBox();$box->readCSS($pipeline->get_current_css_state());$box->add_child(InlineBox::create_from_text($content,$box->get_css_property(CSS_WHITE_SPACE),$pipeline));return $box;}/*** Layout current block element** @param GenericContainerBox $parent The document element which should be treated as the parent of current element* @param FlowContext $context The flow context containing the additional layout data** @see FlowContext* @see GenericContainerBox* @see InlineBlockBox::reflow** @todo this 'reflow' skeleton is common for all element types; thus, we probably should move the generic 'reflow'* definition to the GenericFormattedBox class, leaving only box-specific 'reflow_static' definitions in specific classes.** @todo make relative positioning more CSS 2.1 compliant; currently, 'bottom' and 'right' CSS properties are ignored.** @todo check whether percentage values should be really ignored during relative positioning*/function reflow(&$parent, &$context) {switch ($this->get_css_property(CSS_POSITION)) {case POSITION_STATIC:$this->reflow_static($parent, $context);return;case POSITION_RELATIVE:/*** CSS 2.1:* Once a box has been laid out according to the normal flow or floated, it may be shifted relative* to this position. This is called relative positioning. Offsetting a box (B1) in this way has no* effect on the box (B2) that follows: B2 is given a position as if B1 were not offset and B2 is* not re-positioned after B1's offset is applied. This implies that relative positioning may cause boxes* to overlap. However, if relative positioning causes an 'overflow:auto' box to have overflow, the UA must* allow the user to access this content, which, through the creation of scrollbars, may affect layout.** @link http://www.w3.org/TR/CSS21/visuren.html#x28 CSS 2.1 Relative positioning*/$this->reflow_static($parent, $context);$this->offsetRelative();return;case POSITION_ABSOLUTE:/*** If this box is positioned absolutely, it is not laid out as usual box;* The reference to this element is stored in the flow context for* futher reference.*/$this->guess_corner($parent);return;case POSITION_FIXED:/*** If this box have 'position: fixed', it is not laid out as usual box;* The reference to this element is stored in the flow context for* futher reference.*/$this->guess_corner($parent);return;};}/*** Reflow absolutely positioned block box. Note that according to CSS 2.1* the only types of boxes which could be absolutely positioned are* 'block' and 'table'** @param FlowContext $context A flow context object containing the additional layout data.** @link http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo CSS 2.1: Relationships between 'display', 'position', and 'float'*/function reflow_absolute(&$context) {$parent_node =& $this->get_parent_node();parent::reflow($parent_node, $context);$width_strategy =& new StrategyWidthAbsolutePositioned();$width_strategy->apply($this, $context);$position_strategy =& new StrategyPositionAbsolute();$position_strategy->apply($this);$this->reflow_content($context);/*** As absolute-positioned box generated new flow context, extend the height to fit all floats*/$this->fitFloats($context);}/*** Reflow fixed-positioned block box. Note that according to CSS 2.1* the only types of boxes which could be absolutely positioned are* 'block' and 'table'** @param FlowContext $context A flow context object containing the additional layout data.** @link http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo CSS 2.1: Relationships between 'display', 'position', and 'float'** @todo it seems that percentage-constrained fixed block width will be calculated incorrectly; we need* to use containing block width instead of $this->get_width() when applying the width constraint*/function reflow_fixed(&$context) {GenericFormattedBox::reflow($this->parent, $context);/*** As fixed-positioned elements are placed relatively to page (so that one element may be shown* several times on different pages), we cannot calculate its position at the moment.* The real position of the element is calculated when it is to be shown - once for each page.** @see BlockBox::show_fixed()*/$this->put_left(0);$this->put_top(0);/*** As sometimes left/right values may not be set, we need to use the "fit" width here.* If box have a width constraint, 'get_max_width' will return constrained value;* othersise, an intrictic width will be returned.** @see GenericContainerBox::get_max_width()*/$this->put_full_width($this->get_max_width($context));/*** Update the width, as it should be calculated based upon containing block width, not real parent.* After this we should remove width constraints or we may encounter problem* in future when we'll try to call get_..._width functions for this box** @todo Update the family of get_..._width function so that they would apply constraint* using the containing block width, not "real" parent width*/$containing_block =& $this->_get_containing_block();$wc = $this->get_css_property(CSS_WIDTH);$this->put_full_width($wc->apply($this->get_width(),$containing_block['right'] - $containing_block['left']));$this->setCSSProperty(CSS_WIDTH, new WCNone());/*** Layout element's children*/$this->reflow_content($context);/*** As fixed-positioned box generated new flow context, extend the height to fit all floats*/$this->fitFloats($context);}/*** Layout static-positioned block box.** Note that static-positioned boxes may be floating boxes** @param GenericContainerBox $parent The document element which should be treated as the parent of current element* @param FlowContext $context The flow context containing the additional layout data** @see FlowContext* @see GenericContainerBox*/function reflow_static(&$parent, &$context) {if ($this->get_css_property(CSS_FLOAT) === FLOAT_NONE) {$this->reflow_static_normal($parent, $context);} else {$this->reflow_static_float($parent, $context);}}/*** Layout normal (non-floating) static-positioned block box.** @param GenericContainerBox $parent The document element which should be treated as the parent of current element* @param FlowContext $context The flow context containing the additional layout data** @see FlowContext* @see GenericContainerBox*/function reflow_static_normal(&$parent, &$context) {GenericFormattedBox::reflow($parent, $context);if ($parent) {/*** Child block will fill the whole content width of the parent block.** 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' +* 'border-right-width' + 'margin-right' = width of containing block** See CSS 2.1 for more detailed explanation** @link http://www.w3.org/TR/CSS21/visudet.html#blockwidth CSS 2.1. 10.3.3 Block-level, non-replaced elements in normal flow*//*** Calculate margin values if they have been set as a percentage; replace percentage-based values* with fixed lengths.*/$this->_calc_percentage_margins($parent);$this->_calc_percentage_padding($parent);/*** Calculate width value if it had been set as a percentage; replace percentage-based value* with fixed value*/$this->put_full_width($parent->get_width());$this->_calc_percentage_width($parent, $context);/*** Calculate 'auto' values of width and margins. Unlike tables, DIV width is either constrained* by some CSS rules or expanded to the parent width; thus, we can calculate 'auto' margin* values immediately.** @link http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins CSS 2.1 Calculating widths and margins*/$this->_calc_auto_width_margins($parent);/*** Collapse top margin** @see GenericFormattedBox::collapse_margin()** @link http://www.w3.org/TR/CSS21/box.html#collapsing-margins CSS 2.1 Collapsing margins*/$y = $this->collapse_margin($parent, $context);/*** At this moment we have top parent/child collapsed margin at the top of context object* margin stack*//*** Apply 'clear' property; the current Y coordinate can be modified as a result of 'clear'.*/$y = $this->apply_clear($y, $context);/*** Store calculated Y coordinate as current Y coordinate in the parent box* No more content will be drawn abowe this mark; current box padding area will* start below.*/$parent->_current_y = $y;/*** Terminate current parent line-box (as current box is not inline)*/$parent->close_line($context);/*** Add current box to the parent's line-box; we will close the line box below* after content will be reflown, so the line box will contain only current box.*/$parent->append_line($this);/*** Now, place the current box upper left content corner. Note that we should not* use get_extra_top here, as _current_y value already culculated using the top margin value* of the current box! The top content edge should be offset from that level only of padding and* border width.*/$border = $this->get_css_property(CSS_BORDER);$padding = $this->get_css_property(CSS_PADDING);$this->moveto( $parent->get_left() + $this->get_extra_left(),$parent->_current_y - $border->top->get_width() - $padding->top->value );}/*** Reflow element's children*/$this->reflow_content($context);if ($this->get_css_property(CSS_OVERFLOW) != OVERFLOW_VISIBLE) {$this->fitFloats($context);}/*** After child elements have been reflown, we should the top collapsed margin stack value* replaced by the value of last child bottom collapsed margin;* if no children contained, then this value should be reset to 0.** Note that invisible and* whitespaces boxes would not affect the collapsed margin value, so we need to* use 'get_first' function instead of just accessing the $content array.** @see GenericContainerBox::get_first*/if (!is_null($this->get_first())) {$cm = 0;} else {$cm = $context->get_collapsed_margin();};/*** Update the bottom value, collapsing the latter value with* current box bottom margin.** Note that we need to remove TWO values from the margin stack:* first - the value of collapsed bottom margin of the last child AND* second - the value of collapsed top margin of current element.*/$margin = $this->get_css_property(CSS_MARGIN);if ($parent) {/*** Terminate parent's line box (it contains the current box only)*/$parent->close_line($context);$parent->_current_y = $this->collapse_margin_bottom($parent, $context);};}function show(&$driver) {if ($this->get_css_property(CSS_FLOAT) != FLOAT_NONE ||$this->get_css_property(CSS_POSITION) == POSITION_RELATIVE) {// These boxes will be rendered separatelyreturn true;};return parent::show($driver);}function show_postponed(&$driver) {return parent::show($driver);}/*** Show fixed positioned block box using the specified output driver** Note that 'show_fixed' is called to box _nested_ to the fixed-positioned boxes too!* Thus, we need to check whether actual 'position' values is 'fixed' for this box* and only in that case attempt to move box** @param OutputDriver $driver The output device driver object*/function show_fixed(&$driver) {$position = $this->get_css_property(CSS_POSITION);if ($position == POSITION_FIXED) {/*** Calculate the distance between the top page edge and top box content edge*/$bottom = $this->get_css_property(CSS_BOTTOM);$top = $this->get_css_property(CSS_TOP);if (!$top->isAuto()) {if ($top->isPercentage()) {$vertical_offset = $driver->getPageMaxHeight() / 100 * $top->getPercentage();} else {$vertical_offset = $top->getPoints();};} elseif (!$bottom->isAuto()) {if ($bottom->isPercentage()) {$vertical_offset = $driver->getPageMaxHeight() * (100 - $bottom->getPercentage())/100 - $this->get_height();} else {$vertical_offset = $driver->getPageMaxHeight() - $bottom->getPoints() - $this->get_height();};} else {$vertical_offset = 0;};/*** Calculate the distance between the right page edge and right box content edge*/$left = $this->get_css_property(CSS_LEFT);$right = $this->get_css_property(CSS_RIGHT);if (!$left->isAuto()) {if ($left->isPercentage()) {$horizontal_offset = $driver->getPageWidth() / 100 * $left->getPercentage();} else {$horizontal_offset = $left->getPoints();};} elseif (!$right->isAuto()) {if ($right->isPercentage()) {$horizontal_offset = $driver->getPageWidth() * (100 - $right->getPercentage())/100 - $this->get_width();} else {$horizontal_offset = $driver->getPageWidth() - $right->getPoints() - $this->get_width();};} else {$horizontal_offset = 0;};/*** Offset current box to the required position on the current page (note that* fixed-positioned element are placed relatively to the viewport - page in our case)*/$this->moveto($driver->getPageLeft() + $horizontal_offset,$driver->getPageTop() - $vertical_offset);};/*** After box have benn properly positioned, render it as usual.*/return GenericContainerBox::show_fixed($driver);}function isBlockLevel() {return true;}}?>