GenericContainerBox(); // List of column width constraints $this->cwc = array(); $this->_cached_min_widths = null; } function readCSS(&$state) { parent::readCSS($state); $this->_readCSS($state, array(CSS_BORDER_COLLAPSE, CSS_TABLE_LAYOUT)); $this->_readCSSLengths($state, array(CSS_HTML2PS_CELLPADDING, CSS_HTML2PS_CELLSPACING)); } function &cell($r, $c) { return $this->content[$r]->content[$c]; } function rows_count() { return count($this->content); } // NOTE: assumes that rows are already normalized! function cols_count() { return count($this->content[0]->content); } // FIXME: just a stub function append_line(&$e) {} function &create(&$root, &$pipeline) { $box =& new TableBox(); $box->readCSS($pipeline->get_current_css_state()); // This row should not inherit any table specific properties! // 'overflow' for example // $css_state =& $pipeline->get_current_css_state(); $css_state->pushDefaultState(); $row =& new TableRowBox($root); $row->readCSS($css_state); $box->add_child($row); $css_state->popState(); // Setup cellspacing / cellpadding values if ($box->get_css_property(CSS_BORDER_COLLAPSE) == BORDER_COLLAPSE) { $handler =& CSS::get_handler(CSS_PADDING); $box->setCSSProperty(CSS_PADDING, $handler->default_value()); }; // Set text-align to 'left'; all browsers I've ever seen prevent inheriting of // 'text-align' property by the tables. // Say, in the following example the text inside the table cell will be aligned left, // instead of inheriting 'center' value. // //
// //
TEST //
//
$handler =& CSS::get_handler(CSS_TEXT_ALIGN); $handler->css('left', $pipeline); // Parse table contents $child = $root->first_child(); $col_index = 0; while ($child) { if ($child->node_type() === XML_ELEMENT_NODE) { if ($child->tagname() === 'colgroup') { // COLGROUP tags do not generate boxes; they contain information on the columns // $col_index = $box->parse_colgroup_tag($child, $col_index); } else { $child_box =& create_pdf_box($child, $pipeline); $box->add_child($child_box); }; }; $child = $child->next_sibling(); }; $box->normalize($pipeline); $box->normalize_cwc(); $box->normalize_rhc(); $box->normalize_parent(); return $box; } // Parse the data in COL node; // currently only 'width' attribute is parsed // // @param $root reference to a COL dom node // @param $index index of column corresponding to this node function parse_col(&$root, $index) { if ($root->has_attribute('width')) { // The value if 'width' attrubute is "multi-length"; // it means that it could be: // 1. absolute value (10) // 2. percentage value (10%) // 3. relative value (3* or just *) // // TODO: support for relative values $value = $root->get_attribute('width'); if (is_percentage($value)) { $this->cwc[$index] = new WCFraction(((int)$value) / 100); } else { $this->cwc[$index] = new WCConstant(px2pt((int)$value)); }; }; } // Traverse the COLGROUP node and save the column-specific information // // @param $root COLGROUP node // @param $start_index index of the first column in this column group // @return index of column after the last processed // function parse_colgroup_tag(&$root, $start_index) { $index = $start_index; // COLGROUP may contain zero or more COLs // $child = $root->first_child(); while ($child) { if ($child->tagname() === 'col') { $this->parse_col($child, $index); $index ++; }; $child = $child->next_sibling(); }; return $index; } function normalize_parent() { for ($i=0; $icontent); $i++) { $this->content[$i]->parent =& $this; for ($j=0; $jcontent[$i]->content); $j++) { $this->content[$i]->content[$j]->parent =& $this; // Set the column number for the cell to further reference $this->content[$i]->content[$j]->column = $j; // Set the column number for the cell to further reference $this->content[$i]->content[$j]->row = $i; } } } // Normalize row height constraints // // no return value // function normalize_rhc() { // Initialize the constraint array with the empty constraints $this->rhc = array(); for ($i=0, $size = count($this->content); $i < $size; $i++) { $this->rhc[$i] = new HCConstraint(null, null, null); }; // Scan all cells for ($i=0, $num_rows = count($this->content); $i < $num_rows; $i++) { $row =& $this->content[$i]; for ($j=0, $num_cells = count($row->content); $j < $num_cells; $j++) { $cell = $row->content[$j]; // Ignore cells with rowspans if ($cell->rowspan > 1) { continue; } // Put current cell width constraint as a columns with constraint $this->rhc[$i] = merge_height_constraint($this->rhc[$i], $cell->get_height_constraint()); // Now reset the cell width constraint; cell width should be affected by ceolumn constraint only $hc = new HCConstraint(null, null, null); $cell->put_height_constraint($hc); }; }; } // Normalize column width constraints // Note that cwc array may be partially prefilled by a GOLGROUP/COL-generated constraints! // function normalize_cwc() { // Note we've called 'normalize' method prior to 'normalize_cwc', // so we already have all rows of equal length // for ($i=0, $num_cols = count($this->content[0]->content); $i < $num_cols; $i++) { // Check if there's already COL-generated constraint for this column // if (!isset($this->cwc[$i])) { $this->cwc[$i] = new WCNone; }; } // For each column (we should have table already normalized - so lengths of all rows are equal) for ($i=0, $num_cols = count($this->content[0]->content); $i < $num_cols; $i++) { // For each row for ($j=0, $num_rows = count($this->content); $j < $num_rows; $j++) { $cell =& $this->content[$j]->content[$i]; // Ignore cells with colspans if ($cell->colspan > 1) { continue; } // Put current cell width constraint as a columns with constraint $this->cwc[$i] = merge_width_constraint($this->cwc[$i], $cell->get_css_property(CSS_WIDTH)); // Now reset the cell width constraint; cell width should be affected by ceolumn constraint only $cell->setCSSProperty(CSS_WIDTH, new WCNone); } } // Now fix the overconstrained columns; first of all, sum of all percentage-constrained // columns should be less or equal than 100%. If sum is greater, the last column // percentage is reduced in order to get 100% as a result. $rest = 1; for ($i=0, $num_cols = count($this->content[0]->content); $i < $num_cols; $i++) { // Get current CWC $wc =& $this->cwc[$i]; if ($wc->isFraction()) { $wc->fraction = min($rest, $wc->fraction); $rest -= $wc->fraction; }; }; /** * Now, let's process cells spanninig several columns. */ /** * Let's check if there's any colspanning cells filling the whole table width and * containing non-100% percentage constraint */ // For each row for ($j=0; $jcontent); $j++) { /** * Check if the first cell in this row satisfies the above condition */ $cell =& $this->content[$j]->content[0]; /** * Note that there should be '>='; '==' is not enough, as sometimes cell is declared to span * more columns than there are in the table */ $cell_wc = $cell->get_css_property(CSS_WIDTH); if (!$cell->is_fake() && $cell_wc->isFraction() && $cell->colspan >= count($this->content[$j])) { /** * Clear the constraint; anyway, it should be replaced with 100% in this case, as * this cell is the only cell in the row */ $wc = new WCNone; $cell->setCSSProperty(CSS_WIDTH, $wc); }; }; } /** * Normalize table by adding fake cells for colspans and rowspans * Also, if there is any empty rows (without cells), add at least one fake cell */ function normalize(&$pipeline) { /** * Fix empty rows by adding a fake cell */ for ($i=0; $icontent); $i++) { $row =& $this->content[$i]; if (count($row->content) == 0) { $this->content[$i]->add_fake_cell_before(0, $pipeline); }; }; /** * first, normalize colspans */ for ($i=0; $icontent); $i++) { $this->content[$i]->normalize($pipeline); }; /** * second, normalize rowspans * * We should scan table column-by-column searching for row-spanned cells; * consider the following example: * * * * * * * * * * * * * * * * *
A1B1C1
A2C2
C3
*/ $i_col = 0; do { $flag = false; for ($i_row=0; $i_rowcontent); $i_row++) { $row =& $this->content[$i_row]; if ($i_col < count($row->content)) { $flag = true; // Check if this rowspan runs off the last row $row->content[$i_col]->rowspan = min($row->content[$i_col]->rowspan, count($this->content) - $i_row); if ($row->content[$i_col]->rowspan > 1) { // Note that min($i_row + $row->content[$i_col]->rowspan, count($this->content)) is // required, as we cannot be sure that table actually contains the number // of rows used in rowspan // for ($k=$i_row+1; $kcontent[$i_col]->rowspan, count($this->content)); $k++) { // Note that if rowspanned cell have a colspan, we should insert SEVERAL fake cells! // for ($cs = 0; $cs < $row->content[$i_col]->colspan; $cs++) { $this->content[$k]->add_fake_cell_before($i_col, $pipeline); }; }; }; }; }; $i_col ++; } while ($flag); // third, make all rows equal in length by padding with fake-cells $length = 0; for ($i=0; $icontent); $i++) { $length = max($length, count($this->content[$i]->content)); } for ($i=0; $icontent); $i++) { $row =& $this->content[$i]; while ($length > count($row->content)) { $row->append_fake_cell($pipeline); } } } // Overrides default 'add_child' in GenericFormattedBox function add_child(&$item) { // Check if we're trying to add table cell to current table directly, without any table-rows if ($item->isCell()) { // Add cell to the last row $last_row =& $this->content[count($this->content)-1]; $last_row->add_child($item); } elseif ($item->isTableRow()) { // If previous row is empty, remove it (get rid of automatically generated table row in constructor) if (count($this->content) > 0) { if (count($this->content[count($this->content)-1]->content) == 0) { array_pop($this->content); } }; // Just add passed row $this->content[] =& $item; } elseif ($item->isTableSection()) { // Add table section rows to current table, then drop section box for ($i=0, $size = count($item->content); $i < $size; $i++) { $this->add_child($item->content[$i]); } }; } // Table-specific functions // PREDICATES function is_constrained_column($index) { return !is_a($this->get_cwc($index),"wcnone"); } // ROWSPANS function table_have_rowspan($x,$y) { return $this->content[$y]->content[$x]->rowspan; } function table_fit_rowspans($heights) { $spans = $this->get_rowspans(); // Scan all cells spanning several rows foreach ($spans as $span) { $cell =& $this->content[$span->row]->content[$span->column]; // now check if cell height is less than sum of spanned rows heights $row_heights = array_slice($heights, $span->row, $span->size); // Vertical-align current cell // calculate (approximate) row baseline $baseline = $this->content[$span->row]->get_row_baseline(); // apply vertical-align $vertical_align = $cell->get_css_property(CSS_VERTICAL_ALIGN); $va_fun = CSSVerticalAlign::value2pdf($vertical_align); $va_fun->apply_cell($cell, array_sum($row_heights), $baseline); if (array_sum($row_heights) > $cell->get_full_height()) { // Make cell fill all available vertical space $cell->put_full_height(array_sum($row_heights)); }; } } function get_rowspans() { $spans = array(); for ($i=0; $icontent); $i++) { $spans = array_merge($spans, $this->content[$i]->get_rowspans($i)); }; return $spans; } // ROW-RELATED /** * Calculate set of row heights * * At the moment (*), we have a sum of total content heights of percentage constraned rows in * $ch variable, and a "free" (e.g. table height - sum of all non-percentage constrained heights) height * in the $h variable. Obviously, percentage-constrained rows should be expanded to fill the free space * * On the other size, there should be a maximal value to expand them to; for example, if sum of * percentage constraints is 33%, then all these rows should fill only 1/3 of the table height, * whatever the content height of other rows is. In this case, other (non-constrained) rows * should be expanded to fill space left. * * In the latter case, if there's no non-constrained rows, the additional space should be filled by * "plain" rows without any constraints * * @param $minheight the minimal allowed height of the row; as we'll need to expand rows later * and rows containing totally empty cells will have zero height * @return array of row heights in media points */ function _row_heights($minheight) { $heights = array(); $cheights = array(); $height = $this->get_height(); // Calculate "content" and "constrained" heights of table rows for ($i=0; $icontent); $i++) { $heights[] = max($minheight, $this->content[$i]->row_height()); // Apply row height constraint // we need to specify box which parent will serve as a base for height calculation; $hc = $this->get_rhc($i); $cheights[] = $hc->apply($heights[$i], $this->content[$i], null); }; // Collapse "constrained" heights of percentage-constrained rows, if they're // taking more that available space $flags = $this->get_non_percentage_constrained_height_flags(); $h = $height; $ch = 0; for ($i=0; $i 0) { $scale = $h / $ch; if ($scale < 1) { for ($i=0; $iget_non_constrained_height_flags(); $h = $height; $ch = 0; for ($i=0; $i 0) { $scale = $h / $ch; if ($scale < 1) { for ($i=0; $iget_non_percentage_constrained_height_flags(); $h = $height; $ch = 0; for ($i=0; $i 0) { $scale = $h / $ch; if ($scale < 1) { for ($i=0; $iget_top(); $size = count($heights); for ($i=0; $i<$size; $i++) { $this->content[$i]->table_resize_row($heights[$i], $row_top); $row_top -= $heights[$i]; } // Set table height to sum of row heights $this->put_height(array_sum($heights)); } // // Calculate given table row height // // // // @param $index zero-based row index // // @return value of row height (in media points) // // // function table_row_height($index) { // // Select row // $row =& $this->content[$index]; // // Calculate height of each cell contained in this row // $height = 0; // for ($i=0; $icontent); $i++) { // if ($this->table_have_rowspan($i, $index) <= 1) { // $height = max($height, $row->content[$i]->get_full_height()); // } // } // return $height; // } // function get_row_baseline($index) { // // Get current row // $row =& $this->content[$index]; // // Calculate maximal baseline for each cell contained // $baseline = 0; // for ($i = 0; $i < count($row->content); $i++) { // // Cell baseline is the baseline of its first line box inside this cell // if (count($row->content[$i]->content) > 0) { // $baseline = max($baseline, $row->content[$i]->content[0]->baseline); // }; // }; // return $baseline; // } // Width constraints function get_cwc($col) { return $this->cwc[$col]; } // Get height constraint for the given row // // @param $row number of row (zero-based) // // @return HCConstraint object // function get_rhc($row) { return $this->rhc[$row]; } // Width calculation // // Note that if table have no width constraint AND some columns are percentage constrained, // then the width of the table can be determined based on the minimal column width; // e.g. if some column have minimal width of 10px and 10% width constraint, // then table will have minimal width of 100px. If there's several percentage-constrained columns, // then we choose from the generated values the maximal one // // Of course, all of the above can be applied ONLY to table without width constraint; // of theres any w.c. applied to the table, it will have greater than column constraints // // We must take constrained table width into account; if there's a width constraint, // then we must choose the maximal value between the constrained width and sum of minimal // columns widths - so, expanding the constrained width in case it is not enough to fit // the table contents // // @param $context referene to a flow context object // @return minimal box width (including the padding/margin/border width! NOT content width) // function get_min_width(&$context) { $widths = $this->get_table_columns_min_widths($context); $maxw = $this->get_table_columns_max_widths($context); // Expand some columns to fit colspanning cells $widths = $this->_table_apply_colspans($widths, $context, 'get_min_width', $widths, $maxw); $width = array_sum($widths); $base_width = $width; $wc = $this->get_css_property(CSS_WIDTH); if (!$wc->isNull()) { // Check if constrained table width should be expanded to fit the table contents // $width = max($width, $wc->apply(0, $this->parent->get_available_width($context))); } else { // Now check if there's any percentage column width constraints (note that // if we've get here, than the table width is not constrained). Calculate // the table width basing on these values and select the maximal value // for ($i=0; $i<$this->cols_count(); $i++) { $cwc = $this->get_cwc($i); $width = max($width, min($cwc->apply_inverse($widths[$i], $base_width), $this->parent->get_available_width($context) - $this->_get_hor_extra())); }; }; return $width + $this->_get_hor_extra(); } function get_min_width_natural(&$context) { return $this->get_min_width($context); } function get_max_width(&$context) { $wc = $this->get_css_property(CSS_WIDTH); if ($wc->isConstant()) { return $wc->apply(0, $this->parent->get_available_width($context)); } else { $widths = $this->get_table_columns_max_widths($context); $minwc = $this->get_table_columns_min_widths($context); $widths = $this->_table_apply_colspans($widths, $context, 'get_max_width', $minwc, $widths); $width = array_sum($widths); $base_width = $width; // Now check if there's any percentage column width constraints (note that // if we've get here, than the table width is not constrained). Calculate // the table width based on these values and select the maximal value // for ($i=0; $i<$this->cols_count(); $i++) { $cwc = $this->get_cwc($i); $width = max($width, min($cwc->apply_inverse($widths[$i], $base_width), $this->parent->get_available_width($context) - $this->_get_hor_extra())); }; return $width + $this->_get_hor_extra(); } } function get_max_width_natural(&$context) { return $this->get_max_width($context); } function get_width() { $wc = $this->get_css_property(CSS_WIDTH); $pwc = $this->parent->get_css_property(CSS_WIDTH); if (!$this->parent->isCell() || !$pwc->isNull() || !$wc->isFraction()) { $width = $wc->apply($this->width, $this->parent->width); } else { $width = $this->width; }; // Note that table 'padding' property for is handled differently // by different browsers; for example, IE 6 ignores it completely, // while FF 1.5 subtracts horizontal padding value from constrained // table width. We emulate FF behavior here return $width - $this->get_padding_left() - $this->get_padding_right(); } function table_column_widths(&$context) { $table_layout = $this->get_css_property(CSS_TABLE_LAYOUT); switch ($table_layout) { case TABLE_LAYOUT_FIXED: // require_once(HTML2PS_DIR.'strategy.table.layout.fixed.php'); // $strategy =& new StrategyTableLayoutFixed(); // break; case TABLE_LAYOUT_AUTO: default: require_once(HTML2PS_DIR.'strategy.table.layout.auto.php'); $strategy =& new StrategyTableLayoutAuto(); break; }; return $strategy->apply($this, $context); } // Extend some columns widths (if needed) to fit colspanned cell contents // function _table_apply_colspans($widths, &$context, $width_fun, $minwc, $maxwc) { $colspans = $this->get_colspans(); foreach ($colspans as $colspan) { $cell = $this->content[$colspan->row]->content[$colspan->column]; // apply colspans to the corresponsing colspanned-cell dimension // $cell_width = $cell->$width_fun($context); // Apply cell constraint width, if any AND if table width is constrained // if table width is not constrained, we should not do this, as current value // of $table->get_width is maximal width (parent width), not the actual // width of the table $wc = $this->get_css_property(CSS_WIDTH); if (!$wc->isNull()) { $cell_wc = $cell->get_css_property(CSS_WIDTH); $cell_width = $cell_wc->apply($cell_width, $this->get_width()); // On the other side, constrained with cannot be less than cell minimal width $cell_width = max($cell_width, $cell->get_min_width($context)); }; // now select the pre-calculated widths of columns covered by this cell // select the list of resizable columns covered by this cell $spanned_widths = array(); $spanned_resizable = array(); for ($i=$colspan->column; $i < $colspan->column+$colspan->size; $i++) { $spanned_widths[] = $widths[$i]; $spanned_resizable[] = ($minwc[$i] != $maxwc[$i]); } // Sometimes we may encounter the colspan over the empty columns (I mean ALL columns are empty); in this case // we need to make these columns reizable in order to fit colspanned cell contents // if (array_sum($spanned_widths) == 0) { for ($i=0; $icolumn, $colspan->size, $spanned_widths); }; return $widths; } function get_table_columns_max_widths(&$context) { $widths = array(); for ($i=0; $icontent[0]->content); $i++) { $widths[] = 0; }; for ($i=0; $icontent); $i++) { // Calculate column widths for a current row $roww = $this->content[$i]->get_table_columns_max_widths($context); for ($j=0; $jget_cwc($i); // Newertheless, percentage constraints should not be applied IF table // does not have constrained width // if (!is_a($cwc,"wcfraction")) { $widths[$i] = $cwc->apply($widths[$i], $this->get_width()); }; } // TODO: colspans return $widths; } /** * Optimization: calculated widths are cached */ function get_table_columns_min_widths(&$context) { if (!is_null($this->_cached_min_widths)) { return $this->_cached_min_widths; }; $widths = array(); for ($i=0; $icontent[0]->content); $i++) { $widths[] = 0; }; $content_size = count($this->content); for ($i=0; $i<$content_size; $i++) { // Calculate column widths for a current row $roww = $this->content[$i]->get_table_columns_min_widths($context); $row_size = count($roww); for ($j=0; $j<$row_size; $j++) { $widths[$j] = max($roww[$j], $widths[$j]); } } $this->_cached_min_widths = $widths; return $widths; } function get_colspans() { $colspans = array(); for ($i=0; $icontent); $i++) { $colspans = array_merge($colspans, $this->content[$i]->get_colspans($i)); }; return $colspans; } function check_constrained_colspan($col) { for ($i=0; $i<$this->rows_count(); $i++) { $cell =& $this->cell($i, $col); $cell_wc = $cell->get_css_property(CSS_WIDTH); if ($cell->colspan > 1 && !$cell_wc->isNull()) { return true; }; }; return false; } // Tries to change minimal constrained width so that columns will fit into the given // table width // // Note that every width constraint have its own priority; first, the unconstrained columns are collapsed, // then - percentage constrained and after all - columns having fixed width // // @param $width table width // @param $minw array of unconstrained minimal widths // @param $minwc array of constrained minimal widths // @return list of normalized minimal constrained widths // function normalize_min_widths($width, $minw, $minwc) { // Check if sum of constrained widths is too big // Note that we compare sum of constrained width with the MAXIMAL value of table width and // sum of uncostrained minimal width; it will prevent from unneeded collapsing of table cells // if table content will expand its width anyway // $twidth = max($width, array_sum($minw)); // compare with sum of minimal constrained widths // if (array_sum($minwc) > $twidth) { $delta = array_sum($minwc) - $twidth; // Calculate the amount of difference between minimal and constrained minimal width for each columns $diff = array(); for ($i=0; $icheck_constrained_colspan($i)) { $diff[$i] = $minwc[$i] - $minw[$i]; } else { $diff[$i] = 0; }; } // If no difference is found, we can collapse no columns // otherwise scale some columns... $cwdelta = array_sum($diff); if ($cwdelta > 0) { for ($i=0; $icontent[$y]->content[$x]->colspan; } // Flow-control function reflow(&$parent, &$context) { if ($this->get_css_property(CSS_FLOAT) === FLOAT_NONE) { $status = $this->reflow_static_normal($parent, $context); } else { $status = $this->reflow_static_float($parent, $context); } return $status; } function reflow_absolute(&$context) { GenericFormattedBox::reflow($parent, $context); // Calculate margin values if they have been set as a percentage $this->_calc_percentage_margins($parent); // Calculate width value if it had been set as a percentage $this->_calc_percentage_width($parent, $context); $wc = $this->get_css_property(CSS_WIDTH); if (!$wc->isNull()) { $col_width = $this->get_table_columns_min_widths($context); $maxw = $this->get_table_columns_max_widths($context); $col_width = $this->_table_apply_colspans($col_width, $context, 'get_min_width', $col_width, $maxw); if (array_sum($col_width) > $this->get_width()) { $wc = new WCConstant(array_sum($col_width)); }; }; $position_strategy =& new StrategyPositionAbsolute(); $position_strategy->apply($this); $this->reflow_content($context); } /** * TODO: unlike block elements, table unconstrained width is determined * with its content, so it may be smaller than parent available width! */ function reflow_static_normal(&$parent, &$context) { GenericFormattedBox::reflow($parent, $context); // Calculate margin values if they have been set as a percentage $this->_calc_percentage_margins($parent); // Calculate width value if it had been set as a percentage $this->_calc_percentage_width($parent, $context); $wc = $this->get_css_property(CSS_WIDTH); if (!$wc->isNull()) { $col_width = $this->get_table_columns_min_widths($context); $maxw = $this->get_table_columns_max_widths($context); $col_width = $this->_table_apply_colspans($col_width, $context, 'get_min_width', $col_width, $maxw); if (array_sum($col_width) > $this->get_width()) { $wc = new WCConstant(array_sum($col_width)); }; }; // As table width can be deterimined by its contents, we may calculate auto values // only AFTER the contents have been reflown; thus, we'll offset the table // as a whole by a value of left margin AFTER the content reflow // Do margin collapsing $y = $this->collapse_margin($parent, $context); // At this moment we have top parent/child collapsed margin at the top of context object // margin stack $y = $this->apply_clear($y, $context); // Store calculated Y coordinate as current Y in the parent box $parent->_current_y = $y; // Terminate current parent line-box $parent->close_line($context); // And add current box to the parent's line-box (alone) $parent->append_line($this); // Determine upper-left _content_ corner position of current box // Also see note above regarding margins $border = $this->get_css_property(CSS_BORDER); $padding = $this->get_css_property(CSS_PADDING); $this->put_left($parent->_current_x + $border->left->get_width() + $padding->left->value); // Note that top margin already used above during maring collapsing $this->put_top($parent->_current_y - $border->top->get_width() - $padding->top->value); /** * By default, child block box will fill all available parent width; * note that actual width will be smaller because of non-zero padding, border and margins */ $this->put_full_width($parent->get_available_width($context)); // Reflow contents $this->reflow_content($context); // Update the collapsed margin value with current box bottom margin $margin = $this->get_css_property(CSS_MARGIN); $context->pop_collapsed_margin(); $context->pop_collapsed_margin(); $context->push_collapsed_margin($margin->bottom->value); // Calculate margins and/or width is 'auto' values have been specified $this->_calc_auto_width_margins($parent); $this->offset($margin->left->value, 0); // Extend parent's height to fit current box $parent->extend_height($this->get_bottom_margin()); // Terminate parent's line box $parent->close_line($context); } // Get a list of boolean values indicating if table rows are height constrained // // @return array containing 'true' value at index I if I-th row is not height-constrained // and 'false' otherwise // function get_non_constrained_flags() { $flags = array(); for ($i=0; $icontent); $i++) { $hc = $this->get_rhc($i); $flags[$i] = (is_null($hc->constant)) && (is_null($hc->min)) && (is_null($hc->max)); }; return $flags; } // Get a list of boolean values indicating if table rows are height constrained using percentage values // // @return array containing 'true' value at index I if I-th row is not height-constrained // and 'false' otherwise // function get_non_percentage_constrained_height_flags() { $flags = array(); for ($i=0; $icontent); $i++) { $hc = $this->get_rhc($i); $flags[$i] = (!is_null($hc->constant) ? !$hc->constant[1] : true) && (!is_null($hc->min) ? !$hc->min[1] : true) && (!is_null($hc->max) ? !$hc->max[1] : true); }; return $flags; } function get_non_constrained_height_flags() { $flags = array(); for ($i=0; $icontent); $i++) { $hc = $this->get_rhc($i); $flags[$i] = $hc->is_null(); }; return $flags; } // Get a list of boolean values indicating if table columns are height constrained // // @return array containing 'true' value at index I if I-th columns is not width-constrained // and 'false' otherwise // function get_non_constrained_width_flags() { $flags = array(); for ($i=0; $i<$this->cols_count(); $i++) { $wc = $this->get_cwc($i); $flags[$i] = is_a($wc,"wcnone"); }; return $flags; } function get_non_constant_constrained_width_flags() { $flags = array(); for ($i=0; $i<$this->cols_count(); $i++) { $wc = $this->get_cwc($i); $flags[$i] = !is_a($wc,"WCConstant"); }; return $flags; } function check_if_column_image_constrained($col) { for ($i=0; $i<$this->rows_count(); $i++) { $cell =& $this->cell($i, $col); for ($j=0; $jcontent); $j++) { if (!$cell->content[$j]->is_null() && !is_a($cell->content[$j], "GenericImgBox")) { return false; }; }; }; return true; } function get_non_image_constrained_width_flags() { $flags = array(); for ($i=0; $i<$this->cols_count(); $i++) { $flags[$i] = !$this->check_if_column_image_constrained($i); }; return $flags; } // Get a list of boolean values indicating if table rows are NOT constant constrained // // @return array containing 'true' value at index I if I-th row is height-constrained // and 'false' otherwise // function get_non_constant_constrained_flags() { $flags = array(); for ($i=0; $icontent); $i++) { $hc = $this->get_rhc($i); $flags[$i] = is_null($hc->constant); }; return $flags; } function reflow_content(&$context) { // Reflow content // Reset current Y value // $this->_current_y = $this->get_top(); // Determine the base table width // if width constraint exists, the actual table width will not be changed anyway // $this->put_width(min($this->get_max_width($context), $this->get_width())); // Calculate widths of table columns $columns = $this->table_column_widths($context); // Collapse table to minimum width (if width is not constrained) $real_width = array_sum($columns); $this->put_width($real_width); // If width is constrained, and is less than calculated, update the width constraint // // if ($this->get_width() < $real_width) { // // $this->put_width_constraint(new WCConstant($real_width)); // }; // Flow cells horizontally in each table row for ($i=0; $icontent); $i++) { // Row flow started // Reset current X coordinate to the far left of the table $this->_current_x = $this->get_left(); // Flow each cell in the row $span = 0; for ($j=0; $jcontent[$i]->content); $j++) { // Skip cells covered by colspans (fake cells, anyway) if ($span == 0) { // Flow current cell // Any colspans here? $span = $this->table_have_colspan($j, $i); // Get sum of width for the current cell (or several cells in colspan) // In most cases, $span == 1 here (just a single cell) $cw = array_sum(array_slice($columns, $j, $span)); // store calculated width of the current cell $cell =& $this->content[$i]->content[$j]; $cell->put_full_width($cw); $cell->setCSSProperty(CSS_WIDTH, new WCConstant($cw - $cell->_get_hor_extra())); // TODO: check for rowspans // Flow cell $this->content[$i]->content[$j]->reflow($this, $context); // Offset current X value by the cell width $this->_current_x += $cw; }; // Current cell have been processed or skipped $span = max(0, $span-1); } // calculate row height and do vertical align // $this->table_fit_row($i); // row height calculation offset current Y coordinate by the row height calculated // $this->_current_y -= $this->table_row_height($i); $this->_current_y -= $this->content[$i]->row_height(); } // Calculate (and possibly adjust height of table rows) $heights = $this->_row_heights(0.1); // adjust row heights to fit cells spanning several rows foreach ($this->get_rowspans() as $rowspan) { // Get height of the cell $cell_height = $this->content[$rowspan->row]->content[$rowspan->column]->get_full_height(); // Get calculated height of the spanned-over rows $cell_row_heights = array_slice($heights, $rowspan->row, $rowspan->size); // Get list of non-constrained columns $flags = array_slice($this->get_non_constrained_flags(), $rowspan->row, $rowspan->size); // Expand row heights (only for non-constrained columns) $new_heights = expand_to_with_flags($cell_height, $cell_row_heights, $flags); // Check if rows could not be expanded // if (array_sum($new_heights) < $cell_height-1) { if (array_sum($new_heights) < $cell_height - EPSILON) { // Get list of non-constant-constrained columns $flags = array_slice($this->get_non_constant_constrained_flags(), $rowspan->row, $rowspan->size); // use non-constant-constrained rows $new_heights = expand_to_with_flags($cell_height, $cell_row_heights, $flags); }; // Update the rows heights array_splice($heights, $rowspan->row, $rowspan->size, $new_heights); } // Now expand rows to full table height $table_height = max($this->get_height(), array_sum($heights)); // Get list of non-constrained columns $flags = $this->get_non_constrained_height_flags(); // Expand row heights (only for non-constrained columns) $heights = expand_to_with_flags($table_height, $heights, $flags); // Check if rows could not be expanded if (array_sum($heights) < $table_height - EPSILON) { // Get list of non-constant-constrained columns $flags = $this->get_non_constant_constrained_flags(); // use non-constant-constrained rows $heights = expand_to_with_flags($table_height, $heights, $flags); }; // Now we calculated row heights, time to actually resize them $this->table_resize_rows($heights); // Update size of cells spanning several rows $this->table_fit_rowspans($heights); // Expand total table height, if needed $total_height = array_sum($heights); if ($total_height > $this->get_height()) { $hc = new HCConstraint(array($total_height, false), array($total_height, false), array($total_height, false)); $this->put_height_constraint($hc); }; } function isBlockLevel() { return true; } } ?>