static function translate_attributes(Frame $frame)
 {
     $node = $frame->get_node();
     $tag = $node->tagName;
     if (!isset(self::$__ATTRIBUTE_LOOKUP[$tag])) {
         return;
     }
     $valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
     $attrs = $node->attributes;
     $style = rtrim($node->getAttribute(self::$_style_attr), "; ");
     if ($style != "") {
         $style .= ";";
     }
     foreach ($attrs as $attr => $attr_node) {
         if (!isset($valid_attrs[$attr])) {
             continue;
         }
         $value = $attr_node->value;
         $target = $valid_attrs[$attr];
         // Look up $value in $target, if $target is an array:
         if (is_array($target)) {
             if (isset($target[$value])) {
                 $style .= " " . self::_resolve_target($node, $target[$value], $value);
             }
         } else {
             // otherwise use target directly
             $style .= " " . self::_resolve_target($node, $target, $value);
         }
     }
     if (!is_null($style)) {
         $style = ltrim($style);
         $node->setAttribute(self::$_style_attr, $style);
     }
 }
 function render(Frame $frame)
 {
     $style = $frame->get_style();
     list($x, $y, $w, $h) = $frame->get_border_box();
     $this->_set_opacity($frame->get_opacity($style->opacity));
     if ($frame->get_node()->nodeName === "body") {
         $h = $frame->get_containing_block("h") - $style->length_in_pt(array($style->margin_top, $style->padding_top, $style->border_top_width, $style->border_bottom_width, $style->padding_bottom, $style->margin_bottom), $style->width);
     }
     // Draw our background, border and content
     if (($bg = $style->background_color) !== "transparent") {
         $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
     }
     if (($url = $style->background_image) && $url !== "none") {
         $this->_background_image($url, $x, $y, $w, $h, $style);
     }
     $this->_render_border($frame);
     $this->_render_outline($frame);
     if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
         $this->_debug_layout($frame->get_border_box(), "red");
         if (DEBUG_LAYOUT_PADDINGBOX) {
             $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
         }
     }
     if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
         foreach ($frame->get_decorator()->get_line_boxes() as $line) {
             $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
         }
     }
 }
Example #3
0
 /**
  * "Destructor": forcibly free all references held by this frame
  *
  * @param bool $recursive if true, call dispose on all children
  */
 function dispose($recursive = false)
 {
     if ($recursive) {
         while ($child = $this->_first_child) {
             $child->dispose(true);
         }
     }
     // Remove this frame from the tree
     if ($this->_prev_sibling) {
         $this->_prev_sibling->_next_sibling = $this->_next_sibling;
     }
     if ($this->_next_sibling) {
         $this->_next_sibling->_prev_sibling = $this->_prev_sibling;
     }
     if ($this->_parent && $this->_parent->_first_child === $this) {
         $this->_parent->_first_child = $this->_next_sibling;
     }
     if ($this->_parent && $this->_parent->_last_child === $this) {
         $this->_parent->_last_child = $this->_prev_sibling;
     }
     if ($this->_parent) {
         $this->_parent->get_node()->removeChild($this->_node);
     }
     $this->_style->dispose();
     unset($this->_style);
     $this->_original_style->dispose();
     unset($this->_original_style);
 }
 function render(Frame $frame)
 {
     if (!$this->_dompdf->get_option("enable_javascript")) {
         return;
     }
     $this->insert($frame->get_node()->nodeValue);
 }
 function render(Frame $frame)
 {
     if (!DOMPDF_ENABLE_JAVASCRIPT) {
         return;
     }
     $this->insert($frame->get_node()->nodeValue);
 }
 function add_frame_to_line(Frame $frame)
 {
     if (!$frame->is_in_flow()) {
         return;
     }
     $style = $frame->get_style();
     $frame->set_containing_line($this->_line_boxes[$this->_cl]);
     if ($frame instanceof Inline_Frame_Decorator) {
         if ($frame->get_node()->nodeName === "br") {
             $this->maximize_line_height($style->length_in_pt($style->line_height), $frame);
             $this->add_line(true);
         }
         return;
     }
     if ($this->get_current_line_box()->w == 0 && $frame->is_text_node() && !$frame->is_pre()) {
         $frame->set_text(ltrim($frame->get_text()));
         $frame->recalculate_width();
     }
     $w = $frame->get_margin_width();
     if ($w == 0) {
         return;
     }
     $line = $this->_line_boxes[$this->_cl];
     if ($line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) {
         $this->add_line();
     }
     $frame->position();
     $current_line = $this->_line_boxes[$this->_cl];
     $current_line->add_frame($frame);
     if ($frame->is_text_node()) {
         $current_line->wc += count(preg_split("/\\s+/", trim($frame->get_text())));
     }
     $this->increase_line_width($w);
     $this->maximize_line_height($frame->get_margin_height(), $frame);
 }
 /**
  * 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";
 }
 function __construct(Frame $frame, DOMPDF $dompdf)
 {
     if ($frame->get_node()->nodeName !== "#text") {
         throw new DOMPDF_Exception("Text_Decorator can only be applied to #text nodes.");
     }
     parent::__construct($frame, $dompdf);
     $this->_text_spacing = null;
 }
  function render(Frame $frame) {
    $style = $frame->get_style();
    $node = $frame->get_node();

    list($x, $y, $w, $h) = $frame->get_border_box();

    $this->_set_opacity( $frame->get_opacity( $style->opacity ) );

    if ( $node->nodeName === "body" ) {
      $h = $frame->get_containing_block("h") - $style->length_in_pt(array(
        $style->margin_top,
        $style->border_top_width,
        $style->border_bottom_width,
        $style->margin_bottom),
      $style->width);
    }

    // Handle anchors & links
    if ( $node->nodeName === "a" && $href = $node->getAttribute("href") ) {
      $this->_canvas->add_link($href, $x, $y, $w, $h);
    }

    // Draw our background, border and content
    list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);

    if ( $tl + $tr + $br + $bl > 0 ) {
      $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
    }

    if ( ($bg = $style->background_color) !== "transparent" ) {
      $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg );
    }

    if ( ($url = $style->background_image) && $url !== "none" ) {
      $this->_background_image($url, $x, $y, $w, $h, $style);
    }

    if ( $tl + $tr + $br + $bl > 0 ) {
      $this->_canvas->clipping_end();
    }

    $border_box = array($x, $y, $w, $h);
    $this->_render_border($frame, $border_box);
    $this->_render_outline($frame, $border_box);

    if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
      $this->_debug_layout($frame->get_border_box(), "red");
      if (DEBUG_LAYOUT_PADDINGBOX) {
        $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
      }
    }

    if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
      foreach ($frame->get_decorator()->get_line_boxes() as $line) {
        $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
      }
    }
  }
 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;
 }
Example #12
0
    /**
     * split this frame at $child.
     *
     * The current frame is cloned and $child and all children following
     * $child are added to the clone.  The clone is then passed to the
     * current frame's parent->split() method.
     *
     * @param Frame $child
     * @param boolean $force_pagebreak
     */
    function split($child = null, $force_pagebreak = false)
    {
        if (is_null($child)) {
            $this->get_parent()->split($this, $force_pagebreak);
            return;
        }

        if ($child->get_parent() !== $this)
            throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");

        $node = $this->_frame->get_node();

        // mark the frame as splitted (don't use the find_***_parent cache)
        //$this->_splitted = true;

        $split = $this->copy($node->cloneNode());
        $split->reset();
        $split->get_original_style()->text_indent = 0;

        // The body's properties must be kept
        if ($node->nodeName !== "body") {
            // Style reset on the first and second parts
            $style = $this->_frame->get_style();
            $style->margin_bottom = 0;
            $style->padding_bottom = 0;
            $style->border_bottom = 0;

            // second
            $orig_style = $split->get_original_style();
            $orig_style->text_indent = 0;
            $orig_style->margin_top = 0;
            $orig_style->padding_top = 0;
            $orig_style->border_top = 0;
        }

        $this->get_parent()->insert_child_after($split, $this);

        // Add $frame and all following siblings to the new split node
        $iter = $child;
        while ($iter) {
            $frame = $iter;
            $iter = $iter->get_next_sibling();
            $frame->reset();
            $split->append_child($frame);
        }

        $this->get_parent()->split($split, $force_pagebreak);
    }
 /**
  * split this frame at $child.
  *
  * The current frame is cloned and $child and all children following
  * $child are added to the clone.  The clone is then passed to the
  * current frame's parent->split() method.
  *
  * @param Frame $child
  */
 function split($child = null)
 {
     if (is_null($child)) {
         $this->get_parent()->split($this);
         return;
     }
     if ($child->get_parent() !== $this) {
         throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
     }
     $split = $this->copy($this->_frame->get_node()->cloneNode());
     $split->reset();
     $this->get_parent()->insert_child_after($split, $this);
     // Add $frame and all following siblings to the new split node
     $iter = $child;
     while ($iter) {
         $frame = $iter;
         $iter = $iter->get_next_sibling();
         $frame->reset();
         $split->append_child($frame);
     }
     $this->get_parent()->split($split);
 }
 /**
  * Determine if a page break is allowed before $frame
  *
  * @param Frame $frame the frame to check
  * @return bool true if a break is allowed, false otherwise
  */
 protected function _page_break_allowed(Frame $frame)
 {
     /**
      *
      * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
      * /*
      * In the normal flow, page breaks can occur at the following places:
      * 
      *    1. In the vertical margin between block boxes. When a page
      *    break occurs here, the used values of the relevant
      *    'margin-top' and 'margin-bottom' properties are set to '0'.
      *    2. Between line boxes inside a block box.
      * 
      * These breaks are subject to the following rules:
      * 
      *   * Rule A: Breaking at (1) is allowed only if the
      *     'page-break-after' and 'page-break-before' properties of
      *     all the elements generating boxes that meet at this margin
      *     allow it, which is when at least one of them has the value
      *     'always', 'left', or 'right', or when all of them are
      *     'auto'.
      *
      *   * Rule B: However, if all of them are 'auto' and the
      *     nearest common ancestor of all the elements has a
      *     'page-break-inside' value of 'avoid', then breaking here is
      *     not allowed.
      *
      *   * Rule C: Breaking at (2) is allowed only if the number of
      *     line boxes between the break and the start of the enclosing
      *     block box is the value of 'orphans' or more, and the number
      *     of line boxes between the break and the end of the box is
      *     the value of 'widows' or more.
      *
      *   * Rule D: In addition, breaking at (2) is allowed only if
      *     the 'page-break-inside' property is 'auto'.
      * 
      * If the above doesn't provide enough break points to keep
      * content from overflowing the page boxes, then rules B and D are
      * dropped in order to find additional breakpoints.
      * 
      * If that still does not lead to sufficient break points, rules A
      * and C are dropped as well, to find still more break points.
      *
      * [endquote]
      *
      * We will also allow breaks between table rows.  However, when
      * splitting a table, the table headers should carry over to the
      * next page (but they don't yet).
      */
     $block_types = array("block", "list-item", "table");
     //      echo "\nbreak_allowed: " . $frame->get_node()->nodeName ."\n";
     $display = $frame->get_style()->display;
     // Block Frames (1):
     if (in_array($display, $block_types)) {
         // Avoid breaks within table-cells
         if ($this->_in_table) {
             //          echo "In table: " . $this->_in_table ."\n";
             return false;
         }
         // Rules A & B
         if ($frame->get_style()->page_break_before == "avoid") {
             //          echo "before: avoid\n";
             return false;
         }
         // Find the preceeding block-level sibling
         $prev = $frame->get_prev_sibling();
         while ($prev && !in_array($prev->get_style()->display, $block_types)) {
             $prev = $prev->get_prev_sibling();
         }
         // Does the previous element allow a page break after?
         if ($prev && $prev->get_style()->page_break_after == "avoid") {
             //        echo "after: avoid\n";
             return false;
         }
         // If both $prev & $frame have the same parent, check the parent's
         // page_break_inside property.
         $parent = $frame->get_parent();
         if ($prev && $parent && $parent->get_style()->page_break_inside == "avoid") {
             //          echo "parent inside: avoid\n";
             return false;
         }
         // To prevent cascading page breaks when a top-level element has
         // page-break-inside: avoid, ensure that at least one frame is
         // on the page before splitting.
         if ($parent->get_node()->nodeName == "body" && !$prev) {
             // We are the body's first child
             //          echo "Body's first child.\n";
             return false;
         }
         // If the frame is the first block-level frame, use the value from
         // $frame's parent instead.
         if (!$prev && $parent) {
             return $this->_page_break_allowed($parent);
         }
         //       echo "block: break allowed\n";
         return true;
     } else {
         if (in_array($display, Style::$INLINE_TYPES)) {
             // Avoid breaks within table-cells
             if ($this->_in_table) {
                 //          echo "In table: " . $this->_in_table ."\n";
                 return false;
             }
             // Rule C
             $block_parent = $frame->find_block_parent();
             if (count($block_parent->get_lines()) < $frame->get_style()->orphans) {
                 //          echo "orphans\n";
                 return false;
             }
             // FIXME: Checking widows is tricky without having laid out the
             // remaining line boxes.  Just ignore it for now...
             // Rule D
             if ($block_parent->get_style()->page_break_inside == "avoid") {
                 //          echo "parent->inside: avoid\n";
                 return false;
             }
             // To prevent cascading page breaks when a top-level element has
             // page-break-inside: avoid, ensure that at least one frame with
             // some content is on the page before splitting.
             $prev = $frame->get_prev_sibling();
             while ($prev && ($prev->get_node()->nodeName == "#text" && trim($prev->get_node()->nodeValue) == "")) {
                 $prev = $prev->get_prev_sibling();
             }
             if ($block_parent->get_node()->nodeName == "body" && !$prev) {
                 // We are the body's first child
                 //          echo "Body's first child.\n";
                 return false;
             }
             // Skip breaks on empty text nodes
             if ($frame->get_node()->nodeName == "#text" && $frame->get_node()->nodeValue == "") {
                 return false;
             }
             //       echo "inline: break allowed\n";
             return true;
             // Table-rows
         } else {
             if ($display == "table-row") {
                 // Simply check if the parent table's page_break_inside property is
                 // not 'avoid'
                 $p = Table_Frame_Decorator::find_parent_table($frame);
                 if ($p->get_style()->page_break_inside == "avoid") {
                     //          echo "table: page-break-inside: avoid\n";
                     return false;
                 }
                 // Check the table's parent element
                 $table_parent = $p->get_parent();
                 if ($table_parent->get_style()->page_break_inside == "avoid") {
                     //          echo "table->parent: page-break-inside: avoid\n";
                     return false;
                 }
                 // Avoid breaking after the first row of a table
                 if ($p->get_first_child() === $frame) {
                     //         echo "table: first-row\n";
                     return false;
                 }
                 //       echo "table-row/row-groups: break allowed\n";
                 return true;
             } else {
                 if (in_array($display, Table_Frame_Decorator::$ROW_GROUPS)) {
                     // Disallow breaks at row-groups: only split at row boundaries
                     return false;
                 } else {
                     //       echo "? " . $frame->get_style()->display . "\n";
                     return false;
                 }
             }
         }
     }
 }
 function add_frame_to_line(Frame $frame)
 {
     // Handle inline frames (which are effectively wrappers)
     if ($frame instanceof Inline_Frame_Decorator) {
         // Add each child of the inline frame to the line individually
         foreach ($frame->get_children() as $child) {
             $this->add_frame_to_line($child);
         }
         return;
     }
     if ($frame->get_margin_width() == 0) {
         return;
     }
     $w = $frame->get_margin_width();
     // Debugging code:
     //     pre_r("\nAdding frame to line:");
     //     pre_r("Me: " . $this->get_node()->nodeName . " (" . (string)$this->get_node() . ")");
     //     pre_r("Node: " . $frame->get_node()->nodeName . " (" . (string)$frame->get_node() . ")");
     //     if ( $frame->get_node()->nodeName == "#text" )
     //       pre_r($frame->get_node()->nodeValue);
     //     pre_r("Line width: " . $this->_lines[$this->_cl]["w"]);
     //     pre_r("Frame width: "  . $w);
     //     pre_r("Frame height: " . $frame->get_margin_height());
     //     pre_r("Containing block width: " . $this->get_containing_block("w"));
     // End debugging
     if ($this->_lines[$this->_cl]["w"] + $w >= $this->get_containing_block("w")) {
         $this->add_line();
     }
     $frame->position();
     $this->_lines[$this->_cl]["frames"][] = $frame;
     if ($frame->get_node()->nodeName == "#text") {
         $this->_lines[$this->_cl]["wc"] += count(preg_split("/\\s+/", $frame->get_text()));
     }
     $this->_lines[$this->_cl]["w"] += $w;
     $this->_lines[$this->_cl]["h"] = max($this->_lines[$this->_cl]["h"], $frame->get_margin_height());
 }
 function add_frame_to_line(Frame $frame)
 {
     // Handle inline frames (which are effectively wrappers)
     if ($frame instanceof Inline_Frame_Decorator) {
         // Handle line breaks
         if ($frame->get_node()->nodeName == "br") {
             $this->maximize_line_height($frame->get_style()->length_in_pt($frame->get_style()->line_height));
             $this->add_line();
             return;
         }
         // Add each child of the inline frame to the line individually
         foreach ($frame->get_children() as $child) {
             $this->add_frame_to_line($child);
         }
         return;
     }
     // Trim leading text if this is an empty line.  Kinda a hack to put it here,
     // but what can you do...
     if ($this->_lines[$this->_cl]["w"] == 0 && $frame->get_node()->nodeName == "#text" && ($frame->get_style()->white_space != "pre" || $frame->get_style()->white_space != "pre-wrap")) {
         $frame->set_text(ltrim($frame->get_text()));
         $frame->recalculate_width();
     }
     $w = $frame->get_margin_width();
     if ($w == 0) {
         return;
     }
     // Debugging code:
     /*
     pre_r("\nAdding frame to line:");
     
     //    pre_r("Me: " . $this->get_node()->nodeName . " (" . (string)$this->get_node() . ")");
     //    pre_r("Node: " . $frame->get_node()->nodeName . " (" . (string)$frame->get_node() . ")");
     if ( $frame->get_node()->nodeName == "#text" )
       pre_r($frame->get_node()->nodeValue);
     
     pre_r("Line width: " . $this->_lines[$this->_cl]["w"]);
     pre_r("Frame: " . get_class($frame));
     pre_r("Frame width: "  . $w);
     pre_r("Frame height: " . $frame->get_margin_height());
     pre_r("Containing block width: " . $this->get_containing_block("w"));
     */
     // End debugging
     if ($this->_lines[$this->_cl]["w"] + $w > $this->get_containing_block("w")) {
         $this->add_line();
     }
     $frame->position();
     $this->_lines[$this->_cl]["frames"][] = $frame;
     if ($frame->get_node()->nodeName == "#text") {
         $this->_lines[$this->_cl]["wc"] += count(preg_split("/\\s+/", $frame->get_text()));
     }
     $this->_lines[$this->_cl]["w"] += $w;
     $this->_lines[$this->_cl]["h"] = max($this->_lines[$this->_cl]["h"], $frame->get_margin_height());
 }
 /**
  * Decorate a Frame
  *
  * @param $root Frame The frame to decorate
  * @param $dompdf DOMPDF The dompdf instance
  * @return Frame_Decorator
  * FIXME-notes: this is admittedly a little smelly...
  */
 static function decorate_frame(Frame $frame, DOMPDF $dompdf)
 {
     if (is_null($dompdf)) {
         throw new Exception("foo");
     }
     $style = $frame->get_style();
     switch ($style->display) {
         case "block":
             $positioner = "Block";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "inline-block":
             $positioner = "Inline";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "inline":
             $positioner = "Inline";
             if ($frame->is_text_node()) {
                 $decorator = "Text";
                 $reflower = "Text";
             } else {
                 if (DOMPDF_ENABLE_CSS_FLOAT && $style->float !== "none") {
                     $decorator = "Block";
                     $reflower = "Block";
                 } else {
                     $decorator = "Inline";
                     $reflower = "Inline";
                 }
             }
             break;
         case "table":
             $positioner = "Block";
             $decorator = "Table";
             $reflower = "Table";
             break;
         case "inline-table":
             $positioner = "Inline";
             $decorator = "Table";
             $reflower = "Table";
             break;
         case "table-row-group":
         case "table-header-group":
         case "table-footer-group":
             $positioner = "Null";
             $decorator = "Table_Row_Group";
             $reflower = "Table_Row_Group";
             break;
         case "table-row":
             $positioner = "Null";
             $decorator = "Table_Row";
             $reflower = "Table_Row";
             break;
         case "table-cell":
             $positioner = "Table_Cell";
             $decorator = "Table_Cell";
             $reflower = "Table_Cell";
             break;
         case "list-item":
             $positioner = "Block";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "-dompdf-list-bullet":
             if ($style->list_style_position === "inside") {
                 $positioner = "Inline";
             } else {
                 $positioner = "List_Bullet";
             }
             if ($style->list_style_image !== "none") {
                 $decorator = "List_Bullet_Image";
             } else {
                 $decorator = "List_Bullet";
             }
             $reflower = "List_Bullet";
             break;
         case "-dompdf-image":
             $positioner = "Inline";
             $decorator = "Image";
             $reflower = "Image";
             break;
         case "-dompdf-br":
             $positioner = "Inline";
             $decorator = "Inline";
             $reflower = "Inline";
             break;
         default:
             // Fixme-note: should throw some sort of warning or something?
         // Fixme-note: should throw some sort of warning or something?
         case "none":
             $positioner = "Null";
             $decorator = "Null";
             $reflower = "Null";
             break;
     }
     // Handle CSS position
     $position = $style->position;
     if ($position === "absolute") {
         $positioner = "Absolute";
     } else {
         if ($position === "fixed") {
             $positioner = "Fixed";
         }
     }
     // Handle nodeName
     $node_name = $frame->get_node()->nodeName;
     if ($node_name === "img") {
         $style->display = "-dompdf-image";
         $decorator = "Image";
         $reflower = "Image";
     }
     $positioner .= "_Positioner";
     $decorator .= "_Frame_Decorator";
     $reflower .= "_Frame_Reflower";
     $deco = new $decorator($frame, $dompdf);
     $deco->set_positioner(new $positioner($deco));
     $reflow = new $reflower($deco);
     $deco->set_reflower($reflow);
     return $deco;
 }
 function render(Frame $frame)
 {
     $style = $frame->get_style();
     if (!$frame->get_first_child()) {
         return;
     }
     $bp = $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"]));
     list($x, $y) = $frame->get_first_child()->get_position();
     $w = null;
     $h = 0;
     $this->_set_opacity($frame->get_opacity($style->opacity));
     $first_row = true;
     foreach ($frame->get_children() as $child) {
         list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
         if (!is_null($w) && $child_x < $x + $w) {
             if (($bg = $style->background_color) !== "transparent") {
                 $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
             }
             if (($url = $style->background_image) && $url !== "none") {
                 $this->_background_image($url, $x, $y, $w, $h, $style);
             }
             if ($first_row) {
                 if ($bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0) {
                     $method = "_border_" . $bp["left"]["style"];
                     $this->{$method}($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
                 }
                 $first_row = false;
             }
             if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0) {
                 $method = "_border_" . $bp["top"]["style"];
                 $this->{$method}($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
             }
             if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0) {
                 $method = "_border_" . $bp["bottom"]["style"];
                 $this->{$method}($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
             }
             $link_node = null;
             if ($frame->get_node()->nodeName === "a") {
                 $link_node = $frame->get_node();
             } else {
                 if ($frame->get_parent()->get_node()->nodeName === "a") {
                     $link_node = $frame->get_parent()->get_node();
                 }
             }
             if ($link_node && ($href = $link_node->getAttribute("href"))) {
                 $this->_canvas->add_link($href, $x, $y, $w, $h);
             }
             $x = $child_x;
             $y = $child_y;
             $w = $child_w;
             $h = $child_h;
             continue;
         }
         if (is_null($w)) {
             $w = $child_w;
         } else {
             $w += $child_w;
         }
         $h = max($h, $child_h);
     }
     if (($bg = $style->background_color) !== "transparent") {
         $this->_canvas->filled_rectangle($x + $widths[3], $y + $widths[0], $w, $h, $bg);
     }
     if (($url = $style->background_image) && $url !== "none") {
         $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
     }
     $w += $widths[1] + $widths[3];
     $h += $widths[0] + $widths[2];
     $left_margin = $style->length_in_pt($style->margin_left);
     $x += $left_margin;
     if ($first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0) {
         $method = "_border_" . $bp["left"]["style"];
         $this->{$method}($x, $y, $h, $bp["left"]["color"], $widths, "left");
     }
     if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0) {
         $method = "_border_" . $bp["top"]["style"];
         $this->{$method}($x, $y, $w, $bp["top"]["color"], $widths, "top");
     }
     if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0) {
         $method = "_border_" . $bp["bottom"]["style"];
         $this->{$method}($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
     }
     if ($bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0) {
         $method = "_border_" . $bp["right"]["style"];
         $this->{$method}($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
     }
     $link_node = null;
     if ($frame->get_node()->nodeName === "a") {
         $link_node = $frame->get_node();
         if (($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id"))) {
             $this->_canvas->add_named_dest($name);
         }
     }
     if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") {
         $link_node = $frame->get_parent()->get_node();
     }
     if ($link_node) {
         if ($href = $link_node->getAttribute("href")) {
             $this->_canvas->add_link($href, $x, $y, $w, $h);
         }
     }
     if (DEBUG_LAYOUT && DEBUG_LAYOUT_INLINE) {
         $this->_debug_layout($child->get_border_box(), "blue");
         if (DEBUG_LAYOUT_PADDINGBOX) {
             $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5));
         }
     }
 }
 function render(Frame $frame)
 {
     $style = $frame->get_style();
     $font_size = $style->get_font_size();
     $line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w"));
     $this->_set_opacity($frame->get_opacity($style->opacity));
     // Handle list-style-image
     // If list style image is requested but missing, fall back to predefined types
     if ($style->list_style_image !== "none" && strcmp($img = $frame->get_image_url(), DOMPDF_LIB_DIR . "/res/broken_image.png") != 0) {
         list($x, $y) = $frame->get_position();
         //For expected size and aspect, instead of box size, use image natural size scaled to DPI.
         // 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.
         //$w = $frame->get_width();
         //$h = $frame->get_height();
         list($width, $height) = dompdf_getimagesize($img);
         $w = (double) rtrim($width, "px") * 72 / DOMPDF_DPI;
         $h = (double) rtrim($height, "px") * 72 / DOMPDF_DPI;
         $x -= $w;
         $y -= ($line_height - $font_size) / 2;
         //Reverse hinting of list_bullet_positioner
         $this->_canvas->image($img, $frame->get_image_ext(), $x, $y, $w, $h);
     } else {
         $bullet_style = $style->list_style_type;
         $fill = false;
         switch ($bullet_style) {
             default:
             case "disc":
                 $fill = true;
             case "circle":
                 list($x, $y) = $frame->get_position();
                 $r = $font_size * List_Bullet_Frame_Decorator::BULLET_SIZE / 2;
                 $x -= $font_size * (List_Bullet_Frame_Decorator::BULLET_SIZE / 2);
                 $y += $font_size * (1 - List_Bullet_Frame_Decorator::BULLET_DESCENT) / 2;
                 $o = $font_size * List_Bullet_Frame_Decorator::BULLET_THICKNESS;
                 $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill);
                 break;
             case "square":
                 list($x, $y) = $frame->get_position();
                 $w = $font_size * List_Bullet_Frame_Decorator::BULLET_SIZE;
                 $x -= $w;
                 $y += $font_size * (1 - List_Bullet_Frame_Decorator::BULLET_DESCENT - List_Bullet_Frame_Decorator::BULLET_SIZE) / 2;
                 $this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color);
                 break;
             case "decimal-leading-zero":
             case "decimal":
             case "lower-alpha":
             case "lower-latin":
             case "lower-roman":
             case "lower-greek":
             case "upper-alpha":
             case "upper-latin":
             case "upper-roman":
             case "1":
                 // HTML 4.0 compatibility
             // HTML 4.0 compatibility
             case "a":
             case "i":
             case "A":
             case "I":
                 list($x, $y) = $frame->get_position();
                 $pad = null;
                 if ($bullet_style === "decimal-leading-zero") {
                     $pad = strlen($frame->get_parent()->get_parent()->get_node()->getAttribute("dompdf-children-count"));
                 }
                 $index = $frame->get_node()->getAttribute("dompdf-counter");
                 $text = $this->make_counter($index, $bullet_style, $pad);
                 $font_family = $style->font_family;
                 $spacing = 0;
                 //$frame->get_text_spacing() + $style->word_spacing;
                 if (trim($text) == "") {
                     return;
                 }
                 $x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing);
                 $this->_canvas->text($x, $y, $text, $font_family, $font_size, $style->color, $spacing);
             case "none":
                 break;
         }
     }
 }
 function render(Frame $frame)
 {
     $style = $frame->get_style();
     if (trim($frame->get_node()->nodeValue) === "" && $style->empty_cells === "hide") {
         return;
     }
     $this->_set_opacity($frame->get_opacity($style->opacity));
     list($x, $y, $w, $h) = $frame->get_border_box();
     // Draw our background, border and content
     if (($bg = $style->background_color) !== "transparent") {
         $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
     }
     if (($url = $style->background_image) && $url !== "none") {
         $this->_background_image($url, $x, $y, $w, $h, $style);
     }
     $table = Table_Frame_Decorator::find_parent_table($frame);
     if ($table->get_style()->border_collapse !== "collapse") {
         $this->_render_border($frame);
         $this->_render_outline($frame);
         return;
     }
     // The collapsed case is slightly complicated...
     // @todo Add support for outlines here
     $cellmap = $table->get_cellmap();
     $cells = $cellmap->get_spanned_cells($frame);
     $num_rows = $cellmap->get_num_rows();
     $num_cols = $cellmap->get_num_cols();
     // Determine the top row spanned by this cell
     $i = $cells["rows"][0];
     $top_row = $cellmap->get_row($i);
     // Determine if this cell borders on the bottom of the table.  If so,
     // then we draw its bottom border.  Otherwise the next row down will
     // draw its top border instead.
     if (in_array($num_rows - 1, $cells["rows"])) {
         $draw_bottom = true;
         $bottom_row = $cellmap->get_row($num_rows - 1);
     } else {
         $draw_bottom = false;
     }
     // Draw the horizontal borders
     foreach ($cells["columns"] as $j) {
         $bp = $cellmap->get_border_properties($i, $j);
         $y = $top_row["y"] - $bp["top"]["width"] / 2;
         $col = $cellmap->get_column($j);
         $x = $col["x"] - $bp["left"]["width"] / 2;
         $w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"]) / 2;
         if ($bp["top"]["style"] !== "none" && $bp["top"]["width"] > 0) {
             $widths = array($bp["top"]["width"], $bp["right"]["width"], $bp["bottom"]["width"], $bp["left"]["width"]);
             $method = "_border_" . $bp["top"]["style"];
             $this->{$method}($x, $y, $w, $bp["top"]["color"], $widths, "top", "square");
         }
         if ($draw_bottom) {
             $bp = $cellmap->get_border_properties($num_rows - 1, $j);
             if ($bp["bottom"]["style"] === "none" || $bp["bottom"]["width"] <= 0) {
                 continue;
             }
             $y = $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2;
             $widths = array($bp["top"]["width"], $bp["right"]["width"], $bp["bottom"]["width"], $bp["left"]["width"]);
             $method = "_border_" . $bp["bottom"]["style"];
             $this->{$method}($x, $y, $w, $bp["bottom"]["color"], $widths, "bottom", "square");
         }
     }
     $j = $cells["columns"][0];
     $left_col = $cellmap->get_column($j);
     if (in_array($num_cols - 1, $cells["columns"])) {
         $draw_right = true;
         $right_col = $cellmap->get_column($num_cols - 1);
     } else {
         $draw_right = false;
     }
     // Draw the vertical borders
     foreach ($cells["rows"] as $i) {
         $bp = $cellmap->get_border_properties($i, $j);
         $x = $left_col["x"] - $bp["left"]["width"] / 2;
         $row = $cellmap->get_row($i);
         $y = $row["y"] - $bp["top"]["width"] / 2;
         $h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"]) / 2;
         if ($bp["left"]["style"] !== "none" && $bp["left"]["width"] > 0) {
             $widths = array($bp["top"]["width"], $bp["right"]["width"], $bp["bottom"]["width"], $bp["left"]["width"]);
             $method = "_border_" . $bp["left"]["style"];
             $this->{$method}($x, $y, $h, $bp["left"]["color"], $widths, "left", "square");
         }
         if ($draw_right) {
             $bp = $cellmap->get_border_properties($i, $num_cols - 1);
             if ($bp["right"]["style"] === "none" || $bp["right"]["width"] <= 0) {
                 continue;
             }
             $x = $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2;
             $widths = array($bp["top"]["width"], $bp["right"]["width"], $bp["bottom"]["width"], $bp["left"]["width"]);
             $method = "_border_" . $bp["right"]["style"];
             $this->{$method}($x, $y, $h, $bp["right"]["color"], $widths, "right", "square");
         }
     }
 }
 function render(Frame $frame)
 {
     $style = $frame->get_style();
     if (!$frame->get_first_child()) {
         return;
     }
     // No children, no service
     // Draw the left border if applicable
     $bp = $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"]));
     // Draw the background & border behind each child.  To do this we need
     // to figure out just how much space each child takes:
     list($x, $y) = $frame->get_first_child()->get_position();
     $w = null;
     $h = 0;
     //     $x += $widths[3];
     //     $y += $widths[0];
     $this->_set_opacity($frame->get_opacity($style->opacity));
     $first_row = true;
     foreach ($frame->get_children() as $child) {
         list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
         if (!is_null($w) && $child_x < $x + $w) {
             //This branch seems to be supposed to being called on the first part
             //of an inline html element, and the part after the if clause for the
             //parts after a line break.
             //But because $w initially mostly is 0, and gets updated only on the next
             //round, this seem to be never executed and the common close always.
             // The next child is on another line.  Draw the background &
             // borders on this line.
             // Background:
             if (($bg = $style->background_color) !== "transparent") {
                 $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
             }
             if (($url = $style->background_image) && $url !== "none") {
                 $this->_background_image($url, $x, $y, $w, $h, $style);
             }
             // If this is the first row, draw the left border
             if ($first_row) {
                 if ($bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0) {
                     $method = "_border_" . $bp["left"]["style"];
                     $this->{$method}($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
                 }
                 $first_row = false;
             }
             // Draw the top & bottom borders
             if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0) {
                 $method = "_border_" . $bp["top"]["style"];
                 $this->{$method}($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
             }
             if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0) {
                 $method = "_border_" . $bp["bottom"]["style"];
                 $this->{$method}($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
             }
             // Handle anchors & links
             $link_node = null;
             if ($frame->get_node()->nodeName === "a") {
                 $link_node = $frame->get_node();
             } else {
                 if ($frame->get_parent()->get_node()->nodeName === "a") {
                     $link_node = $frame->get_parent()->get_node();
                 }
             }
             if ($link_node && ($href = $link_node->getAttribute("href"))) {
                 $this->_canvas->add_link($href, $x, $y, $w, $h);
             }
             $x = $child_x;
             $y = $child_y;
             $w = $child_w;
             $h = $child_h;
             continue;
         }
         if (is_null($w)) {
             $w = $child_w;
         } else {
             $w += $child_w;
         }
         $h = max($h, $child_h);
         if (DEBUG_LAYOUT && DEBUG_LAYOUT_INLINE) {
             $this->_debug_layout($child->get_border_box(), "blue");
             if (DEBUG_LAYOUT_PADDINGBOX) {
                 $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5));
             }
         }
     }
     // Handle the last child
     if (($bg = $style->background_color) !== "transparent") {
         $this->_canvas->filled_rectangle($x + $widths[3], $y + $widths[0], $w, $h, $bg);
     }
     //On continuation lines (after line break) of inline elements, the style got copied.
     //But a non repeatable background image should not be repeated on the next line.
     //But removing the background image above has never an effect, and removing it below
     //removes it always, even on the initial line.
     //Need to handle it elsewhere, e.g. on certain ...clone()... usages.
     // Repeat not given: default is Style::__construct
     // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ...
     //different position? $this->_background_image($url, $x, $y, $w, $h, $style);
     if (($url = $style->background_image) && $url !== "none") {
         $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
     }
     // Add the border widths
     $w += $widths[1] + $widths[3];
     $h += $widths[0] + $widths[2];
     // make sure the border and background start inside the left margin
     $left_margin = $style->length_in_pt($style->margin_left);
     $x += $left_margin;
     // If this is the first row, draw the left border too
     if ($first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0) {
         $method = "_border_" . $bp["left"]["style"];
         $this->{$method}($x, $y, $h, $bp["left"]["color"], $widths, "left");
     }
     // Draw the top & bottom borders
     if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0) {
         $method = "_border_" . $bp["top"]["style"];
         $this->{$method}($x, $y, $w, $bp["top"]["color"], $widths, "top");
     }
     if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0) {
         $method = "_border_" . $bp["bottom"]["style"];
         $this->{$method}($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
     }
     //    pre_var_dump(get_class($frame->get_next_sibling()));
     //    $last_row = get_class($frame->get_next_sibling()) !== 'Inline_Frame_Decorator';
     // Draw the right border if this is the last row
     if ($bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0) {
         $method = "_border_" . $bp["right"]["style"];
         $this->{$method}($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
     }
     // Only two levels of links frames
     $link_node = null;
     if ($frame->get_node()->nodeName === "a") {
         $link_node = $frame->get_node();
         if (($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id"))) {
             $this->_canvas->add_named_dest($name);
         }
     }
     if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") {
         $link_node = $frame->get_parent()->get_node();
     }
     // Handle anchors & links
     if ($link_node) {
         if ($href = $link_node->getAttribute("href")) {
             $this->_canvas->add_link($href, $x, $y, $w, $h);
         }
     }
 }
Example #22
0
 function render(Frame $frame)
 {
     $style = $frame->get_style();
     if (!$frame->get_first_child()) {
         return;
     }
     // No children, no service
     // Draw the left border if applicable
     $bp = $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"]));
     // Draw the background & border behind each child.  To do this we need
     // to figure out just how much space each child takes:
     list($x, $y) = $frame->get_first_child()->get_position();
     $w = null;
     $h = 0;
     //     $x += $widths[3];
     //     $y += $widths[0];
     $first_row = true;
     foreach ($frame->get_children() as $child) {
         list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
         $child_h += $widths[2];
         if (!is_null($w) && $child_x < $x + $w) {
             // The next child is on another line.  Draw the background &
             // borders on this line.
             // Background:
             if (($bg = $style->background_color) !== "transparent") {
                 $this->_canvas->filled_rectangle($x, $y, $w, $h, $style->background_color);
             }
             if (($url = $style->background_image) && $url !== "none") {
                 $this->_background_image($url, $x, $y, $w, $h, $style);
             }
             // If this is the first row, draw the left border
             if ($first_row) {
                 if ($bp["left"]["style"] != "none" && $bp["left"]["width"] > 0) {
                     $method = "_border_" . $bp["left"]["style"];
                     $this->{$method}($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
                 }
                 $first_row = false;
             }
             // Draw the top & bottom borders
             if ($bp["top"]["style"] != "none" && $bp["top"]["width"] > 0) {
                 $method = "_border_" . $bp["top"]["style"];
                 $this->{$method}($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
             }
             if ($bp["bottom"]["style"] != "none" && $bp["bottom"]["width"] > 0) {
                 $method = "_border_" . $bp["bottom"]["style"];
                 $this->{$method}($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
             }
             // Handle anchors & links
             if ($frame->get_node()->nodeName == "a") {
                 if ($href = $frame->get_node()->getAttribute("href")) {
                     $this->_canvas->add_link($href, $x, $y, $w, $h);
                 }
             }
             $x = $child_x;
             $y = $child_y;
             $w = $child_w;
             $h = $child_h;
             continue;
         }
         if (is_null($w)) {
             $w = $child_w;
         } else {
             $w += $child_w;
         }
         $h = max($h, $child_h);
     }
     // Handle the last child
     if (($bg = $style->background_color) !== "transparent") {
         $this->_canvas->filled_rectangle($x + $widths[3], $y + $widths[0], $w, $h, $style->background_color);
     }
     if (($url = $style->background_image) && $url !== "none") {
         $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
     }
     // Add the border widths
     $w += $widths[1] + $widths[3];
     $h += $widths[0] + $widths[2];
     // If this is the first row, draw the left border too
     if ($first_row && $bp["left"]["style"] != "none" && $widths[3] > 0) {
         $method = "_border_" . $bp["left"]["style"];
         $this->{$method}($x, $y, $h, $bp["left"]["color"], $widths, "left");
     }
     // Draw the top & bottom borders
     if ($bp["top"]["style"] != "none" && $widths[0] > 0) {
         $method = "_border_" . $bp["top"]["style"];
         $this->{$method}($x, $y, $w, $bp["top"]["color"], $widths, "top");
     }
     if ($bp["bottom"]["style"] != "none" && $widths[2] > 0) {
         $method = "_border_" . $bp["bottom"]["style"];
         $this->{$method}($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
     }
     // Draw the right border
     if ($bp["right"]["style"] != "none" && $widths[1] > 0) {
         $method = "_border_" . $bp["right"]["style"];
         $this->{$method}($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
     }
     // Handle anchors & links
     if ($frame->get_node()->nodeName == "a") {
         if ($name = $frame->get_node()->getAttribute("name")) {
             $this->_canvas->add_named_dest($name);
         }
         if ($href = $frame->get_node()->getAttribute("href")) {
             $this->_canvas->add_link($href, $x, $y, $w, $h);
         }
     }
 }
 /**
  * split this frame at $child.
  * The current frame is cloned and $child and all children following
  * $child are added to the clone.  The clone is then passed to the
  * current frame's parent->split() method.
  *
  * @param Frame   $child
  * @param boolean $force_pagebreak
  *
  * @throws DOMPDF_Exception
  * @return void
  */
 function split(Frame $child = null, $force_pagebreak = false)
 {
     // decrement any counters that were incremented on the current node, unless that node is the body
     $style = $this->_frame->get_style();
     if ($this->_frame->get_node()->nodeName !== "body" && $style->counter_increment && ($decrement = $style->counter_increment) !== "none") {
         $this->decrement_counters($decrement);
     }
     if (is_null($child)) {
         // check for counter increment on :before content (always a child of the selected element @link Frame_Reflower::_set_content)
         // this can push the current node to the next page before counter rules have bubbled up (but only if
         // it's been rendered, thus the position check)
         if (!$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id")) {
             foreach ($this->_frame->get_children() as $child) {
                 if ($this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() && $child->get_position('x') !== NULL) {
                     $style = $child->get_style();
                     if ($style->counter_increment && ($decrement = $style->counter_increment) !== "none") {
                         $this->decrement_counters($decrement);
                     }
                 }
             }
         }
         $this->get_parent()->split($this, $force_pagebreak);
         return;
     }
     if ($child->get_parent() !== $this) {
         throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
     }
     $node = $this->_frame->get_node();
     $split = $this->copy($node->cloneNode());
     $split->reset();
     $split->get_original_style()->text_indent = 0;
     $split->_splitted = true;
     // The body's properties must be kept
     if ($node->nodeName !== "body") {
         // Style reset on the first and second parts
         $style = $this->_frame->get_style();
         $style->margin_bottom = 0;
         $style->padding_bottom = 0;
         $style->border_bottom = 0;
         // second
         $orig_style = $split->get_original_style();
         $orig_style->text_indent = 0;
         $orig_style->margin_top = 0;
         $orig_style->padding_top = 0;
         $orig_style->border_top = 0;
     }
     $this->get_parent()->insert_child_after($split, $this);
     // Add $frame and all following siblings to the new split node
     $iter = $child;
     while ($iter) {
         $frame = $iter;
         $iter = $iter->get_next_sibling();
         $frame->reset();
         $split->append_child($frame);
     }
     $this->get_parent()->split($split, $force_pagebreak);
     // If this node resets a counter save the current value to use when rendering on the next page
     if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") {
         $vars = preg_split('/\\s+/', trim($reset), 2);
         $split->_counters['__' . $vars[0]] = $this->lookup_counter_frame($vars[0])->_counters[$vars[0]];
     }
 }
 /**
  * Render frames recursively
  *
  * @param Frame $frame the frame to render
  */
 private function _render(Frame $frame, $depth = 0)
 {
     //$sRet = '';
     $this->_level = $depth;
     $node = $frame->get_node();
     $properties = $frame->get_style();
     $this->countTags = array_count_values(self::$openTags);
     switch ($node->nodeName) {
         case 'p':
         case 'div':
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             if (self::$openPs) {
                 self::$WordML .= '</w:p><w:p>';
             } else {
                 self::$WordML .= '<w:p>';
                 self::$openPs = true;
             }
             self::$WordML .= $this->generatePPr($properties);
             self::$openTags[$depth] = $node->nodeName;
             break;
         case 'ul':
         case 'ol':
             self::$openTags[$depth] = $node->nodeName;
             break;
         case 'li':
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             if (self::$openPs) {
                 self::$WordML .= '</w:p><w:p>';
             } else {
                 self::$WordML .= '<w:p>';
                 self::$openPs = true;
             }
             self::$WordML .= $this->generateListPr($properties);
             self::$openTags[$depth] = $node->nodeName;
             break;
         case 'table':
             self::$openTable = array();
             //TODO cambiar para tablas anidadas
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             if (self::$openPs) {
                 self::$WordML .= '</w:p><w:tbl>';
                 self::$openPs = false;
             } else {
                 self::$WordML .= '<w:tbl>';
             }
             self::$WordML .= $this->generateTblPr($properties);
             self::$openTags[$depth] = $node->nodeName;
             break;
         case 'tr':
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             array_push(self::$openTable, array());
             self::$WordML .= '<w:tr>';
             self::$WordML .= $this->generateTrPr($properties);
             self::$openTags[$depth] = $node->nodeName;
             break;
         case 'td':
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             $firstRow = false;
             //Now we have to deal with the details associated to the rowspan and colspan of this table cell
             $colspan = (int) $node->getAttribute('colspan');
             $colspan = empty($colspan) ? 1 : $colspan;
             $rowspan = (int) $node->getAttribute('rowspan');
             $rowspan = empty($rowspan) ? 1 : $rowspan;
             $row = count(self::$openTable) - 1;
             $column = count(self::$openTable[$row]);
             $vmerge = $this->countEmptyColumns($row, $column);
             for ($k = 0; $k < $colspan; $k++) {
                 array_push(self::$openTable[count(self::$openTable) - 1], $rowspan);
             }
             if ($vmerge > 0) {
                 self::$WordML .= '<w:tc><w:tcPr><w:gridSpan  w:val="' . $vmerge . '" /><w:vMerge w:val="continue" /></w:tcPr><w:p /></w:tc>';
             }
             self::$WordML .= '<w:tc>';
             self::$WordML .= $this->generateTcPr($properties, $colspan, $rowspan, $firstRow);
             //FIXME
             self::$openTags[$depth] = $node->nodeName;
             break;
         case 'th':
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             $firstRow = true;
             //Now we have to deal with the details associated to the rowspan and colspan of this table cell
             $colspan = (int) $node->getAttribute('colspan');
             $colspan = empty($colspan) ? 1 : $colspan;
             $rowspan = (int) $node->getAttribute('rowspan');
             $rowspan = empty($rowspan) ? 1 : $rowspan;
             $row = count(self::$openTable) - 1;
             $column = count(self::$openTable[$row]);
             $vmerge = $this->countEmptyColumns($row, $column);
             for ($k = 0; $k < $colspan; $k++) {
                 array_push(self::$openTable[count(self::$openTable) - 1], $rowspan);
             }
             if ($vmerge > 0) {
                 self::$WordML .= '<w:tc><w:tcPr><w:gridSpan  w:val="' . $vmerge . '" /><w:vMerge w:val="continue" /></w:tcPr><w:p /></w:tc>';
             }
             self::$WordML .= '<w:tc>';
             self::$WordML .= $this->generateTcPr($properties, $colspan, $rowspan, $firstRow);
             //FIXME
             self::$openTags[$depth] = $node->nodeName;
             break;
         case '#text':
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             if (self::$openPs) {
                 self::$WordML .= '<w:r>';
             } else {
                 self::$WordML .= '<w:p>';
                 self::$WordML .= $this->generatePPr($properties);
                 self::$WordML .= '<w:r>';
                 self::$openPs = true;
             }
             self::$WordML .= $this->generateRPr($properties);
             self::$WordML .= '<w:t>' . htmlspecialchars($node->nodeValue);
             self::$WordML .= '</w:t></w:r>';
             self::$openTags[$depth] = $node->nodeName;
             break;
         case 'br':
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             if (self::$openPs) {
                 self::$WordML .= '<w:r><w:br /><w:t></w:t></w:r>';
             } else {
                 self::$WordML .= '<w:p />';
             }
             break;
         case 'close':
             //if(strpos(self::$WordML, '#<w:gridCol/>#') !== false) self::$WordML = str_replace('#<w:gridCol/>#', '<w:gridCol/>', self::$WordML); //TODO forzar limpieza de columnas de tabla?
             self::$WordML .= $this->closePreviousTags($depth, $node->nodeName);
             break;
         default:
             self::$openTags[$depth] = $node->nodeName;
             break;
     }
     ++$depth;
     foreach ($frame->get_children() as $child) {
         //var_dump(gettype($child));
         $this->_render($child, $depth);
     }
 }
 function add_frame_to_line(Frame $frame)
 {
     $style = $frame->get_style();
     if (in_array($style->position, array("absolute", "fixed")) || DOMPDF_ENABLE_CSS_FLOAT && $style->float !== "none") {
         return;
     }
     $frame->set_containing_line($this->_lines[$this->_cl]);
     /*
         // Adds a new line after a block, only if certain conditions are met
         if ((($frame instanceof Inline_Frame_Decorator && $frame->get_node()->nodeName !== "br") || 
               $frame instanceof Text_Frame_Decorator && trim($frame->get_text())) && 
             ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" && 
              $this->_lines[$this->_cl]["w"] > 0 )) {
                
                $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
                $this->add_line();
              
                // Add each child of the inline frame to the line individually
                foreach ($frame->get_children() as $child)
                  $this->add_frame_to_line( $child );     
         }
         else*/
     // Handle inline frames (which are effectively wrappers)
     if ($frame instanceof Inline_Frame_Decorator) {
         // Handle line breaks
         if ($frame->get_node()->nodeName === "br") {
             $this->maximize_line_height($style->length_in_pt($style->line_height), $frame);
             $this->add_line(true);
         }
         return;
     }
     // Trim leading text if this is an empty line.  Kinda a hack to put it here,
     // but what can you do...
     if ($this->_lines[$this->_cl]["w"] == 0 && $frame->get_node()->nodeName === "#text" && ($style->white_space !== "pre" || $style->white_space !== "pre-wrap")) {
         $frame->set_text(ltrim($frame->get_text()));
         $frame->recalculate_width();
     }
     $w = $frame->get_margin_width();
     if ($w == 0) {
         return;
     }
     // Debugging code:
     /*
     pre_r("\n<h3>Adding frame to line:</h3>");
     
     //    pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");
     //    pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
     if ( $frame->get_node()->nodeName === "#text" )
       pre_r('"'.$frame->get_node()->nodeValue.'"');
     
     pre_r("Line width: " . $this->_lines[$this->_cl]["w"]);
     pre_r("Frame: " . get_class($frame));
     pre_r("Frame width: "  . $w);
     pre_r("Frame height: " . $frame->get_margin_height());
     pre_r("Containing block width: " . $this->get_containing_block("w"));
     */
     // End debugging
     $line = $this->_lines[$this->_cl];
     if ($line["left"] + $line["w"] + $line["right"] + $w > $this->get_containing_block("w")) {
         $this->add_line();
     }
     $frame->position();
     $current_line =& $this->_lines[$this->_cl];
     $current_line["frames"][] = $frame;
     if ($frame->get_node()->nodeName === "#text") {
         $current_line["wc"] += count(preg_split("/\\s+/", trim($frame->get_text())));
     }
     $this->increase_line_width($w);
     $this->maximize_line_height($frame->get_margin_height(), $frame);
 }
 function render(Frame $frame)
 {
     $this->evaluate($frame->get_node()->nodeValue);
 }
 static function decorate_frame(Frame $frame, $dompdf)
 {
     if (is_null($dompdf)) {
         throw new Exception("foo");
     }
     switch ($frame->get_style()->display) {
         case "block":
             $positioner = "Block";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "inline-block":
             $positioner = "Inline";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "inline":
             $positioner = "Inline";
             if ($frame->get_node()->nodeName == "#text") {
                 $decorator = "Text";
                 $reflower = "Text";
             } else {
                 $decorator = "Inline";
                 $reflower = "Inline";
             }
             break;
         case "table":
             $positioner = "Block";
             $decorator = "Table";
             $reflower = "Table";
             break;
         case "inline-table":
             $positioner = "Inline";
             $decorator = "Table";
             $reflower = "Table";
             break;
         case "table-row-group":
         case "table-header-group":
         case "table-footer-group":
             $positioner = "Null";
             $decorator = "Table_Row_Group";
             $reflower = "Table_Row_Group";
             break;
         case "table-row":
             $positioner = "Null";
             $decorator = "Table_Row";
             $reflower = "Table_Row";
             break;
         case "table-cell":
             $positioner = "Table_Cell";
             $decorator = "Table_Cell";
             $reflower = "Table_Cell";
             break;
         case "list-item":
             $positioner = "Block";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "-dompdf-list-bullet":
             if ($frame->get_style()->list_style_position == "inside") {
                 $positioner = "Inline";
             } else {
                 $positioner = "List_Bullet";
             }
             if ($frame->get_style()->list_style_image != "none") {
                 $decorator = "List_Bullet_Image";
             } else {
                 $decorator = "List_Bullet";
             }
             $reflower = "List_Bullet";
             break;
         case "-dompdf-image":
             $positioner = "Inline";
             $decorator = "Image";
             $reflower = "Image";
             break;
         case "-dompdf-br":
             $positioner = "Inline";
             $decorator = "Inline";
             $reflower = "Inline";
             break;
         default:
             // FIXME: should throw some sort of warning or something?
         // FIXME: should throw some sort of warning or something?
         case "none":
             $positioner = "Null";
             $decorator = "Null";
             $reflower = "Null";
             break;
     }
     $positioner .= "_Positioner";
     $decorator .= "_Frame_Decorator";
     $reflower .= "_Frame_Reflower";
     $deco = new $decorator($frame, $dompdf);
     $deco->set_positioner(new $positioner($deco));
     $reflow = new $reflower($deco);
     // Generated content is a special case
     if ($frame->get_node()->nodeName == "_dompdf_generated") {
         // Decorate the reflower
         $gen = new Generated_Frame_Reflower($deco);
         $gen->set_reflower($reflow);
         $reflow = $gen;
     }
     $deco->set_reflower($reflow);
     // Images are a special case
     //    if ( $frame->get_node()->nodeName == "img" ) {
     //       // FIXME: This is a hack
     //       $node =$frame->get_node()->ownerDocument->createElement("img_sub");
     //       $node->setAttribute("src", $frame->get_node()->getAttribute("src"));
     //       $img_frame = new Frame( $node );
     //       $style = $frame->get_style()->get_stylesheet()->create_style();
     //       $style->inherit($frame->get_style());
     //       $img_frame->set_style( $style );
     //       $img_deco = new Image_Frame_Decorator($img_frame, $dompdf);
     //       $img_deco->set_reflower( new Image_Frame_Reflower($img_deco) );
     //       $deco->append_child($img_deco);
     //     }
     return $deco;
 }
 /**
  * Decorate a Frame
  *
  * @param Frame  $frame  The frame to decorate
  * @param DOMPDF $dompdf The dompdf instance
  * @param Frame  $root   The frame to decorate
  *
  * @throws DOMPDF_Exception
  * @return Frame_Decorator
  * FIXME: this is admittedly a little smelly...
  */
 static function decorate_frame(Frame $frame, DOMPDF $dompdf, Frame $root = null)
 {
     if (is_null($dompdf)) {
         throw new DOMPDF_Exception("The DOMPDF argument is required");
     }
     $style = $frame->get_style();
     // Floating (and more generally out-of-flow) elements are blocks
     // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/
     if (!$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) {
         $style->display = "block";
     }
     $display = $style->display;
     switch ($display) {
         case "block":
             $positioner = "Block";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "inline-block":
             $positioner = "Inline";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "inline":
             $positioner = "Inline";
             if ($frame->is_text_node()) {
                 $decorator = "Text";
                 $reflower = "Text";
             } else {
                 $enable_css_float = $dompdf->get_option("enable_css_float");
                 if ($enable_css_float && $style->float !== "none") {
                     $decorator = "Block";
                     $reflower = "Block";
                 } else {
                     $decorator = "Inline";
                     $reflower = "Inline";
                 }
             }
             break;
         case "table":
             $positioner = "Block";
             $decorator = "Table";
             $reflower = "Table";
             break;
         case "inline-table":
             $positioner = "Inline";
             $decorator = "Table";
             $reflower = "Table";
             break;
         case "table-row-group":
         case "table-header-group":
         case "table-footer-group":
             $positioner = "Null";
             $decorator = "Table_Row_Group";
             $reflower = "Table_Row_Group";
             break;
         case "table-row":
             $positioner = "Null";
             $decorator = "Table_Row";
             $reflower = "Table_Row";
             break;
         case "table-cell":
             $positioner = "Table_Cell";
             $decorator = "Table_Cell";
             $reflower = "Table_Cell";
             break;
         case "list-item":
             $positioner = "Block";
             $decorator = "Block";
             $reflower = "Block";
             break;
         case "-dompdf-list-bullet":
             if ($style->list_style_position === "inside") {
                 $positioner = "Inline";
             } else {
                 $positioner = "List_Bullet";
             }
             if ($style->list_style_image !== "none") {
                 $decorator = "List_Bullet_Image";
             } else {
                 $decorator = "List_Bullet";
             }
             $reflower = "List_Bullet";
             break;
         case "-dompdf-image":
             $positioner = "Inline";
             $decorator = "Image";
             $reflower = "Image";
             break;
         case "-dompdf-br":
             $positioner = "Inline";
             $decorator = "Inline";
             $reflower = "Inline";
             break;
         default:
             // FIXME: should throw some sort of warning or something?
         // FIXME: should throw some sort of warning or something?
         case "none":
             $positioner = "Null";
             $decorator = "Null";
             $reflower = "Null";
             break;
     }
     // Handle CSS position
     $position = $style->position;
     if ($position === "absolute") {
         $positioner = "Absolute";
     } else {
         if ($position === "fixed") {
             $positioner = "Fixed";
         }
     }
     $node = $frame->get_node();
     // Handle nodeName
     if ($node->nodeName === "img") {
         $style->display = "-dompdf-image";
         $decorator = "Image";
         $reflower = "Image";
     }
     $positioner .= "_Positioner";
     $decorator .= "_Frame_Decorator";
     $reflower .= "_Frame_Reflower";
     $deco = new $decorator($frame, $dompdf);
     $deco->set_positioner(new $positioner($deco));
     $deco->set_reflower(new $reflower($deco));
     if ($root) {
         $deco->set_root($root);
     }
     if ($display === "list-item") {
         // Insert a list-bullet frame
         $xml = $dompdf->get_dom();
         $bullet_node = $xml->createElement("bullet");
         // arbitrary choice
         $b_f = new Frame($bullet_node);
         $node = $frame->get_node();
         $parent_node = $node->parentNode;
         if ($parent_node) {
             if (!$parent_node->hasAttribute("dompdf-children-count")) {
                 $xpath = new DOMXPath($xml);
                 $count = $xpath->query("li", $parent_node)->length;
                 $parent_node->setAttribute("dompdf-children-count", $count);
             }
             if (is_numeric($node->getAttribute("value"))) {
                 $index = intval($node->getAttribute("value"));
             } else {
                 if (!$parent_node->hasAttribute("dompdf-counter")) {
                     $index = $parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1;
                 } else {
                     $index = $parent_node->getAttribute("dompdf-counter") + 1;
                 }
             }
             $parent_node->setAttribute("dompdf-counter", $index);
             $bullet_node->setAttribute("dompdf-counter", $index);
         }
         $new_style = $dompdf->get_css()->create_style();
         $new_style->display = "-dompdf-list-bullet";
         $new_style->inherit($style);
         $b_f->set_style($new_style);
         $deco->prepend_child(Frame_Factory::decorate_frame($b_f, $dompdf, $root));
     }
     return $deco;
 }
 /**
  * Determine if a page break is allowed before $frame
  * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
  * 
  * In the normal flow, page breaks can occur at the following places:
  * 
  *    1. In the vertical margin between block boxes. When a page
  *    break occurs here, the used values of the relevant
  *    'margin-top' and 'margin-bottom' properties are set to '0'.
  *    2. Between line boxes inside a block box.
  *
  * These breaks are subject to the following rules:
  * 
  *   * Rule A: Breaking at (1) is allowed only if the
  *     'page-break-after' and 'page-break-before' properties of
  *     all the elements generating boxes that meet at this margin
  *     allow it, which is when at least one of them has the value
  *     'always', 'left', or 'right', or when all of them are
  *     'auto'.
  *
  *   * Rule B: However, if all of them are 'auto' and the
  *     nearest common ancestor of all the elements has a
  *     'page-break-inside' value of 'avoid', then breaking here is
  *     not allowed.
  *
  *   * Rule C: Breaking at (2) is allowed only if the number of
  *     line boxes between the break and the start of the enclosing
  *     block box is the value of 'orphans' or more, and the number
  *     of line boxes between the break and the end of the box is
  *     the value of 'widows' or more.
  *
  *   * Rule D: In addition, breaking at (2) is allowed only if
  *     the 'page-break-inside' property is 'auto'.
  *
  * If the above doesn't provide enough break points to keep
  * content from overflowing the page boxes, then rules B and D are
  * dropped in order to find additional breakpoints.
  *
  * If that still does not lead to sufficient break points, rules A
  * and C are dropped as well, to find still more break points.
  *
  * We will also allow breaks between table rows.  However, when
  * splitting a table, the table headers should carry over to the
  * next page (but they don't yet).
  * 
  * @param Frame $frame the frame to check
  * @return bool true if a break is allowed, false otherwise
  */
 protected function _page_break_allowed(Frame $frame)
 {
     $block_types = array("block", "list-item", "table", "-dompdf-image");
     dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName . ")");
     $display = $frame->get_style()->display;
     // Block Frames (1):
     if (in_array($display, $block_types)) {
         // Avoid breaks within table-cells
         if ($this->_in_table) {
             dompdf_debug("page-break", "In table: " . $this->_in_table);
             return false;
         }
         // Rules A & B
         if ($frame->get_style()->page_break_before === "avoid") {
             dompdf_debug("page-break", "before: avoid");
             return false;
         }
         // Find the preceeding block-level sibling
         $prev = $frame->get_prev_sibling();
         while ($prev && !in_array($prev->get_style()->display, $block_types)) {
             $prev = $prev->get_prev_sibling();
         }
         // Does the previous element allow a page break after?
         if ($prev && $prev->get_style()->page_break_after === "avoid") {
             dompdf_debug("page-break", "after: avoid");
             return false;
         }
         // If both $prev & $frame have the same parent, check the parent's
         // page_break_inside property.
         $parent = $frame->get_parent();
         if ($prev && $parent && $parent->get_style()->page_break_inside === "avoid") {
             dompdf_debug("page-break", "parent inside: avoid");
             return false;
         }
         // To prevent cascading page breaks when a top-level element has
         // page-break-inside: avoid, ensure that at least one frame is
         // on the page before splitting.
         if ($parent->get_node()->nodeName === "body" && !$prev) {
             // We are the body's first child
             dompdf_debug("page-break", "Body's first child.");
             return false;
         }
         // If the frame is the first block-level frame, use the value from
         // $frame's parent instead.
         if (!$prev && $parent) {
             return $this->_page_break_allowed($parent);
         }
         dompdf_debug("page-break", "block: break allowed");
         return true;
     } else {
         if (in_array($display, Style::$INLINE_TYPES)) {
             // Avoid breaks within table-cells
             if ($this->_in_table) {
                 dompdf_debug("page-break", "In table: " . $this->_in_table);
                 return false;
             }
             // Rule C
             $block_parent = $frame->find_block_parent();
             if (count($block_parent->get_line_boxes()) < $frame->get_style()->orphans) {
                 dompdf_debug("page-break", "orphans");
                 return false;
             }
             // FIXME: Checking widows is tricky without having laid out the
             // remaining line boxes.  Just ignore it for now...
             // Rule D
             $p = $block_parent;
             while ($p) {
                 if ($p->get_style()->page_break_inside === "avoid") {
                     dompdf_debug("page-break", "parent->inside: avoid");
                     return false;
                 }
                 $p = $p->find_block_parent();
             }
             // To prevent cascading page breaks when a top-level element has
             // page-break-inside: avoid, ensure that at least one frame with
             // some content is on the page before splitting.
             $prev = $frame->get_prev_sibling();
             while ($prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "")) {
                 $prev = $prev->get_prev_sibling();
             }
             if ($block_parent->get_node()->nodeName === "body" && !$prev) {
                 // We are the body's first child
                 dompdf_debug("page-break", "Body's first child.");
                 return false;
             }
             // Skip breaks on empty text nodes
             if ($frame->is_text_node() && $frame->get_node()->nodeValue == "") {
                 return false;
             }
             dompdf_debug("page-break", "inline: break allowed");
             return true;
             // Table-rows
         } else {
             if ($display === "table-row") {
                 // Simply check if the parent table's page_break_inside property is
                 // not 'avoid'
                 $p = Table_Frame_Decorator::find_parent_table($frame);
                 while ($p) {
                     if ($p->get_style()->page_break_inside === "avoid") {
                         dompdf_debug("page-break", "parent->inside: avoid");
                         return false;
                     }
                     $p = $p->find_block_parent();
                 }
                 // Avoid breaking after the first row of a table
                 if ($p && $p->get_first_child() === $frame) {
                     dompdf_debug("page-break", "table: first-row");
                     return false;
                 }
                 // If this is a nested table, prevent the page from breaking
                 if ($this->_in_table > 1) {
                     dompdf_debug("page-break", "table: nested table");
                     return false;
                 }
                 dompdf_debug("page-break", "table-row/row-groups: break allowed");
                 return true;
             } else {
                 if (in_array($display, Table_Frame_Decorator::$ROW_GROUPS)) {
                     // Disallow breaks at row-groups: only split at row boundaries
                     return false;
                 } else {
                     dompdf_debug("page-break", "? " . $frame->get_style()->display . "");
                     return false;
                 }
             }
         }
     }
 }
Example #30
0
 function add_frame(Frame $frame)
 {
     $style = $frame->get_style();
     $display = $style->display;
     $collapse = $this->_table->get_style()->border_collapse == "collapse";
     // Recursively add the frames within tables, table-row-groups and table-rows
     if ($display == "table-row" || $display == "table" || $display == "inline-table" || in_array($display, Table_Frame_Decorator::$ROW_GROUPS)) {
         $start_row = $this->__row;
         foreach ($frame->get_children() as $child) {
             $this->add_frame($child);
         }
         if ($display == "table-row") {
             $this->add_row();
         }
         $num_rows = $this->__row - $start_row - 1;
         $key = $frame->get_id();
         // Row groups always span across the entire table
         $this->_frames[$key]["columns"] = range(0, max(0, $this->_num_cols - 1));
         $this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1));
         $this->_frames[$key]["frame"] = $frame;
         if ($display != "table-row" && $collapse) {
             $bp = $style->get_border_properties();
             // Resolve the borders
             for ($i = 0; $i < $num_rows + 1; $i++) {
                 $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
                 $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
             }
             for ($j = 0; $j < $this->_num_cols; $j++) {
                 $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
                 $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
             }
         }
         return;
     }
     // Determine where this cell is going
     $colspan = $frame->get_node()->getAttribute("colspan");
     $rowspan = $frame->get_node()->getAttribute("rowspan");
     if (!$colspan) {
         $colspan = 1;
         $frame->get_node()->setAttribute("colspan", 1);
     }
     if (!$rowspan) {
         $rowspan = 1;
         $frame->get_node()->setAttribute("rowspan", 1);
     }
     $key = $frame->get_id();
     $bp = $style->get_border_properties();
     // Add the frame to the cellmap
     $max_left = $max_right = 0;
     // Find the next available column (fix by Ciro Mondueri)
     $ac = $this->__col;
     while (isset($this->_cells[$this->__row][$ac])) {
         $ac++;
     }
     $this->__col = $ac;
     // Rows:
     for ($i = 0; $i < $rowspan; $i++) {
         $row = $this->__row + $i;
         $this->_frames[$key]["rows"][] = $row;
         for ($j = 0; $j < $colspan; $j++) {
             $this->_cells[$row][$this->__col + $j] = $frame;
         }
         if ($collapse) {
             // Resolve vertical borders
             $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
             $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
         }
     }
     $max_top = $max_bottom = 0;
     // Columns:
     for ($j = 0; $j < $colspan; $j++) {
         $col = $this->__col + $j;
         $this->_frames[$key]["columns"][] = $col;
         if ($collapse) {
             // Resolve horizontal borders
             $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
             $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
         }
     }
     $this->_frames[$key]["frame"] = $frame;
     // Handle seperated border model
     if (!$collapse) {
         list($h, $v) = $this->_table->get_style()->border_spacing;
         // Border spacing is effectively a margin between cells
         $v = $style->length_in_pt($v) / 2;
         $h = $style->length_in_pt($h) / 2;
         $style->margin = "{$v} {$h}";
         // The additional 1/2 width gets added to the table proper
     } else {
         // Drop the frame's actual border
         $style->border_left_width = $max_left / 2;
         $style->border_right_width = $max_right / 2;
         $style->border_top_width = $max_top / 2;
         $style->border_bottom_width = $max_bottom / 2;
         $style->margin = "none";
     }
     // Resolve the frame's width
     list($frame_min, $frame_max) = $frame->get_min_max_width();
     $width = $style->width;
     if (is_percent($width)) {
         $var = "percent";
         $val = (double) rtrim($width, "% ") / $colspan;
     } else {
         if ($width !== "auto") {
             $var = "absolute";
             $val = $style->length_in_pt($frame_min) / $colspan;
         }
     }
     $min = 0;
     $max = 0;
     for ($cs = 0; $cs < $colspan; $cs++) {
         // Resolve the frame's width(s) with other cells
         $col =& $this->get_column($this->__col + $cs);
         // Note: $var is either 'percent' or 'absolute'.  We compare the
         // requested percentage or absolute values with the existing widths
         // and adjust accordingly.
         if (isset($var) && $val > $col[$var]) {
             $col[$var] = $val;
             $col["auto"] = false;
         }
         $min += $col["min-width"];
         $max += $col["max-width"];
     }
     if ($frame_min > $min) {
         // The frame needs more space.  Expand each sub-column
         $inc = ($frame_min - $min) / $colspan;
         for ($c = 0; $c < $colspan; $c++) {
             $col =& $this->get_column($this->__col + $c);
             $col["min-width"] += $inc;
         }
     }
     if ($frame_max > $max) {
         $inc = ($frame_max - $max) / $colspan;
         for ($c = 0; $c < $colspan; $c++) {
             $col =& $this->get_column($this->__col + $c);
             $col["max-width"] += $inc;
         }
     }
     $this->__col += $colspan;
     if ($this->__col > $this->_num_cols) {
         $this->_num_cols = $this->__col;
     }
 }