Frame

The main Frame class.

Defined (1)

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

/lib/dompdf/include/frame.cls.php  
  1. class Frame { 
  2.  
  3. /** 
  4. * The DOMElement or DOMText object this frame represents 
  5. * @var DOMElement|DOMText 
  6. */ 
  7. protected $_node; 
  8.  
  9. /** 
  10. * Unique identifier for this frame. Used to reference this frame 
  11. * via the node. 
  12. * @var string 
  13. */ 
  14. protected $_id; 
  15.  
  16. /** 
  17. * Unique id counter 
  18. */ 
  19. static /**protected*/ $ID_COUNTER = 0; 
  20.  
  21. /** 
  22. * This frame's calculated style 
  23. * @var Style 
  24. */ 
  25. protected $_style; 
  26.  
  27. /** 
  28. * This frame's original style. Needed for cases where frames are 
  29. * split across pages. 
  30. * @var Style 
  31. */ 
  32. protected $_original_style; 
  33.  
  34. /** 
  35. * This frame's parent in the document tree. 
  36. * @var Frame 
  37. */ 
  38. protected $_parent; 
  39.  
  40. /** 
  41. * This frame's children 
  42. * @var Frame[] 
  43. */ 
  44. protected $_frame_list; 
  45.  
  46. /** 
  47. * This frame's first child. All children are handled as a 
  48. * doubly-linked list. 
  49. * @var Frame 
  50. */ 
  51. protected $_first_child; 
  52.  
  53. /** 
  54. * This frame's last child. 
  55. * @var Frame 
  56. */ 
  57. protected $_last_child; 
  58.  
  59. /** 
  60. * This frame's previous sibling in the document tree. 
  61. * @var Frame 
  62. */ 
  63. protected $_prev_sibling; 
  64.  
  65. /** 
  66. * This frame's next sibling in the document tree. 
  67. * @var Frame 
  68. */ 
  69. protected $_next_sibling; 
  70.  
  71. /** 
  72. * This frame's containing block (used in layout): array(x, y, w, h) 
  73. * @var float[] 
  74. */ 
  75. protected $_containing_block; 
  76.  
  77. /** 
  78. * Position on the page of the top-left corner of the margin box of 
  79. * this frame: array(x, y) 
  80. * @var float[] 
  81. */ 
  82. protected $_position; 
  83.  
  84. /** 
  85. * Absolute opacity of this frame 
  86. * @var float 
  87. */ 
  88. protected $_opacity; 
  89.  
  90. /** 
  91. * This frame's decorator 
  92. * @var Frame_Decorator 
  93. */ 
  94. protected $_decorator; 
  95.  
  96. /** 
  97. * This frame's containing line box 
  98. * @var Line_Box 
  99. */ 
  100. protected $_containing_line; 
  101.  
  102. protected $_is_cache = array(); 
  103.  
  104. /** 
  105. * Tells wether the frame was already pushed to the next page 
  106. * @var bool 
  107. */ 
  108. public $_already_pushed = false; 
  109.  
  110. public $_float_next_line = false; 
  111.  
  112. /** 
  113. * Tells wether the frame was split 
  114. * @var bool 
  115. */ 
  116. public $_splitted; 
  117.  
  118. static $_ws_state = self::WS_SPACE; 
  119.  
  120. const WS_TEXT = 1; 
  121. const WS_SPACE = 2; 
  122.  
  123. /** 
  124. * Class destructor 
  125. */ 
  126. function __destruct() { 
  127. clear_object($this); 
  128.  
  129. /** 
  130. * Class constructor 
  131. * @param DOMNode $node the DOMNode this frame represents 
  132. */ 
  133. function __construct(DOMNode $node) { 
  134. $this->_node = $node; 
  135.  
  136. $this->_parent = null; 
  137. $this->_first_child = null; 
  138. $this->_last_child = null; 
  139. $this->_prev_sibling = $this->_next_sibling = null; 
  140.  
  141. $this->_style = null; 
  142. $this->_original_style = null; 
  143.  
  144. $this->_containing_block = array( 
  145. "x" => null,  
  146. "y" => null,  
  147. "w" => null,  
  148. "h" => null,  
  149. ); 
  150.  
  151. $this->_containing_block[0] =& $this->_containing_block["x"]; 
  152. $this->_containing_block[1] =& $this->_containing_block["y"]; 
  153. $this->_containing_block[2] =& $this->_containing_block["w"]; 
  154. $this->_containing_block[3] =& $this->_containing_block["h"]; 
  155.  
  156. $this->_position = array( 
  157. "x" => null,  
  158. "y" => null,  
  159. ); 
  160.  
  161. $this->_position[0] =& $this->_position["x"]; 
  162. $this->_position[1] =& $this->_position["y"]; 
  163.  
  164. $this->_opacity = 1.0; 
  165. $this->_decorator = null; 
  166.  
  167. $this->set_id( self::$ID_COUNTER++ ); 
  168.  
  169. // WIP : preprocessing to remove all the unused whitespace 
  170. protected function ws_trim() { 
  171. if ( $this->ws_keep() ) { 
  172. return; 
  173.  
  174. switch(self::$_ws_state) { 
  175. case self::WS_SPACE: 
  176. $node = $this->_node; 
  177.  
  178. if ( $node->nodeName === "#text" ) { 
  179. $node->nodeValue = preg_replace("/[ \t\r\n\f]+/u", " ", $node->nodeValue); 
  180.  
  181. // starts with a whitespace 
  182. if ( isset($node->nodeValue[0]) && $node->nodeValue[0] === " " ) { 
  183. $node->nodeValue = ltrim($node->nodeValue); 
  184.  
  185. // if not empty 
  186. if ( $node->nodeValue !== "" ) { 
  187. // change the current state (text) 
  188. self::$_ws_state = self::WS_TEXT; 
  189.  
  190. // ends with a whitespace 
  191. if ( preg_match("/[ \t\r\n\f]+$/u", $node->nodeValue) ) { 
  192. $node->nodeValue = ltrim($node->nodeValue); 
  193. break; 
  194.  
  195. case self::WS_TEXT: 
  196.  
  197. protected function ws_keep() { 
  198. $whitespace = $this->get_style()->white_space; 
  199. return in_array($whitespace, array("pre", "pre-wrap", "pre-line")); 
  200.  
  201. protected function ws_is_text() { 
  202. $node = $this->get_node(); 
  203.  
  204. if ($node->nodeName === "img") { 
  205. return true; 
  206.  
  207. if ( !$this->is_in_flow() ) { 
  208. return false; 
  209.  
  210. if ($this->is_text_node()) { 
  211. return trim($node->nodeValue) !== ""; 
  212.  
  213. return true; 
  214.  
  215. /** 
  216. * "Destructor": forcibly free all references held by this frame 
  217. * @param bool $recursive if true, call dispose on all children 
  218. */ 
  219. function dispose($recursive = false) { 
  220.  
  221. if ( $recursive ) { 
  222. while ( $child = $this->_first_child ) { 
  223. $child->dispose(true); 
  224.  
  225. // Remove this frame from the tree 
  226. if ( $this->_prev_sibling ) { 
  227. $this->_prev_sibling->_next_sibling = $this->_next_sibling; 
  228.  
  229. if ( $this->_next_sibling ) { 
  230. $this->_next_sibling->_prev_sibling = $this->_prev_sibling; 
  231.  
  232. if ( $this->_parent && $this->_parent->_first_child === $this ) { 
  233. $this->_parent->_first_child = $this->_next_sibling; 
  234.  
  235. if ( $this->_parent && $this->_parent->_last_child === $this ) { 
  236. $this->_parent->_last_child = $this->_prev_sibling; 
  237.  
  238. if ( $this->_parent ) { 
  239. $this->_parent->get_node()->removeChild($this->_node); 
  240.  
  241. $this->_style->dispose(); 
  242. $this->_style = null; 
  243. unset($this->_style); 
  244.  
  245. $this->_original_style->dispose(); 
  246. $this->_original_style = null; 
  247. unset($this->_original_style); 
  248.  
  249.  
  250. // Re-initialize the frame 
  251. function reset() { 
  252. $this->_position["x"] = null; 
  253. $this->_position["y"] = null; 
  254.  
  255. $this->_containing_block["x"] = null; 
  256. $this->_containing_block["y"] = null; 
  257. $this->_containing_block["w"] = null; 
  258. $this->_containing_block["h"] = null; 
  259.  
  260. $this->_style = null; 
  261. unset($this->_style); 
  262. $this->_style = clone $this->_original_style; 
  263.  
  264. //........................................................................ 
  265.  
  266. /** 
  267. * @return DOMElement|DOMText 
  268. */ 
  269. function get_node() { 
  270. return $this->_node; 
  271.  
  272. /** 
  273. * @return string 
  274. */ 
  275. function get_id() { 
  276. return $this->_id; 
  277.  
  278. /** 
  279. * @return Style 
  280. */ 
  281. function get_style() { 
  282. return $this->_style; 
  283.  
  284. /** 
  285. * @return Style 
  286. */ 
  287. function get_original_style() { 
  288. return $this->_original_style; 
  289.  
  290. /** 
  291. * @return Frame 
  292. */ 
  293. function get_parent() { 
  294. return $this->_parent; 
  295.  
  296. /** 
  297. * @return Frame_Decorator 
  298. */ 
  299. function get_decorator() { 
  300. return $this->_decorator; 
  301.  
  302. /** 
  303. * @return Frame 
  304. */ 
  305. function get_first_child() { 
  306. return $this->_first_child; 
  307.  
  308. /** 
  309. * @return Frame 
  310. */ 
  311. function get_last_child() { 
  312. return $this->_last_child; 
  313.  
  314. /** 
  315. * @return Frame 
  316. */ 
  317. function get_prev_sibling() { 
  318. return $this->_prev_sibling; 
  319.  
  320. /** 
  321. * @return Frame 
  322. */ 
  323. function get_next_sibling() { 
  324. return $this->_next_sibling; 
  325.  
  326. /** 
  327. * @return FrameList|Frame[] 
  328. */ 
  329. function get_children() { 
  330. if ( isset($this->_frame_list) ) { 
  331. return $this->_frame_list; 
  332.  
  333. $this->_frame_list = new FrameList($this); 
  334. return $this->_frame_list; 
  335.  
  336. // Layout property accessors 
  337.  
  338. /** 
  339. * Containing block dimensions 
  340. * @param $i string The key of the wanted containing block's dimension (x, y, x, h) 
  341. * @return float[]|float 
  342. */ 
  343. function get_containing_block($i = null) { 
  344. if ( isset($i) ) { 
  345. return $this->_containing_block[$i]; 
  346. return $this->_containing_block; 
  347.  
  348. /** 
  349. * Block position 
  350. * @param $i string The key of the wanted position value (x, y) 
  351. * @return array|float 
  352. */ 
  353. function get_position($i = null) { 
  354. if ( isset($i) ) { 
  355. return $this->_position[$i]; 
  356. return $this->_position; 
  357.  
  358. //........................................................................ 
  359.  
  360. /** 
  361. * Return the height of the margin box of the frame, in pt. Meaningless 
  362. * unless the height has been calculated properly. 
  363. * @return float 
  364. */ 
  365. function get_margin_height() { 
  366. $style = $this->_style; 
  367.  
  368. return $style->length_in_pt(array( 
  369. $style->height,  
  370. $style->margin_top,  
  371. $style->margin_bottom,  
  372. $style->border_top_width,  
  373. $style->border_bottom_width,  
  374. $style->padding_top,  
  375. $style->padding_bottom 
  376. ), $this->_containing_block["h"]); 
  377.  
  378. /** 
  379. * Return the width of the margin box of the frame, in pt. Meaningless 
  380. * unless the width has been calculated properly. 
  381. * @return float 
  382. */ 
  383. function get_margin_width() { 
  384. $style = $this->_style; 
  385.  
  386. return $style->length_in_pt(array( 
  387. $style->width,  
  388. $style->margin_left,  
  389. $style->margin_right,  
  390. $style->border_left_width,  
  391. $style->border_right_width,  
  392. $style->padding_left,  
  393. $style->padding_right 
  394. ), $this->_containing_block["w"]); 
  395.  
  396. function get_break_margins() { 
  397. $style = $this->_style; 
  398.  
  399. return $style->length_in_pt(array( 
  400. //$style->height,  
  401. $style->margin_top,  
  402. $style->margin_bottom,  
  403. $style->border_top_width,  
  404. $style->border_bottom_width,  
  405. $style->padding_top,  
  406. $style->padding_bottom 
  407. ), $this->_containing_block["h"]); 
  408.  
  409. /** 
  410. * Return the padding box (x, y, w, h) of the frame 
  411. * @return array 
  412. */ 
  413. function get_padding_box() { 
  414. $style = $this->_style; 
  415. $cb = $this->_containing_block; 
  416.  
  417. $x = $this->_position["x"] + 
  418. $style->length_in_pt(array($style->margin_left,  
  419. $style->border_left_width),  
  420. $cb["w"]); 
  421.  
  422. $y = $this->_position["y"] + 
  423. $style->length_in_pt(array($style->margin_top,  
  424. $style->border_top_width),  
  425. $cb["h"]); 
  426.  
  427. $w = $style->length_in_pt(array($style->padding_left,  
  428. $style->width,  
  429. $style->padding_right),  
  430. $cb["w"]); 
  431.  
  432. $h = $style->length_in_pt(array($style->padding_top,  
  433. $style->height,  
  434. $style->padding_bottom),  
  435. $cb["h"]); 
  436.  
  437. return array(0 => $x, "x" => $x,  
  438. 1 => $y, "y" => $y,  
  439. 2 => $w, "w" => $w,  
  440. 3 => $h, "h" => $h); 
  441.  
  442. /** 
  443. * Return the border box of the frame 
  444. * @return array 
  445. */ 
  446. function get_border_box() { 
  447. $style = $this->_style; 
  448. $cb = $this->_containing_block; 
  449.  
  450. $x = $this->_position["x"] + $style->length_in_pt($style->margin_left, $cb["w"]); 
  451.  
  452. $y = $this->_position["y"] + $style->length_in_pt($style->margin_top, $cb["h"]); 
  453.  
  454. $w = $style->length_in_pt(array($style->border_left_width,  
  455. $style->padding_left,  
  456. $style->width,  
  457. $style->padding_right,  
  458. $style->border_right_width),  
  459. $cb["w"]); 
  460.  
  461. $h = $style->length_in_pt(array($style->border_top_width,  
  462. $style->padding_top,  
  463. $style->height,  
  464. $style->padding_bottom,  
  465. $style->border_bottom_width),  
  466. $cb["h"]); 
  467.  
  468. return array(0 => $x, "x" => $x,  
  469. 1 => $y, "y" => $y,  
  470. 2 => $w, "w" => $w,  
  471. 3 => $h, "h" => $h); 
  472.  
  473. function get_opacity($opacity = null) { 
  474. if ( $opacity !== null ) { 
  475. $this->set_opacity($opacity); 
  476. return $this->_opacity; 
  477.  
  478. /** 
  479. * @return Line_Box 
  480. */ 
  481. function &get_containing_line() { 
  482. return $this->_containing_line; 
  483.  
  484. //........................................................................ 
  485.  
  486. // Set methods 
  487. function set_id($id) { 
  488. $this->_id = $id; 
  489.  
  490. // We can only set attributes of DOMElement objects (nodeType == 1). 
  491. // Since these are the only objects that we can assign CSS rules to,  
  492. // this shortcoming is okay. 
  493. if ( $this->_node->nodeType == XML_ELEMENT_NODE ) { 
  494. $this->_node->setAttribute("frame_id", $id); 
  495.  
  496. function set_style(Style $style) { 
  497. if ( is_null($this->_style) ) { 
  498. $this->_original_style = clone $style; 
  499.  
  500. //$style->set_frame($this); 
  501. $this->_style = $style; 
  502.  
  503. function set_decorator(Frame_Decorator $decorator) { 
  504. $this->_decorator = $decorator; 
  505.  
  506. function set_containing_block($x = null, $y = null, $w = null, $h = null) { 
  507. if ( is_array($x) ) { 
  508. foreach($x as $key => $val) { 
  509. $$key = $val; 
  510.  
  511. if (is_numeric($x)) { 
  512. $this->_containing_block["x"] = $x; 
  513.  
  514. if (is_numeric($y)) { 
  515. $this->_containing_block["y"] = $y; 
  516.  
  517. if (is_numeric($w)) { 
  518. $this->_containing_block["w"] = $w; 
  519.  
  520. if (is_numeric($h)) { 
  521. $this->_containing_block["h"] = $h; 
  522.  
  523. function set_position($x = null, $y = null) { 
  524. if ( is_array($x) ) { 
  525. list($x, $y) = array($x["x"], $x["y"]); 
  526.  
  527. if ( is_numeric($x) ) { 
  528. $this->_position["x"] = $x; 
  529.  
  530. if ( is_numeric($y) ) { 
  531. $this->_position["y"] = $y; 
  532.  
  533. function set_opacity($opacity) { 
  534. $parent = $this->get_parent(); 
  535. $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0); 
  536. $this->_opacity = $base_opacity * $opacity; 
  537.  
  538. function set_containing_line(Line_Box $line) { 
  539. $this->_containing_line = $line; 
  540.  
  541. //........................................................................ 
  542.  
  543. /** 
  544. * Tells if the frame is a text node 
  545. * @return bool 
  546. */ 
  547. function is_text_node() { 
  548. if ( isset($this->_is_cache["text_node"]) ) { 
  549. return $this->_is_cache["text_node"]; 
  550.  
  551. return $this->_is_cache["text_node"] = ($this->get_node()->nodeName === "#text"); 
  552.  
  553. function is_positionned() { 
  554. if ( isset($this->_is_cache["positionned"]) ) { 
  555. return $this->_is_cache["positionned"]; 
  556.  
  557. $position = $this->get_style()->position; 
  558.  
  559. return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES); 
  560.  
  561. function is_absolute() { 
  562. if ( isset($this->_is_cache["absolute"]) ) { 
  563. return $this->_is_cache["absolute"]; 
  564.  
  565. $position = $this->get_style()->position; 
  566.  
  567. return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed"); 
  568.  
  569. function is_block() { 
  570. if ( isset($this->_is_cache["block"]) ) { 
  571. return $this->_is_cache["block"]; 
  572.  
  573. return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES); 
  574.  
  575. function is_in_flow() { 
  576. if ( isset($this->_is_cache["in_flow"]) ) { 
  577. return $this->_is_cache["in_flow"]; 
  578.  
  579. $enable_css_float = $this->get_style()->get_stylesheet()->get_dompdf()->get_option("enable_css_float"); 
  580. return $this->_is_cache["in_flow"] = !($enable_css_float && $this->get_style()->float !== "none" || $this->is_absolute()); 
  581.  
  582. function is_pre() { 
  583. if ( isset($this->_is_cache["pre"]) ) { 
  584. return $this->_is_cache["pre"]; 
  585.  
  586. $white_space = $this->get_style()->white_space; 
  587.  
  588. return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap")); 
  589.  
  590. function is_table() { 
  591. if ( isset($this->_is_cache["table"]) ) { 
  592. return $this->_is_cache["table"]; 
  593.  
  594. $display = $this->get_style()->display; 
  595.  
  596. return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES); 
  597.  
  598.  
  599. /** 
  600. * Inserts a new child at the beginning of the Frame 
  601. * @param $child Frame The new Frame to insert 
  602. * @param $update_node boolean Whether or not to update the DOM 
  603. */ 
  604. function prepend_child(Frame $child, $update_node = true) { 
  605. if ( $update_node ) { 
  606. $this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null); 
  607.  
  608. // Remove the child from its parent 
  609. if ( $child->_parent ) { 
  610. $child->_parent->remove_child($child, false); 
  611.  
  612. $child->_parent = $this; 
  613. $child->_prev_sibling = null; 
  614.  
  615. // Handle the first child 
  616. if ( !$this->_first_child ) { 
  617. $this->_first_child = $child; 
  618. $this->_last_child = $child; 
  619. $child->_next_sibling = null; 
  620. else { 
  621. $this->_first_child->_prev_sibling = $child; 
  622. $child->_next_sibling = $this->_first_child; 
  623. $this->_first_child = $child; 
  624.  
  625. /** 
  626. * Inserts a new child at the end of the Frame 
  627. * @param $child Frame The new Frame to insert 
  628. * @param $update_node boolean Whether or not to update the DOM 
  629. */ 
  630. function append_child(Frame $child, $update_node = true) { 
  631. if ( $update_node ) { 
  632. $this->_node->appendChild($child->_node); 
  633.  
  634. // Remove the child from its parent 
  635. if ( $child->_parent ) { 
  636. $child->_parent->remove_child($child, false); 
  637.  
  638. $child->_parent = $this; 
  639. $child->_next_sibling = null; 
  640.  
  641. // Handle the first child 
  642. if ( !$this->_last_child ) { 
  643. $this->_first_child = $child; 
  644. $this->_last_child = $child; 
  645. $child->_prev_sibling = null; 
  646. else { 
  647. $this->_last_child->_next_sibling = $child; 
  648. $child->_prev_sibling = $this->_last_child; 
  649. $this->_last_child = $child; 
  650.  
  651. /** 
  652. * Inserts a new child immediately before the specified frame 
  653. * @param $new_child Frame The new Frame to insert 
  654. * @param $ref Frame The Frame after the new Frame 
  655. * @param $update_node boolean Whether or not to update the DOM 
  656. * @throws DOMPDF_Exception 
  657. */ 
  658. function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) { 
  659. if ( $ref === $this->_first_child ) { 
  660. $this->prepend_child($new_child, $update_node); 
  661. return; 
  662.  
  663. if ( is_null($ref) ) { 
  664. $this->append_child($new_child, $update_node); 
  665. return; 
  666.  
  667. if ( $ref->_parent !== $this ) { 
  668. throw new DOMPDF_Exception("Reference child is not a child of this node."); 
  669.  
  670. // Update the node 
  671. if ( $update_node ) { 
  672. $this->_node->insertBefore($new_child->_node, $ref->_node); 
  673.  
  674. // Remove the child from its parent 
  675. if ( $new_child->_parent ) { 
  676. $new_child->_parent->remove_child($new_child, false); 
  677.  
  678. $new_child->_parent = $this; 
  679. $new_child->_next_sibling = $ref; 
  680. $new_child->_prev_sibling = $ref->_prev_sibling; 
  681.  
  682. if ( $ref->_prev_sibling ) { 
  683. $ref->_prev_sibling->_next_sibling = $new_child; 
  684.  
  685. $ref->_prev_sibling = $new_child; 
  686.  
  687. /** 
  688. * Inserts a new child immediately after the specified frame 
  689. * @param $new_child Frame The new Frame to insert 
  690. * @param $ref Frame The Frame before the new Frame 
  691. * @param $update_node boolean Whether or not to update the DOM 
  692. * @throws DOMPDF_Exception 
  693. */ 
  694. function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) { 
  695. if ( $ref === $this->_last_child ) { 
  696. $this->append_child($new_child, $update_node); 
  697. return; 
  698.  
  699. if ( is_null($ref) ) { 
  700. $this->prepend_child($new_child, $update_node); 
  701. return; 
  702.  
  703. if ( $ref->_parent !== $this ) { 
  704. throw new DOMPDF_Exception("Reference child is not a child of this node."); 
  705.  
  706. // Update the node 
  707. if ( $update_node ) { 
  708. if ( $ref->_next_sibling ) { 
  709. $next_node = $ref->_next_sibling->_node; 
  710. $this->_node->insertBefore($new_child->_node, $next_node); 
  711. else { 
  712. $new_child->_node = $this->_node->appendChild($new_child->_node); 
  713.  
  714. // Remove the child from its parent 
  715. if ( $new_child->_parent ) { 
  716. $new_child->_parent->remove_child($new_child, false); 
  717.  
  718. $new_child->_parent = $this; 
  719. $new_child->_prev_sibling = $ref; 
  720. $new_child->_next_sibling = $ref->_next_sibling; 
  721.  
  722. if ( $ref->_next_sibling ) { 
  723. $ref->_next_sibling->_prev_sibling = $new_child; 
  724.  
  725. $ref->_next_sibling = $new_child; 
  726.  
  727.  
  728. /** 
  729. * Remove a child frame 
  730. * @param Frame $child 
  731. * @param boolean $update_node Whether or not to remove the DOM node 
  732. * @throws DOMPDF_Exception 
  733. * @return Frame The removed child frame 
  734. */ 
  735. function remove_child(Frame $child, $update_node = true) { 
  736. if ( $child->_parent !== $this ) { 
  737. throw new DOMPDF_Exception("Child not found in this frame"); 
  738.  
  739. if ( $update_node ) { 
  740. $this->_node->removeChild($child->_node); 
  741.  
  742. if ( $child === $this->_first_child ) { 
  743. $this->_first_child = $child->_next_sibling; 
  744.  
  745. if ( $child === $this->_last_child ) { 
  746. $this->_last_child = $child->_prev_sibling; 
  747.  
  748. if ( $child->_prev_sibling ) { 
  749. $child->_prev_sibling->_next_sibling = $child->_next_sibling; 
  750.  
  751. if ( $child->_next_sibling ) { 
  752. $child->_next_sibling->_prev_sibling = $child->_prev_sibling; 
  753.  
  754. $child->_next_sibling = null; 
  755. $child->_prev_sibling = null; 
  756. $child->_parent = null; 
  757. return $child; 
  758.  
  759. //........................................................................ 
  760.  
  761. // Debugging function: 
  762. function __toString() { 
  763. // Skip empty text frames 
  764. // if ( $this->is_text_node() && 
  765. // preg_replace("/\s/", "", $this->_node->data) === "" ) 
  766. // return ""; 
  767.  
  768.  
  769. $str = "<b>" . $this->_node->nodeName . ":</b><br/>"; 
  770. //$str .= spl_object_hash($this->_node) . "<br/>"; 
  771. $str .= "Id: " .$this->get_id() . "<br/>"; 
  772. $str .= "Class: " .get_class($this) . "<br/>"; 
  773.  
  774. if ( $this->is_text_node() ) { 
  775. $tmp = htmlspecialchars($this->_node->nodeValue); 
  776. $str .= "<pre>'" . mb_substr($tmp, 0, 70) . 
  777. (mb_strlen($tmp) > 70 ? "..." : "") . "'</pre>"; 
  778. elseif ( $css_class = $this->_node->getAttribute("class") ) { 
  779. $str .= "CSS class: '$css_class'<br/>"; 
  780.  
  781. if ( $this->_parent ) { 
  782. $str .= "\nParent:" . $this->_parent->_node->nodeName . 
  783. " (" . spl_object_hash($this->_parent->_node) . ") " . 
  784. "<br/>"; 
  785.  
  786. if ( $this->_prev_sibling ) { 
  787. $str .= "Prev: " . $this->_prev_sibling->_node->nodeName . 
  788. " (" . spl_object_hash($this->_prev_sibling->_node) . ") " . 
  789. "<br/>"; 
  790.  
  791. if ( $this->_next_sibling ) { 
  792. $str .= "Next: " . $this->_next_sibling->_node->nodeName . 
  793. " (" . spl_object_hash($this->_next_sibling->_node) . ") " . 
  794. "<br/>"; 
  795.  
  796. $d = $this->get_decorator(); 
  797. while ($d && $d != $d->get_decorator()) { 
  798. $str .= "Decorator: " . get_class($d) . "<br/>"; 
  799. $d = $d->get_decorator(); 
  800.  
  801. $str .= "Position: " . pre_r($this->_position, true); 
  802. $str .= "\nContaining block: " . pre_r($this->_containing_block, true); 
  803. $str .= "\nMargin width: " . pre_r($this->get_margin_width(), true); 
  804. $str .= "\nMargin height: " . pre_r($this->get_margin_height(), true); 
  805.  
  806. $str .= "\nStyle: <pre>". $this->_style->__toString() . "</pre>"; 
  807.  
  808. if ( $this->_decorator instanceof Block_Frame_Decorator ) { 
  809. $str .= "Lines:<pre>"; 
  810. foreach ($this->_decorator->get_line_boxes() as $line) { 
  811. foreach ($line->get_frames() as $frame) { 
  812. if ($frame instanceof Text_Frame_Decorator) { 
  813. $str .= "\ntext: "; 
  814. $str .= "'". htmlspecialchars($frame->get_text()) ."'"; 
  815. else { 
  816. $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")"; 
  817.  
  818. $str .= 
  819. "\ny => " . $line->y . "\n" . 
  820. "w => " . $line->w . "\n" . 
  821. "h => " . $line->h . "\n" . 
  822. "left => " . $line->left . "\n" . 
  823. "right => " . $line->right . "\n"; 
  824. $str .= "</pre>"; 
  825.  
  826. $str .= "\n"; 
  827. if ( php_sapi_name() === "cli" ) { 
  828. $str = strip_tags(str_replace(array("<br/>", "<b>", "</b>"),  
  829. array("\n", "", ""),  
  830. $str)); 
  831.  
  832. return $str;