protected function _render_border(Frame_Decorator $frame, $corner_style = "bevel")
 {
     $cb = $frame->get_containing_block();
     $style = $frame->get_style();
     $bbox = $frame->get_border_box();
     $bp = $frame->get_style()->get_border_properties();
     $widths = array($style->length_in_pt($bp["top"]["width"]), $style->length_in_pt($bp["right"]["width"]), $style->length_in_pt($bp["bottom"]["width"]), $style->length_in_pt($bp["left"]["width"]));
     foreach ($bp as $side => $props) {
         list($x, $y, $w, $h) = $bbox;
         if (!$props["style"] || $props["style"] == "none" || $props["width"] <= 0) {
             continue;
         }
         switch ($side) {
             case "top":
                 $length = $w;
                 break;
             case "bottom":
                 $length = $w;
                 $y += $h;
                 break;
             case "left":
                 $length = $h;
                 break;
             case "right":
                 $length = $h;
                 $x += $w;
                 break;
             default:
                 break;
         }
         $method = "_border_" . $props["style"];
         $this->{$method}($x, $y, $length, $props["color"], $widths, $side, $corner_style);
     }
 }
 function reflow(Frame_Decorator $block = null)
 {
     $frame = $this->_frame;
     $page = $frame->get_root();
     $page->check_forced_page_break($frame);
     if ($page->is_full()) {
         return;
     }
     $style = $frame->get_style();
     $this->_set_content();
     $frame->position();
     $cb = $frame->get_containing_block();
     if (($f = $frame->get_first_child()) && $f instanceof Text_Frame_Decorator) {
         $f_style = $f->get_style();
         $f_style->margin_left = $style->margin_left;
         $f_style->padding_left = $style->padding_left;
         $f_style->border_left = $style->border_left;
     }
     if (($l = $frame->get_last_child()) && $l instanceof Text_Frame_Decorator) {
         $l_style = $l->get_style();
         $l_style->margin_right = $style->margin_right;
         $l_style->padding_right = $style->padding_right;
         $l_style->border_right = $style->border_right;
     }
     if ($block) {
         $block->add_frame_to_line($this->_frame);
     }
     foreach ($frame->get_children() as $child) {
         $child->set_containing_block($cb);
         $child->reflow($block);
     }
 }
Exemplo n.º 3
0
    function reflow(Frame_Decorator $block = null)
    {
        $frame = $this->_frame;
        $page = $frame->get_root();
        $page->check_forced_page_break($this->_frame);

        if ($page->is_full())
            return;

        $this->_block_parent = /*isset($block) ? $block : */
            $frame->find_block_parent();

        // Left trim the text if this is the first text on the line and we're
        // collapsing white space
//     if ( $this->_block_parent->get_current_line()->w == 0 &&
//          ($frame->get_style()->white_space !== "pre" ||
//           $frame->get_style()->white_space !== "pre-wrap") ) {
//       $frame->set_text( ltrim( $frame->get_text() ) );
//     }

        $frame->position();

        $this->_layout_line();

        if ($block) {
            $block->add_frame_to_line($frame);
        }
    }
 function reflow(Frame_Decorator $block = null)
 {
     $this->_frame->position();
     $this->get_min_max_width();
     if ($block) {
         $block->add_frame_to_line($this->_frame);
     }
 }
Exemplo n.º 5
0
 function move($offset_x, $offset_y, $ignore_self = false)
 {
     list($x, $y) = $this->_frame->get_position();
     if (!$ignore_self) {
         $this->_frame->set_position($x + $offset_x, $y + $offset_y);
     }
     foreach ($this->_frame->get_children() as $child) {
         $child->move($offset_x, $offset_y);
     }
 }
 protected function _render_outline(Frame_Decorator $frame, $corner_style = "bevel")
 {
     $style = $frame->get_style();
     $props = array("width" => $style->outline_width, "style" => $style->outline_style, "color" => $style->outline_color);
     if (!$props["style"] || $props["style"] === "none" || $props["width"] <= 0) {
         return;
     }
     $bbox = $frame->get_border_box();
     $offset = $style->length_in_pt($props["width"]);
     $pattern = $this->_get_dash_pattern($props["style"], $offset);
     if (in_array($props["style"], array("solid", "dashed", "dotted"))) {
         $bbox[0] -= $offset / 2;
         $bbox[1] -= $offset / 2;
         $bbox[2] += $offset;
         $bbox[3] += $offset;
         list($x, $y, $w, $h) = $bbox;
         $this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern);
         return;
     }
     $bbox[0] -= $offset;
     $bbox[1] -= $offset;
     $bbox[2] += $offset * 2;
     $bbox[3] += $offset * 2;
     $method = "_border_" . $props["style"];
     $widths = array_fill(0, 4, $props["width"]);
     $sides = array("top", "right", "left", "bottom");
     foreach ($sides as $side) {
         list($x, $y, $w, $h) = $bbox;
         switch ($side) {
             case "top":
                 $length = $w;
                 break;
             case "bottom":
                 $length = $w;
                 $y += $h;
                 break;
             case "left":
                 $length = $h;
                 break;
             case "right":
                 $length = $h;
                 $x += $w;
                 break;
             default:
                 break;
         }
         $this->{$method}($x, $y, $length, $props["color"], $widths, $side, $corner_style);
     }
 }
 function reflow(Frame_Decorator $block = null)
 {
     $this->_frame->position();
     //FLOAT
     //$frame = $this->_frame;
     //$page = $frame->get_root();
     //if (DOMPDF_ENABLE_CSS_FLOAT && $frame->get_style()->float !== "none" ) {
     //  $page->add_floating_frame($this);
     //}
     // Set the frame's width
     $this->get_min_max_width();
     if ($block) {
         $block->add_frame_to_line($this->_frame);
     }
 }
 /**
  * Class constructor
  *
  * @param Frame $frame the frame to decorate
  * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
  */
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     global $_dompdf_warnings;
     parent::__construct($frame, $dompdf);
     $url = $frame->get_node()->getAttribute("src");
     list($this->_image_url, $this->_image_ext) = Image_Cache::resolve_url($url, $dompdf->get_protocol(), $dompdf->get_host(), $dompdf->get_base_path());
 }
 function copy(DomNode $node)
 {
     $deco = parent::copy($node);
     $deco->_cellmap->set_columns($this->_cellmap->get_columns());
     $deco->_cellmap->lock_columns();
     return $deco;
 }
Exemplo n.º 10
0
 function reset()
 {
     parent::reset();
     $this->_lines = array(array("frames" => array(), "wc" => 0, "y" => null, "w" => 0, "h" => 0));
     $this->_counters = array(self::DEFAULT_COUNTER => 0);
     $this->_cl = 0;
 }
 /**
  * Class constructor
  *
  * @param Frame $frame the bullet frame to decorate
  * @param DOMPDF $dompdf the document's dompdf object
  */
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     $style = $frame->get_style();
     $url = $style->list_style_image;
     $frame->get_node()->setAttribute("src", $url);
     $this->_img = new Image_Frame_Decorator($frame, $dompdf);
     parent::__construct($this->_img, $dompdf);
     list($width, $height) = dompdf_getimagesize($this->_img->get_image_url());
     // Resample the bullet image to be consistent with 'auto' sized images
     // See also Image_Frame_Reflower::get_min_max_width
     // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
     $this->_width = (double) rtrim($width, "px") * 72 / DOMPDF_DPI;
     $this->_height = (double) rtrim($height, "px") * 72 / DOMPDF_DPI;
     //If an image is taller as the containing block/box, the box should be extended.
     //Neighbour elements are overwriting the overlapping image areas.
     //Todo: Where can the box size be extended?
     //Code below has no effect.
     //See block_frame_reflower _calculate_restricted_height
     //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S.
     //Leave for now
     //if ($style->min_height < $this->_height ) {
     //  $style->min_height = $this->_height;
     //}
     //$style->height = "auto";
 }
Exemplo n.º 12
0
 function reset()
 {
     parent::reset();
     $this->_line_boxes = array(new Line_Box($this));
     $this->_counters = array(self::DEFAULT_COUNTER => 0);
     $this->_cl = 0;
 }
 function reset()
 {
     parent::reset();
     $this->_lines = array(self::$_initial_line_state);
     $this->_counters = array(self::DEFAULT_COUNTER => 0);
     $this->_cl = 0;
 }
  /**
   * Override split() to remove all child rows and this element from the cellmap
   *
   * @param Frame $child
   * @param bool  $force_pagebreak
   *
   * @return void
   */
  function split(Frame $child = null, $force_pagebreak = false) {

    if ( is_null($child) ) {
      parent::split();
      return;
    }

    // Remove child & all subsequent rows from the cellmap
    $cellmap = $this->get_parent()->get_cellmap();
    $iter = $child;

    while ( $iter ) {
      $cellmap->remove_row($iter);
      $iter = $iter->get_next_sibling();
    }

    // If we are splitting at the first child remove the
    // table-row-group from the cellmap as well
    if ( $child === $this->get_first_child() ) {
      $cellmap->remove_row_group($this);
      parent::split();
      return;
    }

    $cellmap->update_row_group($this, $child->get_prev_sibling());
    parent::split($child);

  }
 function set_containing_block($x = null, $y = null, $w = null, $h = null)
 {
     parent::set_containing_block($x, $y, $w, $h);
     if (isset($h)) {
         $this->_bottom_page_margin = $h;
     }
 }
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     parent::__construct($frame);
     $url = $frame->get_node()->getAttribute("src");
     if (!DOMPDF_ENABLE_REMOTE && strstr($url, "://")) {
         $this->_remote = false;
         $this->_image_url = DOMPDF_LIB_DIR . "/res/broken_image.png";
     } else {
         if (DOMPDF_ENABLE_REMOTE && strstr($url, "://")) {
             // Download remote files to a temporary directory
             $this->_remote = true;
             $this->_image_url = tempnam(DOMPDF_TEMP_DIR, "dompdf_img_");
             file_put_contents($this->_image_url, file_get_contents($url));
         } else {
             $this->_remote = false;
             $this->_image_url = build_url($dompdf->get_protocol(), $dompdf->get_host(), $dompdf->get_base_path(), $url);
         }
     }
     if (!is_readable($this->_image_url) || !filesize($this->_image_url)) {
         $this->_remote = false;
         $this->_image_url = DOMPDF_LIB_DIR . "/res/broken_image.png";
     }
     // We need to preserve the file extenstion
     $i = strrpos($url, ".");
     if ($i === false) {
         throw new DOMPDF_Exception("Unknown image type: {$url}.");
     }
     $this->_image_ext = strtolower(substr($url, $i + 1));
 }
Exemplo n.º 17
0
    function reflow(Frame_Decorator $block = null)
    {
        $frame = $this->_frame;

        // Check if a page break is forced
        $page = $frame->get_root();
        $page->check_forced_page_break($frame);

        if ($page->is_full())
            return;

        $style = $frame->get_style();

        // Generated content
        $this->_set_content();

        $frame->position();

        $cb = $frame->get_containing_block();

        // Add our margin, padding & border to the first and last children
        if (($f = $frame->get_first_child()) && $f instanceof Text_Frame_Decorator) {
            $f_style = $f->get_style();
            $f_style->margin_left = $style->margin_left;
            $f_style->padding_left = $style->padding_left;
            $f_style->border_left = $style->border_left;
        }

        if (($l = $frame->get_last_child()) && $l instanceof Text_Frame_Decorator) {
            $l_style = $l->get_style();
            $l_style->margin_right = $style->margin_right;
            $l_style->padding_right = $style->padding_right;
            $l_style->border_right = $style->border_right;
        }

        if ($block) {
            $block->add_frame_to_line($this->_frame);
        }

        // Set the containing blocks and reflow each child.  The containing
        // block is not changed by line boxes.
        foreach ($frame->get_children() as $child) {
            $child->set_containing_block($cb);
            $child->reflow($block);
        }
    }
Exemplo n.º 18
0
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     parent::__construct($frame, $dompdf);
     $style = $this->_frame->get_style();
     $style->width = 0;
     $style->height = 0;
     $style->margin = 0;
     $style->padding = 0;
 }
 /**
  * Split the table at $row.  $row and all subsequent rows will be
  * added to the clone.  This method is overidden in order to remove
  * frames from the cellmap properly.
  *
  * @param Frame $row
  */
 function split($child = null)
 {
     parent::split($child);
     // Update the cellmap
     $iter = $child;
     while ($iter) {
         $this->_cellmap->remove_row($iter);
         $iter = $iter->get_next_sibling();
     }
 }
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     $style = $frame->get_style();
     $url = $style->list_style_image;
     $frame->get_node()->setAttribute("src", $url);
     $this->_img = new Image_Frame_Decorator($frame, $dompdf);
     parent::__construct($this->_img, $dompdf);
     list($width, $height) = dompdf_getimagesize($this->_img->get_image_url());
     $this->_width = (double) rtrim($width, "px") * 72 / DOMPDF_DPI;
     $this->_height = (double) rtrim($height, "px") * 72 / DOMPDF_DPI;
 }
 /**
  * Class constructor
  *
  * @param Frame $frame the bullet frame to decorate
  * @param DOMPDF $dompdf the document's dompdf object
  */
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     $url = $frame->get_style()->list_style_image;
     $frame->get_node()->setAttribute("src", $url);
     $this->_img = new Image_Frame_Decorator($frame, $dompdf);
     parent::__construct($this->_img, $dompdf);
     list($width, $height) = getimagesize($this->_img->get_image_url());
     // Resample the bullet image to be consistent with 'auto' sized images
     $this->_width = (double) rtrim($width, "px") * 72 / DOMPDF_DPI;
     $this->_height = (double) rtrim($height, "px") * 72 / DOMPDF_DPI;
 }
 /**
  * Class constructor
  *
  * @param Frame  $frame  the frame to decorate
  * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
  */
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     parent::__construct($frame, $dompdf);
     $url = $frame->get_node()->getAttribute("src");
     $debug_png = $dompdf->get_option("debug_png");
     if ($debug_png) {
         print '[__construct ' . $url . ']';
     }
     list($this->_image_url, , $this->_image_msg) = Image_Cache::resolve_url($url, $dompdf->get_protocol(), $dompdf->get_host(), $dompdf->get_base_path(), $dompdf);
     if (Image_Cache::is_broken($this->_image_url) && ($alt = $frame->get_node()->getAttribute("alt"))) {
         $style = $frame->get_style();
         $style->width = 4 / 3 * Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing);
         $style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size);
     }
 }
 /**
  * Class constructor
  *
  * @param Frame $frame the frame to decorate
  * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
  */
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     global $_dompdf_warnings;
     parent::__construct($frame, $dompdf);
     $url = $frame->get_node()->getAttribute("src");
     //debugpng
     if (DEBUGPNG) {
         print '[__construct ' . $url . ']';
     }
     list($this->_image_url, $this->_image_ext) = Image_Cache::resolve_url($url, $dompdf->get_protocol(), $dompdf->get_host(), $dompdf->get_base_path());
     if (strrpos($this->_image_url, DOMPDF_LIB_DIR . "/res/broken_image.png", 0) !== false && ($alt = $frame->get_node()->getAttribute("alt"))) {
         $style = $frame->get_style();
         $style->width = 4 / 3 * Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing);
         $style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size);
     }
 }
 function split($child = null, $force_pagebreak = false)
 {
     if (is_null($child)) {
         parent::split();
         return;
     }
     $cellmap = $this->get_parent()->get_cellmap();
     $iter = $child;
     while ($iter) {
         $cellmap->remove_row($iter);
         $iter = $iter->get_next_sibling();
     }
     if ($child === $this->get_first_child()) {
         $cellmap->remove_row_group($this);
         parent::split();
         return;
     }
     $cellmap->update_row_group($this, $child->get_prev_sibling());
     parent::split($child);
 }
Exemplo n.º 25
0
    /**
     * Class constructor
     *
     * @param Frame $frame the frame to decorate
     * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
     */
    function __construct(Frame $frame, DOMPDF $dompdf)
    {
        global $_dompdf_warnings;

        parent::__construct($frame, $dompdf);
        $url = $frame->get_node()->getAttribute("src");

        //debugpng
        if (DEBUGPNG) print '[__construct ' . $url . ']';

        list($this->_image_url, $type, $this->_image_msg) = Image_Cache::resolve_url($url,
            $dompdf->get_protocol(),
            $dompdf->get_host(),
            $dompdf->get_base_path());

        if (Image_Cache::is_broken($this->_image_url) &&
            $alt = $frame->get_node()->getAttribute("alt")
        ) {
            $style = $frame->get_style();
            $style->width = (4 / 3) * Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing);
            $style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size);
        }
    }
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     parent::__construct($frame, $dompdf);
 }
 /**
  * Return a copy of this frame with $node as its node
  *
  * @param DOMNode $node
  *
  * @return Frame
  */
 function copy(DOMNode $node)
 {
     $deco = parent::copy($node);
     // In order to keep columns' widths through pages
     $deco->_cellmap->set_columns($this->_cellmap->get_columns());
     $deco->_cellmap->lock_columns();
     return $deco;
 }
Exemplo n.º 28
0
 /**
  * Set the frame's containing block.  Overridden to set $this->_bottom_page_margin.
  *
  * @param float $x
  * @param float $y
  * @param float $w
  * @param float $h
  */
 function set_containing_block($x = null, $y = null, $w = null, $h = null)
 {
     parent::set_containing_block($x, $y, $w, $h);
     //$w = $this->get_containing_block("w");
     if (isset($h)) {
         $this->_bottom_page_margin = $h;
     }
     // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w);
 }
 function reflow(Frame_Decorator $block = null)
 {
     // Check if a page break is forced
     $page = $this->_frame->get_root();
     $page->check_forced_page_break($this->_frame);
     // Bail if the page is full
     if ($page->is_full()) {
         return;
     }
     // Generated content
     $this->_set_content();
     // Collapse margins if required
     $this->_collapse_margins();
     $style = $this->_frame->get_style();
     $cb = $this->_frame->get_containing_block();
     if ($style->position === "fixed") {
         $cb = $this->_frame->get_root()->get_containing_block();
     }
     // Determine the constraints imposed by this frame: calculate the width
     // of the content area:
     list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
     // Store the calculated properties
     $style->width = $w . "pt";
     $style->margin_left = $left_margin . "pt";
     $style->margin_right = $right_margin . "pt";
     $style->left = $left . "pt";
     $style->right = $right . "pt";
     // Update the position
     $this->_frame->position();
     list($x, $y) = $this->_frame->get_position();
     // Adjust the first line based on the text-indent property
     $indent = $style->length_in_pt($style->text_indent, $cb["w"]);
     $this->_frame->increase_line_width($indent);
     // Determine the content edge
     $top = $style->length_in_pt(array($style->margin_top, $style->padding_top, $style->border_top_width), $cb["h"]);
     $bottom = $style->length_in_pt(array($style->border_bottom_width, $style->margin_bottom, $style->padding_bottom), $cb["h"]);
     $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width, $style->padding_left), $cb["w"]);
     $cb_y = $y + $top;
     $cb_h = $cb["h"] + $cb["y"] - $bottom - $cb_y;
     // Set the y position of the first line in this block
     $this->_frame->set_current_line($cb_y);
     $this->_frame->get_current_line_box()->get_float_offsets();
     // Set the containing blocks and reflow each child
     foreach ($this->_frame->get_children() as $child) {
         // Bail out if the page is full
         if ($page->is_full()) {
             break;
         }
         $child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
         $this->process_clear($child);
         $child->reflow($this->_frame);
         // Don't add the child to the line if a page break has occurred
         if ($page->check_page_break($child)) {
             break;
         }
         $this->process_float($child, $cb_x, $w);
     }
     // Determine our height
     list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
     $style->height = $height;
     $style->margin_top = $margin_top;
     $style->margin_bottom = $margin_bottom;
     $style->top = $top;
     $style->bottom = $bottom;
     $needs_reposition = $style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto");
     // Absolute positioning measurement
     if ($needs_reposition) {
         $orig_style = $this->_frame->get_original_style();
         if ($orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto")) {
             $width = 0;
             foreach ($this->_frame->get_line_boxes() as $line) {
                 $width = max($line->w, $width);
             }
             $style->width = $width;
         }
         $style->left = $orig_style->left;
         $style->right = $orig_style->right;
     }
     $this->_text_align();
     $this->vertical_align();
     // Absolute positioning
     if ($needs_reposition) {
         list($x, $y) = $this->_frame->get_position();
         $this->_frame->position();
         list($new_x, $new_y) = $this->_frame->get_position();
         $this->_frame->move($new_x - $x, $new_y - $y, true);
     }
     if ($block && $this->_frame->is_in_flow()) {
         $block->add_frame_to_line($this->_frame);
         // May be inline-block
         if ($style->display === "block") {
             $block->add_line();
         }
     }
 }
Exemplo n.º 30
0
 function __toString()
 {
     // Skip empty text frames
     if ($this->_node->nodeName == "#text" && preg_replace("/\\s/", "", $this->_node->data) === "") {
         return "";
     }
     $str = "<b>" . $this->_node->nodeName . ":</b><br/>";
     $str .= (string) $this->_node . "<br/>";
     $str .= "Id: " . $this->get_id() . "<br/>";
     $str .= "Class: " . get_class($this) . "<br/>";
     if ($this->_node->nodeName == "#text") {
         $tmp = htmlspecialchars($this->_node->nodeValue);
         $str .= "<pre>'" . mb_substr($tmp, 0, 70) . (mb_strlen($tmp) > 70 ? "..." : "") . "'</pre>";
     }
     if ($this->_parent) {
         $str .= "\nParent:" . $this->_parent->_node->nodeName . " (" . (string) $this->_parent->_node . ") " . "<br/>";
     }
     if ($this->_prev_sibling) {
         $str .= "Prev: " . $this->_prev_sibling->_node->nodeName . " (" . (string) $this->_prev_sibling->_node . ") " . "<br/>";
     }
     if ($this->_next_sibling) {
         $str .= "Next: " . $this->_next_sibling->_node->nodeName . " (" . (string) $this->_next_sibling->_node . ") " . "<br/>";
     }
     $d = $this->get_decorator();
     while ($d && $d != $d->get_decorator()) {
         $str .= "Decorator: " . get_class($d) . "<br/>";
         $d = $d->get_decorator();
     }
     $str .= "Position: " . pre_r($this->_position, true);
     $str .= "\nContaining block: " . pre_r($this->_containing_block, true);
     $str .= "\nMargin width: " . pre_r($this->get_margin_width(), true);
     $str .= "\nMargin height: " . pre_r($this->get_margin_height(), true);
     $str .= "\nStyle: <pre>" . $this->_style->__toString() . "</pre>";
     if ($this->_decorator instanceof Block_Frame_Decorator) {
         $str .= "Lines:<pre>";
         foreach ($this->_decorator->get_lines() as $line) {
             foreach ($line["frames"] as $frame) {
                 if ($frame instanceof Text_Frame_Decorator) {
                     $str .= "\ntext: ";
                     $str .= htmlspecialchars($frame->get_text());
                 } else {
                     $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . (string) $frame->get_node() . ")";
                 }
             }
             $str .= "\ny => " . $line["y"] . "\n" . "w => " . $line["w"] . "\n" . "h => " . $line["h"] . "\n";
         }
         $str .= "</pre>";
     }
     $str .= "\n";
     if (php_sapi_name() == "cli") {
         $str = strip_tags(str_replace(array("<br/>", "<b>", "</b>"), array("\n", "", ""), $str));
     }
     return $str;
 }