File size: 10,980 Bytes
07c3cdd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
<?php
// $Header: /cvsroot/html2ps/box.frame.php,v 1.24 2007/02/18 09:55:10 Konstantin Exp $

class FrameBox extends GenericContainerBox {
  function &create(&$root, &$pipeline) {
    $box =& new FrameBox($root, $pipeline);
    $box->readCSS($pipeline->getCurrentCSSState());
    return $box;
  }

  function reflow(&$parent, &$context) {
    // If frame contains no boxes (for example, the src link is broken)
    // we just return - no further processing will be done
    if (count($this->content) == 0) { return; };

    // First box contained in a frame should always fill all its height
    $this->content[0]->put_full_height($this->get_height());

    $hc = new HCConstraint(array($this->get_height(), false),
                           array($this->get_height(), false), 
                           array($this->get_height(), false));
    $this->content[0]->put_height_constraint($hc);

    $context->push_collapsed_margin(0);
    $context->push_container_uid($this->uid);

    $this->reflow_content($context);

    $context->pop_collapsed_margin();
    $context->pop_container_uid();
  }

  /**
   * Reflow absolutely positioned block box. Note that according to CSS 2.1 
   * the only types of boxes which could be absolutely positioned are 
   * 'block' and 'table'
   * 
   * @param FlowContext $context A flow context object containing the additional layout data.
   *
   * @link http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo CSS 2.1: Relationships between 'display', 'position', and 'float'
   */
  function reflow_absolute(&$context) {
    GenericFormattedBox::reflow($this->parent, $context);

    $position_strategy =& new StrategyPositionAbsolute();
    $position_strategy->apply($this);
    
    /**
     * As sometimes left/right values may not be set, we need to use the "fit" width here.
     * If box have a width constraint, 'get_max_width' will return constrained value; 
     * othersise, an intrictic width will be returned. 
     * 
     * Note that get_max_width returns width _including_ external space line margins, borders and padding;
     * as we're setting the "internal" - content width, we must subtract "extra" space width from the 
     * value received
     *
     * @see GenericContainerBox::get_max_width()
     */

    $this->put_width($this->get_max_width($context) - $this->_get_hor_extra());
    
    /**
     * Update the width, as it should be calculated based upon containing block width, not real parent.
     * 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
     */
    $wc = $this->getCSSProperty(CSS_WIDTH);

    $containing_block =& $this->_get_containing_block();
    $this->put_width($wc->apply($this->get_width(), 
                                $containing_block['right'] - $containing_block['left']));
    $this->setCSSProperty(CSS_WIDTH, new WCNone());

    /**
     * Layout element's children 
     */
    $this->reflow_content($context);

    /**
     * As absolute-positioned box generated new flow contexy, extend the height to fit all floats
     */
    $this->fitFloats($context);

    /** 
     * If element have been positioned using 'right' or 'bottom' property,
     * we need to offset it, as we assumed it had zero width and height at
     * the moment we placed it
     */
    $right = $this->getCSSProperty(CSS_RIGHT);
    $left  = $this->getCSSProperty(CSS_LEFT);
    if ($left->isAuto() && !$right->isAuto()) {
      $this->offset(-$this->get_width(), 0);
    };

    $bottom = $this->getCSSProperty(CSS_BOTTOM);
    $top    = $this->getCSSProperty(CSS_TOP);
    if ($top->isAuto() && !$bottom->isAuto()) {
      $this->offset(0, $this->get_height());
    };
  }

  function FrameBox(&$root, &$pipeline) {
    $css_state =& $pipeline->getCurrentCSSState();

    // Inherit 'border' CSS value from parent (FRAMESET tag), if current FRAME 
    // has no FRAMEBORDER attribute, and FRAMESET has one
    $parent = $root->parent();
    if (!$root->has_attribute('frameborder') &&
        $parent->has_attribute('frameborder')) {
      $parent_border = $css_state->getPropertyOnLevel(CSS_BORDER, CSS_PROPERTY_LEVEL_PARENT);
      $css_state->setProperty(CSS_BORDER, $parent_border->copy());
    }

    $this->GenericContainerBox($root);

    // If NO src attribute specified, just return.
    if (!$root->has_attribute('src')) { return; };

    // Determine the fullly qualified URL of the frame content
    $src  = $root->get_attribute('src');
    $url  = $pipeline->guess_url($src);
    $data = $pipeline->fetch($url);

    /**
     * If framed page could not be fetched return immediately
     */
    if (is_null($data)) { return; };

    /**
     * Render only iframes containing HTML only
     *
     * Note that content-type header may contain additional information after the ';' sign
     */
    $content_type = $data->get_additional_data('Content-Type');
    $content_type_array = explode(';', $content_type);
    if ($content_type_array[0] != "text/html") { return; };

    $html = $data->get_content();
      
    // Remove control symbols if any
    $html = preg_replace('/[\x00-\x07]/', "", $html);
    $converter = Converter::create();
    $html = $converter->to_utf8($html, $data->detect_encoding());
    $html = html2xhtml($html);
    $tree = TreeBuilder::build($html);
      
    // Save current stylesheet, as each frame may load its own stylesheets
    //
    $pipeline->pushCSS();
    $css =& $pipeline->getCurrentCSS();
    $css->scan_styles($tree, $pipeline);
    
    $frame_root = traverse_dom_tree_pdf($tree);   
    $box_child  =& create_pdf_box($frame_root, $pipeline);
    $this->add_child($box_child);
    
    // Restore old stylesheet
    //
    $pipeline->popCSS();

    $pipeline->pop_base_url();
  }

  /**
   * Note that if both top and bottom are 'auto', box will use vertical coordinate 
   * calculated using guess_corder in 'reflow' method which could be used if this
   * box had 'position: static'
   */
  function _positionAbsoluteVertically($containing_block) {
    $bottom = $this->getCSSProperty(CSS_BOTTOM);
    $top    = $this->getCSSProperty(CSS_TOP);

    if (!$top->isAuto()) {
      if ($top->isPercentage()) {
        $top_value = ($containing_block['top'] - $containing_block['bottom']) / 100 * $top->getPercentage();
      } else {
        $top_value = $top->getPoints();
      };
      $this->put_top($containing_block['top'] - $top_value - $this->get_extra_top());
    } elseif (!$bottom->isAuto()) { 
      if ($bottom->isPercentage()) {
        $bottom_value = ($containing_block['top'] - $containing_block['bottom']) / 100 * $bottom->getPercentage();
      } else {
        $bottom_value = $bottom->getPoints();
      };
      $this->put_top($containing_block['bottom'] + $bottom_value + $this->get_extra_bottom());
    };
  }

  /**
   * Note that  if both  'left' and 'right'  are 'auto', box  will use
   * horizontal coordinate  calculated using guess_corder  in 'reflow'
   * method which could be used if this box had 'position: static'
   */
  function _positionAbsoluteHorizontally($containing_block) {
    $left  = $this->getCSSProperty(CSS_LEFT);
    $right = $this->getCSSProperty(CSS_RIGHT);

    if (!$left->isAuto()) { 
      if ($left->isPercentage()) {
        $left_value = ($containing_block['right'] - $containing_block['left']) / 100 * $left->getPercentage();
      } else {
        $left_value = $left->getPoints();
      };
      $this->put_left($containing_block['left'] + $left_value + $this->get_extra_left());
    } elseif (!$right->isAuto()) {
      if ($right->isPercentage()) {
        $right_value = ($containing_block['right'] - $containing_block['left']) / 100 * $right->getPercentage();
      } else {
        $right_value = $right->getPoints();
      };
      $this->put_left($containing_block['right'] - $right_value - $this->get_extra_right());
    };
  }
}

class FramesetBox extends GenericContainerBox {
  var $rows;
  var $cols;

  function &create(&$root, &$pipeline) {
    $box =& new FramesetBox($root, $pipeline);
    $box->readCSS($pipeline->getCurrentCSSState());
    return $box;
  }

  function FramesetBox(&$root, $pipeline) {
    $this->GenericContainerBox($root);
    $this->create_content($root, $pipeline);
    
    // Now determine the frame layout inside the frameset
    $this->rows = $root->has_attribute('rows') ? $root->get_attribute('rows') : "100%";
    $this->cols = $root->has_attribute('cols') ? $root->get_attribute('cols') : "100%";
  }

  function reflow(&$parent, &$context) {
    $viewport =& $context->get_viewport();

    // Frameset always fill all available space in viewport
    $this->put_left($viewport->get_left() + $this->get_extra_left());
    $this->put_top($viewport->get_top() - $this->get_extra_top());

    $this->put_full_width($viewport->get_width());
    $this->setCSSProperty(CSS_WIDTH, new WCConstant($viewport->get_width()));

    $this->put_full_height($viewport->get_height());
    $this->put_height_constraint(new WCConstant($viewport->get_height()));    
    
    // Parse layout-control values
    $rows = guess_lengths($this->rows, $this->get_height());
    $cols = guess_lengths($this->cols, $this->get_width());
    
    // Now reflow all frames in frameset
    $cur_col = 0;
    $cur_row = 0;
    for ($i=0; $i < count($this->content); $i++) {
      // Had we run out of cols/rows?
      if ($cur_row >= count($rows)) {
        // In valid HTML we never should get here, but someone can provide less frame cells 
        // than frames. Extra frames will not be rendered at all
        return;
      }

      $frame =& $this->content[$i];

      /**
       * Depending on the source HTML, FramesetBox may contain some non-frame boxes; 
       * we'll just ignore them
       */
      if (!is_a($frame, "FramesetBox") &&
          !is_a($frame, "FrameBox")) {
        continue;
      };

      // Guess frame size and position
      $frame->put_left($this->get_left() + array_sum(array_slice($cols, 0, $cur_col)) + $frame->get_extra_left());
      $frame->put_top($this->get_top()   - array_sum(array_slice($rows, 0, $cur_row)) - $frame->get_extra_top());

      $frame->put_full_width($cols[$cur_col]);
      $frame->setCSSProperty(CSS_WIDTH, new WCConstant($frame->get_width()));

      $frame->put_full_height($rows[$cur_row]);
      $frame->put_height_constraint(new WCConstant($frame->get_height()));

      // Reflow frame contents
      $context->push_viewport(FlowViewport::create($frame));
      $frame->reflow($this, $context);
      $context->pop_viewport();

      // Move to the next frame position
      // Next columns
      $cur_col ++;
      if ($cur_col >= count($cols)) {
        // Next row
        $cur_col = 0;
        $cur_row ++;
      }
    }
  }
}
?>