123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756 |
- <?php
-
- /**
- * @package dompdf
- * @link http://dompdf.github.com/
- * @author Benj Carson <benjcarson@digitaljunkies.ca>
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
- /**
- * Base Frame_Decorator class
- *
- * @access private
- * @package dompdf
- */
- abstract class Frame_Decorator extends Frame
- {
- const DEFAULT_COUNTER = "-dompdf-default-counter";
-
- public $_counters = array(); // array([id] => counter_value) (for generated content)
-
- /**
- * The root node of the DOM tree
- *
- * @var Frame
- */
- protected $_root;
-
- /**
- * The decorated frame
- *
- * @var Frame
- */
- protected $_frame;
-
- /**
- * Positioner object used to position this frame (Strategy pattern)
- *
- * @var Positioner
- */
- protected $_positioner;
-
- /**
- * Reflower object used to calculate frame dimensions (Strategy pattern)
- *
- * @var Frame_Reflower
- */
- protected $_reflower;
-
- /**
- * Reference to the current dompdf instance
- *
- * @var DOMPDF
- */
- protected $_dompdf;
-
- /**
- * First block parent
- *
- * @var Block_Frame_Decorator
- */
- private $_block_parent;
-
- /**
- * First positionned parent (position: relative | absolute | fixed)
- *
- * @var Frame_Decorator
- */
- private $_positionned_parent;
-
- /**
- * Class constructor
- *
- * @param Frame $frame The decoration target
- * @param DOMPDF $dompdf The DOMPDF object
- */
- function __construct(Frame $frame, DOMPDF $dompdf)
- {
- $this->_frame = $frame;
- $this->_root = null;
- $this->_dompdf = $dompdf;
- $frame->set_decorator($this);
- }
-
- /**
- * "Destructor": foribly free all references held by this object
- *
- * @param bool $recursive if true, call dispose on all children
- */
- function dispose($recursive = false)
- {
- if ($recursive) {
- while ($child = $this->get_first_child()) {
- $child->dispose(true);
- }
- }
-
- $this->_root = null;
- unset($this->_root);
-
- $this->_frame->dispose(true);
- $this->_frame = null;
- unset($this->_frame);
-
- $this->_positioner = null;
- unset($this->_positioner);
-
- $this->_reflower = null;
- unset($this->_reflower);
- }
-
- /**
- * Return a copy of this frame with $node as its node
- *
- * @param DOMNode $node
- *
- * @return Frame
- */
- function copy(DOMNode $node)
- {
- $frame = new Frame($node);
- $frame->set_style(clone $this->_frame->get_original_style());
-
- return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
- }
-
- /**
- * Create a deep copy: copy this node and all children
- *
- * @return Frame
- */
- function deep_copy()
- {
- $frame = new Frame($this->get_node()->cloneNode());
- $frame->set_style(clone $this->_frame->get_original_style());
-
- $deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
-
- foreach ($this->get_children() as $child) {
- $deco->append_child($child->deep_copy());
- }
-
- return $deco;
- }
-
- /**
- * Delegate calls to decorated frame object
- */
- function reset()
- {
- $this->_frame->reset();
-
- $this->_counters = array();
-
- // Reset all children
- foreach ($this->get_children() as $child) {
- $child->reset();
- }
- }
-
- // Getters -----------
- function get_id()
- {
- return $this->_frame->get_id();
- }
-
- /**
- * @return Frame
- */
- function get_frame()
- {
- return $this->_frame;
- }
-
- /**
- * @return DOMElement|DOMText
- */
- function get_node()
- {
- return $this->_frame->get_node();
- }
-
- /**
- * @return Style
- */
- function get_style()
- {
- return $this->_frame->get_style();
- }
-
- /**
- * @return Style
- */
- function get_original_style()
- {
- return $this->_frame->get_original_style();
- }
-
- /**
- * @param integer $i
- *
- * @return array|float
- */
- function get_containing_block($i = null)
- {
- return $this->_frame->get_containing_block($i);
- }
-
- /**
- * @param integer $i
- *
- * @return array|float
- */
- function get_position($i = null)
- {
- return $this->_frame->get_position($i);
- }
-
- /**
- * @return DOMPDF
- */
- function get_dompdf()
- {
- return $this->_dompdf;
- }
-
- /**
- * @return float
- */
- function get_margin_height()
- {
- return $this->_frame->get_margin_height();
- }
-
- /**
- * @return float
- */
- function get_margin_width()
- {
- return $this->_frame->get_margin_width();
- }
-
- /**
- * @return array
- */
- function get_padding_box()
- {
- return $this->_frame->get_padding_box();
- }
-
- /**
- * @return array
- */
- function get_border_box()
- {
- return $this->_frame->get_border_box();
- }
-
- /**
- * @param integer $id
- */
- function set_id($id)
- {
- $this->_frame->set_id($id);
- }
-
- /**
- * @param Style $style
- */
- function set_style(Style $style)
- {
- $this->_frame->set_style($style);
- }
-
- /**
- * @param float $x
- * @param float $y
- * @param float $w
- * @param float $h
- */
- function set_containing_block($x = null, $y = null, $w = null, $h = null)
- {
- $this->_frame->set_containing_block($x, $y, $w, $h);
- }
-
- /**
- * @param float $x
- * @param float $y
- */
- function set_position($x = null, $y = null)
- {
- $this->_frame->set_position($x, $y);
- }
-
- /**
- * @return string
- */
- function __toString()
- {
- return $this->_frame->__toString();
- }
-
- /**
- * @param Frame $child
- * @param bool $update_node
- */
- function prepend_child(Frame $child, $update_node = true)
- {
- while ($child instanceof Frame_Decorator) {
- $child = $child->_frame;
- }
-
- $this->_frame->prepend_child($child, $update_node);
- }
-
- /**
- * @param Frame $child
- * @param bool $update_node
- */
- function append_child(Frame $child, $update_node = true)
- {
- while ($child instanceof Frame_Decorator) {
- $child = $child->_frame;
- }
-
- $this->_frame->append_child($child, $update_node);
- }
-
- /**
- * @param Frame $new_child
- * @param Frame $ref
- * @param bool $update_node
- */
- function insert_child_before(Frame $new_child, Frame $ref, $update_node = true)
- {
- while ($new_child instanceof Frame_Decorator) {
- $new_child = $new_child->_frame;
- }
-
- if ($ref instanceof Frame_Decorator) {
- $ref = $ref->_frame;
- }
-
- $this->_frame->insert_child_before($new_child, $ref, $update_node);
- }
-
- /**
- * @param Frame $new_child
- * @param Frame $ref
- * @param bool $update_node
- */
- function insert_child_after(Frame $new_child, Frame $ref, $update_node = true)
- {
- while ($new_child instanceof Frame_Decorator) {
- $new_child = $new_child->_frame;
- }
-
- while ($ref instanceof Frame_Decorator) {
- $ref = $ref->_frame;
- }
-
- $this->_frame->insert_child_after($new_child, $ref, $update_node);
- }
-
- /**
- * @param Frame $child
- * @param bool $update_node
- *
- * @return Frame
- */
- function remove_child(Frame $child, $update_node = true)
- {
- while ($child instanceof Frame_Decorator) {
- $child = $child->_frame;
- }
-
- return $this->_frame->remove_child($child, $update_node);
- }
-
- /**
- * @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;
- }
-
- /**
- * @return Frame_Decorator
- */
- function get_first_child()
- {
- $c = $this->_frame->get_first_child();
- if ($c && $deco = $c->get_decorator()) {
- while ($tmp = $deco->get_decorator()) {
- $deco = $tmp;
- }
-
- return $deco;
- } else if ($c) {
- return $c;
- }
-
- return null;
- }
-
- /**
- * @return Frame_Decorator
- */
- function get_last_child()
- {
- $c = $this->_frame->get_last_child();
- if ($c && $deco = $c->get_decorator()) {
- while ($tmp = $deco->get_decorator()) {
- $deco = $tmp;
- }
-
- return $deco;
- } else if ($c) {
- return $c;
- }
-
- return null;
- }
-
- /**
- * @return Frame_Decorator
- */
- function get_prev_sibling()
- {
- $s = $this->_frame->get_prev_sibling();
- if ($s && $deco = $s->get_decorator()) {
- while ($tmp = $deco->get_decorator()) {
- $deco = $tmp;
- }
- return $deco;
- } else if ($s) {
- return $s;
- }
-
- return null;
- }
-
- /**
- * @return Frame_Decorator
- */
- function get_next_sibling()
- {
- $s = $this->_frame->get_next_sibling();
- if ($s && $deco = $s->get_decorator()) {
- while ($tmp = $deco->get_decorator()) {
- $deco = $tmp;
- }
-
- return $deco;
- } else if ($s) {
- return $s;
- }
-
- return null;
- }
-
- /**
- * @return FrameTreeList
- */
- function get_subtree()
- {
- return new FrameTreeList($this);
- }
-
- function set_positioner(Positioner $posn)
- {
- $this->_positioner = $posn;
- if ($this->_frame instanceof Frame_Decorator) {
- $this->_frame->set_positioner($posn);
- }
- }
-
- function set_reflower(Frame_Reflower $reflower)
- {
- $this->_reflower = $reflower;
- if ($this->_frame instanceof Frame_Decorator) {
- $this->_frame->set_reflower($reflower);
- }
- }
-
- /**
- * @return Frame_Reflower
- */
- function get_reflower()
- {
- return $this->_reflower;
- }
-
- /**
- * @param Frame $root
- */
- function set_root(Frame $root)
- {
- $this->_root = $root;
-
- if ($this->_frame instanceof Frame_Decorator) {
- $this->_frame->set_root($root);
- }
- }
-
- /**
- * @return Page_Frame_Decorator
- */
- function get_root()
- {
- return $this->_root;
- }
-
- /**
- * @return Block_Frame_Decorator
- */
- function find_block_parent()
- {
- // Find our nearest block level parent
- $p = $this->get_parent();
-
- while ($p) {
- if ($p->is_block()) {
- break;
- }
-
- $p = $p->get_parent();
- }
-
- return $this->_block_parent = $p;
- }
-
- /**
- * @return Frame_Decorator
- */
- function find_positionned_parent()
- {
- // Find our nearest relative positionned parent
- $p = $this->get_parent();
- while ($p) {
- if ($p->is_positionned()) {
- break;
- }
-
- $p = $p->get_parent();
- }
-
- if (!$p) {
- $p = $this->_root->get_first_child(); // <body>
- }
-
- return $this->_positionned_parent = $p;
- }
-
- /**
- * 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
- $style = $this->_frame->get_style();
- if ($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());
-
-
-
- $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]];
- }
- }
-
-
- function reset_counter($id = self::DEFAULT_COUNTER, $value = 0)
- {
- $this->get_parent()->_counters[$id] = intval($value);
- }
-
- function decrement_counters($counters)
- {
- foreach ($counters as $id => $increment) {
- $this->increment_counter($id, intval($increment) * -1);
- }
- }
-
-
- function increment_counters($counters)
- {
- foreach ($counters as $id => $increment) {
- $this->increment_counter($id, intval($increment));
- }
- }
-
- function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1)
- {
- $counter_frame = $this->lookup_counter_frame($id);
-
- if ($counter_frame) {
- if (!isset($counter_frame->_counters[$id])) {
- $counter_frame->_counters[$id] = 0;
- }
-
- $counter_frame->_counters[$id] += $increment;
- }
- }
-
- function lookup_counter_frame($id = self::DEFAULT_COUNTER)
- {
- $f = $this->get_parent();
-
- while ($f) {
- if (isset($f->_counters[$id])) {
- return $f;
- }
- $fp = $f->get_parent();
-
- if (!$fp) {
- return $f;
- }
-
- $f = $fp;
- }
- }
-
- // TODO: What version is the best : this one or the one in List_Bullet_Renderer ?
- function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal")
- {
- $type = mb_strtolower($type);
-
- if (!isset($this->_counters[$id])) {
- $this->_counters[$id] = 0;
- }
-
- $value = $this->_counters[$id];
-
- switch ($type) {
- default:
- case "decimal":
- return $value;
-
- case "decimal-leading-zero":
- return str_pad($value, 2, "0");
-
- case "lower-roman":
- return dec2roman($value);
-
- case "upper-roman":
- return mb_strtoupper(dec2roman($value));
-
- case "lower-latin":
- case "lower-alpha":
- return chr(($value % 26) + ord('a') - 1);
-
- case "upper-latin":
- case "upper-alpha":
- return chr(($value % 26) + ord('A') - 1);
-
- case "lower-greek":
- return unichr($value + 944);
-
- case "upper-greek":
- return unichr($value + 912);
- }
- }
-
- final function position()
- {
- $this->_positioner->position();
- }
-
- final function move($offset_x, $offset_y, $ignore_self = false)
- {
- $this->_positioner->move($offset_x, $offset_y, $ignore_self);
- }
-
- final function reflow(Block_Frame_Decorator $block = null)
- {
- // Uncomment this to see the frames before they're laid out, instead of
- // during rendering.
- //echo $this->_frame; flush();
- $this->_reflower->reflow($block);
- }
-
- final function get_min_max_width()
- {
- return $this->_reflower->get_min_max_width();
- }
- }
|