composer package updates
[openemr.git] / vendor / dompdf / dompdf / src / Frame.php
blob0a3061b4a9c0d06f7e115781b4e510fc28e2fb9d
1 <?php
3 namespace Dompdf;
5 use Dompdf\Css\Style;
6 use Dompdf\Frame\FrameList;
8 /**
9 * @package dompdf
10 * @link http://dompdf.github.com/
11 * @author Benj Carson <benjcarson@digitaljunkies.ca>
12 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15 /**
16 * The main Frame class
18 * This class represents a single HTML element. This class stores
19 * positioning information as well as containing block location and
20 * dimensions. Style information for the element is stored in a {@link
21 * Style} object. Tree structure is maintained via the parent & children
22 * links.
24 * @package dompdf
26 class Frame
28 const WS_TEXT = 1;
29 const WS_SPACE = 2;
31 /**
32 * The DOMElement or DOMText object this frame represents
34 * @var \DOMElement|\DOMText
36 protected $_node;
38 /**
39 * Unique identifier for this frame. Used to reference this frame
40 * via the node.
42 * @var string
44 protected $_id;
46 /**
47 * Unique id counter
49 public static $ID_COUNTER = 0; /*protected*/
51 /**
52 * This frame's calculated style
54 * @var Style
56 protected $_style;
58 /**
59 * This frame's original style. Needed for cases where frames are
60 * split across pages.
62 * @var Style
64 protected $_original_style;
66 /**
67 * This frame's parent in the document tree.
69 * @var Frame
71 protected $_parent;
73 /**
74 * This frame's children
76 * @var Frame[]
78 protected $_frame_list;
80 /**
81 * This frame's first child. All children are handled as a
82 * doubly-linked list.
84 * @var Frame
86 protected $_first_child;
88 /**
89 * This frame's last child.
91 * @var Frame
93 protected $_last_child;
95 /**
96 * This frame's previous sibling in the document tree.
98 * @var Frame
100 protected $_prev_sibling;
103 * This frame's next sibling in the document tree.
105 * @var Frame
107 protected $_next_sibling;
110 * This frame's containing block (used in layout): array(x, y, w, h)
112 * @var float[]
114 protected $_containing_block;
117 * Position on the page of the top-left corner of the margin box of
118 * this frame: array(x,y)
120 * @var float[]
122 protected $_position;
125 * Absolute opacity of this frame
127 * @var float
129 protected $_opacity;
132 * This frame's decorator
134 * @var \Dompdf\FrameDecorator\AbstractFrameDecorator
136 protected $_decorator;
139 * This frame's containing line box
141 * @var LineBox
143 protected $_containing_line;
146 * @var array
148 protected $_is_cache = array();
151 * Tells wether the frame was already pushed to the next page
153 * @var bool
155 public $_already_pushed = false;
158 * @var bool
160 public $_float_next_line = false;
163 * Tells wether the frame was split
165 * @var bool
167 public $_splitted;
170 * @var int
172 public static $_ws_state = self::WS_SPACE;
175 * Class constructor
177 * @param \DOMNode $node the DOMNode this frame represents
179 public function __construct(\DOMNode $node)
181 $this->_node = $node;
183 $this->_parent = null;
184 $this->_first_child = null;
185 $this->_last_child = null;
186 $this->_prev_sibling = $this->_next_sibling = null;
188 $this->_style = null;
189 $this->_original_style = null;
191 $this->_containing_block = array(
192 "x" => null,
193 "y" => null,
194 "w" => null,
195 "h" => null,
198 $this->_containing_block[0] =& $this->_containing_block["x"];
199 $this->_containing_block[1] =& $this->_containing_block["y"];
200 $this->_containing_block[2] =& $this->_containing_block["w"];
201 $this->_containing_block[3] =& $this->_containing_block["h"];
203 $this->_position = array(
204 "x" => null,
205 "y" => null,
208 $this->_position[0] =& $this->_position["x"];
209 $this->_position[1] =& $this->_position["y"];
211 $this->_opacity = 1.0;
212 $this->_decorator = null;
214 $this->set_id(self::$ID_COUNTER++);
218 * WIP : preprocessing to remove all the unused whitespace
220 protected function ws_trim()
222 if ($this->ws_keep()) {
223 return;
226 if (self::$_ws_state === self::WS_SPACE) {
227 $node = $this->_node;
229 if ($node->nodeName === "#text" && !empty($node->nodeValue)) {
230 $node->nodeValue = preg_replace("/[ \t\r\n\f]+/u", " ", trim($node->nodeValue));
231 self::$_ws_state = self::WS_TEXT;
237 * @return bool
239 protected function ws_keep()
241 $whitespace = $this->get_style()->white_space;
243 return in_array($whitespace, array("pre", "pre-wrap", "pre-line"));
247 * @return bool
249 protected function ws_is_text()
251 $node = $this->get_node();
253 if ($node->nodeName === "img") {
254 return true;
257 if (!$this->is_in_flow()) {
258 return false;
261 if ($this->is_text_node()) {
262 return trim($node->nodeValue) !== "";
265 return true;
269 * "Destructor": forcibly free all references held by this frame
271 * @param bool $recursive if true, call dispose on all children
273 public function dispose($recursive = false)
275 if ($recursive) {
276 while ($child = $this->_first_child) {
277 $child->dispose(true);
281 // Remove this frame from the tree
282 if ($this->_prev_sibling) {
283 $this->_prev_sibling->_next_sibling = $this->_next_sibling;
286 if ($this->_next_sibling) {
287 $this->_next_sibling->_prev_sibling = $this->_prev_sibling;
290 if ($this->_parent && $this->_parent->_first_child === $this) {
291 $this->_parent->_first_child = $this->_next_sibling;
294 if ($this->_parent && $this->_parent->_last_child === $this) {
295 $this->_parent->_last_child = $this->_prev_sibling;
298 if ($this->_parent) {
299 $this->_parent->get_node()->removeChild($this->_node);
302 $this->_style->dispose();
303 $this->_style = null;
304 unset($this->_style);
306 $this->_original_style->dispose();
307 $this->_original_style = null;
308 unset($this->_original_style);
313 * Re-initialize the frame
315 public function reset()
317 $this->_position["x"] = null;
318 $this->_position["y"] = null;
320 $this->_containing_block["x"] = null;
321 $this->_containing_block["y"] = null;
322 $this->_containing_block["w"] = null;
323 $this->_containing_block["h"] = null;
325 $this->_style = null;
326 unset($this->_style);
327 $this->_style = clone $this->_original_style;
329 // If this represents a generated node then child nodes represent generated content.
330 // Remove the children since the content will be generated next time this frame is reflowed.
331 if ($this->_node->nodeName === "dompdf_generated" && $this->_style->content != "normal") {
332 foreach ($this->get_children() as $child) {
333 $this->remove_child($child);
339 * @return \DOMElement|\DOMText
341 public function get_node()
343 return $this->_node;
347 * @return string
349 public function get_id()
351 return $this->_id;
355 * @return Style
357 public function get_style()
359 return $this->_style;
363 * @return Style
365 public function get_original_style()
367 return $this->_original_style;
371 * @return Frame
373 public function get_parent()
375 return $this->_parent;
379 * @return \Dompdf\FrameDecorator\AbstractFrameDecorator
381 public function get_decorator()
383 return $this->_decorator;
387 * @return Frame
389 public function get_first_child()
391 return $this->_first_child;
395 * @return Frame
397 public function get_last_child()
399 return $this->_last_child;
403 * @return Frame
405 public function get_prev_sibling()
407 return $this->_prev_sibling;
411 * @return Frame
413 public function get_next_sibling()
415 return $this->_next_sibling;
419 * @return FrameList|Frame[]
421 public function get_children()
423 if (isset($this->_frame_list)) {
424 return $this->_frame_list;
427 $this->_frame_list = new FrameList($this);
429 return $this->_frame_list;
432 // Layout property accessors
435 * Containing block dimensions
437 * @param $i string The key of the wanted containing block's dimension (x, y, w, h)
439 * @return float[]|float
441 public function get_containing_block($i = null)
443 if (isset($i)) {
444 return $this->_containing_block[$i];
447 return $this->_containing_block;
451 * Block position
453 * @param $i string The key of the wanted position value (x, y)
455 * @return array|float
457 public function get_position($i = null)
459 if (isset($i)) {
460 return $this->_position[$i];
463 return $this->_position;
466 //........................................................................
469 * Return the height of the margin box of the frame, in pt. Meaningless
470 * unless the height has been calculated properly.
472 * @return float
474 public function get_margin_height()
476 $style = $this->_style;
478 return (float)$style->length_in_pt(array(
479 $style->height,
480 $style->margin_top,
481 $style->margin_bottom,
482 $style->border_top_width,
483 $style->border_bottom_width,
484 $style->padding_top,
485 $style->padding_bottom
486 ), $this->_containing_block["h"]);
490 * Return the width of the margin box of the frame, in pt. Meaningless
491 * unless the width has been calculated properly.
493 * @return float
495 public function get_margin_width()
497 $style = $this->_style;
499 return (float)$style->length_in_pt(array(
500 $style->width,
501 $style->margin_left,
502 $style->margin_right,
503 $style->border_left_width,
504 $style->border_right_width,
505 $style->padding_left,
506 $style->padding_right
507 ), $this->_containing_block["w"]);
511 * @return float
513 public function get_break_margins()
515 $style = $this->_style;
517 return (float)$style->length_in_pt(array(
518 //$style->height,
519 $style->margin_top,
520 $style->margin_bottom,
521 $style->border_top_width,
522 $style->border_bottom_width,
523 $style->padding_top,
524 $style->padding_bottom
525 ), $this->_containing_block["h"]);
529 * Return the content box (x,y,w,h) of the frame
531 * @return array
533 public function get_content_box()
535 $style = $this->_style;
536 $cb = $this->_containing_block;
538 $x = $this->_position["x"] +
539 (float)$style->length_in_pt(array($style->margin_left,
540 $style->border_left_width,
541 $style->padding_left),
542 $cb["w"]);
544 $y = $this->_position["y"] +
545 (float)$style->length_in_pt(array($style->margin_top,
546 $style->border_top_width,
547 $style->padding_top),
548 $cb["h"]);
550 $w = $style->length_in_pt($style->width, $cb["w"]);
552 $h = $style->length_in_pt($style->height, $cb["h"]);
554 return array(0 => $x, "x" => $x,
555 1 => $y, "y" => $y,
556 2 => $w, "w" => $w,
557 3 => $h, "h" => $h);
561 * Return the padding box (x,y,w,h) of the frame
563 * @return array
565 public function get_padding_box()
567 $style = $this->_style;
568 $cb = $this->_containing_block;
570 $x = $this->_position["x"] +
571 (float)$style->length_in_pt(array($style->margin_left,
572 $style->border_left_width),
573 $cb["w"]);
575 $y = $this->_position["y"] +
576 (float)$style->length_in_pt(array($style->margin_top,
577 $style->border_top_width),
578 $cb["h"]);
580 $w = $style->length_in_pt(array($style->padding_left,
581 $style->width,
582 $style->padding_right),
583 $cb["w"]);
585 $h = $style->length_in_pt(array($style->padding_top,
586 $style->height,
587 $style->padding_bottom),
588 $cb["h"]);
590 return array(0 => $x, "x" => $x,
591 1 => $y, "y" => $y,
592 2 => $w, "w" => $w,
593 3 => $h, "h" => $h);
597 * Return the border box of the frame
599 * @return array
601 public function get_border_box()
603 $style = $this->_style;
604 $cb = $this->_containing_block;
606 $x = $this->_position["x"] + (float)$style->length_in_pt($style->margin_left, $cb["w"]);
608 $y = $this->_position["y"] + (float)$style->length_in_pt($style->margin_top, $cb["h"]);
610 $w = $style->length_in_pt(array($style->border_left_width,
611 $style->padding_left,
612 $style->width,
613 $style->padding_right,
614 $style->border_right_width),
615 $cb["w"]);
617 $h = $style->length_in_pt(array($style->border_top_width,
618 $style->padding_top,
619 $style->height,
620 $style->padding_bottom,
621 $style->border_bottom_width),
622 $cb["h"]);
624 return array(0 => $x, "x" => $x,
625 1 => $y, "y" => $y,
626 2 => $w, "w" => $w,
627 3 => $h, "h" => $h);
631 * @param null $opacity
633 * @return float
635 public function get_opacity($opacity = null)
637 if ($opacity !== null) {
638 $this->set_opacity($opacity);
641 return $this->_opacity;
645 * @return LineBox
647 public function &get_containing_line()
649 return $this->_containing_line;
652 //........................................................................
654 // Set methods
656 * @param $id
658 public function set_id($id)
660 $this->_id = $id;
662 // We can only set attributes of DOMElement objects (nodeType == 1).
663 // Since these are the only objects that we can assign CSS rules to,
664 // this shortcoming is okay.
665 if ($this->_node->nodeType == XML_ELEMENT_NODE) {
666 $this->_node->setAttribute("frame_id", $id);
671 * @param Style $style
673 public function set_style(Style $style)
675 if (is_null($this->_style)) {
676 $this->_original_style = clone $style;
679 //$style->set_frame($this);
680 $this->_style = $style;
684 * @param \Dompdf\FrameDecorator\AbstractFrameDecorator $decorator
686 public function set_decorator(FrameDecorator\AbstractFrameDecorator $decorator)
688 $this->_decorator = $decorator;
692 * @param null $x
693 * @param null $y
694 * @param null $w
695 * @param null $h
697 public function set_containing_block($x = null, $y = null, $w = null, $h = null)
699 if (is_array($x)) {
700 foreach ($x as $key => $val) {
701 $$key = $val;
705 if (is_numeric($x)) {
706 $this->_containing_block["x"] = $x;
709 if (is_numeric($y)) {
710 $this->_containing_block["y"] = $y;
713 if (is_numeric($w)) {
714 $this->_containing_block["w"] = $w;
717 if (is_numeric($h)) {
718 $this->_containing_block["h"] = $h;
723 * @param null $x
724 * @param null $y
726 public function set_position($x = null, $y = null)
728 if (is_array($x)) {
729 list($x, $y) = array($x["x"], $x["y"]);
732 if (is_numeric($x)) {
733 $this->_position["x"] = $x;
736 if (is_numeric($y)) {
737 $this->_position["y"] = $y;
742 * @param $opacity
744 public function set_opacity($opacity)
746 $parent = $this->get_parent();
747 $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0);
748 $this->_opacity = $base_opacity * $opacity;
752 * @param LineBox $line
754 public function set_containing_line(LineBox $line)
756 $this->_containing_line = $line;
760 * Indicates if the margin height is auto sized
762 * @return bool
764 public function is_auto_height()
766 $style = $this->_style;
768 return in_array(
769 "auto",
770 array(
771 $style->height,
772 $style->margin_top,
773 $style->margin_bottom,
774 $style->border_top_width,
775 $style->border_bottom_width,
776 $style->padding_top,
777 $style->padding_bottom,
778 $this->_containing_block["h"]
780 true
785 * Indicates if the margin width is auto sized
787 * @return bool
789 public function is_auto_width()
791 $style = $this->_style;
793 return in_array(
794 "auto",
795 array(
796 $style->width,
797 $style->margin_left,
798 $style->margin_right,
799 $style->border_left_width,
800 $style->border_right_width,
801 $style->padding_left,
802 $style->padding_right,
803 $this->_containing_block["w"]
805 true
810 * Tells if the frame is a text node
812 * @return bool
814 public function is_text_node()
816 if (isset($this->_is_cache["text_node"])) {
817 return $this->_is_cache["text_node"];
820 return $this->_is_cache["text_node"] = ($this->get_node()->nodeName === "#text");
824 * @return bool
826 public function is_positionned()
828 if (isset($this->_is_cache["positionned"])) {
829 return $this->_is_cache["positionned"];
832 $position = $this->get_style()->position;
834 return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES);
838 * @return bool
840 public function is_absolute()
842 if (isset($this->_is_cache["absolute"])) {
843 return $this->_is_cache["absolute"];
846 $position = $this->get_style()->position;
848 return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed");
852 * @return bool
854 public function is_block()
856 if (isset($this->_is_cache["block"])) {
857 return $this->_is_cache["block"];
860 return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES);
864 * @return bool
866 public function is_inline_block()
868 if (isset($this->_is_cache["inline_block"])) {
869 return $this->_is_cache["inline_block"];
872 return $this->_is_cache["inline_block"] = ($this->get_style()->display === 'inline-block');
876 * @return bool
878 public function is_in_flow()
880 if (isset($this->_is_cache["in_flow"])) {
881 return $this->_is_cache["in_flow"];
883 return $this->_is_cache["in_flow"] = !($this->get_style()->float !== "none" || $this->is_absolute());
887 * @return bool
889 public function is_pre()
891 if (isset($this->_is_cache["pre"])) {
892 return $this->_is_cache["pre"];
895 $white_space = $this->get_style()->white_space;
897 return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap"));
901 * @return bool
903 public function is_table()
905 if (isset($this->_is_cache["table"])) {
906 return $this->_is_cache["table"];
909 $display = $this->get_style()->display;
911 return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES);
916 * Inserts a new child at the beginning of the Frame
918 * @param $child Frame The new Frame to insert
919 * @param $update_node boolean Whether or not to update the DOM
921 public function prepend_child(Frame $child, $update_node = true)
923 if ($update_node) {
924 $this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null);
927 // Remove the child from its parent
928 if ($child->_parent) {
929 $child->_parent->remove_child($child, false);
932 $child->_parent = $this;
933 $child->_prev_sibling = null;
935 // Handle the first child
936 if (!$this->_first_child) {
937 $this->_first_child = $child;
938 $this->_last_child = $child;
939 $child->_next_sibling = null;
940 } else {
941 $this->_first_child->_prev_sibling = $child;
942 $child->_next_sibling = $this->_first_child;
943 $this->_first_child = $child;
948 * Inserts a new child at the end of the Frame
950 * @param $child Frame The new Frame to insert
951 * @param $update_node boolean Whether or not to update the DOM
953 public function append_child(Frame $child, $update_node = true)
955 if ($update_node) {
956 $this->_node->appendChild($child->_node);
959 // Remove the child from its parent
960 if ($child->_parent) {
961 $child->_parent->remove_child($child, false);
964 $child->_parent = $this;
965 $decorator = $child->get_decorator();
966 // force an update to the cached parent
967 if ($decorator !== null) {
968 $decorator->get_parent(false);
970 $child->_next_sibling = null;
972 // Handle the first child
973 if (!$this->_last_child) {
974 $this->_first_child = $child;
975 $this->_last_child = $child;
976 $child->_prev_sibling = null;
977 } else {
978 $this->_last_child->_next_sibling = $child;
979 $child->_prev_sibling = $this->_last_child;
980 $this->_last_child = $child;
985 * Inserts a new child immediately before the specified frame
987 * @param $new_child Frame The new Frame to insert
988 * @param $ref Frame The Frame after the new Frame
989 * @param $update_node boolean Whether or not to update the DOM
991 * @throws Exception
993 public function insert_child_before(Frame $new_child, Frame $ref, $update_node = true)
995 if ($ref === $this->_first_child) {
996 $this->prepend_child($new_child, $update_node);
998 return;
1001 if (is_null($ref)) {
1002 $this->append_child($new_child, $update_node);
1004 return;
1007 if ($ref->_parent !== $this) {
1008 throw new Exception("Reference child is not a child of this node.");
1011 // Update the node
1012 if ($update_node) {
1013 $this->_node->insertBefore($new_child->_node, $ref->_node);
1016 // Remove the child from its parent
1017 if ($new_child->_parent) {
1018 $new_child->_parent->remove_child($new_child, false);
1021 $new_child->_parent = $this;
1022 $new_child->_next_sibling = $ref;
1023 $new_child->_prev_sibling = $ref->_prev_sibling;
1025 if ($ref->_prev_sibling) {
1026 $ref->_prev_sibling->_next_sibling = $new_child;
1029 $ref->_prev_sibling = $new_child;
1033 * Inserts a new child immediately after the specified frame
1035 * @param $new_child Frame The new Frame to insert
1036 * @param $ref Frame The Frame before the new Frame
1037 * @param $update_node boolean Whether or not to update the DOM
1039 * @throws Exception
1041 public function insert_child_after(Frame $new_child, Frame $ref, $update_node = true)
1043 if ($ref === $this->_last_child) {
1044 $this->append_child($new_child, $update_node);
1046 return;
1049 if (is_null($ref)) {
1050 $this->prepend_child($new_child, $update_node);
1052 return;
1055 if ($ref->_parent !== $this) {
1056 throw new Exception("Reference child is not a child of this node.");
1059 // Update the node
1060 if ($update_node) {
1061 if ($ref->_next_sibling) {
1062 $next_node = $ref->_next_sibling->_node;
1063 $this->_node->insertBefore($new_child->_node, $next_node);
1064 } else {
1065 $new_child->_node = $this->_node->appendChild($new_child->_node);
1069 // Remove the child from its parent
1070 if ($new_child->_parent) {
1071 $new_child->_parent->remove_child($new_child, false);
1074 $new_child->_parent = $this;
1075 $new_child->_prev_sibling = $ref;
1076 $new_child->_next_sibling = $ref->_next_sibling;
1078 if ($ref->_next_sibling) {
1079 $ref->_next_sibling->_prev_sibling = $new_child;
1082 $ref->_next_sibling = $new_child;
1086 * Remove a child frame
1088 * @param Frame $child
1089 * @param boolean $update_node Whether or not to remove the DOM node
1091 * @throws Exception
1092 * @return Frame The removed child frame
1094 public function remove_child(Frame $child, $update_node = true)
1096 if ($child->_parent !== $this) {
1097 throw new Exception("Child not found in this frame");
1100 if ($update_node) {
1101 $this->_node->removeChild($child->_node);
1104 if ($child === $this->_first_child) {
1105 $this->_first_child = $child->_next_sibling;
1108 if ($child === $this->_last_child) {
1109 $this->_last_child = $child->_prev_sibling;
1112 if ($child->_prev_sibling) {
1113 $child->_prev_sibling->_next_sibling = $child->_next_sibling;
1116 if ($child->_next_sibling) {
1117 $child->_next_sibling->_prev_sibling = $child->_prev_sibling;
1120 $child->_next_sibling = null;
1121 $child->_prev_sibling = null;
1122 $child->_parent = null;
1124 return $child;
1127 //........................................................................
1129 // Debugging function:
1131 * @return string
1133 public function __toString()
1135 // Skip empty text frames
1136 // if ( $this->is_text_node() &&
1137 // preg_replace("/\s/", "", $this->_node->data) === "" )
1138 // return "";
1141 $str = "<b>" . $this->_node->nodeName . ":</b><br/>";
1142 //$str .= spl_object_hash($this->_node) . "<br/>";
1143 $str .= "Id: " . $this->get_id() . "<br/>";
1144 $str .= "Class: " . get_class($this) . "<br/>";
1146 if ($this->is_text_node()) {
1147 $tmp = htmlspecialchars($this->_node->nodeValue);
1148 $str .= "<pre>'" . mb_substr($tmp, 0, 70) .
1149 (mb_strlen($tmp) > 70 ? "..." : "") . "'</pre>";
1150 } elseif ($css_class = $this->_node->getAttribute("class")) {
1151 $str .= "CSS class: '$css_class'<br/>";
1154 if ($this->_parent) {
1155 $str .= "\nParent:" . $this->_parent->_node->nodeName .
1156 " (" . spl_object_hash($this->_parent->_node) . ") " .
1157 "<br/>";
1160 if ($this->_prev_sibling) {
1161 $str .= "Prev: " . $this->_prev_sibling->_node->nodeName .
1162 " (" . spl_object_hash($this->_prev_sibling->_node) . ") " .
1163 "<br/>";
1166 if ($this->_next_sibling) {
1167 $str .= "Next: " . $this->_next_sibling->_node->nodeName .
1168 " (" . spl_object_hash($this->_next_sibling->_node) . ") " .
1169 "<br/>";
1172 $d = $this->get_decorator();
1173 while ($d && $d != $d->get_decorator()) {
1174 $str .= "Decorator: " . get_class($d) . "<br/>";
1175 $d = $d->get_decorator();
1178 $str .= "Position: " . Helpers::pre_r($this->_position, true);
1179 $str .= "\nContaining block: " . Helpers::pre_r($this->_containing_block, true);
1180 $str .= "\nMargin width: " . Helpers::pre_r($this->get_margin_width(), true);
1181 $str .= "\nMargin height: " . Helpers::pre_r($this->get_margin_height(), true);
1183 $str .= "\nStyle: <pre>" . $this->_style->__toString() . "</pre>";
1185 if ($this->_decorator instanceof FrameDecorator\Block) {
1186 $str .= "Lines:<pre>";
1187 foreach ($this->_decorator->get_line_boxes() as $line) {
1188 foreach ($line->get_frames() as $frame) {
1189 if ($frame instanceof FrameDecorator\Text) {
1190 $str .= "\ntext: ";
1191 $str .= "'" . htmlspecialchars($frame->get_text()) . "'";
1192 } else {
1193 $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")";
1197 $str .=
1198 "\ny => " . $line->y . "\n" .
1199 "w => " . $line->w . "\n" .
1200 "h => " . $line->h . "\n" .
1201 "left => " . $line->left . "\n" .
1202 "right => " . $line->right . "\n";
1204 $str .= "</pre>";
1207 $str .= "\n";
1208 if (php_sapi_name() === "cli") {
1209 $str = strip_tags(str_replace(array("<br/>", "<b>", "</b>"),
1210 array("\n", "", ""),
1211 $str));
1214 return $str;