Frame_Decorator

Base Frame_Decorator class.

Defined (1)

The class is defined in the following location(s).

/lib/dompdf/include/frame_decorator.cls.php  
  1. abstract class Frame_Decorator extends Frame { 
  2. const DEFAULT_COUNTER = "-dompdf-default-counter"; 
  3.  
  4. public $_counters = array(); // array([id] => counter_value) (for generated content) 
  5.  
  6. /** 
  7. * The root node of the DOM tree 
  8. * @var Frame 
  9. */ 
  10. protected $_root; 
  11.  
  12. /** 
  13. * The decorated frame 
  14. * @var Frame 
  15. */ 
  16. protected $_frame; 
  17.  
  18. /** 
  19. * Positioner object used to position this frame (Strategy pattern) 
  20. * @var Positioner 
  21. */ 
  22. protected $_positioner; 
  23.  
  24. /** 
  25. * Reflower object used to calculate frame dimensions (Strategy pattern) 
  26. * @var Frame_Reflower 
  27. */ 
  28. protected $_reflower; 
  29.  
  30. /** 
  31. * Reference to the current dompdf instance 
  32. * @var DOMPDF 
  33. */ 
  34. protected $_dompdf; 
  35.  
  36. /** 
  37. * First block parent 
  38. *  
  39. * @var Block_Frame_Decorator 
  40. */ 
  41. private $_block_parent; 
  42.  
  43. /** 
  44. * First positionned parent (position: relative | absolute | fixed) 
  45. *  
  46. * @var Frame_Decorator 
  47. */ 
  48. private $_positionned_parent; 
  49.  
  50. /** 
  51. * Class constructor 
  52. * @param Frame $frame The decoration target 
  53. * @param DOMPDF $dompdf The DOMPDF object 
  54. */ 
  55. function __construct(Frame $frame, DOMPDF $dompdf) { 
  56. $this->_frame = $frame; 
  57. $this->_root = null; 
  58. $this->_dompdf = $dompdf; 
  59. $frame->set_decorator($this); 
  60.  
  61. /** 
  62. * "Destructor": foribly free all references held by this object 
  63. * @param bool $recursive if true, call dispose on all children 
  64. */ 
  65. function dispose($recursive = false) { 
  66. if ( $recursive ) { 
  67. while ( $child = $this->get_first_child() ) { 
  68. $child->dispose(true); 
  69.  
  70. $this->_root = null; 
  71. unset($this->_root); 
  72.  
  73. $this->_frame->dispose(true); 
  74. $this->_frame = null; 
  75. unset($this->_frame); 
  76.  
  77. $this->_positioner = null; 
  78. unset($this->_positioner); 
  79.  
  80. $this->_reflower = null; 
  81. unset($this->_reflower); 
  82.  
  83. /** 
  84. * Return a copy of this frame with $node as its node 
  85. *  
  86. * @param DOMNode $node 
  87. * @return Frame 
  88. */  
  89. function copy(DOMNode $node) { 
  90. $frame = new Frame($node); 
  91. $frame->set_style(clone $this->_frame->get_original_style()); 
  92.  
  93. return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root); 
  94.  
  95. /** 
  96. * Create a deep copy: copy this node and all children 
  97. * @return Frame 
  98. */ 
  99. function deep_copy() { 
  100. $frame = new Frame($this->get_node()->cloneNode()); 
  101. $frame->set_style(clone $this->_frame->get_original_style()); 
  102.  
  103. $deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root); 
  104.  
  105. foreach ($this->get_children() as $child) { 
  106. $deco->append_child($child->deep_copy()); 
  107.  
  108. return $deco; 
  109.  
  110. /** 
  111. * Delegate calls to decorated frame object 
  112. */ 
  113. function reset() { 
  114. $this->_frame->reset(); 
  115.  
  116. $this->_counters = array(); 
  117.  
  118. // Reset all children 
  119. foreach ($this->get_children() as $child) { 
  120. $child->reset(); 
  121.  
  122. // Getters ----------- 
  123. function get_id() { 
  124. return $this->_frame->get_id(); 
  125.  
  126. /** 
  127. * @return Frame 
  128. */ 
  129. function get_frame() { 
  130. return $this->_frame; 
  131.  
  132. /** 
  133. * @return DOMElement|DOMText 
  134. */ 
  135. function get_node() { 
  136. return $this->_frame->get_node(); 
  137.  
  138. /** 
  139. * @return Style 
  140. */ 
  141. function get_style() { 
  142. return $this->_frame->get_style(); 
  143.  
  144. /** 
  145. * @return Style 
  146. */ 
  147. function get_original_style() { 
  148. return $this->_frame->get_original_style(); 
  149.  
  150. /** 
  151. * @param integer $i 
  152. * @return array|float 
  153. */ 
  154. function get_containing_block($i = null) { 
  155. return $this->_frame->get_containing_block($i); 
  156.  
  157. /** 
  158. * @param integer $i 
  159. * @return array|float 
  160. */ 
  161. function get_position($i = null) { 
  162. return $this->_frame->get_position($i); 
  163.  
  164. /** 
  165. * @return DOMPDF 
  166. */ 
  167. function get_dompdf() { 
  168. return $this->_dompdf; 
  169.  
  170. /** 
  171. * @return float 
  172. */ 
  173. function get_margin_height() { 
  174. return $this->_frame->get_margin_height(); 
  175.  
  176. /** 
  177. * @return float 
  178. */ 
  179. function get_margin_width() { 
  180. return $this->_frame->get_margin_width(); 
  181.  
  182. /** 
  183. * @return array 
  184. */ 
  185. function get_padding_box() { 
  186. return $this->_frame->get_padding_box(); 
  187.  
  188. /** 
  189. * @return array 
  190. */ 
  191. function get_border_box() { 
  192. return $this->_frame->get_border_box(); 
  193.  
  194. /** 
  195. * @param integer $id 
  196. */ 
  197. function set_id($id) { 
  198. $this->_frame->set_id($id); 
  199.  
  200. /** 
  201. * @param Style $style 
  202. */ 
  203. function set_style(Style $style) { 
  204. $this->_frame->set_style($style); 
  205.  
  206. /** 
  207. * @param float $x 
  208. * @param float $y 
  209. * @param float $w 
  210. * @param float $h 
  211. */ 
  212. function set_containing_block($x = null, $y = null, $w = null, $h = null) { 
  213. $this->_frame->set_containing_block($x, $y, $w, $h); 
  214.  
  215. /** 
  216. * @param float $x 
  217. * @param float $y 
  218. */ 
  219. function set_position($x = null, $y = null) { 
  220. $this->_frame->set_position($x, $y); 
  221.  
  222. /** 
  223. * @return string 
  224. */ 
  225. function __toString() { 
  226. return $this->_frame->__toString(); 
  227.  
  228. /** 
  229. * @param Frame $child 
  230. * @param bool $update_node 
  231. */ 
  232. function prepend_child(Frame $child, $update_node = true) { 
  233. while ( $child instanceof Frame_Decorator ) { 
  234. $child = $child->_frame; 
  235.  
  236. $this->_frame->prepend_child($child, $update_node); 
  237.  
  238. /** 
  239. * @param Frame $child 
  240. * @param bool $update_node 
  241. */ 
  242. function append_child(Frame $child, $update_node = true) { 
  243. while ( $child instanceof Frame_Decorator ) { 
  244. $child = $child->_frame; 
  245.  
  246. $this->_frame->append_child($child, $update_node); 
  247.  
  248. /** 
  249. * @param Frame $new_child 
  250. * @param Frame $ref 
  251. * @param bool $update_node 
  252. */ 
  253. function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) { 
  254. while ( $new_child instanceof Frame_Decorator ) { 
  255. $new_child = $new_child->_frame; 
  256.  
  257. if ( $ref instanceof Frame_Decorator ) { 
  258. $ref = $ref->_frame; 
  259.  
  260. $this->_frame->insert_child_before($new_child, $ref, $update_node); 
  261.  
  262. /** 
  263. * @param Frame $new_child 
  264. * @param Frame $ref 
  265. * @param bool $update_node 
  266. */ 
  267. function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) { 
  268. while ( $new_child instanceof Frame_Decorator ) { 
  269. $new_child = $new_child->_frame; 
  270.  
  271. while ( $ref instanceof Frame_Decorator ) { 
  272. $ref = $ref->_frame; 
  273.  
  274. $this->_frame->insert_child_after($new_child, $ref, $update_node); 
  275.  
  276. /** 
  277. * @param Frame $child 
  278. * @param bool $update_node 
  279. * @return Frame 
  280. */ 
  281. function remove_child(Frame $child, $update_node = true) { 
  282. while ( $child instanceof Frame_Decorator ) { 
  283. $child = $child->_frame; 
  284.  
  285. return $this->_frame->remove_child($child, $update_node); 
  286.  
  287. /** 
  288. * @return Frame_Decorator 
  289. */ 
  290. function get_parent() { 
  291. $p = $this->_frame->get_parent(); 
  292. if ( $p && $deco = $p->get_decorator() ) { 
  293. while ( $tmp = $deco->get_decorator() ) { 
  294. $deco = $tmp; 
  295.  
  296. return $deco; 
  297. else if ( $p ) { 
  298. return $p; 
  299.  
  300. return null; 
  301.  
  302. /** 
  303. * @return Frame_Decorator 
  304. */ 
  305. function get_first_child() { 
  306. $c = $this->_frame->get_first_child(); 
  307. if ( $c && $deco = $c->get_decorator() ) { 
  308. while ( $tmp = $deco->get_decorator() ) { 
  309. $deco = $tmp; 
  310.  
  311. return $deco; 
  312. else if ( $c ) { 
  313. return $c; 
  314.  
  315. return null; 
  316.  
  317. /** 
  318. * @return Frame_Decorator 
  319. */ 
  320. function get_last_child() { 
  321. $c = $this->_frame->get_last_child(); 
  322. if ( $c && $deco = $c->get_decorator() ) { 
  323. while ( $tmp = $deco->get_decorator() ) { 
  324. $deco = $tmp; 
  325.  
  326. return $deco; 
  327. else if ( $c ) { 
  328. return $c; 
  329.  
  330. return null; 
  331.  
  332. /** 
  333. * @return Frame_Decorator 
  334. */ 
  335. function get_prev_sibling() { 
  336. $s = $this->_frame->get_prev_sibling(); 
  337. if ( $s && $deco = $s->get_decorator() ) { 
  338. while ( $tmp = $deco->get_decorator() ) { 
  339. $deco = $tmp; 
  340. return $deco; 
  341. else if ( $s ) { 
  342. return $s; 
  343.  
  344. return null; 
  345.  
  346. /** 
  347. * @return Frame_Decorator 
  348. */ 
  349. function get_next_sibling() { 
  350. $s = $this->_frame->get_next_sibling(); 
  351. if ( $s && $deco = $s->get_decorator() ) { 
  352. while ( $tmp = $deco->get_decorator() ) { 
  353. $deco = $tmp; 
  354.  
  355. return $deco; 
  356. else if ( $s ) { 
  357. return $s; 
  358.  
  359. return null; 
  360.  
  361. /** 
  362. * @return FrameTreeList 
  363. */ 
  364. function get_subtree() { 
  365. return new FrameTreeList($this); 
  366.  
  367. function set_positioner(Positioner $posn) { 
  368. $this->_positioner = $posn; 
  369. if ( $this->_frame instanceof Frame_Decorator ) { 
  370. $this->_frame->set_positioner($posn); 
  371.  
  372. function set_reflower(Frame_Reflower $reflower) { 
  373. $this->_reflower = $reflower; 
  374. if ( $this->_frame instanceof Frame_Decorator ) { 
  375. $this->_frame->set_reflower( $reflower ); 
  376.  
  377. /** 
  378. * @return Frame_Reflower 
  379. */ 
  380. function get_reflower() { 
  381. return $this->_reflower; 
  382.  
  383. /** 
  384. * @param Frame $root 
  385. */ 
  386. function set_root(Frame $root) { 
  387. $this->_root = $root; 
  388.  
  389. if ( $this->_frame instanceof Frame_Decorator ) { 
  390. $this->_frame->set_root($root); 
  391.  
  392. /** 
  393. * @return Page_Frame_Decorator 
  394. */ 
  395. function get_root() { 
  396. return $this->_root; 
  397.  
  398. /** 
  399. * @return Block_Frame_Decorator 
  400. */ 
  401. function find_block_parent() { 
  402. // Find our nearest block level parent 
  403. $p = $this->get_parent(); 
  404.  
  405. while ( $p ) { 
  406. if ( $p->is_block() ) { 
  407. break; 
  408.  
  409. $p = $p->get_parent(); 
  410.  
  411. return $this->_block_parent = $p; 
  412.  
  413. /** 
  414. * @return Frame_Decorator 
  415. */ 
  416. function find_positionned_parent() { 
  417. // Find our nearest relative positionned parent 
  418. $p = $this->get_parent(); 
  419. while ( $p ) { 
  420. if ( $p->is_positionned() ) { 
  421. break; 
  422.  
  423. $p = $p->get_parent(); 
  424.  
  425. if ( !$p ) { 
  426. $p = $this->_root->get_first_child(); // <body> 
  427.  
  428. return $this->_positionned_parent = $p; 
  429.  
  430. /** 
  431. * split this frame at $child. 
  432. * The current frame is cloned and $child and all children following 
  433. * $child are added to the clone. The clone is then passed to the 
  434. * current frame's parent->split() method. 
  435. * @param Frame $child 
  436. * @param boolean $force_pagebreak 
  437. * @throws DOMPDF_Exception 
  438. * @return void 
  439. */ 
  440. function split(Frame $child = null, $force_pagebreak = false) { 
  441. // decrement any counters that were incremented on the current node, unless that node is the body 
  442. $style = $this->_frame->get_style(); 
  443. if ( $this->_frame->get_node()->nodeName !== "body" && $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) { 
  444. $this->decrement_counters($decrement); 
  445.  
  446. if ( is_null( $child ) ) { 
  447. // check for counter increment on :before content (always a child of the selected element @link Frame_Reflower::_set_content) 
  448. // this can push the current node to the next page before counter rules have bubbled up (but only if 
  449. // it's been rendered, thus the position check) 
  450. if ( !$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id") ) { 
  451. foreach($this->_frame->get_children() as $child) { 
  452. if ( $this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() && $child->get_position('x') !== NULL ) { 
  453. $style = $child->get_style(); 
  454. if ( $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) { 
  455. $this->decrement_counters($decrement); 
  456. $this->get_parent()->split($this, $force_pagebreak); 
  457. return; 
  458.  
  459. if ( $child->get_parent() !== $this ) { 
  460. throw new DOMPDF_Exception("Unable to split: frame is not a child of this one."); 
  461.  
  462. $node = $this->_frame->get_node(); 
  463.  
  464. $split = $this->copy( $node->cloneNode() ); 
  465. $split->reset(); 
  466. $split->get_original_style()->text_indent = 0; 
  467. $split->_splitted = true; 
  468.  
  469. // The body's properties must be kept 
  470. if ( $node->nodeName !== "body" ) { 
  471. // Style reset on the first and second parts 
  472. $style = $this->_frame->get_style(); 
  473. $style->margin_bottom = 0; 
  474. $style->padding_bottom = 0; 
  475. $style->border_bottom = 0; 
  476.  
  477. // second 
  478. $orig_style = $split->get_original_style(); 
  479. $orig_style->text_indent = 0; 
  480. $orig_style->margin_top = 0; 
  481. $orig_style->padding_top = 0; 
  482. $orig_style->border_top = 0; 
  483.  
  484. $this->get_parent()->insert_child_after($split, $this); 
  485.  
  486. // Add $frame and all following siblings to the new split node 
  487. $iter = $child; 
  488. while ($iter) { 
  489. $frame = $iter; 
  490. $iter = $iter->get_next_sibling(); 
  491. $frame->reset(); 
  492. $split->append_child($frame); 
  493.  
  494. $this->get_parent()->split($split, $force_pagebreak); 
  495.  
  496. // If this node resets a counter save the current value to use when rendering on the next page 
  497. if ( $style->counter_reset && ( $reset = $style->counter_reset ) !== "none" ) { 
  498. $vars = preg_split( '/\s+/' , trim( $reset ) , 2 ); 
  499. $split->_counters[ '__' . $vars[0] ] = $this->lookup_counter_frame( $vars[0] )->_counters[$vars[0]]; 
  500.  
  501. function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) { 
  502. $this->get_parent()->_counters[$id] = intval($value); 
  503.  
  504. function decrement_counters($counters) { 
  505. foreach($counters as $id => $increment) { 
  506. $this->increment_counter($id, intval($increment) * -1); 
  507.  
  508. function increment_counters($counters) { 
  509. foreach($counters as $id => $increment) { 
  510. $this->increment_counter($id, intval($increment)); 
  511.  
  512. function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) { 
  513. $counter_frame = $this->lookup_counter_frame($id); 
  514.  
  515. if ( $counter_frame ) { 
  516. if ( !isset($counter_frame->_counters[$id]) ) { 
  517. $counter_frame->_counters[$id] = 0; 
  518.  
  519. $counter_frame->_counters[$id] += $increment; 
  520.  
  521. function lookup_counter_frame($id = self::DEFAULT_COUNTER) { 
  522. $f = $this->get_parent(); 
  523.  
  524. while( $f ) { 
  525. if( isset($f->_counters[$id]) ) { 
  526. return $f; 
  527. $fp = $f->get_parent(); 
  528.  
  529. if ( !$fp ) { 
  530. return $f; 
  531.  
  532. $f = $fp; 
  533.  
  534. // TODO: What version is the best : this one or the one in List_Bullet_Renderer ? 
  535. function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") { 
  536. $type = mb_strtolower($type); 
  537.  
  538. if ( !isset($this->_counters[$id]) ) { 
  539. $this->_counters[$id] = 0; 
  540.  
  541. $value = $this->_counters[$id]; 
  542.  
  543. switch ($type) { 
  544. default: 
  545. case "decimal": 
  546. return $value; 
  547.  
  548. case "decimal-leading-zero": 
  549. return str_pad($value, 2, "0"); 
  550.  
  551. case "lower-roman": 
  552. return dec2roman($value); 
  553.  
  554. case "upper-roman": 
  555. return mb_strtoupper(dec2roman($value)); 
  556.  
  557. case "lower-latin": 
  558. case "lower-alpha": 
  559. return chr( ($value % 26) + ord('a') - 1); 
  560.  
  561. case "upper-latin": 
  562. case "upper-alpha": 
  563. return chr( ($value % 26) + ord('A') - 1); 
  564.  
  565. case "lower-greek": 
  566. return unichr($value + 944); 
  567.  
  568. case "upper-greek": 
  569. return unichr($value + 912); 
  570.  
  571. final function position() { 
  572. $this->_positioner->position(); 
  573.  
  574. final function move($offset_x, $offset_y, $ignore_self = false) { 
  575. $this->_positioner->move($offset_x, $offset_y, $ignore_self);  
  576.  
  577. final function reflow(Block_Frame_Decorator $block = null) { 
  578. // Uncomment this to see the frames before they're laid out, instead of 
  579. // during rendering. 
  580. //echo $this->_frame; flush(); 
  581. $this->_reflower->reflow($block); 
  582.  
  583. final function get_min_max_width() { 
  584. return $this->_reflower->get_min_max_width();