Frame_Tree

Represents an entire document as a tree of frames.

Defined (1)

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

/lib/dompdf/include/frame_tree.cls.php  
  1. class Frame_Tree { 
  2.  
  3. /** 
  4. * Tags to ignore while parsing the tree 
  5. * @var array 
  6. */ 
  7. static protected $_HIDDEN_TAGS = array("area", "base", "basefont", "head", "style",  
  8. "meta", "title", "colgroup",  
  9. "noembed", "noscript", "param", "#comment");  
  10. /** 
  11. * The main DomDocument 
  12. * @see http://ca2.php.net/manual/en/ref.dom.php 
  13. * @var DomDocument 
  14. */ 
  15. protected $_dom; 
  16.  
  17. /** 
  18. * The root node of the FrameTree. 
  19. * @var Frame 
  20. */ 
  21. protected $_root; 
  22.  
  23. /** 
  24. * Subtrees of absolutely positioned elements 
  25. * @var array of Frames 
  26. */ 
  27. protected $_absolute_frames; 
  28.  
  29. /** 
  30. * A mapping of {@link Frame} objects to DOMNode objects 
  31. * @var array 
  32. */ 
  33. protected $_registry; 
  34.  
  35.  
  36. /** 
  37. * Class constructor 
  38. * @param DomDocument $dom the main DomDocument object representing the current html document 
  39. */ 
  40. function __construct(DomDocument $dom) { 
  41. $this->_dom = $dom; 
  42. $this->_root = null; 
  43. $this->_registry = array(); 
  44.  
  45. function __destruct() { 
  46. clear_object($this); 
  47.  
  48. /** 
  49. * Returns the DomDocument object representing the curent html document 
  50. * @return DOMDocument 
  51. */ 
  52. function get_dom() { 
  53. return $this->_dom; 
  54.  
  55. /** 
  56. * Returns the root frame of the tree 
  57. *  
  58. * @return Page_Frame_Decorator 
  59. */ 
  60. function get_root() { 
  61. return $this->_root; 
  62.  
  63. /** 
  64. * Returns a specific frame given its id 
  65. * @param string $id 
  66. * @return Frame 
  67. */ 
  68. function get_frame($id) { 
  69. return isset($this->_registry[$id]) ? $this->_registry[$id] : null; 
  70.  
  71. /** 
  72. * Returns a post-order iterator for all frames in the tree 
  73. * @return FrameTreeList|Frame[] 
  74. */ 
  75. function get_frames() { 
  76. return new FrameTreeList($this->_root); 
  77.  
  78. /** 
  79. * Builds the tree 
  80. */ 
  81. function build_tree() { 
  82. $html = $this->_dom->getElementsByTagName("html")->item(0); 
  83. if ( is_null($html) ) { 
  84. $html = $this->_dom->firstChild; 
  85.  
  86. if ( is_null($html) ) { 
  87. throw new DOMPDF_Exception("Requested HTML document contains no data."); 
  88.  
  89. $this->fix_tables(); 
  90.  
  91. $this->_root = $this->_build_tree_r($html); 
  92.  
  93. /** 
  94. * Adds missing TBODYs around TR 
  95. */ 
  96. protected function fix_tables() { 
  97. $xp = new DOMXPath($this->_dom); 
  98.  
  99. // Move table caption before the table 
  100. // FIXME find a better way to deal with it... 
  101. $captions = $xp->query("//table/caption"); 
  102. foreach($captions as $caption) { 
  103. $table = $caption->parentNode; 
  104. $table->parentNode->insertBefore($caption, $table); 
  105.  
  106. $rows = $xp->query("//table/tr"); 
  107. foreach($rows as $row) { 
  108. $tbody = $this->_dom->createElement("tbody"); 
  109. $tbody = $row->parentNode->insertBefore($tbody, $row); 
  110. $tbody->appendChild($row); 
  111.  
  112. /** 
  113. * Recursively adds {@link Frame} objects to the tree 
  114. * Recursively build a tree of Frame objects based on a dom tree. 
  115. * No layout information is calculated at this time, although the 
  116. * tree may be adjusted (i.e. nodes and frames for generated content 
  117. * and images may be created). 
  118. * @param DOMNode $node the current DOMNode being considered 
  119. * @return Frame 
  120. */ 
  121. protected function _build_tree_r(DOMNode $node) { 
  122.  
  123. $frame = new Frame($node); 
  124. $id = $frame->get_id(); 
  125. $this->_registry[ $id ] = $frame; 
  126.  
  127. if ( !$node->hasChildNodes() ) { 
  128. return $frame; 
  129.  
  130. // Fixes 'cannot access undefined property for object with 
  131. // overloaded access', fix by Stefan radulian 
  132. // <stefan.radulian@symbion.at>  
  133. //foreach ($node->childNodes as $child) { 
  134.  
  135. // Store the children in an array so that the tree can be modified 
  136. $children = array(); 
  137. for ($i = 0; $i < $node->childNodes->length; $i++) { 
  138. $children[] = $node->childNodes->item($i); 
  139.  
  140. foreach ($children as $child) { 
  141. $node_name = mb_strtolower($child->nodeName); 
  142.  
  143. // Skip non-displaying nodes 
  144. if ( in_array($node_name, self::$_HIDDEN_TAGS) ) { 
  145. if ( $node_name !== "head" && $node_name !== "style" ) { 
  146. $child->parentNode->removeChild($child); 
  147.  
  148. continue; 
  149.  
  150. // Skip empty text nodes 
  151. if ( $node_name === "#text" && $child->nodeValue == "" ) { 
  152. $child->parentNode->removeChild($child); 
  153. continue; 
  154.  
  155. // Skip empty image nodes 
  156. if ( $node_name === "img" && $child->getAttribute("src") == "" ) { 
  157. $child->parentNode->removeChild($child); 
  158. continue; 
  159.  
  160. $frame->append_child($this->_build_tree_r($child), false); 
  161.  
  162. return $frame; 
  163.  
  164. public function insert_node(DOMNode $node, DOMNode $new_node, $pos) { 
  165. if ( $pos === "after" || !$node->firstChild ) { 
  166. $node->appendChild($new_node); 
  167. else { 
  168. $node->insertBefore($new_node, $node->firstChild); 
  169.  
  170. $this->_build_tree_r($new_node); 
  171.  
  172. $frame_id = $new_node->getAttribute("frame_id"); 
  173. $frame = $this->get_frame($frame_id); 
  174.  
  175. $parent_id = $node->getAttribute("frame_id"); 
  176. $parent = $this->get_frame($parent_id); 
  177.  
  178. if ( $parent ) { 
  179. if ( $pos === "before" ) { 
  180. $parent->prepend_child($frame, false); 
  181. else { 
  182. $parent->append_child($frame, false); 
  183.  
  184. return $frame_id;