vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php line 890

Open in your IDE?
  1. <?php
  2. namespace Dompdf\FrameDecorator;
  3. use DOMElement;
  4. use DOMNode;
  5. use DOMText;
  6. use Dompdf\Helpers;
  7. use Dompdf\Dompdf;
  8. use Dompdf\Frame;
  9. use Dompdf\Frame\FrameTreeList;
  10. use Dompdf\Frame\Factory;
  11. use Dompdf\FrameReflower\AbstractFrameReflower;
  12. use Dompdf\Css\Style;
  13. use Dompdf\Positioner\AbstractPositioner;
  14. use Dompdf\Exception;
  15. /**
  16.  * @package dompdf
  17.  * @link    http://dompdf.github.com/
  18.  * @author  Benj Carson <benjcarson@digitaljunkies.ca>
  19.  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  20.  */
  21. /**
  22.  * Base AbstractFrameDecorator class
  23.  *
  24.  * @package dompdf
  25.  */
  26. abstract class AbstractFrameDecorator extends Frame
  27. {
  28.     const DEFAULT_COUNTER "-dompdf-default-counter";
  29.     public $_counters = []; // array([id] => counter_value) (for generated content)
  30.     /**
  31.      * The root node of the DOM tree
  32.      *
  33.      * @var Frame
  34.      */
  35.     protected $_root;
  36.     /**
  37.      * The decorated frame
  38.      *
  39.      * @var Frame
  40.      */
  41.     protected $_frame;
  42.     /**
  43.      * AbstractPositioner object used to position this frame (Strategy pattern)
  44.      *
  45.      * @var AbstractPositioner
  46.      */
  47.     protected $_positioner;
  48.     /**
  49.      * Reflower object used to calculate frame dimensions (Strategy pattern)
  50.      *
  51.      * @var \Dompdf\FrameReflower\AbstractFrameReflower
  52.      */
  53.     protected $_reflower;
  54.     /**
  55.      * Reference to the current dompdf instance
  56.      *
  57.      * @var Dompdf
  58.      */
  59.     protected $_dompdf;
  60.     /**
  61.      * First block parent
  62.      *
  63.      * @var Block
  64.      */
  65.     private $_block_parent;
  66.     /**
  67.      * First positionned parent (position: relative | absolute | fixed)
  68.      *
  69.      * @var AbstractFrameDecorator
  70.      */
  71.     private $_positionned_parent;
  72.     /**
  73.      * Cache for the get_parent while loop results
  74.      *
  75.      * @var Frame
  76.      */
  77.     private $_cached_parent;
  78.     /**
  79.      * Class constructor
  80.      *
  81.      * @param Frame $frame   The decoration target
  82.      * @param Dompdf $dompdf The Dompdf object
  83.      */
  84.     function __construct(Frame $frameDompdf $dompdf)
  85.     {
  86.         $this->_frame $frame;
  87.         $this->_root null;
  88.         $this->_dompdf $dompdf;
  89.         $frame->set_decorator($this);
  90.     }
  91.     /**
  92.      * "Destructor": foribly free all references held by this object
  93.      *
  94.      * @param bool $recursive if true, call dispose on all children
  95.      */
  96.     function dispose($recursive false)
  97.     {
  98.         if ($recursive) {
  99.             while ($child $this->get_first_child()) {
  100.                 $child->dispose(true);
  101.             }
  102.         }
  103.         $this->_root null;
  104.         unset($this->_root);
  105.         $this->_frame->dispose(true);
  106.         $this->_frame null;
  107.         unset($this->_frame);
  108.         $this->_positioner null;
  109.         unset($this->_positioner);
  110.         $this->_reflower null;
  111.         unset($this->_reflower);
  112.     }
  113.     /**
  114.      * Return a copy of this frame with $node as its node
  115.      *
  116.      * @param DOMNode $node
  117.      *
  118.      * @return Frame
  119.      */
  120.     function copy(DOMNode $node)
  121.     {
  122.         $frame = new Frame($node);
  123.         $frame->set_style(clone $this->_frame->get_original_style());
  124.         return Factory::decorate_frame($frame$this->_dompdf$this->_root);
  125.     }
  126.     /**
  127.      * Create a deep copy: copy this node and all children
  128.      *
  129.      * @return Frame
  130.      */
  131.     function deep_copy()
  132.     {
  133.         $node $this->_frame->get_node();
  134.         if ($node instanceof DOMElement && $node->hasAttribute("id")) {
  135.             $node->setAttribute("data-dompdf-original-id"$node->getAttribute("id"));
  136.             $node->removeAttribute("id");
  137.         }
  138.         $frame = new Frame($node->cloneNode());
  139.         $frame->set_style(clone $this->_frame->get_original_style());
  140.         $deco Factory::decorate_frame($frame$this->_dompdf$this->_root);
  141.         foreach ($this->get_children() as $child) {
  142.             $deco->append_child($child->deep_copy());
  143.         }
  144.         return $deco;
  145.     }
  146.     /**
  147.      * Delegate calls to decorated frame object
  148.      */
  149.     function reset()
  150.     {
  151.         $this->_frame->reset();
  152.         $this->_counters = [];
  153.         $this->_cached_parent null//clear get_parent() cache
  154.         // Reset all children
  155.         foreach ($this->get_children() as $child) {
  156.             $child->reset();
  157.         }
  158.     }
  159.     // Getters -----------
  160.     /**
  161.      * @return string
  162.      */
  163.     function get_id()
  164.     {
  165.         return $this->_frame->get_id();
  166.     }
  167.     /**
  168.      * @return Frame
  169.      */
  170.     function get_frame()
  171.     {
  172.         return $this->_frame;
  173.     }
  174.     /**
  175.      * @return DOMElement|DOMText
  176.      */
  177.     function get_node()
  178.     {
  179.         return $this->_frame->get_node();
  180.     }
  181.     /**
  182.      * @return Style
  183.      */
  184.     function get_style()
  185.     {
  186.         return $this->_frame->get_style();
  187.     }
  188.     /**
  189.      * @return Style
  190.      */
  191.     function get_original_style()
  192.     {
  193.         return $this->_frame->get_original_style();
  194.     }
  195.     /**
  196.      * @param integer $i
  197.      *
  198.      * @return array|float
  199.      */
  200.     function get_containing_block($i null)
  201.     {
  202.         return $this->_frame->get_containing_block($i);
  203.     }
  204.     /**
  205.      * @param integer $i
  206.      *
  207.      * @return array|float
  208.      */
  209.     function get_position($i null)
  210.     {
  211.         return $this->_frame->get_position($i);
  212.     }
  213.     /**
  214.      * @return Dompdf
  215.      */
  216.     function get_dompdf()
  217.     {
  218.         return $this->_dompdf;
  219.     }
  220.     /**
  221.      * @return float
  222.      */
  223.     function get_margin_height()
  224.     {
  225.         return $this->_frame->get_margin_height();
  226.     }
  227.     /**
  228.      * @return float
  229.      */
  230.     function get_margin_width()
  231.     {
  232.         return $this->_frame->get_margin_width();
  233.     }
  234.     /**
  235.      * @return array
  236.      */
  237.     function get_content_box()
  238.     {
  239.         return $this->_frame->get_content_box();
  240.     }
  241.     /**
  242.      * @return array
  243.      */
  244.     function get_padding_box()
  245.     {
  246.         return $this->_frame->get_padding_box();
  247.     }
  248.     /**
  249.      * @return array
  250.      */
  251.     function get_border_box()
  252.     {
  253.         return $this->_frame->get_border_box();
  254.     }
  255.     /**
  256.      * @param integer $id
  257.      */
  258.     function set_id($id)
  259.     {
  260.         $this->_frame->set_id($id);
  261.     }
  262.     /**
  263.      * @param Style $style
  264.      */
  265.     function set_style(Style $style)
  266.     {
  267.         $this->_frame->set_style($style);
  268.     }
  269.     /**
  270.      * @param float $x
  271.      * @param float $y
  272.      * @param float $w
  273.      * @param float $h
  274.      */
  275.     function set_containing_block($x null$y null$w null$h null)
  276.     {
  277.         $this->_frame->set_containing_block($x$y$w$h);
  278.     }
  279.     /**
  280.      * @param float $x
  281.      * @param float $y
  282.      */
  283.     function set_position($x null$y null)
  284.     {
  285.         $this->_frame->set_position($x$y);
  286.     }
  287.     /**
  288.      * @return bool
  289.      */
  290.     function is_auto_height()
  291.     {
  292.         return $this->_frame->is_auto_height();
  293.     }
  294.     /**
  295.      * @return bool
  296.      */
  297.     function is_auto_width()
  298.     {
  299.         return $this->_frame->is_auto_width();
  300.     }
  301.     /**
  302.      * @return string
  303.      */
  304.     function __toString()
  305.     {
  306.         return $this->_frame->__toString();
  307.     }
  308.     /**
  309.      * @param Frame $child
  310.      * @param bool $update_node
  311.      */
  312.     function prepend_child(Frame $child$update_node true)
  313.     {
  314.         while ($child instanceof AbstractFrameDecorator) {
  315.             $child $child->_frame;
  316.         }
  317.         $this->_frame->prepend_child($child$update_node);
  318.     }
  319.     /**
  320.      * @param Frame $child
  321.      * @param bool $update_node
  322.      */
  323.     function append_child(Frame $child$update_node true)
  324.     {
  325.         while ($child instanceof AbstractFrameDecorator) {
  326.             $child $child->_frame;
  327.         }
  328.         $this->_frame->append_child($child$update_node);
  329.     }
  330.     /**
  331.      * @param Frame $new_child
  332.      * @param Frame $ref
  333.      * @param bool $update_node
  334.      */
  335.     function insert_child_before(Frame $new_childFrame $ref$update_node true)
  336.     {
  337.         while ($new_child instanceof AbstractFrameDecorator) {
  338.             $new_child $new_child->_frame;
  339.         }
  340.         if ($ref instanceof AbstractFrameDecorator) {
  341.             $ref $ref->_frame;
  342.         }
  343.         $this->_frame->insert_child_before($new_child$ref$update_node);
  344.     }
  345.     /**
  346.      * @param Frame $new_child
  347.      * @param Frame $ref
  348.      * @param bool $update_node
  349.      */
  350.     function insert_child_after(Frame $new_childFrame $ref$update_node true)
  351.     {
  352.         $insert_frame $new_child;
  353.         while ($insert_frame instanceof AbstractFrameDecorator) {
  354.             $insert_frame $insert_frame->_frame;
  355.         }
  356.         $reference_frame $ref;
  357.         while ($reference_frame instanceof AbstractFrameDecorator) {
  358.             $reference_frame $reference_frame->_frame;
  359.         }
  360.         $this->_frame->insert_child_after($insert_frame$reference_frame$update_node);
  361.     }
  362.     /**
  363.      * @param Frame $child
  364.      * @param bool $update_node
  365.      *
  366.      * @return Frame
  367.      */
  368.     function remove_child(Frame $child$update_node true)
  369.     {
  370.         while ($child instanceof AbstractFrameDecorator) {
  371.             $child $child->_frame;
  372.         }
  373.         return $this->_frame->remove_child($child$update_node);
  374.     }
  375.     /**
  376.      * @param bool $use_cache
  377.      * @return AbstractFrameDecorator
  378.      */
  379.     function get_parent($use_cache true)
  380.     {
  381.         if ($use_cache && $this->_cached_parent) {
  382.             return $this->_cached_parent;
  383.         }
  384.         $p $this->_frame->get_parent();
  385.         if ($p && $deco $p->get_decorator()) {
  386.             while ($tmp $deco->get_decorator()) {
  387.                 $deco $tmp;
  388.             }
  389.             return $this->_cached_parent $deco;
  390.         } else {
  391.             return $this->_cached_parent $p;
  392.         }
  393.     }
  394.     /**
  395.      * @return AbstractFrameDecorator
  396.      */
  397.     function get_first_child()
  398.     {
  399.         $c $this->_frame->get_first_child();
  400.         if ($c && $deco $c->get_decorator()) {
  401.             while ($tmp $deco->get_decorator()) {
  402.                 $deco $tmp;
  403.             }
  404.             return $deco;
  405.         } else {
  406.             if ($c) {
  407.                 return $c;
  408.             }
  409.         }
  410.         return null;
  411.     }
  412.     /**
  413.      * @return AbstractFrameDecorator
  414.      */
  415.     function get_last_child()
  416.     {
  417.         $c $this->_frame->get_last_child();
  418.         if ($c && $deco $c->get_decorator()) {
  419.             while ($tmp $deco->get_decorator()) {
  420.                 $deco $tmp;
  421.             }
  422.             return $deco;
  423.         } else {
  424.             if ($c) {
  425.                 return $c;
  426.             }
  427.         }
  428.         return null;
  429.     }
  430.     /**
  431.      * @return AbstractFrameDecorator
  432.      */
  433.     function get_prev_sibling()
  434.     {
  435.         $s $this->_frame->get_prev_sibling();
  436.         if ($s && $deco $s->get_decorator()) {
  437.             while ($tmp $deco->get_decorator()) {
  438.                 $deco $tmp;
  439.             }
  440.             return $deco;
  441.         } else {
  442.             if ($s) {
  443.                 return $s;
  444.             }
  445.         }
  446.         return null;
  447.     }
  448.     /**
  449.      * @return AbstractFrameDecorator
  450.      */
  451.     function get_next_sibling()
  452.     {
  453.         $s $this->_frame->get_next_sibling();
  454.         if ($s && $deco $s->get_decorator()) {
  455.             while ($tmp $deco->get_decorator()) {
  456.                 $deco $tmp;
  457.             }
  458.             return $deco;
  459.         } else {
  460.             if ($s) {
  461.                 return $s;
  462.             }
  463.         }
  464.         return null;
  465.     }
  466.     /**
  467.      * @return FrameTreeList
  468.      */
  469.     function get_subtree()
  470.     {
  471.         return new FrameTreeList($this);
  472.     }
  473.     function set_positioner(AbstractPositioner $posn)
  474.     {
  475.         $this->_positioner $posn;
  476.         if ($this->_frame instanceof AbstractFrameDecorator) {
  477.             $this->_frame->set_positioner($posn);
  478.         }
  479.     }
  480.     function set_reflower(AbstractFrameReflower $reflower)
  481.     {
  482.         $this->_reflower $reflower;
  483.         if ($this->_frame instanceof AbstractFrameDecorator) {
  484.             $this->_frame->set_reflower($reflower);
  485.         }
  486.     }
  487.     /**
  488.      * @return \Dompdf\FrameReflower\AbstractFrameReflower
  489.      */
  490.     function get_reflower()
  491.     {
  492.         return $this->_reflower;
  493.     }
  494.     /**
  495.      * @param Frame $root
  496.      */
  497.     function set_root(Frame $root)
  498.     {
  499.         $this->_root $root;
  500.         if ($this->_frame instanceof AbstractFrameDecorator) {
  501.             $this->_frame->set_root($root);
  502.         }
  503.     }
  504.     /**
  505.      * @return Page
  506.      */
  507.     function get_root()
  508.     {
  509.         return $this->_root;
  510.     }
  511.     /**
  512.      * @return Block
  513.      */
  514.     function find_block_parent()
  515.     {
  516.         // Find our nearest block level parent
  517.         $p $this->get_parent();
  518.         while ($p) {
  519.             if ($p->is_block()) {
  520.                 break;
  521.             }
  522.             $p $p->get_parent();
  523.         }
  524.         return $this->_block_parent $p;
  525.     }
  526.     /**
  527.      * @return AbstractFrameDecorator
  528.      */
  529.     function find_positionned_parent()
  530.     {
  531.         // Find our nearest relative positionned parent
  532.         $p $this->get_parent();
  533.         while ($p) {
  534.             if ($p->is_positionned()) {
  535.                 break;
  536.             }
  537.             $p $p->get_parent();
  538.         }
  539.         if (!$p) {
  540.             $p $this->_root->get_first_child(); // <body>
  541.         }
  542.         return $this->_positionned_parent $p;
  543.     }
  544.     /**
  545.      * split this frame at $child.
  546.      * The current frame is cloned and $child and all children following
  547.      * $child are added to the clone.  The clone is then passed to the
  548.      * current frame's parent->split() method.
  549.      *
  550.      * @param Frame $child
  551.      * @param boolean $force_pagebreak
  552.      *
  553.      * @throws Exception
  554.      * @return void
  555.      */
  556.     function split(Frame $child null$force_pagebreak false)
  557.     {
  558.         // decrement any counters that were incremented on the current node, unless that node is the body
  559.         $style $this->_frame->get_style();
  560.         if (
  561.             $this->_frame->get_node()->nodeName !== "body" &&
  562.             $style->counter_increment &&
  563.             ($decrement $style->counter_increment) !== "none"
  564.         ) {
  565.             $this->decrement_counters($decrement);
  566.         }
  567.         if (is_null($child)) {
  568.             // check for counter increment on :before content (always a child of the selected element @link AbstractFrameReflower::_set_content)
  569.             // this can push the current node to the next page before counter rules have bubbled up (but only if
  570.             // it's been rendered, thus the position check)
  571.             if (!$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id")) {
  572.                 foreach ($this->_frame->get_children() as $child) {
  573.                     if (
  574.                         $this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() &&
  575.                         $child->get_position('x') !== null
  576.                     ) {
  577.                         $style $child->get_style();
  578.                         if ($style->counter_increment && ($decrement $style->counter_increment) !== "none") {
  579.                             $this->decrement_counters($decrement);
  580.                         }
  581.                     }
  582.                 }
  583.             }
  584.             $this->get_parent()->split($this$force_pagebreak);
  585.             return;
  586.         }
  587.         if ($child->get_parent() !== $this) {
  588.             throw new Exception("Unable to split: frame is not a child of this one.");
  589.         }
  590.         $node $this->_frame->get_node();
  591.         if ($node instanceof DOMElement && $node->hasAttribute("id")) {
  592.             $node->setAttribute("data-dompdf-original-id"$node->getAttribute("id"));
  593.             $node->removeAttribute("id");
  594.         }
  595.         $split $this->copy($node->cloneNode());
  596.         $split->reset();
  597.         $split->get_original_style()->text_indent 0;
  598.         $split->_splitted true;
  599.         $split->_already_pushed true;
  600.         // The body's properties must be kept
  601.         if ($node->nodeName !== "body") {
  602.             // Style reset on the first and second parts
  603.             $style $this->_frame->get_style();
  604.             $style->margin_bottom 0;
  605.             $style->padding_bottom 0;
  606.             $style->border_bottom 0;
  607.             // second
  608.             $orig_style $split->get_original_style();
  609.             $orig_style->text_indent 0;
  610.             $orig_style->margin_top 0;
  611.             $orig_style->padding_top 0;
  612.             $orig_style->border_top 0;
  613.             $orig_style->page_break_before "auto";
  614.         }
  615.         // recalculate the float offsets after paging
  616.         $this->get_parent()->insert_child_after($split$this);
  617.         if ($this instanceof Block) {
  618.             foreach ($this->get_line_boxes() as $index => $line_box) {
  619.                 $line_box->get_float_offsets();
  620.             }
  621.         }
  622.         // Add $frame and all following siblings to the new split node
  623.         $iter $child;
  624.         while ($iter) {
  625.             $frame $iter;
  626.             $iter $iter->get_next_sibling();
  627.             $frame->reset();
  628.             $frame->_parent $split;
  629.             $split->append_child($frame);
  630.             // recalculate the float offsets
  631.             if ($frame instanceof Block) {
  632.                 foreach ($frame->get_line_boxes() as $index => $line_box) {
  633.                     $line_box->get_float_offsets();
  634.                 }
  635.             }
  636.         }
  637.         $this->get_parent()->split($split$force_pagebreak);
  638.         // If this node resets a counter save the current value to use when rendering on the next page
  639.         if ($style->counter_reset && ($reset $style->counter_reset) !== "none") {
  640.             $vars preg_split('/\s+/'trim($reset), 2);
  641.             $split->_counters['__' $vars[0]] = $this->lookup_counter_frame($vars[0])->_counters[$vars[0]];
  642.         }
  643.     }
  644.     /**
  645.      * @param string $id
  646.      * @param int $value
  647.      */
  648.     function reset_counter($id self::DEFAULT_COUNTER$value 0)
  649.     {
  650.         $this->get_parent()->_counters[$id] = intval($value);
  651.     }
  652.     /**
  653.      * @param $counters
  654.      */
  655.     function decrement_counters($counters)
  656.     {
  657.         foreach ($counters as $id => $increment) {
  658.             $this->increment_counter($idintval($increment) * -1);
  659.         }
  660.     }
  661.     /**
  662.      * @param $counters
  663.      */
  664.     function increment_counters($counters)
  665.     {
  666.         foreach ($counters as $id => $increment) {
  667.             $this->increment_counter($idintval($increment));
  668.         }
  669.     }
  670.     /**
  671.      * @param string $id
  672.      * @param int $increment
  673.      */
  674.     function increment_counter($id self::DEFAULT_COUNTER$increment 1)
  675.     {
  676.         $counter_frame $this->lookup_counter_frame($id);
  677.         if ($counter_frame) {
  678.             if (!isset($counter_frame->_counters[$id])) {
  679.                 $counter_frame->_counters[$id] = 0;
  680.             }
  681.             $counter_frame->_counters[$id] += $increment;
  682.         }
  683.     }
  684.     /**
  685.      * @param string $id
  686.      * @return AbstractFrameDecorator|null
  687.      */
  688.     function lookup_counter_frame($id self::DEFAULT_COUNTER)
  689.     {
  690.         $f $this->get_parent();
  691.         while ($f) {
  692.             if (isset($f->_counters[$id])) {
  693.                 return $f;
  694.             }
  695.             $fp $f->get_parent();
  696.             if (!$fp) {
  697.                 return $f;
  698.             }
  699.             $f $fp;
  700.         }
  701.         return null;
  702.     }
  703.     /**
  704.      * @param string $id
  705.      * @param string $type
  706.      * @return bool|string
  707.      *
  708.      * TODO: What version is the best : this one or the one in ListBullet ?
  709.      */
  710.     function counter_value($id self::DEFAULT_COUNTER$type "decimal")
  711.     {
  712.         $type mb_strtolower($type);
  713.         if (!isset($this->_counters[$id])) {
  714.             $this->_counters[$id] = 0;
  715.         }
  716.         $value $this->_counters[$id];
  717.         switch ($type) {
  718.             default:
  719.             case "decimal":
  720.                 return $value;
  721.             case "decimal-leading-zero":
  722.                 return str_pad($value2"0"STR_PAD_LEFT);
  723.             case "lower-roman":
  724.                 return Helpers::dec2roman($value);
  725.             case "upper-roman":
  726.                 return mb_strtoupper(Helpers::dec2roman($value));
  727.             case "lower-latin":
  728.             case "lower-alpha":
  729.                 return chr(($value 26) + ord('a') - 1);
  730.             case "upper-latin":
  731.             case "upper-alpha":
  732.                 return chr(($value 26) + ord('A') - 1);
  733.             case "lower-greek":
  734.                 return Helpers::unichr($value 944);
  735.             case "upper-greek":
  736.                 return Helpers::unichr($value 912);
  737.         }
  738.     }
  739.     /**
  740.      *
  741.      */
  742.     final function position()
  743.     {
  744.         $this->_positioner->position($this);
  745.     }
  746.     /**
  747.      * @param $offset_x
  748.      * @param $offset_y
  749.      * @param bool $ignore_self
  750.      */
  751.     final function move($offset_x$offset_y$ignore_self false)
  752.     {
  753.         $this->_positioner->move($this$offset_x$offset_y$ignore_self);
  754.     }
  755.     /**
  756.      * @param Block|null $block
  757.      */
  758.     final function reflow(Block $block null)
  759.     {
  760.         // Uncomment this to see the frames before they're laid out, instead of
  761.         // during rendering.
  762.         //echo $this->_frame; flush();
  763.         $this->_reflower->reflow($block);
  764.     }
  765.     /**
  766.      * @return array
  767.      */
  768.     final function get_min_max_width()
  769.     {
  770.         return $this->_reflower->get_min_max_width();
  771.     }
  772.     /**
  773.      * Determine current frame width based on contents
  774.      *
  775.      * @return float
  776.      */
  777.     final function calculate_auto_width()
  778.     {
  779.         return $this->_reflower->calculate_auto_width();
  780.     }
  781. }