1<?php 2// $Header: /cvsroot/html2ps/box.generic.php,v 1.73 2007/05/06 18:49:29 Konstantin Exp $ 3 4require_once(HTML2PS_DIR.'globals.php'); 5 6class GenericBox { 7 var $_cache; 8 var $_css; 9 var $_left; 10 var $_top; 11 var $_parent; 12 var $baseline; 13 var $default_baseline; 14 var $_tagname; 15 var $_id; 16 17 var $_cached_base_font_size; 18 19 function GenericBox() { 20 $this->_cache = array(); 21 $this->_css = array(); 22 $this->_cached_base_font_size = null; 23 24 $this->_left = 0; 25 $this->_top = 0; 26 27 $this->_parent = null; 28 29 $this->baseline = 0; 30 $this->default_baseline = 0; 31 32 $this->set_tagname(null); 33 34 /** 35 * Assign an unique box identifier 36 */ 37 $GLOBALS['g_box_uid']++; 38 $this->uid = $GLOBALS['g_box_uid']; 39 40 $this->_id = null; 41 } 42 43 function destroy() { 44 unset($this->_cache); 45 unset($this->_css); 46 unset($this->_left); 47 unset($this->_top); 48 unset($this->_parent); 49 unset($this->baseline); 50 unset($this->default_baseline); 51 } 52 53 /** 54 * see get_property for optimization description 55 */ 56 function setCSSProperty($code, $value) { 57 static $cache = array(); 58 if (!isset($cache[$code])) { 59 $cache[$code] =& CSS::get_handler($code); 60 }; 61 62 $cache[$code]->replace_array($value, $this->_css); 63 } 64 65 /** 66 * Optimization: this function is called very often, 67 * so even a slight overhead for CSS::get_handler call 68 * accumulates in a significiant processing delay. 69 */ 70 function &get_css_property($code) { 71 static $cache = array(); 72 if (!isset($cache[$code])) { 73 $cache[$code] =& CSS::get_handler($code); 74 }; 75 76 $value =& $cache[$code]->get($this->_css); 77 return $value; 78 } 79 80 function get_tagname() { 81 return $this->_tagname; 82 } 83 84 function set_tagname($tagname) { 85 $this->_tagname = $tagname; 86 } 87 88 function get_content() { 89 return ''; 90 } 91 92 function show_postponed(&$driver) { 93 $this->show($driver); 94 } 95 96 function copy_style(&$box) { 97 // TODO: object references 98 $this->_css = $box->_css; 99 } 100 101 /** 102 * Optimization: _readCSSLength is usually called several times 103 * while initializing box object. $base_font_size cound be calculated 104 * only once and stored in a static variable. 105 */ 106 function _readCSSLengths($state, $property_list) { 107 if (is_null($this->_cached_base_font_size)) { 108 $font =& $this->get_css_property(CSS_FONT); 109 $this->_cached_base_font_size = $font->size->getPoints(); 110 }; 111 112 foreach ($property_list as $property) { 113 $value =& $state->get_property($property); 114 115 if ($value === CSS_PROPERTY_INHERIT) { 116 $value =& $state->getInheritedProperty($property); 117 }; 118 119 if (is_object($value)) { 120 $value =& $value->copy(); 121 $value->doInherit($state); 122 $value->units2pt($this->_cached_base_font_size); 123 }; 124 125 $this->setCSSProperty($property, $value); 126 } 127 } 128 129 function _readCSS($state, $property_list) { 130 foreach ($property_list as $property) { 131 $value = $state->get_property($property); 132 133 // Note that order is important; composite object-value could be inherited and 134 // object itself could contain subvalues with 'inherit' value 135 136 if ($value === CSS_PROPERTY_INHERIT) { 137 $value = $state->getInheritedProperty($property); 138 }; 139 140 if (is_object($value)) { 141 $value = $value->copy(); 142 $value->doInherit($state); 143 }; 144 145 $this->setCSSProperty($property, $value); 146 } 147 } 148 149 function readCSS(&$state) { 150 /** 151 * Determine font size to be used in this box (required for em/ex units) 152 */ 153 $value = $state->get_property(CSS_FONT); 154 if ($value === CSS_PROPERTY_INHERIT) { 155 $value = $state->getInheritedProperty(CSS_FONT); 156 }; 157 $base_font_size = $state->getBaseFontSize(); 158 159 if (is_object($value)) { 160 $value = $value->copy(); 161 $value->doInherit($state); 162 $value->units2pt($base_font_size); 163 }; 164 165 $this->setCSSProperty(CSS_FONT, $value); 166 167 /** 168 * Continue working with other properties 169 */ 170 171 $this->_readCSS($state, 172 array(CSS_COLOR, 173 CSS_DISPLAY, 174 CSS_VISIBILITY)); 175 176 $this->_readCSSLengths($state, 177 array(CSS_VERTICAL_ALIGN)); 178 179 // '-html2ps-link-destination' 180 global $g_config; 181 if ($g_config["renderlinks"]) { 182 $this->_readCSS($state, 183 array(CSS_HTML2PS_LINK_DESTINATION)); 184 }; 185 186 // Save ID attribute value 187 $id = $state->get_property(CSS_HTML2PS_LINK_DESTINATION); 188 if (!is_null($id)) { 189 $this->set_id($id); 190 }; 191 } 192 193 function set_id($id) { 194 $this->_id = $id; 195 196 if (!isset($GLOBALS['__html_box_id_map'][$id])) { 197 $GLOBALS['__html_box_id_map'][$id] =& $this; 198 }; 199 } 200 201 function get_id() { 202 return $this->_id; 203 } 204 205 function show(&$driver) { 206 // If debugging mode is on, draw the box outline 207 global $g_config; 208 if ($g_config['debugbox']) { 209 // Copy the border object of current box 210 $driver->setlinewidth(0.1); 211 $driver->setrgbcolor(0,0,0); 212 $driver->rect($this->get_left(), $this->get_top(), $this->get_width(), -$this->get_height()); 213 $driver->stroke(); 214 } 215 216 // Set current text color 217 // Note that text color is used not only for text drawing (for example, list item markers 218 // are drawn with text color) 219 $color = $this->get_css_property(CSS_COLOR); 220 $color->apply($driver); 221 } 222 223 /** 224 * Render box having position: fixed or contained in such box 225 * (Default behaviour) 226 */ 227 function show_fixed(&$driver) { 228 return $this->show($driver); 229 } 230 231 function pre_reflow_images() {} 232 233 function set_top($value) { 234 $this->_top = $value; 235 } 236 237 function set_left($value) { 238 $this->_left = $value; 239 } 240 241 function offset($dx, $dy) { 242 $this->_left += $dx; 243 $this->_top += $dy; 244 } 245 246 // Calculate the content upper-left corner position in curent flow 247 function guess_corner(&$parent) { 248 $this->put_left($parent->_current_x + $this->get_extra_left()); 249 $this->put_top($parent->_current_y - $this->get_extra_top()); 250 } 251 252 function put_left($value) { 253 $this->_left = $value; 254 } 255 256 function put_top($value) { 257 $this->_top = $value + $this->getBaselineOffset(); 258 } 259 260 /** 261 * Get Y coordinate of the top content area edge 262 */ 263 function get_top() { 264 return 265 $this->_top - 266 $this->getBaselineOffset(); 267 } 268 269 function get_right() { 270 return $this->get_left() + $this->get_width(); 271 } 272 273 function get_left() { 274 return $this->_left; 275 } 276 277 function get_bottom() { 278 return $this->get_top() - $this->get_height(); 279 } 280 281 function getBaselineOffset() { 282 return $this->baseline - $this->default_baseline; 283 } 284 285 function &make_anchor(&$media, $link_destination, $page_heights) { 286 $page_index = 0; 287 $pages_count = count($page_heights); 288 $bottom = mm2pt($media->height() - $media->margins['top']); 289 do { 290 $bottom -= $page_heights[$page_index]; 291 $page_index ++; 292 } while ($this->get_top() < $bottom && $page_index < $pages_count); 293 294 /** 295 * Now let's calculate the coordinates on this particular page 296 * 297 * X coordinate calculation is pretty straightforward (and, actually, unused, as it would be 298 * a bad idea to scroll PDF horiaontally). 299 */ 300 $x = $this->get_left(); 301 302 /** 303 * Y coordinate should be calculated relatively to the bottom page edge 304 */ 305 $y = ($this->get_top() - $bottom) + (mm2pt($media->real_height()) - $page_heights[$page_index-1]) + mm2pt($media->margins['bottom']); 306 307 $anchor =& new Anchor($link_destination, 308 $page_index, 309 $x, 310 $y); 311 return $anchor; 312 } 313 314 function reflow_anchors(&$driver, &$anchors, $page_heights) { 315 if ($this->is_null()) { 316 return; 317 }; 318 319 $link_destination = $this->get_css_property(CSS_HTML2PS_LINK_DESTINATION); 320 if (!is_null($link_destination)) { 321 $anchors[$link_destination] =& $this->make_anchor($driver->media, $link_destination, $page_heights); 322 }; 323 } 324 325 function reflow(&$parent, &$context) {} 326 327 function reflow_inline() { } 328 329 function out_of_flow() { 330 return false; 331 } 332 333 function get_bottom_margin() { return $this->get_bottom(); } 334 335 function get_top_margin() { 336 return $this->get_top(); 337 } 338 339 function get_full_height() { return $this->get_height(); } 340 function get_width() { return $this->width; } 341 342 function get_full_width() { 343 return $this->width; 344 } 345 346 function get_height() { 347 return $this->height; 348 } 349 350 function get_baseline() { 351 return $this->baseline; 352 } 353 354 function is_container() { return false; } 355 356 function isVisibleInFlow() { return true; } 357 358 function reflow_text() { return true; } 359 360 /** 361 * Note that linebox is started by any non-whitespace inline element; all whitespace elements before 362 * that moment should be ignored. 363 * 364 * @param boolean $linebox_started Flag indicating that a new line box have just started and it already contains 365 * some inline elements 366 * @param boolean $previous_whitespace Flag indicating that a previous inline element was an whitespace element. 367 */ 368 function reflow_whitespace(&$linebox_started, &$previous_whitespace) { 369 return; 370 } 371 372 function is_null() { 373 return false; 374 } 375 376 function isCell() { 377 return false; 378 } 379 380 function isTableRow() { 381 return false; 382 } 383 384 function isTableSection() { 385 return false; 386 } 387 388 // CSS 2.1: 389 // 9.2.1 Block-level elements and block boxes 390 // Block-level elements are those elements of the source document that are formatted visually as blocks 391 // (e.g., paragraphs). Several values of the 'display' property make an element block-level: 392 // 'block', 'list-item', 'compact' and 'run-in' (part of the time; see compact and run-in boxes), and 'table'. 393 // 394 function isBlockLevel() { 395 return false; 396 } 397 398 function hasAbsolutePositionedParent() { 399 if (is_null($this->parent)) { 400 return false; 401 }; 402 403 return 404 $this->parent->get_css_property(CSS_POSITION) == POSITION_ABSOLUTE || 405 $this->parent->hasAbsolutePositionedParent(); 406 } 407 408 function hasFixedPositionedParent() { 409 if (is_null($this->parent)) { 410 return false; 411 }; 412 413 return 414 $this->parent->get_css_property(CSS_POSITION) == POSITION_FIXED || 415 $this->parent->hasFixedPositionedParent(); 416 } 417 418 /** 419 * Box can be expanded if it has no width constrains and 420 * all it parents has no width constraints 421 */ 422 function mayBeExpanded() { 423 $wc = $this->get_css_property(CSS_WIDTH); 424 if (!$wc->isNull()) { return false; }; 425 426 if ($this->get_css_property(CSS_FLOAT) <> FLOAT_NONE) { 427 return true; 428 }; 429 430 if ($this->get_css_property(CSS_POSITION) <> POSITION_STATIC && 431 $this->get_css_property(CSS_POSITION) <> POSITION_RELATIVE) { 432 return true; 433 }; 434 435 if (is_null($this->parent)) { 436 return true; 437 }; 438 439 return $this->parent->mayBeExpanded(); 440 } 441 442 function isLineBreak() { 443 return false; 444 } 445 446 function get_min_width_natural($context) { 447 return $this->get_min_width($context); 448 } 449 450 function is_note_call() { 451 return isset($this->note_call); 452 } 453 454 /* DOM compatibility */ 455 function &get_parent_node() { 456 return $this->parent; 457 } 458} 459?>