1<?php 2// $Header: /cvsroot/html2ps/box.frame.php,v 1.24 2007/02/18 09:55:10 Konstantin Exp $ 3 4class FrameBox extends GenericContainerBox { 5 function &create(&$root, &$pipeline) { 6 $box =& new FrameBox($root, $pipeline); 7 $box->readCSS($pipeline->get_current_css_state()); 8 return $box; 9 } 10 11 function reflow(&$parent, &$context) { 12 // If frame contains no boxes (for example, the src link is broken) 13 // we just return - no further processing will be done 14 if (count($this->content) == 0) { return; }; 15 16 // First box contained in a frame should always fill all its height 17 $this->content[0]->put_full_height($this->get_height()); 18 19 $hc = new HCConstraint(array($this->get_height(), false), 20 array($this->get_height(), false), 21 array($this->get_height(), false)); 22 $this->content[0]->put_height_constraint($hc); 23 24 $context->push_collapsed_margin(0); 25 $context->push_container_uid($this->uid); 26 27 $this->reflow_content($context); 28 29 $context->pop_collapsed_margin(); 30 $context->pop_container_uid(); 31 } 32 33 /** 34 * Reflow absolutely positioned block box. Note that according to CSS 2.1 35 * the only types of boxes which could be absolutely positioned are 36 * 'block' and 'table' 37 * 38 * @param FlowContext $context A flow context object containing the additional layout data. 39 * 40 * @link http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo CSS 2.1: Relationships between 'display', 'position', and 'float' 41 */ 42 function reflow_absolute(&$context) { 43 GenericFormattedBox::reflow($this->parent, $context); 44 45 $position_strategy =& new StrategyPositionAbsolute(); 46 $position_strategy->apply($this); 47 48 /** 49 * As sometimes left/right values may not be set, we need to use the "fit" width here. 50 * If box have a width constraint, 'get_max_width' will return constrained value; 51 * othersise, an intrictic width will be returned. 52 * 53 * Note that get_max_width returns width _including_ external space line margins, borders and padding; 54 * as we're setting the "internal" - content width, we must subtract "extra" space width from the 55 * value received 56 * 57 * @see GenericContainerBox::get_max_width() 58 */ 59 60 $this->put_width($this->get_max_width($context) - $this->_get_hor_extra()); 61 62 /** 63 * Update the width, as it should be calculated based upon containing block width, not real parent. 64 * After this we should remove width constraints or we may encounter problem 65 * in future when we'll try to call get_..._width functions for this box 66 * 67 * @todo Update the family of get_..._width function so that they would apply constraint 68 * using the containing block width, not "real" parent width 69 */ 70 $wc = $this->get_css_property(CSS_WIDTH); 71 72 $containing_block =& $this->_get_containing_block(); 73 $this->put_width($wc->apply($this->get_width(), 74 $containing_block['right'] - $containing_block['left'])); 75 $this->setCSSProperty(CSS_WIDTH, new WCNone()); 76 77 /** 78 * Layout element's children 79 */ 80 $this->reflow_content($context); 81 82 /** 83 * As absolute-positioned box generated new flow contexy, extend the height to fit all floats 84 */ 85 $this->fitFloats($context); 86 87 /** 88 * If element have been positioned using 'right' or 'bottom' property, 89 * we need to offset it, as we assumed it had zero width and height at 90 * the moment we placed it 91 */ 92 $right = $this->get_css_property(CSS_RIGHT); 93 $left = $this->get_css_property(CSS_LEFT); 94 if ($left->isAuto() && !$right->isAuto()) { 95 $this->offset(-$this->get_width(), 0); 96 }; 97 98 $bottom = $this->get_css_property(CSS_BOTTOM); 99 $top = $this->get_css_property(CSS_TOP); 100 if ($top->isAuto() && !$bottom->isAuto()) { 101 $this->offset(0, $this->get_height()); 102 }; 103 } 104 105 function FrameBox(&$root, &$pipeline) { 106 $css_state =& $pipeline->get_current_css_state(); 107 108 // Inherit 'border' CSS value from parent (FRAMESET tag), if current FRAME 109 // has no FRAMEBORDER attribute, and FRAMESET has one 110 $parent = $root->parent(); 111 if (!$root->has_attribute('frameborder') && 112 $parent->has_attribute('frameborder')) { 113 $parent_border = $css_state->get_propertyOnLevel(CSS_BORDER, CSS_PROPERTY_LEVEL_PARENT); 114 $css_state->set_property(CSS_BORDER, $parent_border->copy()); 115 } 116 117 $this->GenericContainerBox($root); 118 119 // If NO src attribute specified, just return. 120 if (!$root->has_attribute('src')) { return; }; 121 122 // Determine the fullly qualified URL of the frame content 123 $src = $root->get_attribute('src'); 124 $url = $pipeline->guess_url($src); 125 $data = $pipeline->fetch($url); 126 127 /** 128 * If framed page could not be fetched return immediately 129 */ 130 if (is_null($data)) { return; }; 131 132 /** 133 * Render only iframes containing HTML only 134 * 135 * Note that content-type header may contain additional information after the ';' sign 136 */ 137 $content_type = $data->get_additional_data('Content-Type'); 138 $content_type_array = explode(';', $content_type); 139 if ($content_type_array[0] != "text/html") { return; }; 140 141 $html = $data->get_content(); 142 143 // Remove control symbols if any 144 $html = preg_replace('/[\x00-\x07]/', "", $html); 145 $converter = Converter::create(); 146 $html = $converter->to_utf8($html, $data->detect_encoding()); 147 $html = html2xhtml($html); 148 $tree = TreeBuilder::build($html); 149 150 // Save current stylesheet, as each frame may load its own stylesheets 151 // 152 $pipeline->pushCSS(); 153 $css =& $pipeline->get_current_css(); 154 $css->scan_styles($tree, $pipeline); 155 156 $frame_root = traverse_dom_tree_pdf($tree); 157 $box_child =& create_pdf_box($frame_root, $pipeline); 158 $this->add_child($box_child); 159 160 // Restore old stylesheet 161 // 162 $pipeline->pop_css(); 163 164 $pipeline->pop_base_url(); 165 } 166 167 /** 168 * Note that if both top and bottom are 'auto', box will use vertical coordinate 169 * calculated using guess_corder in 'reflow' method which could be used if this 170 * box had 'position: static' 171 */ 172 function _positionAbsoluteVertically($containing_block) { 173 $bottom = $this->get_css_property(CSS_BOTTOM); 174 $top = $this->get_css_property(CSS_TOP); 175 176 if (!$top->isAuto()) { 177 if ($top->isPercentage()) { 178 $top_value = ($containing_block['top'] - $containing_block['bottom']) / 100 * $top->getPercentage(); 179 } else { 180 $top_value = $top->getPoints(); 181 }; 182 $this->put_top($containing_block['top'] - $top_value - $this->get_extra_top()); 183 } elseif (!$bottom->isAuto()) { 184 if ($bottom->isPercentage()) { 185 $bottom_value = ($containing_block['top'] - $containing_block['bottom']) / 100 * $bottom->getPercentage(); 186 } else { 187 $bottom_value = $bottom->getPoints(); 188 }; 189 $this->put_top($containing_block['bottom'] + $bottom_value + $this->get_extra_bottom()); 190 }; 191 } 192 193 /** 194 * Note that if both 'left' and 'right' are 'auto', box will use 195 * horizontal coordinate calculated using guess_corder in 'reflow' 196 * method which could be used if this box had 'position: static' 197 */ 198 function _positionAbsoluteHorizontally($containing_block) { 199 $left = $this->get_css_property(CSS_LEFT); 200 $right = $this->get_css_property(CSS_RIGHT); 201 202 if (!$left->isAuto()) { 203 if ($left->isPercentage()) { 204 $left_value = ($containing_block['right'] - $containing_block['left']) / 100 * $left->getPercentage(); 205 } else { 206 $left_value = $left->getPoints(); 207 }; 208 $this->put_left($containing_block['left'] + $left_value + $this->get_extra_left()); 209 } elseif (!$right->isAuto()) { 210 if ($right->isPercentage()) { 211 $right_value = ($containing_block['right'] - $containing_block['left']) / 100 * $right->getPercentage(); 212 } else { 213 $right_value = $right->getPoints(); 214 }; 215 $this->put_left($containing_block['right'] - $right_value - $this->get_extra_right()); 216 }; 217 } 218} 219 220class FramesetBox extends GenericContainerBox { 221 var $rows; 222 var $cols; 223 224 function &create(&$root, &$pipeline) { 225 $box =& new FramesetBox($root, $pipeline); 226 $box->readCSS($pipeline->get_current_css_state()); 227 return $box; 228 } 229 230 function FramesetBox(&$root, $pipeline) { 231 $this->GenericContainerBox($root); 232 $this->create_content($root, $pipeline); 233 234 // Now determine the frame layout inside the frameset 235 $this->rows = $root->has_attribute('rows') ? $root->get_attribute('rows') : "100%"; 236 $this->cols = $root->has_attribute('cols') ? $root->get_attribute('cols') : "100%"; 237 } 238 239 function reflow(&$parent, &$context) { 240 $viewport =& $context->get_viewport(); 241 242 // Frameset always fill all available space in viewport 243 $this->put_left($viewport->get_left() + $this->get_extra_left()); 244 $this->put_top($viewport->get_top() - $this->get_extra_top()); 245 246 $this->put_full_width($viewport->get_width()); 247 $this->setCSSProperty(CSS_WIDTH, new WCConstant($viewport->get_width())); 248 249 $this->put_full_height($viewport->get_height()); 250 $this->put_height_constraint(new WCConstant($viewport->get_height())); 251 252 // Parse layout-control values 253 $rows = guess_lengths($this->rows, $this->get_height()); 254 $cols = guess_lengths($this->cols, $this->get_width()); 255 256 // Now reflow all frames in frameset 257 $cur_col = 0; 258 $cur_row = 0; 259 for ($i=0; $i < count($this->content); $i++) { 260 // Had we run out of cols/rows? 261 if ($cur_row >= count($rows)) { 262 // In valid HTML we never should get here, but someone can provide less frame cells 263 // than frames. Extra frames will not be rendered at all 264 return; 265 } 266 267 $frame =& $this->content[$i]; 268 269 /** 270 * Depending on the source HTML, FramesetBox may contain some non-frame boxes; 271 * we'll just ignore them 272 */ 273 if (!is_a($frame, "FramesetBox") && 274 !is_a($frame, "FrameBox")) { 275 continue; 276 }; 277 278 // Guess frame size and position 279 $frame->put_left($this->get_left() + array_sum(array_slice($cols, 0, $cur_col)) + $frame->get_extra_left()); 280 $frame->put_top($this->get_top() - array_sum(array_slice($rows, 0, $cur_row)) - $frame->get_extra_top()); 281 282 $frame->put_full_width($cols[$cur_col]); 283 $frame->setCSSProperty(CSS_WIDTH, new WCConstant($frame->get_width())); 284 285 $frame->put_full_height($rows[$cur_row]); 286 $frame->put_height_constraint(new WCConstant($frame->get_height())); 287 288 // Reflow frame contents 289 $context->push_viewport(FlowViewport::create($frame)); 290 $frame->reflow($this, $context); 291 $context->pop_viewport(); 292 293 // Move to the next frame position 294 // Next columns 295 $cur_col ++; 296 if ($cur_col >= count($cols)) { 297 // Next row 298 $cur_col = 0; 299 $cur_row ++; 300 } 301 } 302 } 303} 304?>