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.
//
//
$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:
*
*
*
* A1 |
* B1 |
* C1 |
*
*
*
* A2 |
* C2 |
*
*
*
* 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;
}
}
?>