Example #1
0
 /**
  * @return Frame_Decorator
  */
 function get_parent()
 {
     $p = $this->_frame->get_parent();
     if ($p && $deco = $p->get_decorator()) {
         while ($tmp = $deco->get_decorator())
             $deco = $tmp;
         return $deco;
     } else if ($p)
         return $p;
     else
         return null;
 }
Example #2
0
 function render(Frame $frame)
 {
     // Render background & borders
     //parent::render($frame);
     $p = $frame->get_parent();
     $style = $frame->get_style();
     $cb = $frame->get_containing_block();
     list($x, $y) = $frame->get_padding_box();
     $x += $style->length_in_pt($style->padding_left, $cb["w"]);
     $y += $style->length_in_pt($style->padding_top, $cb["h"]);
     $w = $style->length_in_pt($style->width, $cb["w"]);
     $h = $style->length_in_pt($style->height, $cb["h"]);
     $this->_canvas->image($frame->get_image_url(), $frame->get_image_ext(), $x, $y, $w, $h);
 }
 /**
  * @return Frame_Decorator
  */
 function get_parent()
 {
     $p = $this->_frame->get_parent();
     if ($p && ($deco = $p->get_decorator())) {
         while ($tmp = $deco->get_decorator()) {
             $deco = $tmp;
         }
         return $deco;
     } else {
         if ($p) {
             return $p;
         }
     }
     return null;
 }
 /**
  * Check if $frame will fit on the page.  If the frame does not fit,
  * the frame tree is modified so that a page break occurs in the
  * correct location.
  *
  * @param Frame $frame the frame to check
  * @return Frame the frame following the page break
  */
 function check_page_break(Frame $frame)
 {
     // Do not split if we have already or if the frame was already
     // pushed to the next page (prevents infinite loops)
     if ($this->_page_full || $frame->_already_pushed) {
         return false;
     }
     // If the frame is absolute of fixed it shouldn't break
     $p = $frame;
     do {
         if ($p->is_absolute()) {
             return false;
         }
     } while ($p = $p->get_parent());
     $margin_height = $frame->get_margin_height();
     // FIXME If the row is taller than the page and
     // if it the first of the page, we don't break
     if ($frame->get_style()->display === "table-row" && !$frame->get_prev_sibling() && $margin_height > $this->get_margin_height()) {
         return false;
     }
     // Determine the frame's maximum y value
     $max_y = $frame->get_position("y") + $margin_height;
     // If a split is to occur here, then the bottom margins & paddings of all
     // parents of $frame must fit on the page as well:
     $p = $frame->get_parent();
     while ($p) {
         $style = $p->get_style();
         $max_y += $style->length_in_pt(array($style->margin_bottom, $style->padding_bottom, $style->border_bottom_width));
         $p = $p->get_parent();
     }
     // Check if $frame flows off the page
     if ($max_y <= $this->_bottom_page_margin) {
         // no: do nothing
         return false;
     }
     dompdf_debug("page-break", "check_page_break");
     dompdf_debug("page-break", "in_table: " . $this->_in_table);
     // yes: determine page break location
     $iter = $frame;
     $flg = false;
     $in_table = $this->_in_table;
     dompdf_debug("page-break", "Starting search");
     while ($iter) {
         //       echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). "";
         if ($iter === $this) {
             dompdf_debug("page-break", "reached root.");
             // We've reached the root in our search.  Just split at $frame.
             break;
         }
         if ($this->_page_break_allowed($iter)) {
             dompdf_debug("page-break", "break allowed, splitting.");
             $iter->split(null, true);
             $this->_page_full = true;
             $this->_in_table = $in_table;
             $frame->_already_pushed = true;
             return true;
         }
         if (!$flg && ($next = $iter->get_last_child())) {
             dompdf_debug("page-break", "following last child.");
             if ($next->is_table()) {
                 $this->_in_table++;
             }
             $iter = $next;
             continue;
         }
         if ($next = $iter->get_prev_sibling()) {
             dompdf_debug("page-break", "following prev sibling.");
             if ($next->is_table() && !$iter->is_table()) {
                 $this->_in_table++;
             } else {
                 if (!$next->is_table() && $iter->is_table()) {
                     $this->_in_table--;
                 }
             }
             $iter = $next;
             $flg = false;
             continue;
         }
         if ($next = $iter->get_parent()) {
             dompdf_debug("page-break", "following parent.");
             if ($iter->is_table()) {
                 $this->_in_table--;
             }
             $iter = $next;
             $flg = true;
             continue;
         }
         break;
     }
     $this->_in_table = $in_table;
     // No valid page break found.  Just break at $frame.
     dompdf_debug("page-break", "no valid break found, just splitting.");
     // If we are in a table, backtrack to the nearest top-level table row
     if ($this->_in_table) {
         $num_tables = $this->_in_table - 1;
         $iter = $frame;
         while ($iter && $num_tables && $iter->get_style()->display !== "table") {
             $iter = $iter->get_parent();
             $num_tables--;
         }
         $iter = $frame;
         while ($iter && $iter->get_style()->display !== "table-row") {
             $iter = $iter->get_parent();
         }
     }
     $frame->split(null, true);
     $this->_page_full = true;
     $frame->_already_pushed = true;
     return true;
 }
 /**
  * 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;
                 }
             }
         }
     }
 }
 /**
  * Parses the CSS "content" property
  * 
  * @return string The resulting string
  */
 protected function _parse_content()
 {
     // Matches generated content
     $re = "/\n" . "\\s(counters?\\([^)]*\\))|\n" . "\\A(counters?\\([^)]*\\))|\n" . "\\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\3|\n" . "\\A([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\5|\n" . "\\s([^\\s\"']+)|\n" . "\\A([^\\s\"']+)\n" . "/xi";
     $content = $this->_frame->get_style()->content;
     $quotes = $this->_parse_quotes();
     // split on spaces, except within quotes
     if (!preg_match_all($re, $content, $matches, PREG_SET_ORDER)) {
         return;
     }
     $text = "";
     foreach ($matches as $match) {
         if (isset($match[2]) && $match[2] !== "") {
             $match[1] = $match[2];
         }
         if (isset($match[6]) && $match[6] !== "") {
             $match[4] = $match[6];
         }
         if (isset($match[8]) && $match[8] !== "") {
             $match[7] = $match[8];
         }
         if (isset($match[1]) && $match[1] !== "") {
             // counters?(...)
             $match[1] = mb_strtolower(trim($match[1]));
             // Handle counter() references:
             // http://www.w3.org/TR/CSS21/generate.html#content
             $i = mb_strpos($match[1], ")");
             if ($i === false) {
                 continue;
             }
             $args = explode(",", mb_substr($match[1], 8, $i - 8));
             $counter_id = $args[0];
             if ($match[1][7] === "(") {
                 // counter(name [,style])
                 if (isset($args[1])) {
                     $type = trim($args[1]);
                 } else {
                     $type = null;
                 }
                 $p = $this->_frame->find_block_parent();
                 $text .= $p->counter_value($counter_id, $type);
             } else {
                 if ($match[1][7] === "s") {
                     // counters(name, string [,style])
                     if (isset($args[1])) {
                         $string = $this->_parse_string(trim($args[1]));
                     } else {
                         $string = "";
                     }
                     if (isset($args[2])) {
                         $type = $args[2];
                     } else {
                         $type = null;
                     }
                     $p = $this->_frame->find_block_parent();
                     $tmp = "";
                     while ($p) {
                         $tmp = $p->counter_value($counter_id, $type) . $string . $tmp;
                         $p = $p->find_block_parent();
                     }
                     $text .= $tmp;
                 } else {
                     // countertops?
                     continue;
                 }
             }
         } else {
             if (isset($match[4]) && $match[4] !== "") {
                 // String match
                 $text .= $this->_parse_string($match[4]);
             } else {
                 if (isset($match[7]) && $match[7] !== "") {
                     // Directive match
                     if ($match[7] === "open-quote") {
                         // FIXME: do something here
                         $text .= $quotes[0][0];
                     } else {
                         if ($match[7] === "close-quote") {
                             // FIXME: do something else here
                             $text .= $quotes[0][1];
                         } else {
                             if ($match[7] === "no-open-quote") {
                                 // FIXME:
                             } else {
                                 if ($match[7] === "no-close-quote") {
                                     // FIXME:
                                 } else {
                                     if (mb_strpos($match[7], "attr(") === 0) {
                                         $i = mb_strpos($match[7], ")");
                                         if ($i === false) {
                                             continue;
                                         }
                                         $attr = mb_substr($match[7], 5, $i - 5);
                                         if ($attr == "") {
                                             continue;
                                         }
                                         $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
                                     } else {
                                         continue;
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     return $text;
 }
 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);
         }
     }
 }
 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;
         }
     }
 }
 /**
  * Check if $frame will fit on the page.  If the frame does not fit,
  * the frame tree is modified so that a page break occurs in the
  * correct location.
  *
  * @param Frame $frame the frame to check
  * @return Frame the frame following the page break
  */
 function check_page_break(Frame $frame)
 {
     // Do not split if we have already
     if ($this->_page_full) {
         return false;
     }
     // Determine the frame's maximum y value
     $max_y = $frame->get_position("y") + $frame->get_margin_height();
     // If a split is to occur here, then the bottom margins & paddings of all
     // parents of $frame must fit on the page as well:
     $p = $frame->get_parent();
     while ($p) {
         $style = $p->get_style();
         $max_y += $style->length_in_pt(array($style->margin_bottom, $style->padding_bottom, $style->border_bottom_width));
         $p = $p->get_parent();
     }
     // Check if $frame flows off the page
     if ($max_y <= $this->_bottom_page_margin) {
         // no: do nothing (?)
         return false;
     }
     //    echo "check_page_break\n";
     // yes: determine page break location
     $iter = $frame;
     $flg = false;
     $in_table = $this->_in_table;
     //     echo "Starting search\n";
     while ($iter) {
         //       echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".(string)$iter->get_node(). "\n";
         if ($iter === $this) {
             //         echo "reached root.\n";
             // We've reached the root in our search.  Just split at $frame.
             break;
         }
         if ($this->_page_break_allowed($iter)) {
             //        echo "break allowed, splitting.\n";
             $iter->split();
             $this->_page_full = true;
             $this->_in_table = $in_table;
             return true;
         }
         if (!$flg && ($next = $iter->get_last_child())) {
             //         echo "following last child.\n";
             if (in_array($next->get_style()->display, Style::$TABLE_TYPES)) {
                 $this->_in_table++;
             }
             $iter = $next;
             continue;
         }
         if ($next = $iter->get_prev_sibling()) {
             //         echo "following prev sibling.\n";
             if (in_array($next->get_style()->display, Style::$TABLE_TYPES) && !in_array($iter->get_style()->display, Style::$TABLE_TYPES)) {
                 $this->_in_table++;
             } else {
                 if (!in_array($next->get_style()->display, Style::$TABLE_TYPES) && in_array($iter->get_style()->display, Style::$TABLE_TYPES)) {
                     $this->_in_table--;
                 }
             }
             $iter = $next;
             $flg = false;
             continue;
         }
         if ($next = $iter->get_parent()) {
             //         echo "following parent.\n";
             if (in_array($iter->get_style()->display, Style::$TABLE_TYPES)) {
                 $this->_in_table--;
             }
             $iter = $next;
             $flg = true;
             continue;
         }
         break;
     }
     $this->_in_table = $in_table;
     // No valid page break found.  Just break at $frame.
     //     echo "no valid break found, just splitting.\n";
     // If we are in a table, backtrack to the nearest table row
     if ($this->_in_table) {
         $tr = $frame;
         while ($tr && $tr->get_style()->display != "table-row") {
             $tr = $tr->get_parent();
         }
         $tr->split();
         $this->_page_full = true;
         return true;
     }
     $frame->split();
     $this->_page_full = true;
     return true;
 }
 /**
  * Parses the CSS "content" property
  *
  * @return string|null The resulting string
  */
 protected function _parse_content()
 {
     // Matches generated content
     $re = "/\n" . "\\s(counters?\\([^)]*\\))|\n" . "\\A(counters?\\([^)]*\\))|\n" . "\\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\3|\n" . "\\A([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\5|\n" . "\\s([^\\s\"']+)|\n" . "\\A([^\\s\"']+)\n" . "/xi";
     $content = $this->_frame->get_style()->content;
     $quotes = $this->_parse_quotes();
     // split on spaces, except within quotes
     if (!preg_match_all($re, $content, $matches, PREG_SET_ORDER)) {
         return null;
     }
     $text = "";
     foreach ($matches as $match) {
         if (isset($match[2]) && $match[2] !== "") {
             $match[1] = $match[2];
         }
         if (isset($match[6]) && $match[6] !== "") {
             $match[4] = $match[6];
         }
         if (isset($match[8]) && $match[8] !== "") {
             $match[7] = $match[8];
         }
         if (isset($match[1]) && $match[1] !== "") {
             // counters?(...)
             $match[1] = mb_strtolower(trim($match[1]));
             // Handle counter() references:
             // http://www.w3.org/TR/CSS21/generate.html#content
             $i = mb_strpos($match[1], ")");
             if ($i === false) {
                 continue;
             }
             preg_match('/(counters?)(^\\()*?\\(\\s*([^\\s,]+)\\s*(,\\s*["\']?([^"\'\\)]+)["\']?\\s*(,\\s*([^\\s)]+)\\s*)?)?\\)/i', $match[1], $args);
             $counter_id = $args[3];
             if (strtolower($args[1]) == 'counter') {
                 // counter(name [,style])
                 if (isset($args[5])) {
                     $type = trim($args[5]);
                 } else {
                     $type = null;
                 }
                 $p = $this->_frame->lookup_counter_frame($counter_id);
                 $text .= $p->counter_value($counter_id, $type);
             } else {
                 if (strtolower($args[1]) == 'counters') {
                     // counters(name, string [,style])
                     if (isset($args[5])) {
                         $string = $this->_parse_string($args[5]);
                     } else {
                         $string = "";
                     }
                     if (isset($args[7])) {
                         $type = trim($args[7]);
                     } else {
                         $type = null;
                     }
                     $p = $this->_frame->lookup_counter_frame($counter_id);
                     $tmp = array();
                     while ($p) {
                         // We only want to use the counter values when they actually increment the counter
                         if (array_key_exists($counter_id, $p->_counters)) {
                             array_unshift($tmp, $p->counter_value($counter_id, $type));
                         }
                         $p = $p->lookup_counter_frame($counter_id);
                     }
                     $text .= implode($string, $tmp);
                 } else {
                     // countertops?
                     continue;
                 }
             }
         } else {
             if (isset($match[4]) && $match[4] !== "") {
                 // String match
                 $text .= $this->_parse_string($match[4]);
             } else {
                 if (isset($match[7]) && $match[7] !== "") {
                     // Directive match
                     if ($match[7] === "open-quote") {
                         // FIXME: do something here
                         $text .= $quotes[0][0];
                     } else {
                         if ($match[7] === "close-quote") {
                             // FIXME: do something else here
                             $text .= $quotes[0][1];
                         } else {
                             if ($match[7] === "no-open-quote") {
                                 // FIXME:
                             } else {
                                 if ($match[7] === "no-close-quote") {
                                     // FIXME:
                                 } else {
                                     if (mb_strpos($match[7], "attr(") === 0) {
                                         $i = mb_strpos($match[7], ")");
                                         if ($i === false) {
                                             continue;
                                         }
                                         $attr = mb_substr($match[7], 5, $i - 5);
                                         if ($attr == "") {
                                             continue;
                                         }
                                         $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
                                     } else {
                                         continue;
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     return $text;
 }
 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 ) );

    $li = $frame->get_parent();

    // Don't render bullets twice if if was split
    if ($li->_splitted) {
      return;
    }

    // Handle list-style-image
    // If list style image is requested but missing, fall back to predefined types
    if ( $style->list_style_image !== "none" &&
         !Image_Cache::is_broken($img = $frame->get_image_url())) {

      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);
      $dpi = $this->_dompdf->get_option("dpi");
      $w = ((float)rtrim($width, "px") * 72) / $dpi;
      $h = ((float)rtrim($height, "px") * 72) / $dpi;

      $x -= $w;
      $y -= ($line_height - $font_size)/2; //Reverse hinting of list_bullet_positioner

      $this->_canvas->image( $img, $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 /*-List_Bullet_Frame_Decorator::BULLET_THICKNESS*/ ))/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
      case "a":
      case "i":
      case "A":
      case "I":
        $pad = null;
        if ( $bullet_style === "decimal-leading-zero" ) {
          $pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count"));
        }

        $node = $frame->get_node();

        if ( !$node->hasAttribute("dompdf-counter") ) {
          return;
        }

        $index = $node->getAttribute("dompdf-counter");
        $text = $this->make_counter($index, $bullet_style, $pad);

        if ( trim($text) == "" ) {
          return;
        }

        $spacing = 0;
        $font_family = $style->font_family;

        $line = $li->get_containing_line();
        list($x, $y) = array($frame->get_position("x"), $line->y);

        $x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing);

        // Take line-height into account
        $line_height = $style->line_height;
        $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results

        $this->_canvas->text($x, $y, $text,
                             $font_family, $font_size,
                             $style->color, $spacing);

      case "none":
        break;
      }
    }
  }
 function check_page_break(Frame $frame)
 {
     if ($this->_page_full || $frame->_already_pushed) {
         return false;
     }
     $p = $frame;
     do {
         if ($p->is_absolute()) {
             return false;
         }
     } while ($p = $p->get_parent());
     $margin_height = $frame->get_margin_height();
     if ($frame->get_style()->display === "table-row" && !$frame->get_prev_sibling() && $margin_height > $this->get_margin_height()) {
         return false;
     }
     $max_y = $frame->get_position("y") + $margin_height;
     $p = $frame->get_parent();
     while ($p) {
         $style = $p->get_style();
         $max_y += $style->length_in_pt(array($style->margin_bottom, $style->padding_bottom, $style->border_bottom_width));
         $p = $p->get_parent();
     }
     if ($max_y <= $this->_bottom_page_margin) {
         return false;
     }
     dompdf_debug("page-break", "check_page_break");
     dompdf_debug("page-break", "in_table: " . $this->_in_table);
     $iter = $frame;
     $flg = false;
     $in_table = $this->_in_table;
     dompdf_debug("page-break", "Starting search");
     while ($iter) {
         if ($iter === $this) {
             dompdf_debug("page-break", "reached root.");
             break;
         }
         if ($this->_page_break_allowed($iter)) {
             dompdf_debug("page-break", "break allowed, splitting.");
             $iter->split(null, true);
             $this->_page_full = true;
             $this->_in_table = $in_table;
             $frame->_already_pushed = true;
             return true;
         }
         if (!$flg && ($next = $iter->get_last_child())) {
             dompdf_debug("page-break", "following last child.");
             if ($next->is_table()) {
                 $this->_in_table++;
             }
             $iter = $next;
             continue;
         }
         if ($next = $iter->get_prev_sibling()) {
             dompdf_debug("page-break", "following prev sibling.");
             if ($next->is_table() && !$iter->is_table()) {
                 $this->_in_table++;
             } else {
                 if (!$next->is_table() && $iter->is_table()) {
                     $this->_in_table--;
                 }
             }
             $iter = $next;
             $flg = false;
             continue;
         }
         if ($next = $iter->get_parent()) {
             dompdf_debug("page-break", "following parent.");
             if ($iter->is_table()) {
                 $this->_in_table--;
             }
             $iter = $next;
             $flg = true;
             continue;
         }
         break;
     }
     $this->_in_table = $in_table;
     dompdf_debug("page-break", "no valid break found, just splitting.");
     if ($this->_in_table) {
         $num_tables = $this->_in_table - 1;
         $iter = $frame;
         while ($iter && $num_tables && $iter->get_style()->display !== "table") {
             $iter = $iter->get_parent();
             $num_tables--;
         }
         $iter = $frame;
         while ($iter && $iter->get_style()->display !== "table-row") {
             $iter = $iter->get_parent();
         }
     }
     $frame->split(null, true);
     $this->_page_full = true;
     $frame->_already_pushed = true;
     return true;
 }
 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));
     if ($style->list_style_image !== "none" && !Image_Cache::is_broken($img = $frame->get_image_url())) {
         list($x, $y) = $frame->get_position();
         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;
         $this->_canvas->image($img, $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":
             case "a":
             case "i":
             case "A":
             case "I":
                 $li = $frame->get_parent();
                 $pad = null;
                 if ($bullet_style === "decimal-leading-zero") {
                     $pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count"));
                 }
                 $index = $frame->get_node()->getAttribute("dompdf-counter");
                 $text = $this->make_counter($index, $bullet_style, $pad);
                 if (trim($text) == "") {
                     return;
                 }
                 $spacing = 0;
                 $font_family = $style->font_family;
                 $line = $li->get_containing_line();
                 list($x, $y) = array($frame->get_position("x"), $line->y);
                 $x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing);
                 $line_height = $style->line_height;
                 $y += ($line_height - $font_size) / 4;
                 $this->_canvas->text($x, $y, $text, $font_family, $font_size, $style->color, $spacing);
             case "none":
                 break;
         }
     }
 }