<?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\FrameDecorator;





use Dompdf\Dompdf;


use Dompdf\Frame;


use Dompdf\LineBox;





/**


 * Decorates frames for block layout


 *


 * @access  private


 * @package dompdf


 */


class Block extends AbstractFrameDecorator


{


    /**


     * Current line index


     *


     * @var int


     */


    protected $_cl;





    /**


     * The block's line boxes


     *


     * @var LineBox[]


     */


    protected $_line_boxes;





    /**


     * Block constructor.


     * @param Frame $frame


     * @param Dompdf $dompdf


     */


    function __construct(Frame $frame, Dompdf $dompdf)


    {


        parent::__construct($frame, $dompdf);





        $this->_line_boxes = [new LineBox($this)];


        $this->_cl = 0;


    }





    /**


     *


     */


    function reset()


    {


        parent::reset();





        $this->_line_boxes = [new LineBox($this)];


        $this->_cl = 0;


    }





    /**


     * @return LineBox


     */


    function get_current_line_box()


    {


        return $this->_line_boxes[$this->_cl];


    }





    /**


     * @return integer


     */


    function get_current_line_number()


    {


        return $this->_cl;


    }





    /**


     * @return LineBox[]


     */


    function get_line_boxes()


    {


        return $this->_line_boxes;


    }





    /**


     * @param integer $line_number


     * @return integer


     */


    function set_current_line_number($line_number)


    {


        $line_boxes_count = count($this->_line_boxes);


        $cl = max(min($line_number, $line_boxes_count), 0);


        return ($this->_cl = $cl);


    }





    /**


     * @param integer $i


     */


    function clear_line($i)


    {


        if (isset($this->_line_boxes[$i])) {


            unset($this->_line_boxes[$i]);


        }


    }





    /**


     * @param Frame $frame


     */


    function add_frame_to_line(Frame $frame)


    {


        if (!$frame->is_in_flow()) {


            return;


        }





        $style = $frame->get_style();





        $frame->set_containing_line($this->_line_boxes[$this->_cl]);





        /*


        // Adds a new line after a block, only if certain conditions are met


        if ((($frame instanceof Inline && $frame->get_node()->nodeName !== "br") ||


              $frame instanceof Text && trim($frame->get_text())) &&


            ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" &&


             $this->_line_boxes[$this->_cl]->w > 0 )) {





               $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );


               $this->add_line();





               // Add each child of the inline frame to the line individually


               foreach ($frame->get_children() as $child)


                 $this->add_frame_to_line( $child );


        }


        else*/





        // Handle inline frames (which are effectively wrappers)


        if ($frame instanceof Inline) {


            // Handle line breaks


            if ($frame->get_node()->nodeName === "br") {


                $this->maximize_line_height($style->line_height, $frame);


                $this->add_line(true);


            }





            return;


        }





        // Trim leading text if this is an empty line.  Kinda a hack to put it here,


        // but what can you do...


        if ($this->get_current_line_box()->w == 0 &&


            $frame->is_text_node() &&


            !$frame->is_pre()


        ) {


            $frame->set_text(ltrim($frame->get_text()));


            $frame->recalculate_width();


        }





        $w = $frame->get_margin_width();





        // FIXME: Why? Doesn't quite seem to be the correct thing to do,


        // but does appear to be necessary. Hack to handle wrapped white space?


        if ($w == 0 && $frame->get_node()->nodeName !== "hr" && !$frame->is_pre()) {


            return;


        }





        // Debugging code:


        /*


        Helpers::pre_r("\n<h3>Adding frame to line:</h3>");





        //    Helpers::pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");


        //    Helpers::pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");


        if ( $frame->is_text_node() )


          Helpers::pre_r('"'.$frame->get_node()->nodeValue.'"');





        Helpers::pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w);


        Helpers::pre_r("Frame: " . get_class($frame));


        Helpers::pre_r("Frame width: "  . $w);


        Helpers::pre_r("Frame height: " . $frame->get_margin_height());


        Helpers::pre_r("Containing block width: " . $this->get_containing_block("w"));


        */


        // End debugging





        $line = $this->_line_boxes[$this->_cl];


        if ($line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) {


            $this->add_line();


        }





        $frame->position();





        $current_line = $this->_line_boxes[$this->_cl];


        $current_line->add_frame($frame);





        if ($frame->is_text_node()) {


            // split the text into words (used to determine spacing between words on justified lines)


            // The regex splits on everything that's a separator (^\S double negative), excluding nbsp (\xa0)


            // This currently excludes the "narrow nbsp" character


            $words = preg_split('/[^\S\xA0]+/u', trim($frame->get_text()));


            $current_line->wc += count($words);


        }





        $this->increase_line_width($w);





        $this->maximize_line_height($frame->get_margin_height(), $frame);


    }





    /**


     * @param Frame $frame


     */


    function remove_frames_from_line(Frame $frame)


    {


        // Search backwards through the lines for $frame


        $i = $this->_cl;


        $j = null;





        while ($i >= 0) {


            if (($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false) {


                break;


            }





            $i--;


        }





        if ($j === false) {


            return;


        }





        // Remove $frame and all frames that follow


        while ($j < count($this->_line_boxes[$i]->get_frames())) {


            $frames = $this->_line_boxes[$i]->get_frames();


            $f = $frames[$j];


            $frames[$j] = null;


            unset($frames[$j]);


            $j++;


            $this->_line_boxes[$i]->w -= $f->get_margin_width();


        }





        // Recalculate the height of the line


        $h = 0;


        foreach ($this->_line_boxes[$i]->get_frames() as $f) {


            $h = max($h, $f->get_margin_height());


        }





        $this->_line_boxes[$i]->h = $h;





        // Remove all lines that follow


        while ($this->_cl > $i) {


            $this->_line_boxes[$this->_cl] = null;


            unset($this->_line_boxes[$this->_cl]);


            $this->_cl--;


        }


    }





    /**


     * @param float $w


     */


    function increase_line_width($w)


    {


        $this->_line_boxes[$this->_cl]->w += $w;


    }





    /**


     * @param $val


     * @param Frame $frame


     */


    function maximize_line_height($val, Frame $frame)


    {


        if ($val > $this->_line_boxes[$this->_cl]->h) {


            $this->_line_boxes[$this->_cl]->tallest_frame = $frame;


            $this->_line_boxes[$this->_cl]->h = $val;


        }


    }





    /**


     * @param bool $br


     */


    function add_line($br = false)


    {


        $this->_line_boxes[$this->_cl]->br = $br;


        $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h;





        $new_line = new LineBox($this, $y);





        $this->_line_boxes[++$this->_cl] = $new_line;


    }





    //........................................................................


}


