1<?php
2// $Header: /cvsroot/html2ps/box.table.cell.php,v 1.40 2007/01/24 18:55:45 Konstantin Exp $
3
4class TableCellBox extends GenericContainerBox {
5  var $colspan;
6  var $rowspan;
7  var $column;
8
9  var $_suppress_first;
10  var $_suppress_last;
11
12  function TableCellBox() {
13    // Call parent constructor
14    $this->GenericContainerBox();
15
16    $this->_suppress_first = false;
17    $this->_suppress_last  = false;
18
19    $this->colspan = 1;
20    $this->rowspan = 1;
21
22    // This value will be overwritten in table 'normalize_parent' method
23    //
24    $this->column  = 0;
25    $this->row     = 0;
26  }
27
28  function get_min_width(&$context) {
29    if (isset($this->_cache[CACHE_MIN_WIDTH])) {
30      return $this->_cache[CACHE_MIN_WIDTH];
31    };
32
33    $content_size = count($this->content);
34
35    /**
36     * If box does not have any context, its minimal width is determined by extra horizontal space:
37     * padding, border width and margins
38     */
39    if ($content_size == 0) {
40      $min_width = $this->_get_hor_extra();
41      $this->_cache[CACHE_MIN_WIDTH] = $min_width;
42      return $min_width;
43    };
44
45    /**
46     * If we're in 'nowrap' mode, minimal and maximal width will be equal
47     */
48    $white_space = $this->get_css_property(CSS_WHITE_SPACE);
49    $pseudo_nowrap = $this->get_css_property(CSS_HTML2PS_NOWRAP);
50    if ($white_space   == WHITESPACE_NOWRAP ||
51        $pseudo_nowrap == NOWRAP_NOWRAP) {
52      $min_width = $this->get_min_nowrap_width($context);
53      $this->_cache[CACHE_MIN_WIDTH] = $min_width;
54      return $min_width;
55    }
56
57    /**
58     * We need to add text indent size to the with of the first item
59     */
60    $start_index = 0;
61    while ($start_index < $content_size &&
62           $this->content[$start_index]->out_of_flow()) {
63      $start_index++;
64    };
65
66    if ($start_index < $content_size) {
67      $ti = $this->get_css_property(CSS_TEXT_INDENT);
68      $minw =
69        $ti->calculate($this) +
70        $this->content[$start_index]->get_min_width($context);
71    } else {
72      $minw = 0;
73    };
74
75    for ($i=$start_index; $i<$content_size; $i++) {
76      $item =& $this->content[$i];
77      if (!$item->out_of_flow()) {
78        $minw = max($minw, $item->get_min_width_natural($context));
79      };
80    }
81
82    /**
83     * Apply width constraint to min width. Return maximal value
84     */
85    $wc = $this->get_css_property(CSS_WIDTH);
86    $min_width = max($minw,
87                     $wc->apply($minw, $this->parent->get_width())) + $this->_get_hor_extra();
88    $this->_cache[CACHE_MIN_WIDTH] = $min_width;
89    return $min_width;
90  }
91
92  function readCSS(&$state) {
93    parent::readCSS($state);
94
95    $this->_readCSS($state,
96                    array(CSS_BORDER_COLLAPSE));
97
98    $this->_readCSSLengths($state,
99                           array(CSS_HTML2PS_CELLPADDING,
100                                 CSS_HTML2PS_CELLSPACING,
101                                 CSS_HTML2PS_TABLE_BORDER));
102  }
103
104  function isCell() {
105    return true;
106  }
107
108  function is_fake() {
109    return false;
110  }
111
112  function &create(&$root, &$pipeline) {
113    $css_state = $pipeline->get_current_css_state();
114
115    $box =& new TableCellBox();
116    $box->readCSS($css_state);
117
118    // Use cellspacing / cellpadding values from the containing table
119    $cellspacing = $box->get_css_property(CSS_HTML2PS_CELLSPACING);
120    $cellpadding = $box->get_css_property(CSS_HTML2PS_CELLPADDING);
121
122    // FIXME: I'll need to resolve that issue with COLLAPSING border model. Now borders
123    // are rendered separated
124
125    // if not border set explicitly, inherit value set via border attribute of TABLE tag
126    $border_handler = CSS::get_handler(CSS_BORDER);
127    if ($border_handler->is_default($box->get_css_property(CSS_BORDER))) {
128      $table_border = $box->get_css_property(CSS_HTML2PS_TABLE_BORDER);
129      $box->setCSSProperty(CSS_BORDER, $table_border);
130    };
131
132    $margin =& CSS::get_handler(CSS_MARGIN);
133    $box->setCSSProperty(CSS_MARGIN, $margin->default_value());
134
135    $h_padding =& CSS::get_handler(CSS_PADDING);
136    $padding = $box->get_css_property(CSS_PADDING);
137
138    if ($h_padding->is_default($padding)) {
139      $padding->left->_units       = $cellpadding;
140      $padding->left->auto         = false;
141      $padding->left->percentage   = null;
142
143      $padding->right->_units      = $cellpadding;
144      $padding->right->auto        = false;
145      $padding->right->percentage  = null;
146
147      $padding->top->_units        = $cellpadding;
148      $padding->top->auto          = false;
149      $padding->top->percentage    = null;
150
151      $padding->bottom->_units     = $cellpadding;
152      $padding->bottom->auto       = false;
153      $padding->bottom->percentage = null;
154
155      /**
156       * Note that cellpadding/cellspacing values never use font-size based units
157       * ('em' and 'ex'), so we may pass 0 as base_font_size parameter - it
158       * will not be used anyway
159       */
160      $padding->units2pt(0);
161
162      $box->setCSSProperty(CSS_PADDING, $padding);
163    };
164
165    if ($box->get_css_property(CSS_BORDER_COLLAPSE) != BORDER_COLLAPSE) {
166      $margin_value = $box->get_css_property(CSS_MARGIN);
167      if ($margin->is_default($margin_value)) {
168        $length = $cellspacing->copy();
169        $length->scale(0.5);
170
171        $margin_value->left->_units       = $length;
172        $margin_value->left->auto         = false;
173        $margin_value->left->percentage   = null;
174
175        $margin_value->right->_units      = $length;
176        $margin_value->right->auto        = false;
177        $margin_value->right->percentage  = null;
178
179        $margin_value->top->_units        = $length;
180        $margin_value->top->auto          = false;
181        $margin_value->top->percentage    = null;
182
183        $margin_value->bottom->_units     = $length;
184        $margin_value->bottom->auto       = false;
185        $margin_value->bottom->percentage = null;
186
187        /**
188         * Note that cellpadding/cellspacing values never use font-size based units
189         * ('em' and 'ex'), so we may pass 0 as base_font_size parameter - it
190         * will not be used anyway
191         */
192        $margin_value->units2pt(0);
193
194        $box->setCSSProperty(CSS_MARGIN, $margin_value);
195      }
196    };
197
198    // Save colspan and rowspan information
199    $box->colspan = max(1,(int)$root->get_attribute('colspan'));
200    $box->rowspan = max(1,(int)$root->get_attribute('rowspan'));
201
202    // Create content
203
204    // 'vertical-align' CSS value is not inherited from the table cells
205    $css_state->pushState();
206
207    $handler =& CSS::get_handler(CSS_VERTICAL_ALIGN);
208    $handler->replace($handler->default_value(),
209                      $css_state);
210
211    $box->create_content($root, $pipeline);
212
213    global $g_config;
214    if ($g_config['mode'] == "quirks") {
215      // QUIRKS MODE:
216      // H1-H6 and P elements should have their top/bottom margin suppressed if they occur as the first/last table cell child
217      // correspondingly; note that we cannot do it usung CSS rules, as there's no selectors for the last child.
218      //
219      $child = $root->first_child();
220      if ($child) {
221        while ($child && $child->node_type() != XML_ELEMENT_NODE) {
222          $child = $child->next_sibling();
223        };
224
225        if ($child) {
226          if (array_search(strtolower($child->tagname()), array("h1","h2","h3","h4","h5","h6","p"))) {
227            $box->_suppress_first = true;
228          }
229        };
230      };
231
232      $child = $root->last_child();
233      if ($child) {
234        while ($child && $child->node_type() != XML_ELEMENT_NODE) {
235          $child = $child->previous_sibling();
236        };
237
238        if ($child) {
239          if (array_search(strtolower($child->tagname()), array("h1","h2","h3","h4","h5","h6","p"))) {
240            $box->_suppress_last = true;
241          }
242        };
243      };
244    };
245
246    // pop the default vertical-align value
247    $css_state->popState();
248
249    return $box;
250  }
251
252  // Inherited from GenericFormattedBox
253
254  function get_cell_baseline() {
255    $content = $this->get_first_data();
256    if (is_null($content)) {
257      return 0;
258    }
259    return $content->baseline;
260  }
261
262  // Flow-control
263  function reflow(&$parent, &$context) {
264    GenericFormattedBox::reflow($parent, $context);
265
266    global $g_config;
267    $size = count($this->content);
268    if ($g_config['mode'] == "quirks" && $size > 0) {
269      // QUIRKS MODE:
270      // H1-H6 and P elements should have their top/bottom margin suppressed if they occur as the first/last table cell child
271      // correspondingly; note that we cannot do it usung CSS rules, as there's no selectors for the last child.
272      //
273
274      $first =& $this->get_first();
275      if (!is_null($first) && $this->_suppress_first && $first->isBlockLevel()) {
276        $first->margin->top->value = 0;
277        $first->margin->top->percentage = null;
278      };
279
280      $last =& $this->get_last();
281      if (!is_null($last) && $this->_suppress_last && $last->isBlockLevel()) {
282        $last->margin->bottom->value = 0;
283        $last->margin->bottom->percentage = null;
284      };
285    };
286
287    // Determine upper-left _content_ corner position of current box
288    $this->put_left($parent->_current_x + $this->get_extra_left());
289
290    // NOTE: Table cell margin is used as a cell-spacing value
291    $border = $this->get_css_property(CSS_BORDER);
292    $padding = $this->get_css_property(CSS_PADDING);
293    $this->put_top($parent->_current_y -
294                   $border->top->get_width() -
295                   $padding->top->value);
296
297    // CSS 2.1:
298    // Floats, absolutely positioned elements, inline-blocks, table-cells, and elements with 'overflow' other than
299    // 'visible' establish new block formatting contexts.
300    $context->push();
301    $context->push_container_uid($this->uid);
302
303    // Reflow cell content
304    $this->reflow_content($context);
305
306    // Extend the table cell height to fit all contained floats
307    //
308    // Determine the bottom edge corrdinate of the bottommost float
309    //
310    $float_bottom = $context->float_bottom();
311
312    if (!is_null($float_bottom)) {
313      $this->extend_height($float_bottom);
314    };
315
316    // Restore old context
317    $context->pop_container_uid();
318    $context->pop();
319  }
320}
321
322?>