Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// $Header: /cvsroot/html2ps/box.block.php,v 1.56 2007/01/24 18:55:43 Konstantin Exp $
3
 
4
/**
5
 * @package HTML2PS
6
 * @subpackage Document
7
 *
8
 * Class defined in this file handles the layout of block HTML elements
9
 */
10
 
11
/**
12
 * @package HTML2PS
13
 * @subpackage Document
14
 *
15
 * The BlockBox class describes the layout and behavior of HTML element having
16
 * 'display: block' CSS property.
17
 *
18
 * @link http://www.w3.org/TR/CSS21/visuren.html#block-box CSS 2.1 Block-level elements and block boxes
19
 */
20
class BlockBox extends GenericContainerBox {
21
  /**
22
   * Create empty block element
23
   */
24
  function BlockBox() {
25
    $this->GenericContainerBox();
26
  }
27
 
28
  /**
29
   * Create new block element and automatically fill in its contents using
30
   * parsed HTML data
31
   *
32
   * @param mixed $root the HTML element corresponding to the element being created
33
   *
34
   * @return BlockBox new BlockBox object (with contents filled)
35
   *
36
   * @see GenericContainerBox::create_content()
37
   */
38
  function &create(&$root, &$pipeline) {
39
    $box = new BlockBox();
40
    $box->readCSS($pipeline->get_current_css_state());
41
    $box->create_content($root, $pipeline);
42
    return $box;
43
  }
44
 
45
  /**
46
   * Create new block element and automatically initialize its contents
47
   * with the given text string
48
   *
49
   * @param string $content The text string to be put inside the block box
50
   *
51
   * @return BlockBox new BlockBox object (with contents filled)
52
   *
53
   * @see InlineBox
54
   * @see InlineBox::create_from_text()
55
   */
56
  function &create_from_text($content, &$pipeline) {
57
    $box = new BlockBox();
58
    $box->readCSS($pipeline->get_current_css_state());
59
    $box->add_child(InlineBox::create_from_text($content,
60
                                                $box->get_css_property(CSS_WHITE_SPACE),
61
                                                $pipeline));
62
    return $box;
63
  }
64
 
65
  /**
66
   * Layout current block element
67
   *
68
   * @param GenericContainerBox $parent The document element which should be treated as the parent of current element
69
   * @param FlowContext $context The flow context containing the additional layout data
70
   *
71
   * @see FlowContext
72
   * @see GenericContainerBox
73
   * @see InlineBlockBox::reflow
74
   *
75
   * @todo this 'reflow' skeleton is common for all element types; thus, we probably should move the generic 'reflow'
76
   * definition to the GenericFormattedBox class, leaving only box-specific 'reflow_static' definitions in specific classes.
77
   *
78
   * @todo make relative positioning more CSS 2.1 compliant; currently, 'bottom' and 'right' CSS properties are ignored.
79
   *
80
   * @todo check whether percentage values should be really ignored during relative positioning
81
   */
82
  function reflow(&$parent, &$context) {
83
    switch ($this->get_css_property(CSS_POSITION)) {
84
    case POSITION_STATIC:
85
      $this->reflow_static($parent, $context);
86
      return;
87
 
88
    case POSITION_RELATIVE:
89
      /**
90
       * CSS 2.1:
91
       * Once a box has been laid out according to the normal flow or floated, it may be shifted relative
92
       * to this position. This is called relative positioning. Offsetting a box (B1) in this way has no
93
       * effect on the box (B2) that follows: B2 is given a position as if B1 were not offset and B2 is
94
       * not re-positioned after B1's offset is applied. This implies that relative positioning may cause boxes
95
       * to overlap. However, if relative positioning causes an 'overflow:auto' box to have overflow, the UA must
96
       * allow the user to access this content, which, through the creation of scrollbars, may affect layout.
97
       *
98
       * @link http://www.w3.org/TR/CSS21/visuren.html#x28 CSS 2.1 Relative positioning
99
       */
100
      $this->reflow_static($parent, $context);
101
      $this->offsetRelative();
102
      return;
103
 
104
    case POSITION_ABSOLUTE:
105
      /**
106
       * If this box is positioned absolutely, it is not laid out as usual box;
107
       * The reference to this element is stored in the flow context for
108
       * futher reference.
109
       */
110
      $this->guess_corner($parent);
111
      return;
112
 
113
    case POSITION_FIXED:
114
      /**
115
       * If this box have 'position: fixed', it is not laid out as usual box;
116
       * The reference to this element is stored in the flow context for
117
       * futher reference.
118
       */
119
      $this->guess_corner($parent);
120
      return;
121
    };
122
  }
123
 
124
  /**
125
   * Reflow absolutely positioned block box. Note that according to CSS 2.1
126
   * the only types of boxes which could be absolutely positioned are
127
   * 'block' and 'table'
128
   *
129
   * @param FlowContext $context A flow context object containing the additional layout data.
130
   *
131
   * @link http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo CSS 2.1: Relationships between 'display', 'position', and 'float'
132
   */
133
  function reflow_absolute(&$context) {
134
    $parent_node =& $this->get_parent_node();
135
    parent::reflow($parent_node, $context);
136
 
137
    $width_strategy =& new StrategyWidthAbsolutePositioned();
138
    $width_strategy->apply($this, $context);
139
 
140
    $position_strategy =& new StrategyPositionAbsolute();
141
    $position_strategy->apply($this);
142
 
143
    $this->reflow_content($context);
144
 
145
    /**
146
     * As absolute-positioned box generated new flow context, extend the height to fit all floats
147
     */
148
    $this->fitFloats($context);
149
  }
150
 
151
  /**
152
   * Reflow fixed-positioned block box. Note that according to CSS 2.1
153
   * the only types of boxes which could be absolutely positioned are
154
   * 'block' and 'table'
155
   *
156
   * @param FlowContext $context A flow context object containing the additional layout data.
157
   *
158
   * @link http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo CSS 2.1: Relationships between 'display', 'position', and 'float'
159
   *
160
   * @todo it seems that percentage-constrained fixed block width will be calculated incorrectly; we need
161
   * to use containing block width instead of $this->get_width() when applying the width constraint
162
   */
163
  function reflow_fixed(&$context) {
164
    GenericFormattedBox::reflow($this->parent, $context);
165
 
166
    /**
167
     * As fixed-positioned elements are placed relatively to page (so that one element may be shown
168
     * several times on different pages), we cannot calculate its position at the moment.
169
     * The real position of the element is calculated when it is to be shown - once for each page.
170
     *
171
     * @see BlockBox::show_fixed()
172
     */
173
    $this->put_left(0);
174
    $this->put_top(0);
175
 
176
    /**
177
     * As sometimes left/right values may not be set, we need to use the "fit" width here.
178
     * If box have a width constraint, 'get_max_width' will return constrained value;
179
     * othersise, an intrictic width will be returned.
180
     *
181
     * @see GenericContainerBox::get_max_width()
182
     */
183
    $this->put_full_width($this->get_max_width($context));
184
 
185
    /**
186
     * Update the width, as it should be calculated based upon containing block width, not real parent.
187
     * After this we should remove width constraints or we may encounter problem
188
     * in future when we'll try to call get_..._width functions for this box
189
     *
190
     * @todo Update the family of get_..._width function so that they would apply constraint
191
     * using the containing block width, not "real" parent width
192
     */
193
    $containing_block =& $this->_get_containing_block();
194
    $wc = $this->get_css_property(CSS_WIDTH);
195
    $this->put_full_width($wc->apply($this->get_width(),
196
                                     $containing_block['right'] - $containing_block['left']));
197
    $this->setCSSProperty(CSS_WIDTH, new WCNone());
198
 
199
    /**
200
     * Layout element's children
201
     */
202
    $this->reflow_content($context);
203
 
204
    /**
205
     * As fixed-positioned box generated new flow context, extend the height to fit all floats
206
     */
207
    $this->fitFloats($context);
208
  }
209
 
210
  /**
211
   * Layout static-positioned block box.
212
   *
213
   * Note that static-positioned boxes may be floating boxes
214
   *
215
   * @param GenericContainerBox $parent The document element which should be treated as the parent of current element
216
   * @param FlowContext $context The flow context containing the additional layout data
217
   *
218
   * @see FlowContext
219
   * @see GenericContainerBox
220
   */
221
  function reflow_static(&$parent, &$context) {
222
    if ($this->get_css_property(CSS_FLOAT) === FLOAT_NONE) {
223
      $this->reflow_static_normal($parent, $context);
224
    } else {
225
      $this->reflow_static_float($parent, $context);
226
    }
227
  }
228
 
229
  /**
230
   * Layout normal (non-floating) static-positioned block box.
231
   *
232
   * @param GenericContainerBox $parent The document element which should be treated as the parent of current element
233
   * @param FlowContext $context The flow context containing the additional layout data
234
   *
235
   * @see FlowContext
236
   * @see GenericContainerBox
237
   */
238
  function reflow_static_normal(&$parent, &$context) {
239
    GenericFormattedBox::reflow($parent, $context);
240
 
241
    if ($parent) {
242
      /**
243
       * Child block will fill the whole content width of the parent block.
244
       *
245
       * 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' +
246
       * 'border-right-width' + 'margin-right' = width of containing block
247
       *
248
       * See CSS 2.1 for more detailed explanation
249
       *
250
       * @link http://www.w3.org/TR/CSS21/visudet.html#blockwidth CSS 2.1. 10.3.3 Block-level, non-replaced elements in normal flow
251
       */
252
 
253
      /**
254
       * Calculate margin values if they have been set as a percentage; replace percentage-based values
255
       * with fixed lengths.
256
       */
257
      $this->_calc_percentage_margins($parent);
258
      $this->_calc_percentage_padding($parent);
259
 
260
      /**
261
       * Calculate width value if it had been set as a percentage; replace percentage-based value
262
       * with fixed value
263
       */
264
      $this->put_full_width($parent->get_width());
265
      $this->_calc_percentage_width($parent, $context);
266
 
267
      /**
268
       * Calculate 'auto' values of width and margins. Unlike tables, DIV width is either constrained
269
       * by some CSS rules or expanded to the parent width; thus, we can calculate 'auto' margin
270
       * values immediately.
271
       *
272
       * @link http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins CSS 2.1 Calculating widths and margins
273
       */
274
      $this->_calc_auto_width_margins($parent);
275
 
276
      /**
277
       * Collapse top margin
278
       *
279
       * @see GenericFormattedBox::collapse_margin()
280
       *
281
       * @link http://www.w3.org/TR/CSS21/box.html#collapsing-margins CSS 2.1 Collapsing margins
282
       */
283
      $y = $this->collapse_margin($parent, $context);
284
 
285
      /**
286
       * At this moment we have top parent/child collapsed margin at the top of context object
287
       * margin stack
288
       */
289
 
290
      /**
291
       * Apply 'clear' property; the current Y coordinate can be modified as a result of 'clear'.
292
       */
293
      $y = $this->apply_clear($y, $context);
294
 
295
      /**
296
       * Store calculated Y coordinate as current Y coordinate in the parent box
297
       * No more content will be drawn abowe this mark; current box padding area will
298
       * start below.
299
       */
300
      $parent->_current_y = $y;
301
 
302
      /**
303
       * Terminate current parent line-box (as current box is not inline)
304
       */
305
      $parent->close_line($context);
306
 
307
      /**
308
       * Add current box to the parent's line-box; we will close the line box below
309
       * after content will be reflown, so the line box will contain only current box.
310
       */
311
      $parent->append_line($this);
312
 
313
      /**
314
       * Now, place the current box upper left content corner. Note that we should not
315
       * use get_extra_top here, as _current_y value already culculated using the top margin value
316
       * of the current box! The top content edge should be offset from that level only of padding and
317
       * border width.
318
       */
319
      $border  = $this->get_css_property(CSS_BORDER);
320
      $padding = $this->get_css_property(CSS_PADDING);
321
 
322
      $this->moveto( $parent->get_left() + $this->get_extra_left(),
323
                     $parent->_current_y - $border->top->get_width()  - $padding->top->value );
324
    }
325
 
326
    /**
327
     * Reflow element's children
328
     */
329
    $this->reflow_content($context);
330
 
331
    if ($this->get_css_property(CSS_OVERFLOW) != OVERFLOW_VISIBLE) {
332
      $this->fitFloats($context);
333
    }
334
 
335
    /**
336
     * After child elements have been reflown, we should the top collapsed margin stack value
337
     * replaced by the value of last child bottom collapsed margin;
338
     * if no children contained, then this value should be reset to 0.
339
     *
340
     * Note that invisible and
341
     * whitespaces boxes would not affect the collapsed margin value, so we need to
342
     * use 'get_first' function instead of just accessing the $content array.
343
     *
344
     * @see GenericContainerBox::get_first
345
     */
346
    if (!is_null($this->get_first())) {
347
      $cm = 0;
348
    } else {
349
      $cm = $context->get_collapsed_margin();
350
    };
351
 
352
    /**
353
     * Update the bottom  value, collapsing the latter value with
354
     * current box bottom margin.
355
     *
356
     * Note that we need to remove TWO values from the margin stack:
357
     * first - the value of collapsed bottom margin of the last child AND
358
     * second - the value of collapsed top margin of current element.
359
     */
360
    $margin = $this->get_css_property(CSS_MARGIN);
361
 
362
    if ($parent) {
363
      /**
364
       * Terminate parent's line box (it contains the current box only)
365
       */
366
      $parent->close_line($context);
367
 
368
      $parent->_current_y = $this->collapse_margin_bottom($parent, $context);
369
    };
370
  }
371
 
372
  function show(&$driver) {
373
    if ($this->get_css_property(CSS_FLOAT)    != FLOAT_NONE ||
374
        $this->get_css_property(CSS_POSITION) == POSITION_RELATIVE) {
375
      // These boxes will be rendered separately
376
      return true;
377
    };
378
 
379
    return parent::show($driver);
380
  }
381
 
382
  function show_postponed(&$driver) {
383
    return parent::show($driver);
384
  }
385
 
386
  /**
387
   * Show fixed positioned block box using the specified output driver
388
   *
389
   * Note that 'show_fixed' is called to box _nested_ to the fixed-positioned boxes too!
390
   * Thus, we need to check whether actual 'position' values is 'fixed' for this box
391
   * and only in that case attempt to move box
392
   *
393
   * @param OutputDriver $driver The output device driver object
394
   */
395
  function show_fixed(&$driver) {
396
    $position = $this->get_css_property(CSS_POSITION);
397
 
398
    if ($position == POSITION_FIXED) {
399
      /**
400
       * Calculate the distance between the top page edge and top box content edge
401
       */
402
      $bottom = $this->get_css_property(CSS_BOTTOM);
403
      $top    = $this->get_css_property(CSS_TOP);
404
 
405
      if (!$top->isAuto()) {
406
        if ($top->isPercentage()) {
407
          $vertical_offset = $driver->getPageMaxHeight() / 100 * $top->getPercentage();
408
        } else {
409
          $vertical_offset = $top->getPoints();
410
        };
411
 
412
      } elseif (!$bottom->isAuto()) {
413
        if ($bottom->isPercentage()) {
414
          $vertical_offset = $driver->getPageMaxHeight() * (100 - $bottom->getPercentage())/100 - $this->get_height();
415
        } else {
416
          $vertical_offset = $driver->getPageMaxHeight() - $bottom->getPoints() - $this->get_height();
417
        };
418
 
419
      } else {
420
        $vertical_offset = 0;
421
      };
422
 
423
      /**
424
       * Calculate the distance between the right page edge and right box content edge
425
       */
426
      $left  = $this->get_css_property(CSS_LEFT);
427
      $right = $this->get_css_property(CSS_RIGHT);
428
 
429
      if (!$left->isAuto()) {
430
        if ($left->isPercentage()) {
431
          $horizontal_offset = $driver->getPageWidth() / 100 * $left->getPercentage();
432
        } else {
433
          $horizontal_offset = $left->getPoints();
434
        };
435
 
436
      } elseif (!$right->isAuto()) {
437
        if ($right->isPercentage()) {
438
          $horizontal_offset = $driver->getPageWidth() * (100 - $right->getPercentage())/100 - $this->get_width();
439
        } else {
440
          $horizontal_offset = $driver->getPageWidth() - $right->getPoints() - $this->get_width();
441
        };
442
 
443
      } else {
444
        $horizontal_offset = 0;
445
      };
446
 
447
      /**
448
       * Offset current box to the required position on the current page (note that
449
       * fixed-positioned element are placed relatively to the viewport - page in our case)
450
       */
451
      $this->moveto($driver->getPageLeft() + $horizontal_offset,
452
                    $driver->getPageTop()  - $vertical_offset);
453
    };
454
 
455
    /**
456
     * After box have benn properly positioned, render it as usual.
457
     */
458
    return GenericContainerBox::show_fixed($driver);
459
  }
460
 
461
  function isBlockLevel() {
462
    return true;
463
  }
464
}
465
?>