function apply(&$box, &$context)
 {
     $this->_maxw = 0;
     // We need to add text indent to the max width
     $text_indent = $box->getCSSProperty(CSS_TEXT_INDENT);
     # kornev
     if (!$text_indent) {
         $text_indent = CSSTextIndent::default_value();
     }
     $this->_cmaxw = $text_indent->calculate($box);
     for ($i = 0, $size = count($box->content); $i < $size; $i++) {
         $child =& $box->content[$i];
         if ($child->isLineBreak()) {
             $this->line_break();
         } elseif (!$child->out_of_flow()) {
             if (is_inline($child) || $child->getCSSProperty(CSS_FLOAT) !== FLOAT_NONE) {
                 $this->add_width($child->get_max_width($context, $this->_limit));
             } else {
                 $this->line_break();
                 $this->add_width($child->get_max_width($context, $this->_limit));
                 // Process special case with percentage constrained table
                 $item_wc = $child->getCSSProperty(CSS_WIDTH);
                 if (is_a($child, "TableBox") && is_a($item_wc, "WCFraction")) {
                     $this->_cmaxw = max($this->_cmaxw, $item_wc->apply($box->get_width(), $box->parent->get_expandable_width()));
                 }
                 $this->line_break();
             }
         }
     }
     // Check if last line have maximal width
     //
     $this->line_break();
     // Note that max width cannot differ from constrained width,
     // if any width constraints apply
     //
     $wc = $box->getCSSProperty(CSS_WIDTH);
     # kornev
     if (!$wc) {
         $wc = CSSCompositeWidth::default_value();
     }
     if ($wc->applicable($box)) {
         if ($box->parent) {
             $this->_maxw = $wc->apply($this->_maxw, $box->parent->get_width());
         } else {
             $this->_maxw = $wc->apply($this->_maxw, $this->_maxw);
         }
     }
     return $this->_maxw + $box->_get_hor_extra();
 }
 function apply(&$box, &$context)
 {
     $this->_maxw = 0;
     // We need to add text indent to the width
     $ti = $box->getCSSProperty(CSS_TEXT_INDENT);
     # kornev
     if (!$ti) {
         $ti = CSSTextIndent::default_value();
     }
     $this->add_width($ti->calculate($box));
     for ($i = 0, $size = count($box->content); $i < $size; $i++) {
         $child =& $box->content[$i];
         if ($child->isLineBreak()) {
             $this->line_break();
         } elseif (!$child->out_of_flow()) {
             if (is_inline($child)) {
                 // Inline boxes content will not be wrapped, so we may calculate its max width
                 $this->add_width($child->get_max_width($context));
             } else {
                 // Non-inline boxes cause line break
                 $this->line_break();
                 $this->add_width($child->get_min_width($context));
                 $this->line_break();
             }
         }
     }
     // Check if last line have maximal width
     $this->line_break();
     // Apply width constraint to min width. Return maximal value
     $wc = $box->getCSSProperty(CSS_WIDTH);
     # kornev
     if (!$wc) {
         $wc = CSSCompositeWidth::default_value();
     }
     return max($this->_maxw, $wc->apply($this->_maxw, $box->parent->get_width())) + $box->_get_hor_extra();
 }
 /**
  * See also  CSS 2.1,  p 10.3.7 Absolutely  positioned, non-replaced
  * elements
  */
 function apply(&$box, &$context)
 {
     $containing_block =& $box->_get_containing_block();
     $containing_block_width = $containing_block['right'] - $containing_block['left'];
     $right =& $box->getCSSProperty(CSS_RIGHT);
     $left =& $box->getCSSProperty(CSS_LEFT);
     $wc =& $box->getCSSProperty(CSS_WIDTH);
     // For the purposes of this section and the next, the term "static
     // position" (of  an element) refers, roughly, to  the position an
     // element would have had in the normal flow. More precisely:
     //
     // * The static position for 'left'  is the distance from the left
     //   edge of  the containing  block to the  left margin edge  of a
     //   hypothetical box  that would have  been the first box  of the
     //   element  if its  'position'  property had  been 'static'  and
     //   'float'  had  been  'none'.  The  value is  negative  if  the
     //   hypothetical box is to the left of the containing block.
     //
     // * The  static position  for 'right'  is the  distance  from the
     //   right edge of  the containing block to the  right margin edge
     //   of the same hypothetical box  as above. The value is positive
     //   if  the hypothetical  box is  to the  left of  the containing
     //   block's edge.
     //
     // For  the  purposes  of  calculating the  static  position,  the
     // containing block  of fixed  positioned elements is  the initial
     // containing block  instead of  the viewport, and  all scrollable
     // boxes should be assumed to be scrolled to their origin.
     //
     // @todo: implement
     $static_left = 0;
     $static_right = 0;
     // Calculation   of  the   shrink-to-fit  width   is   similar  to
     // calculating the width of a table cell using the automatic table
     // layout  algorithm. Roughly:  calculate the  preferred  width by
     // formatting the content without  breaking lines other than where
     // explicit line  breaks occur,  and also calculate  the preferred
     // minimum width,  e.g., by trying  all possible line  breaks. CSS
     // 2.1 does not define the exact algorithm. Thirdly, calculate the
     // available  width: this is  found by  solving for  'width' after
     // setting 'left' (in case 1) or 'right' (in case 3) to 0.
     //
     // Then  the  shrink-to-fit  width is:  min(max(preferred  minimum
     // width, available width), preferred width).
     $preferred_minimum_width = $box->get_preferred_minimum_width($context);
     $available_width = $containing_block_width - ($left->isAuto() ? 0 : $left->getPoints($containing_block_width)) - ($right->isAuto() ? 0 : $right->getPoints($containing_block_width)) - $box->_get_hor_extra();
     $preferred_width = $box->get_preferred_width($context);
     $shrink_to_fit_width = min(max($preferred_minimum_width, $available_width), $preferred_width);
     // The  constraint  that  determines  the used  values  for  these
     // elements is:
     //
     // 'left' + 'margin-left' + 'border-left-width' + 'padding-left' +
     // 'width'    +   'padding-right'    +    'border-right-width'   +
     // 'margin-right' + 'right' + scrollbar  width (if any) = width of
     // containing block
     // If all three of 'left',  'width', and 'right' are 'auto': First
     // set any  'auto' values for 'margin-left'  and 'margin-right' to
     // 0. Then, if the 'direction' property of the containing block is
     // 'ltr' set 'left'  to the static position and  apply rule number
     // three below; otherwise, set  'right' to the static position and
     // apply rule number one below.
     if ($left->isAuto() && $right->isAuto() && $wc->isNull()) {
         // @todo: support 'direction' property for the containing block
         $box->setCSSProperty(CSS_LEFT, ValueLeft::fromString('0'));
     }
     // If  none of  the three  is  'auto': If  both 'margin-left'  and
     // 'margin-right' are  'auto', solve the equation  under the extra
     // constraint that  the two margins get equal  values, unless this
     // would make them  negative, in which case when  direction of the
     // containing   block   is   'ltr'  ('rtl'),   set   'margin-left'
     // ('margin-right')   to  zero   and   solve  for   'margin-right'
     // ('margin-left'). If  one of 'margin-left'  or 'margin-right' is
     // 'auto', solve  the equation for  that value. If the  values are
     // over-constrained,  ignore the  value  for 'left'  (in case  the
     // 'direction'  property  of the  containing  block  is 'rtl')  or
     // 'right'  (in case  'direction'  is 'ltr')  and  solve for  that
     // value.
     if (!$left->isAuto() && !$right->isAuto() && !$wc->isNull()) {
         // @todo: implement
         $box->put_width($wc->apply($box->get_width(), $containing_block_width));
     }
     // Otherwise,   set   'auto'    values   for   'margin-left'   and
     // 'margin-right'  to 0,  and pick  the one  of the  following six
     // rules that applies.
     // Case  1 ('left'  and  'width'  are 'auto'  and  'right' is  not
     // 'auto', then the width is shrink-to-fit. Then solve for 'left')
     if ($left->isAuto() && !$right->isAuto() && $wc->isNull()) {
         $box->put_width($shrink_to_fit_width);
     }
     // Case  2 ('left'  and  'right'  are 'auto'  and  'width' is  not
     // 'auto',  then if  the  'direction' property  of the  containing
     // block is 'ltr' set 'left' to the static position, otherwise set
     // 'right'  to the  static  position. Then  solve  for 'left'  (if
     // 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').)
     if ($left->isAuto() && $right->isAuto() && !$wc->isNull()) {
         // @todo: implement 'direction' support
         $box->put_width($wc->apply($box->get_width(), $containing_block_width));
     }
     // Case  3 ('width'  and  'right'  are 'auto'  and  'left' is  not
     // 'auto',  then  the width  is  shrink-to-fit  .  Then solve  for
     // 'right')
     if (!$wc) {
         $wc = CSSCompositeWidth::default_value();
     }
     if (!$left->isAuto() && $right->isAuto() && $wc->isNull()) {
         $box->put_width($shrink_to_fit_width);
     }
     // Case 4 ('left'  is 'auto', 'width' and 'right'  are not 'auto',
     // then solve for 'left')
     if ($left->isAuto() && !$right->isAuto() && !$wc->isNull()) {
         $box->put_width($wc->apply($box->get_width(), $containing_block_width));
     }
     // Case 5 ('width'  is 'auto', 'left' and 'right'  are not 'auto',
     // then solve for 'width')
     if (!$left->isAuto() && !$right->isAuto() && $wc->isNull()) {
         $box->put_width($containing_block_width - $left->getPoints($containing_block_width) - $right->getPoints($containing_block_width));
     }
     // Case 6 ('right'  is 'auto', 'left' and 'width'  are not 'auto',
     // then solve for 'right')
     if (!$left->isAuto() && $right->isAuto() && !$wc->isNull()) {
         $box->put_width($wc->apply($box->get_width(), $containing_block_width));
     }
     /**
      * 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
      */
     $box->setCSSProperty(CSS_WIDTH, new WCConstant($box->get_width()));
 }
 /**
  * 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->getCSSProperty(CSS_HTML2PS_CELLPADDING);
     if (!$cellpadding) {
         $cellpadding = CSSCellPadding::default_value();
     }
     $cellspacing = $table->getCSSProperty(CSS_HTML2PS_CELLSPACING);
     if (!$cellspacing) {
         $cellspacing = CSSCellSpacing::default_value();
     }
     for ($i = 0; $i < count($minw); $i++) {
         $cwc = $table->get_cwc($i);
         if (!$cwc) {
             $cwc = new WCNone();
         }
         // 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();
         $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->getCSSProperty(CSS_WIDTH);
     if (!$wc) {
         $wc = CSSCompositeWidth::default_value();
     }
     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;
 }