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