File "AbstractFrameDecorator.php"
Full Path: /home/clickysoft/public_html/jmapi5.clickysoft.net/vendor/dompdf/dompdf/src/FrameDecorator/AbstractFrameDecorator.php
File size: 22.03 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* @package dompdf
* @link https://github.com/dompdf/dompdf
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace Dompdf\FrameDecorator;
use DOMElement;
use DOMNode;
use Dompdf\Helpers;
use Dompdf\Dompdf;
use Dompdf\Exception;
use Dompdf\Frame;
use Dompdf\Frame\Factory;
use Dompdf\Frame\FrameListIterator;
use Dompdf\Frame\FrameTreeIterator;
use Dompdf\FrameReflower\AbstractFrameReflower;
use Dompdf\Css\Style;
use Dompdf\Positioner\AbstractPositioner;
/**
* Base AbstractFrameDecorator class
*
* @package dompdf
*/
abstract class AbstractFrameDecorator extends Frame
{
const DEFAULT_COUNTER = "-dompdf-default-counter";
/**
* array([id] => counter_value) (for generated content)
*
* @var array
*/
public $_counters = [];
/**
* The root node of the DOM tree
*
* @var Frame
*/
protected $_root;
/**
* The decorated frame
*
* @var Frame
*/
protected $_frame;
/**
* AbstractPositioner object used to position this frame (Strategy pattern)
*
* @var AbstractPositioner
*/
protected $_positioner;
/**
* Reflower object used to calculate frame dimensions (Strategy pattern)
*
* @var AbstractFrameReflower
*/
protected $_reflower;
/**
* Reference to the current dompdf instance
*
* @var Dompdf
*/
protected $_dompdf;
/**
* First block parent
*
* @var Block
*/
private $_block_parent;
/**
* First positioned parent (position: relative | absolute | fixed)
*
* @var AbstractFrameDecorator
*/
private $_positioned_parent;
/**
* Cache for the get_parent while loop results
*
* @var Frame
*/
private $_cached_parent;
/**
* Whether generated content and counters have been set.
*
* @var bool
*/
public $content_set = false;
/**
* Whether the frame has been split
*
* @var bool
*/
public $is_split = false;
/**
* Whether the frame is a split-off frame
*
* @var bool
*/
public $is_split_off = false;
/**
* 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": forcibly 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 AbstractFrameDecorator
*/
function copy(DOMNode $node)
{
$frame = new Frame($node);
$style = clone $this->_frame->get_style();
$style->reset();
$frame->set_style($style);
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
$node->removeAttribute("id");
}
return Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
}
/**
* Create a deep copy: copy this node and all children
*
* @return AbstractFrameDecorator
*/
function deep_copy()
{
$node = $this->_frame->get_node()->cloneNode();
$frame = new Frame($node);
$style = clone $this->_frame->get_style();
$style->reset();
$frame->set_style($style);
if ($node instanceof DOMElement && $node->hasAttribute("id")) {
$node->setAttribute("data-dompdf-original-id", $node->getAttribute("id"));
$node->removeAttribute("id");
}
$deco = Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
foreach ($this->get_children() as $child) {
$deco->append_child($child->deep_copy());
}
return $deco;
}
/**
* Create an anonymous child frame, inheriting styles from this frame.
*
* @param string $node_name
* @param string $display
*
* @return AbstractFrameDecorator
*/
public function create_anonymous_child(string $node_name, string $display): AbstractFrameDecorator
{
$style = $this->get_style();
$child_style = $style->get_stylesheet()->create_style();
$child_style->set_prop("display", $display);
$child_style->inherit($style);
$node = $this->get_node()->ownerDocument->createElement($node_name);
$frame = new Frame($node);
$frame->set_style($child_style);
return Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
}
function reset()
{
$this->_frame->reset();
$this->_reflower->reset();
$this->reset_generated_content();
$this->revert_counter_increment();
$this->content_set = false;
$this->_counters = [];
// clear parent lookup caches
$this->_cached_parent = null;
$this->_block_parent = null;
$this->_positioned_parent = null;
// Reset all children
foreach ($this->get_children() as $child) {
$child->reset();
}
}
/**
* If this represents a generated node then child nodes represent generated
* content. Remove the children since the content will be generated next
* time this frame is reflowed.
*/
protected function reset_generated_content(): void
{
if ($this->content_set
&& $this->get_node()->nodeName === "dompdf_generated"
) {
$content = $this->get_style()->content;
if ($content !== "normal" && $content !== "none") {
foreach ($this->get_children() as $child) {
$this->remove_child($child);
}
}
}
}
/**
* Decrement any counters that were incremented on the current node, unless
* that node is the body.
*/
protected function revert_counter_increment(): void
{
if ($this->content_set
&& $this->get_node()->nodeName !== "body"
&& ($decrement = $this->get_style()->counter_increment) !== "none"
) {
$this->decrement_counters($decrement);
}
}
// Getters -----------
function get_id()
{
return $this->_frame->get_id();
}
/**
* @return Frame
*/
function get_frame()
{
return $this->_frame;
}
function get_node()
{
return $this->_frame->get_node();
}
function get_style()
{
return $this->_frame->get_style();
}
/**
* @deprecated
*/
function get_original_style()
{
return $this->_frame->get_style();
}
function get_containing_block($i = null)
{
return $this->_frame->get_containing_block($i);
}
function get_position($i = null)
{
return $this->_frame->get_position($i);
}
/**
* @return Dompdf
*/
function get_dompdf()
{
return $this->_dompdf;
}
public function get_margin_width(): float
{
return $this->_frame->get_margin_width();
}
public function get_margin_height(): float
{
return $this->_frame->get_margin_height();
}
public function get_content_box(): array
{
return $this->_frame->get_content_box();
}
public function get_padding_box(): array
{
return $this->_frame->get_padding_box();
}
public function get_border_box(): array
{
return $this->_frame->get_border_box();
}
function set_id($id)
{
$this->_frame->set_id($id);
}
public function set_style(Style $style): void
{
$this->_frame->set_style($style);
}
function set_containing_block($x = null, $y = null, $w = null, $h = null)
{
$this->_frame->set_containing_block($x, $y, $w, $h);
}
function set_position($x = null, $y = null)
{
$this->_frame->set_position($x, $y);
}
function is_auto_height()
{
return $this->_frame->is_auto_height();
}
function is_auto_width()
{
return $this->_frame->is_auto_width();
}
function __toString()
{
return $this->_frame->__toString();
}
function prepend_child(Frame $child, $update_node = true)
{
while ($child instanceof AbstractFrameDecorator) {
$child = $child->_frame;
}
$this->_frame->prepend_child($child, $update_node);
}
function append_child(Frame $child, $update_node = true)
{
while ($child instanceof AbstractFrameDecorator) {
$child = $child->_frame;
}
$this->_frame->append_child($child, $update_node);
}
function insert_child_before(Frame $new_child, Frame $ref, $update_node = true)
{
while ($new_child instanceof AbstractFrameDecorator) {
$new_child = $new_child->_frame;
}
if ($ref instanceof AbstractFrameDecorator) {
$ref = $ref->_frame;
}
$this->_frame->insert_child_before($new_child, $ref, $update_node);
}
function insert_child_after(Frame $new_child, Frame $ref, $update_node = true)
{
$insert_frame = $new_child;
while ($insert_frame instanceof AbstractFrameDecorator) {
$insert_frame = $insert_frame->_frame;
}
$reference_frame = $ref;
while ($reference_frame instanceof AbstractFrameDecorator) {
$reference_frame = $reference_frame->_frame;
}
$this->_frame->insert_child_after($insert_frame, $reference_frame, $update_node);
}
function remove_child(Frame $child, $update_node = true)
{
while ($child instanceof AbstractFrameDecorator) {
$child = $child->_frame;
}
return $this->_frame->remove_child($child, $update_node);
}
/**
* @param bool $use_cache
* @return AbstractFrameDecorator
*/
function get_parent($use_cache = true)
{
if ($use_cache && $this->_cached_parent) {
return $this->_cached_parent;
}
$p = $this->_frame->get_parent();
if ($p && $deco = $p->get_decorator()) {
while ($tmp = $deco->get_decorator()) {
$deco = $tmp;
}
return $this->_cached_parent = $deco;
} else {
return $this->_cached_parent = $p;
}
}
/**
* @return AbstractFrameDecorator
*/
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 AbstractFrameDecorator
*/
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 AbstractFrameDecorator
*/
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 AbstractFrameDecorator
*/
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 FrameListIterator<AbstractFrameDecorator>
*/
public function get_children(): FrameListIterator
{
return new FrameListIterator($this);
}
/**
* @return FrameTreeIterator<AbstractFrameDecorator>
*/
function get_subtree(): FrameTreeIterator
{
return new FrameTreeIterator($this);
}
function set_positioner(AbstractPositioner $posn)
{
$this->_positioner = $posn;
if ($this->_frame instanceof AbstractFrameDecorator) {
$this->_frame->set_positioner($posn);
}
}
function set_reflower(AbstractFrameReflower $reflower)
{
$this->_reflower = $reflower;
if ($this->_frame instanceof AbstractFrameDecorator) {
$this->_frame->set_reflower($reflower);
}
}
/**
* @return AbstractPositioner
*/
function get_positioner()
{
return $this->_positioner;
}
/**
* @return AbstractFrameReflower
*/
function get_reflower()
{
return $this->_reflower;
}
/**
* @param Frame $root
*/
function set_root(Frame $root)
{
$this->_root = $root;
if ($this->_frame instanceof AbstractFrameDecorator) {
$this->_frame->set_root($root);
}
}
/**
* @return Page
*/
function get_root()
{
return $this->_root;
}
/**
* @return Block
*/
function find_block_parent()
{
// Find our nearest block level parent
if (isset($this->_block_parent)) {
return $this->_block_parent;
}
$p = $this->get_parent();
while ($p) {
if ($p->is_block()) {
break;
}
$p = $p->get_parent();
}
return $this->_block_parent = $p;
}
/**
* @return AbstractFrameDecorator
*/
function find_positioned_parent()
{
// Find our nearest relative positioned parent
if (isset($this->_positioned_parent)) {
return $this->_positioned_parent;
}
$p = $this->get_parent();
while ($p) {
if ($p->is_positioned()) {
break;
}
$p = $p->get_parent();
}
if (!$p) {
$p = $this->_root;
}
return $this->_positioned_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|null $child
* @param bool $page_break
* @param bool $forced Whether the page break is forced.
*
* @throws Exception
*/
public function split(?Frame $child = null, bool $page_break = false, bool $forced = false): void
{
if (is_null($child)) {
$this->get_parent()->split($this, $page_break, $forced);
return;
}
if ($child->get_parent() !== $this) {
throw new Exception("Unable to split: frame is not a child of this one.");
}
$this->revert_counter_increment();
$node = $this->_frame->get_node();
$split = $this->copy($node->cloneNode());
$style = $this->_frame->get_style();
$split_style = $split->get_style();
// Truncate the box decoration at the split, except for the body
if ($node->nodeName !== "body") {
// Clear bottom decoration of original frame
$style->margin_bottom = 0.0;
$style->padding_bottom = 0.0;
$style->border_bottom_width = 0.0;
$style->border_bottom_left_radius = 0.0;
$style->border_bottom_right_radius = 0.0;
// Clear top decoration of split frame
$split_style->margin_top = 0.0;
$split_style->padding_top = 0.0;
$split_style->border_top_width = 0.0;
$split_style->border_top_left_radius = 0.0;
$split_style->border_top_right_radius = 0.0;
$split_style->page_break_before = "auto";
}
$split_style->text_indent = 0.0;
$split_style->counter_reset = "none";
$this->is_split = true;
$split->is_split_off = true;
$split->_already_pushed = true;
$this->get_parent()->insert_child_after($split, $this);
if ($this instanceof Block) {
// Remove the frames that will be moved to the new split node from
// the line boxes
$this->remove_frames_from_line($child);
// recalculate the float offsets after paging
foreach ($this->get_line_boxes() as $line_box) {
$line_box->get_float_offsets();
}
}
if (!$forced) {
// Reset top margin in case of an unforced page break
// https://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
$child->get_style()->margin_top = 0.0;
}
// Add $child 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, $page_break, $forced);
// Preserve the current counter values. This must be done after the
// parent split, as counters get reset on frame reset
$split->_counters = $this->_counters;
}
/**
* @param array $counters
*/
public function reset_counters(array $counters): void
{
foreach ($counters as $id => $value) {
$this->reset_counter($id, $value);
}
}
/**
* @param string $id
* @param int $value
*/
public function reset_counter(string $id = self::DEFAULT_COUNTER, int $value = 0): void
{
$this->get_parent()->_counters[$id] = $value;
}
/**
* @param array $counters
*/
public function decrement_counters(array $counters): void
{
foreach ($counters as $id => $increment) {
$this->increment_counter($id, $increment * -1);
}
}
/**
* @param array $counters
*/
public function increment_counters(array $counters): void
{
foreach ($counters as $id => $increment) {
$this->increment_counter($id, $increment);
}
}
/**
* @param string $id
* @param int $increment
*/
public function increment_counter(string $id = self::DEFAULT_COUNTER, int $increment = 1): void
{
$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;
}
}
/**
* @param string $id
* @return AbstractFrameDecorator|null
*/
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;
}
return null;
}
/**
* @param string $id
* @param string $type
* @return bool|string
*
* TODO: What version is the best : this one or the one in ListBullet ?
*/
function counter_value(string $id = self::DEFAULT_COUNTER, string $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", STR_PAD_LEFT);
case "lower-roman":
return Helpers::dec2roman($value);
case "upper-roman":
return mb_strtoupper(Helpers::dec2roman($value));
case "lower-latin":
case "lower-alpha":
return chr((($value - 1) % 26) + ord('a'));
case "upper-latin":
case "upper-alpha":
return chr((($value - 1) % 26) + ord('A'));
case "lower-greek":
return Helpers::unichr($value + 944);
case "upper-greek":
return Helpers::unichr($value + 912);
}
}
final function position()
{
$this->_positioner->position($this);
}
/**
* @param float $offset_x
* @param float $offset_y
* @param bool $ignore_self
*/
final function move(float $offset_x, float $offset_y, bool $ignore_self = false): void
{
$this->_positioner->move($this, $offset_x, $offset_y, $ignore_self);
}
/**
* @param Block|null $block
*/
final function reflow(Block $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);
}
/**
* @return array
*/
final public function get_min_max_width(): array
{
return $this->_reflower->get_min_max_width();
}
}