1<?php
2/**
3 * @package dompdf
4 * @link    http://dompdf.github.com/
5 * @author  Benj Carson <benjcarson@digitaljunkies.ca>
6 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7 */
8namespace Dompdf\FrameDecorator;
9
10use Dompdf\Dompdf;
11use Dompdf\Frame;
12use Dompdf\LineBox;
13
14/**
15 * Decorates frames for block layout
16 *
17 * @access  private
18 * @package dompdf
19 */
20class Block extends AbstractFrameDecorator
21{
22    /**
23     * Current line index
24     *
25     * @var int
26     */
27    protected $_cl;
28
29    /**
30     * The block's line boxes
31     *
32     * @var LineBox[]
33     */
34    protected $_line_boxes;
35
36    /**
37     * Block constructor.
38     * @param Frame $frame
39     * @param Dompdf $dompdf
40     */
41    function __construct(Frame $frame, Dompdf $dompdf)
42    {
43        parent::__construct($frame, $dompdf);
44
45        $this->_line_boxes = array(new LineBox($this));
46        $this->_cl = 0;
47    }
48
49    /**
50     *
51     */
52    function reset()
53    {
54        parent::reset();
55
56        $this->_line_boxes = array(new LineBox($this));
57        $this->_cl = 0;
58    }
59
60    /**
61     * @return LineBox
62     */
63    function get_current_line_box()
64    {
65        return $this->_line_boxes[$this->_cl];
66    }
67
68    /**
69     * @return integer
70     */
71    function get_current_line_number()
72    {
73        return $this->_cl;
74    }
75
76    /**
77     * @return LineBox[]
78     */
79    function get_line_boxes()
80    {
81        return $this->_line_boxes;
82    }
83
84    /**
85     * @param integer $line_number
86     * @return integer
87     */
88    function set_current_line_number($line_number)
89    {
90        $line_boxes_count = count($this->_line_boxes);
91        $cl = max(min($line_number, $line_boxes_count), 0);
92        return ($this->_cl = $cl);
93    }
94
95    /**
96     * @param integer $i
97     */
98    function clear_line($i)
99    {
100        if (isset($this->_line_boxes[$i])) {
101            unset($this->_line_boxes[$i]);
102        }
103    }
104
105    /**
106     * @param Frame $frame
107     */
108    function add_frame_to_line(Frame $frame)
109    {
110        if (!$frame->is_in_flow()) {
111            return;
112        }
113
114        $style = $frame->get_style();
115
116        $frame->set_containing_line($this->_line_boxes[$this->_cl]);
117
118        /*
119        // Adds a new line after a block, only if certain conditions are met
120        if ((($frame instanceof Inline && $frame->get_node()->nodeName !== "br") ||
121              $frame instanceof Text && trim($frame->get_text())) &&
122            ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" &&
123             $this->_line_boxes[$this->_cl]->w > 0 )) {
124
125               $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
126               $this->add_line();
127
128               // Add each child of the inline frame to the line individually
129               foreach ($frame->get_children() as $child)
130                 $this->add_frame_to_line( $child );
131        }
132        else*/
133
134        // Handle inline frames (which are effectively wrappers)
135        if ($frame instanceof Inline) {
136            // Handle line breaks
137            if ($frame->get_node()->nodeName === "br") {
138                $this->maximize_line_height($style->length_in_pt($style->line_height), $frame);
139                $this->add_line(true);
140            }
141
142            return;
143        }
144
145        // Trim leading text if this is an empty line.  Kinda a hack to put it here,
146        // but what can you do...
147        if ($this->get_current_line_box()->w == 0 &&
148            $frame->is_text_node() &&
149            !$frame->is_pre()
150        ) {
151            $frame->set_text(ltrim($frame->get_text()));
152            $frame->recalculate_width();
153        }
154
155        $w = $frame->get_margin_width();
156
157        // FIXME: Why? Doesn't quite seem to be the correct thing to do,
158        // but does appear to be necessary. Hack to handle wrapped white space?
159        if ($w == 0 && $frame->get_node()->nodeName !== "hr") {
160            return;
161        }
162
163        // Debugging code:
164        /*
165        Helpers::pre_r("\n<h3>Adding frame to line:</h3>");
166
167        //    Helpers::pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");
168        //    Helpers::pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
169        if ( $frame->is_text_node() )
170          Helpers::pre_r('"'.$frame->get_node()->nodeValue.'"');
171
172        Helpers::pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w);
173        Helpers::pre_r("Frame: " . get_class($frame));
174        Helpers::pre_r("Frame width: "  . $w);
175        Helpers::pre_r("Frame height: " . $frame->get_margin_height());
176        Helpers::pre_r("Containing block width: " . $this->get_containing_block("w"));
177        */
178        // End debugging
179
180        $line = $this->_line_boxes[$this->_cl];
181        if ($line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) {
182            $this->add_line();
183        }
184
185        $frame->position();
186
187        $current_line = $this->_line_boxes[$this->_cl];
188        $current_line->add_frame($frame);
189
190        if ($frame->is_text_node()) {
191            $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text())));
192        }
193
194        $this->increase_line_width($w);
195
196        $this->maximize_line_height($frame->get_margin_height(), $frame);
197    }
198
199    /**
200     * @param Frame $frame
201     */
202    function remove_frames_from_line(Frame $frame)
203    {
204        // Search backwards through the lines for $frame
205        $i = $this->_cl;
206        $j = null;
207
208        while ($i >= 0) {
209            if (($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false) {
210                break;
211            }
212
213            $i--;
214        }
215
216        if ($j === false) {
217            return;
218        }
219
220        // Remove $frame and all frames that follow
221        while ($j < count($this->_line_boxes[$i]->get_frames())) {
222            $frames = $this->_line_boxes[$i]->get_frames();
223            $f = $frames[$j];
224            $frames[$j] = null;
225            unset($frames[$j]);
226            $j++;
227            $this->_line_boxes[$i]->w -= $f->get_margin_width();
228        }
229
230        // Recalculate the height of the line
231        $h = 0;
232        foreach ($this->_line_boxes[$i]->get_frames() as $f) {
233            $h = max($h, $f->get_margin_height());
234        }
235
236        $this->_line_boxes[$i]->h = $h;
237
238        // Remove all lines that follow
239        while ($this->_cl > $i) {
240            $this->_line_boxes[$this->_cl] = null;
241            unset($this->_line_boxes[$this->_cl]);
242            $this->_cl--;
243        }
244    }
245
246    /**
247     * @param float $w
248     */
249    function increase_line_width($w)
250    {
251        $this->_line_boxes[$this->_cl]->w += $w;
252    }
253
254    /**
255     * @param $val
256     * @param Frame $frame
257     */
258    function maximize_line_height($val, Frame $frame)
259    {
260        if ($val > $this->_line_boxes[$this->_cl]->h) {
261            $this->_line_boxes[$this->_cl]->tallest_frame = $frame;
262            $this->_line_boxes[$this->_cl]->h = $val;
263        }
264    }
265
266    /**
267     * @param bool $br
268     */
269    function add_line($br = false)
270    {
271
272//     if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 ||
273//       return;
274
275        $this->_line_boxes[$this->_cl]->br = $br;
276        $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h;
277
278        $new_line = new LineBox($this, $y);
279
280        $this->_line_boxes[++$this->_cl] = $new_line;
281    }
282
283    //........................................................................
284}
285