PK       ! 0W      FrameTreeIterator.phpnu ̗        <?php
namespace Dompdf\Frame;

use Iterator;
use Dompdf\Frame;

/**
 * Pre-order Iterator
 *
 * Returns frames in preorder traversal order (parent then children)
 *
 * @access private
 * @package dompdf
 */
class FrameTreeIterator implements Iterator
{
    /**
     * @var Frame
     */
    protected $_root;

    /**
     * @var Frame[]
     */
    protected $_stack = [];

    /**
     * @var int
     */
    protected $_num;

    /**
     * @param Frame $root
     */
    public function __construct(Frame $root)
    {
        $this->_stack[] = $this->_root = $root;
        $this->_num = 0;
    }

    public function rewind(): void
    {
        $this->_stack = [$this->_root];
        $this->_num = 0;
    }

    /**
     * @return bool
     */
    public function valid(): bool
    {
        return count($this->_stack) > 0;
    }

    /**
     * @return int
     */
    public function key(): int
    {
        return $this->_num;
    }

    /**
     * @return Frame
     */
    public function current(): Frame
    {
        return end($this->_stack);
    }

    public function next(): void
    {
        $b = end($this->_stack);

        // Pop last element
        unset($this->_stack[key($this->_stack)]);
        $this->_num++;

        // Push all children onto the stack in reverse order
        if ($c = $b->get_last_child()) {
            $this->_stack[] = $c;
            while ($c = $c->get_prev_sibling()) {
                $this->_stack[] = $c;
            }
        }
    }
}
PK       ! m      Factory.phpnu ̗        <?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
 */
namespace Dompdf\Frame;

use Dompdf\Dompdf;
use Dompdf\Exception;
use Dompdf\Frame;
use Dompdf\FrameDecorator\AbstractFrameDecorator;
use DOMXPath;
use Dompdf\FrameDecorator\Page as PageFrameDecorator;
use Dompdf\FrameReflower\Page as PageFrameReflower;
use Dompdf\Positioner\AbstractPositioner;

/**
 * Contains frame decorating logic
 *
 * This class is responsible for assigning the correct {@link AbstractFrameDecorator},
 * {@link AbstractPositioner}, and {@link AbstractFrameReflower} objects to {@link Frame}
 * objects.  This is determined primarily by the Frame's display type, but
 * also by the Frame's node's type (e.g. DomElement vs. #text)
 *
 * @access  private
 * @package dompdf
 */
class Factory
{

     /**
     * Array of positioners for specific frame types
     *
     * @var AbstractPositioner[]
     */
    protected static $_positioners;

    /**
     * Decorate the root Frame
     *
     * @param $root   Frame The frame to decorate
     * @param $dompdf Dompdf The dompdf instance
     *
     * @return PageFrameDecorator
     */
    static function decorate_root(Frame $root, Dompdf $dompdf)
    {
        $frame = new PageFrameDecorator($root, $dompdf);
        $frame->set_reflower(new PageFrameReflower($frame));
        $root->set_decorator($frame);

        return $frame;
    }

    /**
     * Decorate a Frame
     *
     * @param Frame $frame   The frame to decorate
     * @param Dompdf $dompdf The dompdf instance
     * @param Frame $root    The root of the frame
     *
     * @throws Exception
     * @return AbstractFrameDecorator
     * FIXME: this is admittedly a little smelly...
     */
    static function decorate_frame(Frame $frame, Dompdf $dompdf, Frame $root = null)
    {
        $style = $frame->get_style();
        $display = $style->display;

        switch ($display) {

            case "block":
                $positioner = "Block";
                $decorator = "Block";
                $reflower = "Block";
                break;

            case "inline-block":
                $positioner = "Inline";
                $decorator = "Block";
                $reflower = "Block";
                break;

            case "inline":
                $positioner = "Inline";
                if ($frame->is_text_node()) {
                    $decorator = "Text";
                    $reflower = "Text";
                } else {
                    $decorator = "Inline";
                    $reflower = "Inline";
                }
                break;

            case "table":
                $positioner = "Block";
                $decorator = "Table";
                $reflower = "Table";
                break;

            case "inline-table":
                $positioner = "Inline";
                $decorator = "Table";
                $reflower = "Table";
                break;

            case "table-row-group":
            case "table-header-group":
            case "table-footer-group":
                $positioner = "NullPositioner";
                $decorator = "TableRowGroup";
                $reflower = "TableRowGroup";
                break;

            case "table-row":
                $positioner = "NullPositioner";
                $decorator = "TableRow";
                $reflower = "TableRow";
                break;

            case "table-cell":
                $positioner = "TableCell";
                $decorator = "TableCell";
                $reflower = "TableCell";
                break;

            case "list-item":
                $positioner = "Block";
                $decorator = "Block";
                $reflower = "Block";
                break;

            case "-dompdf-list-bullet":
                if ($style->list_style_position === "inside") {
                    $positioner = "Inline";
                } else {
                    $positioner = "ListBullet";
                }

                if ($style->list_style_image !== "none") {
                    $decorator = "ListBulletImage";
                } else {
                    $decorator = "ListBullet";
                }

                $reflower = "ListBullet";
                break;

            case "-dompdf-image":
                $positioner = "Inline";
                $decorator = "Image";
                $reflower = "Image";
                break;

            case "-dompdf-br":
                $positioner = "Inline";
                $decorator = "Inline";
                $reflower = "Inline";
                break;

            default:
            case "none":
                if ($style->_dompdf_keep !== "yes") {
                    // Remove the node and the frame
                    $frame->get_parent()->remove_child($frame);
                    return;
                }

                $positioner = "NullPositioner";
                $decorator = "NullFrameDecorator";
                $reflower = "NullFrameReflower";
                break;
        }

        // Handle CSS position
        $position = $style->position;

        if ($position === "absolute") {
            $positioner = "Absolute";
        } else {
            if ($position === "fixed") {
                $positioner = "Fixed";
            }
        }

        $node = $frame->get_node();

        // Handle nodeName
        if ($node->nodeName === "img") {
            $style->display = "-dompdf-image";
            $decorator = "Image";
            $reflower = "Image";
        }

        $decorator  = "Dompdf\\FrameDecorator\\$decorator";
        $reflower   = "Dompdf\\FrameReflower\\$reflower";

        /** @var AbstractFrameDecorator $deco */
        $deco = new $decorator($frame, $dompdf);

        $deco->set_positioner(self::getPositionerInstance($positioner));
        $deco->set_reflower(new $reflower($deco, $dompdf->getFontMetrics()));

        if ($root) {
            $deco->set_root($root);
        }

        if ($display === "list-item") {
            // Insert a list-bullet frame
            $xml = $dompdf->getDom();
            $bullet_node = $xml->createElement("bullet"); // arbitrary choice
            $b_f = new Frame($bullet_node);

            $node = $frame->get_node();
            $parent_node = $node->parentNode;

            if ($parent_node) {
                if (!$parent_node->hasAttribute("dompdf-children-count")) {
                    $xpath = new DOMXPath($xml);
                    $count = $xpath->query("li", $parent_node)->length;
                    $parent_node->setAttribute("dompdf-children-count", $count);
                }

                if (is_numeric($node->getAttribute("value"))) {
                    $index = intval($node->getAttribute("value"));
                } else {
                    if (!$parent_node->hasAttribute("dompdf-counter")) {
                        $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
                    } else {
                        $index = (int)$parent_node->getAttribute("dompdf-counter") + 1;
                    }
                }

                $parent_node->setAttribute("dompdf-counter", $index);
                $bullet_node->setAttribute("dompdf-counter", $index);
            }

            $new_style = $dompdf->getCss()->create_style();
            $new_style->display = "-dompdf-list-bullet";
            $new_style->inherit($style);
            $b_f->set_style($new_style);

            $deco->prepend_child(Factory::decorate_frame($b_f, $dompdf, $root));
        }

        return $deco;
    }

    /**
     * Creates Positioners
     *
     * @param string $type type of positioner to use
     * @return AbstractPositioner
     */
    protected static function getPositionerInstance($type)
    {
        if (!isset(self::$_positioners[$type])) {
            $class = '\\Dompdf\\Positioner\\'.$type;
            self::$_positioners[$type] = new $class();
        }
        return self::$_positioners[$type];
    }
}
PK       ! tR      FrameListIterator.phpnu ̗        <?php
namespace Dompdf\Frame;

use Iterator;
use Dompdf\Frame;

/**
 * Linked-list Iterator
 *
 * Returns children in order and allows for the list to change during iteration,
 * provided the changes occur to or after the current element.
 *
 * @access private
 * @package dompdf
 */
class FrameListIterator implements Iterator
{
    /**
     * @var Frame
     */
    protected $parent;

    /**
     * @var Frame|null
     */
    protected $cur;

    /**
     * @var Frame|null
     */
    protected $prev;

    /**
     * @var int
     */
    protected $num;

    /**
     * @param Frame $frame
     */
    public function __construct(Frame $frame)
    {
        $this->parent = $frame;
        $this->rewind();
    }

    public function rewind(): void
    {
        $this->cur = $this->parent->get_first_child();
        $this->prev = null;
        $this->num = 0;
    }

    /**
     * @return bool
     */
    public function valid(): bool
    {
        return $this->cur !== null;
    }

    /**
     * @return int
     */
    public function key(): int
    {
        return $this->num;
    }

    /**
     * @return Frame|null
     */
    public function current(): ?Frame
    {
        return $this->cur;
    }

    public function next(): void
    {
        if ($this->cur === null) {
            return;
        }

        if ($this->cur->get_parent() === $this->parent) {
            $this->prev = $this->cur;
            $this->cur = $this->cur->get_next_sibling();
            $this->num++;
        } else {
            // Continue from the previous child if the current frame has been
            // moved to another parent
            $this->cur = $this->prev !== null
                ? $this->prev->get_next_sibling()
                : $this->parent->get_first_child();
        }
    }
}
PK       ! i!  !    FrameTree.phpnu ̗        <?php

namespace Dompdf\Frame;

use DOMDocument;
use DOMNode;
use DOMElement;
use DOMXPath;

use Dompdf\Exception;
use Dompdf\Frame;

/**
 * @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
 */

/**
 * Represents an entire document as a tree of frames
 *
 * The FrameTree consists of {@link Frame} objects each tied to specific
 * DOMNode objects in a specific DomDocument.  The FrameTree has the same
 * structure as the DomDocument, but adds additional capabilities for
 * styling and layout.
 *
 * @package dompdf
 */
class FrameTree
{
    /**
     * Tags to ignore while parsing the tree
     *
     * @var array
     */
    protected static $HIDDEN_TAGS = [
        "area",
        "base",
        "basefont",
        "head",
        "style",
        "meta",
        "title",
        "colgroup",
        "noembed",
        "param",
        "#comment"
    ];

    /**
     * The main DomDocument
     *
     * @see http://ca2.php.net/manual/en/ref.dom.php
     * @var DOMDocument
     */
    protected $_dom;

    /**
     * The root node of the FrameTree.
     *
     * @var Frame
     */
    protected $_root;

    /**
     * Subtrees of absolutely positioned elements
     *
     * @var array of Frames
     */
    protected $_absolute_frames;

    /**
     * A mapping of {@link Frame} objects to DOMNode objects
     *
     * @var array
     */
    protected $_registry;

    /**
     * Class constructor
     *
     * @param DOMDocument $dom the main DomDocument object representing the current html document
     */
    public function __construct(DomDocument $dom)
    {
        $this->_dom = $dom;
        $this->_root = null;
        $this->_registry = [];
    }

    /**
     * Returns the DOMDocument object representing the current html document
     *
     * @return DOMDocument
     */
    public function get_dom()
    {
        return $this->_dom;
    }

    /**
     * Returns the root frame of the tree
     *
     * @return Frame
     */
    public function get_root()
    {
        return $this->_root;
    }

    /**
     * Returns a specific frame given its id
     *
     * @param string $id
     *
     * @return Frame|null
     */
    public function get_frame($id)
    {
        return isset($this->_registry[$id]) ? $this->_registry[$id] : null;
    }

    /**
     * Returns a post-order iterator for all frames in the tree
     *
     * @return FrameTreeList|Frame[]
     */
    public function get_frames()
    {
        return new FrameTreeList($this->_root);
    }

    /**
     * Builds the tree
     */
    public function build_tree()
    {
        $html = $this->_dom->getElementsByTagName("html")->item(0);
        if (is_null($html)) {
            $html = $this->_dom->firstChild;
        }

        if (is_null($html)) {
            throw new Exception("Requested HTML document contains no data.");
        }

        $this->fix_tables();

        $this->_root = $this->_build_tree_r($html);
    }

    /**
     * Adds missing TBODYs around TR
     */
    protected function fix_tables()
    {
        $xp = new DOMXPath($this->_dom);

        // Move table caption before the table
        // FIXME find a better way to deal with it...
        $captions = $xp->query('//table/caption');
        foreach ($captions as $caption) {
            $table = $caption->parentNode;
            $table->parentNode->insertBefore($caption, $table);
        }

        $firstRows = $xp->query('//table/tr[1]');
        /** @var DOMElement $tableChild */
        foreach ($firstRows as $tableChild) {
            $tbody = $this->_dom->createElement('tbody');
            $tableNode = $tableChild->parentNode;
            do {
                if ($tableChild->nodeName === 'tr') {
                    $tmpNode = $tableChild;
                    $tableChild = $tableChild->nextSibling;
                    $tableNode->removeChild($tmpNode);
                    $tbody->appendChild($tmpNode);
                } else {
                    if ($tbody->hasChildNodes() === true) {
                        $tableNode->insertBefore($tbody, $tableChild);
                        $tbody = $this->_dom->createElement('tbody');
                    }
                    $tableChild = $tableChild->nextSibling;
                }
            } while ($tableChild);
            if ($tbody->hasChildNodes() === true) {
                $tableNode->appendChild($tbody);
            }
        }
    }

    // FIXME: temporary hack, preferably we will improve rendering of sequential #text nodes
    /**
     * Remove a child from a node
     *
     * Remove a child from a node. If the removed node results in two
     * adjacent #text nodes then combine them.
     *
     * @param DOMNode $node the current DOMNode being considered
     * @param array $children an array of nodes that are the children of $node
     * @param int $index index from the $children array of the node to remove
     */
    protected function _remove_node(DOMNode $node, array &$children, $index)
    {
        $child = $children[$index];
        $previousChild = $child->previousSibling;
        $nextChild = $child->nextSibling;
        $node->removeChild($child);
        if (isset($previousChild, $nextChild)) {
            if ($previousChild->nodeName === "#text" && $nextChild->nodeName === "#text") {
                $previousChild->nodeValue .= $nextChild->nodeValue;
                $this->_remove_node($node, $children, $index+1);
            }
        }
        array_splice($children, $index, 1);
    }

    /**
     * Recursively adds {@link Frame} objects to the tree
     *
     * Recursively build a tree of Frame objects based on a dom tree.
     * No layout information is calculated at this time, although the
     * tree may be adjusted (i.e. nodes and frames for generated content
     * and images may be created).
     *
     * @param DOMNode $node the current DOMNode being considered
     *
     * @return Frame
     */
    protected function _build_tree_r(DOMNode $node)
    {
        $frame = new Frame($node);
        $id = $frame->get_id();
        $this->_registry[$id] = $frame;

        if (!$node->hasChildNodes()) {
            return $frame;
        }

        // Store the children in an array so that the tree can be modified
        $children = [];
        $length = $node->childNodes->length;
        for ($i = 0; $i < $length; $i++) {
            $children[] = $node->childNodes->item($i);
        }
        $index = 0;
        // INFO: We don't advance $index if a node is removed to avoid skipping nodes
        while ($index < count($children)) {
            $child = $children[$index];
            $nodeName = strtolower($child->nodeName);

            // Skip non-displaying nodes
            if (in_array($nodeName, self::$HIDDEN_TAGS)) {
                if ($nodeName !== "head" && $nodeName !== "style") {
                    $this->_remove_node($node, $children, $index);
                } else {
                    $index++;
                }
                continue;
            }
            // Skip empty text nodes
            if ($nodeName === "#text" && $child->nodeValue === "") {
                $this->_remove_node($node, $children, $index);
                continue;
            }
            // Skip empty image nodes
            if ($nodeName === "img" && $child->getAttribute("src") === "") {
                $this->_remove_node($node, $children, $index);
                continue;
            }

            if (is_object($child)) {
                $frame->append_child($this->_build_tree_r($child), false);
            }
            $index++;
        }

        return $frame;
    }

    /**
     * @param DOMElement $node
     * @param DOMElement $new_node
     * @param string $pos
     *
     * @return mixed
     */
    public function insert_node(DOMElement $node, DOMElement $new_node, $pos)
    {
        if ($pos === "after" || !$node->firstChild) {
            $node->appendChild($new_node);
        } else {
            $node->insertBefore($new_node, $node->firstChild);
        }

        $this->_build_tree_r($new_node);

        $frame_id = $new_node->getAttribute("frame_id");
        $frame = $this->get_frame($frame_id);

        $parent_id = $node->getAttribute("frame_id");
        $parent = $this->get_frame($parent_id);

        if ($parent) {
            if ($pos === "before") {
                $parent->prepend_child($frame, false);
            } else {
                $parent->append_child($frame, false);
            }
        }

        return $frame_id;
    }
}
PK       ! 4/  /    FrameTreeList.phpnu ̗        <?php
namespace Dompdf\Frame;

use IteratorAggregate;
use Dompdf\Frame;

/**
 * Pre-order IteratorAggregate
 *
 * @access private
 * @package dompdf
 */
class FrameTreeList implements IteratorAggregate
{
    /**
     * @var Frame
     */
    protected $_root;

    /**
     * @param Frame $root
     */
    public function __construct(Frame $root)
    {
        $this->_root = $root;
    }

    /**
     * @return FrameTreeIterator
     */
    public function getIterator(): FrameTreeIterator
    {
        return new FrameTreeIterator($this->_root);
    }
}
PK       ! <r      FrameList.phpnu ̗        <?php
namespace Dompdf\Frame;

use Dompdf\Frame;
use IteratorAggregate;

/**
 * Linked-list IteratorAggregate
 *
 * @access private
 * @package dompdf
 */
class FrameList implements IteratorAggregate
{
    /**
     * @var Frame
     */
    protected $_frame;

    /**
     * @param Frame $frame
     */
    function __construct($frame)
    {
        $this->_frame = $frame;
    }

    /**
     * @return FrameListIterator
     */
    function getIterator(): FrameListIterator
    {
        return new FrameListIterator($this->_frame);
    }
}
PK         ! 0W                    FrameTreeIterator.phpnu ̗        PK         ! m                   Factory.phpnu ̗        PK         ! tR                =&  FrameListIterator.phpnu ̗        PK         ! i!  !              -  FrameTree.phpnu ̗        PK         ! 4/  /              O  FrameTreeList.phpnu ̗        PK         ! <r                0R  FrameList.phpnu ̗        PK        T 