/**
  * Fit table columns to the given width
  */
 function table_columns_fit(&$table, $width, &$context)
 {
     $minw = $table->get_table_columns_min_widths($context);
     $maxw = $table->get_table_columns_max_widths($context);
     $minw = $this->use_colspans($table, $minw, $context, 'get_min_width', $minw, $maxw);
     $maxw = $this->use_colspans($table, $maxw, $context, 'get_max_width', $minw, $maxw);
     // Store number of columns
     $columns = count($minw);
     // Apply column width constraints
     $minwc = array();
     $maxwc = array();
     $cellpadding = $table->get_css_property(CSS_HTML2PS_CELLPADDING);
     $cellspacing = $table->get_css_property(CSS_HTML2PS_CELLSPACING);
     for ($i = 0; $i < count($minw); $i++) {
         $cwc = $table->get_cwc($i);
         // Do not allow constrained max width be less than min width
         // Do not allow constrained min width be less than min width
         //
         $table_width = $table->get_width();
         $extra = 2 * $cellpadding->getPoints() + $cellspacing->getPoints();
         $extra = 0;
         $minwc[$i] = max($minw[$i], $cwc->apply($minw[$i] - $extra, $table_width) + $extra);
         $maxwc[$i] = max($minw[$i], $cwc->apply($maxw[$i] - $extra, $table_width) + $extra);
     }
     $minwc = $table->normalize_min_widths($width, $minw, $minwc);
     $minwc = $table->_table_apply_colspans($minwc, $context, 'get_min_width', $minwc, $maxwc);
     // We need to normalize widths for the case of colspans width is too big; for example:
     // <table><tr><td width="100">
     // <table><tr><td width="150">TEXT<td>TEXT<tr><td colspan="2" width="200">
     // in this case table SHOULD NOT be expanded over the 100px!
     //
     // $minwc = $table->normalize_min_widths($width, $minw, $minwc);
     $maxwc = $table->_table_apply_colspans($maxwc, $context, 'get_max_width', $minwc, $maxwc);
     // Calculate actual widths
     $widths = array();
     // Calculate widths for all constrained columns
     for ($i = 0; $i < $columns; $i++) {
         if ($table->is_constrained_column($i)) {
             $widths[$i] = $minwc[$i];
         }
     }
     // Quick fix for overconstrained tables: if table have width attribute AND its value is less than sum
     // of constrained columns widths plus minimal widths of uncostrained columns, then we'll expand the width of table
     // to fit all columns
     // 1. calculate sum of constrained column widths
     // 2. calculate sum of unconstrained column minimal widths
     $sum_cw = 0;
     $sum_ucw = 0;
     for ($i = 0; $i < $columns; $i++) {
         if ($table->is_constrained_column($i)) {
             $sum_cw += $widths[$i];
         } else {
             $sum_ucw += $minwc[$i];
         }
     }
     // 3. compare these widths with the table width and choose the maximal value
     $width = max($width, $sum_cw + $sum_ucw);
     // Second pass - disctribute the rest of the width
     // Explanation of the stuff below (I've really had problems with this small piece of code, especially
     // when I was trying to fix "bugs" inside it)
     //
     // First of all, no column can be narrower than it minimal width (determined by its content)
     // Note that constrained columns have their widths distributed above, so we can exclude them for now
     // (just throw them out and imagine that table does not contain any width-constrained cols)
     //
     // Second, the relative widths of columns will have _appoximately_ the same ratio as
     // their maximal content widths. (In exception of cases where the first rule will take place -
     // say for the table containing two columns with the VERY long text in the first and one or two words
     // in the second)
     //
     // In general, this approach can be inoptimal in case of _very_ different font sizes
     // inside the cells, of, say big images; nevertheless, it will give a good approximate
     // AND still fast enough (unlike fully correct methods involving evaluation of the content height of the cell)
     //
     // Thus, we do the following:
     // - calculate the ratio of current column MAXIMAL ($current_max) width to the sum of MAXIMAL widths of all columns left
     //   (inluding current) second rule applied. Note that we need remember about column spans and select
     //   maxw or maxwc in order.
     // - then check if the rest of width will be too small for other columns to fit and decrease current columns
     //   width (see MIN function call)
     // - then check again if our width will be too small for current column to fit (and expand if nesessary) -
     //   MAX function call
     for ($i = 0; $i < $columns; $i++) {
         if (!$table->is_constrained_column($i)) {
             // Get undistributed width (total table width - width of constrained columns)
             $rest = $width - array_sum($widths);
             // get max width of column being processed
             // If width is equal to zero, use max constrained width, as this column could be covered by colspan;
             // If not, we lose nothing, because all constrained columns are already processed earlier, and no more
             // columns except these two types can have different constrained and raw widths
             $current_max = max($maxw[$i], $maxwc[$i]);
             // Get sum of maximal constrained widths of unplaced columns
             $sum_max_cw = 0;
             $sum_min_cw = 0;
             for ($j = 0; $j < $columns; $j++) {
                 if (!isset($widths[$j])) {
                     $sum_max_cw += max($maxw[$j], $maxwc[$j]);
                     $sum_min_cw += max($minw[$j], $minwc[$j]);
                 }
             }
             // If some unplaced columns have maximal (constrained width) greater zero
             if ($sum_max_cw > 0) {
                 $current_max = min($current_max * $rest / $sum_max_cw, $rest - $sum_min_cw + max($minwc[$i], $minw[$i]));
             }
             // Check for minimal width (either unconstrained or constrained) of current column
             $current_max = max($current_max, $minw[$i] == 0 ? $minwc[$i] : $minw[$i]);
             // Store calculated width
             $widths[$i] = $current_max;
         }
     }
     // Process the case of a lone empty table cell (used, for example, for its background color)
     // as we're using floating point numbers, we cannot use equals sign
     if (array_sum($widths) < EPSILON) {
         for ($i = 0; $i < count($widths); $i++) {
             $widths[$i] = 0.01;
         }
     }
     // now - the last attempt; if total width is less than box width, then we have a situation when either
     // all columns AND table are width constrained or the HTML similar to the following:
     //
     // <table cellpadding="0" width="100%" bgcolor="green"><tbody><tr>
     // <td colspan="2" bgcolor="yellow"></td>
     // <td style="width: 100px;" bgcolor="cyan">TEXT
     //
     // e.g. empty column (with zero width) and fixed-width column.
     //
     $wc = $table->get_css_property(CSS_WIDTH);
     if (!$wc->isNull()) {
         if (array_sum($widths) < $width) {
             // Let's make zero-width columns
             // non-zero width (so that they columd be expanded) and re-try expanding columns
             //
             for ($i = 0; $i < count($widths); $i++) {
                 if ($widths[$i] == 0) {
                     $widths[$i] = EPSILON;
                 }
             }
             // Now, if there's at least one non-costrained columns, try expanding them again
             $flags = $table->get_non_constrained_width_flags();
             if (!any_flag_set($flags)) {
                 $flags = $table->get_non_constant_constrained_width_flags();
                 if (!any_flag_set($flags)) {
                     $flags = $table->get_non_image_constrained_width_flags();
                     if (!any_flag_set($flags)) {
                         for ($i = 0; $i < count($flags); $i++) {
                             $flags[$i] = true;
                         }
                     }
                 }
             }
             $widths = expand_to_with_flags($width, $widths, $flags);
         }
         // in case of overconstrained table (e.g. two columns with 20% widths), expand them
         $widths = expand_to($width, $widths);
     }
     $table->put_full_width(array_sum($widths));
     // Now we need to sort array by key keeping key-value associations in order for array_slice to work correctly
     ksort($widths, SORT_NUMERIC);
     return $widths;
 }
 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; $i < count($this->content); $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; $j < count($this->content[$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);
     }
 }