These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @package dompdf |
||
4 | * @link http://dompdf.github.com/ |
||
5 | * @author Benj Carson <[email protected]> |
||
6 | * @author Fabien Ménager <[email protected]> |
||
7 | * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License |
||
8 | */ |
||
9 | namespace Dompdf\FrameReflower; |
||
10 | |||
11 | use Dompdf\Frame; |
||
12 | use Dompdf\FrameDecorator\Block as BlockFrameDecorator; |
||
13 | use Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator; |
||
14 | use Dompdf\FrameDecorator\Text as TextFrameDecorator; |
||
15 | use Dompdf\Exception; |
||
16 | use Dompdf\Css\Style; |
||
17 | |||
18 | /** |
||
19 | * Reflows block frames |
||
20 | * |
||
21 | * @package dompdf |
||
22 | */ |
||
23 | class Block extends AbstractFrameReflower |
||
24 | { |
||
25 | // Minimum line width to justify, as fraction of available width |
||
26 | const MIN_JUSTIFY_WIDTH = 0.80; |
||
27 | |||
28 | /** |
||
29 | * @var BlockFrameDecorator |
||
30 | */ |
||
31 | protected $_frame; |
||
32 | |||
33 | function __construct(BlockFrameDecorator $frame) |
||
34 | { |
||
35 | parent::__construct($frame); |
||
36 | } |
||
37 | |||
38 | /** |
||
39 | * Calculate the ideal used value for the width property as per: |
||
40 | * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins |
||
41 | * |
||
42 | * @param float $width |
||
43 | * |
||
44 | * @return array |
||
45 | */ |
||
46 | protected function _calculate_width($width) |
||
47 | { |
||
48 | $frame = $this->_frame; |
||
49 | $style = $frame->get_style(); |
||
50 | $w = $frame->get_containing_block("w"); |
||
51 | |||
52 | if ($style->position === "fixed") { |
||
53 | $w = $frame->get_parent()->get_containing_block("w"); |
||
54 | } |
||
55 | |||
56 | $rm = $style->length_in_pt($style->margin_right, $w); |
||
57 | $lm = $style->length_in_pt($style->margin_left, $w); |
||
58 | |||
59 | $left = $style->length_in_pt($style->left, $w); |
||
60 | $right = $style->length_in_pt($style->right, $w); |
||
61 | |||
62 | // Handle 'auto' values |
||
63 | $dims = array($style->border_left_width, |
||
64 | $style->border_right_width, |
||
65 | $style->padding_left, |
||
66 | $style->padding_right, |
||
67 | $width !== "auto" ? $width : 0, |
||
68 | $rm !== "auto" ? $rm : 0, |
||
69 | $lm !== "auto" ? $lm : 0); |
||
70 | |||
71 | // absolutely positioned boxes take the 'left' and 'right' properties into account |
||
72 | if ($frame->is_absolute()) { |
||
73 | $absolute = true; |
||
74 | $dims[] = $left !== "auto" ? $left : 0; |
||
75 | $dims[] = $right !== "auto" ? $right : 0; |
||
76 | } else { |
||
77 | $absolute = false; |
||
78 | } |
||
79 | |||
80 | $sum = (float)$style->length_in_pt($dims, $w); |
||
81 | |||
82 | // Compare to the containing block |
||
83 | $diff = $w - $sum; |
||
84 | |||
85 | if ($diff > 0) { |
||
86 | if ($absolute) { |
||
87 | // resolve auto properties: see |
||
88 | // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width |
||
89 | |||
90 | if ($width === "auto" && $left === "auto" && $right === "auto") { |
||
91 | if ($lm === "auto") { |
||
92 | $lm = 0; |
||
93 | } |
||
94 | if ($rm === "auto") { |
||
95 | $rm = 0; |
||
96 | } |
||
97 | |||
98 | // Technically, the width should be "shrink-to-fit" i.e. based on the |
||
99 | // preferred width of the content... a little too costly here as a |
||
100 | // special case. Just get the width to take up the slack: |
||
101 | $left = 0; |
||
102 | $right = 0; |
||
103 | $width = $diff; |
||
104 | } else if ($width === "auto") { |
||
105 | if ($lm === "auto") { |
||
106 | $lm = 0; |
||
107 | } |
||
108 | if ($rm === "auto") { |
||
109 | $rm = 0; |
||
110 | } |
||
111 | if ($left === "auto") { |
||
112 | $left = 0; |
||
113 | } |
||
114 | if ($right === "auto") { |
||
115 | $right = 0; |
||
116 | } |
||
117 | |||
118 | $width = $diff; |
||
119 | } else if ($left === "auto") { |
||
120 | if ($lm === "auto") { |
||
121 | $lm = 0; |
||
122 | } |
||
123 | if ($rm === "auto") { |
||
124 | $rm = 0; |
||
125 | } |
||
126 | if ($right === "auto") { |
||
127 | $right = 0; |
||
128 | } |
||
129 | |||
130 | $left = $diff; |
||
131 | } else if ($right === "auto") { |
||
132 | if ($lm === "auto") { |
||
133 | $lm = 0; |
||
134 | } |
||
135 | if ($rm === "auto") { |
||
136 | $rm = 0; |
||
137 | } |
||
138 | |||
139 | $right = $diff; |
||
140 | } |
||
141 | |||
142 | } else { |
||
143 | // Find auto properties and get them to take up the slack |
||
144 | if ($width === "auto") { |
||
145 | $width = $diff; |
||
146 | } else if ($lm === "auto" && $rm === "auto") { |
||
147 | $lm = $rm = round($diff / 2); |
||
148 | } else if ($lm === "auto") { |
||
149 | $lm = $diff; |
||
150 | } else if ($rm === "auto") { |
||
151 | $rm = $diff; |
||
152 | } |
||
153 | } |
||
154 | } else if ($diff < 0) { |
||
155 | // We are over constrained--set margin-right to the difference |
||
156 | $rm = $diff; |
||
157 | } |
||
158 | |||
159 | return array( |
||
160 | "width" => $width, |
||
161 | "margin_left" => $lm, |
||
162 | "margin_right" => $rm, |
||
163 | "left" => $left, |
||
164 | "right" => $right, |
||
165 | ); |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Call the above function, but resolve max/min widths |
||
170 | * |
||
171 | * @throws Exception |
||
172 | * @return array |
||
173 | */ |
||
174 | protected function _calculate_restricted_width() |
||
175 | { |
||
176 | $frame = $this->_frame; |
||
177 | $style = $frame->get_style(); |
||
178 | $cb = $frame->get_containing_block(); |
||
179 | |||
180 | if ($style->position === "fixed") { |
||
181 | $cb = $frame->get_root()->get_containing_block(); |
||
182 | } |
||
183 | |||
184 | //if ( $style->position === "absolute" ) |
||
185 | // $cb = $frame->find_positionned_parent()->get_containing_block(); |
||
186 | |||
187 | if (!isset($cb["w"])) { |
||
188 | throw new Exception("Box property calculation requires containing block width"); |
||
189 | } |
||
190 | |||
191 | // Treat width 100% as auto |
||
192 | if ($style->width === "100%") { |
||
193 | $width = "auto"; |
||
194 | } else { |
||
195 | $width = $style->length_in_pt($style->width, $cb["w"]); |
||
196 | } |
||
197 | |||
198 | $calculate_width = $this->_calculate_width($width); |
||
199 | $margin_left = $calculate_width['margin_left']; |
||
200 | $margin_right = $calculate_width['margin_right']; |
||
201 | $width = $calculate_width['width']; |
||
202 | $left = $calculate_width['left']; |
||
203 | $right = $calculate_width['right']; |
||
204 | |||
205 | // Handle min/max width |
||
206 | $min_width = $style->length_in_pt($style->min_width, $cb["w"]); |
||
207 | $max_width = $style->length_in_pt($style->max_width, $cb["w"]); |
||
208 | |||
209 | if ($max_width !== "none" && $min_width > $max_width) { |
||
210 | list($max_width, $min_width) = array($min_width, $max_width); |
||
211 | } |
||
212 | |||
213 | if ($max_width !== "none" && $width > $max_width) { |
||
214 | extract($this->_calculate_width($max_width)); |
||
215 | } |
||
216 | |||
217 | if ($width < $min_width) { |
||
218 | $calculate_width = $this->_calculate_width($min_width); |
||
219 | $margin_left = $calculate_width['margin_left']; |
||
220 | $margin_right = $calculate_width['margin_right']; |
||
221 | $width = $calculate_width['width']; |
||
222 | $left = $calculate_width['left']; |
||
223 | $right = $calculate_width['right']; |
||
224 | } |
||
225 | |||
226 | return array($width, $margin_left, $margin_right, $left, $right); |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * Determine the unrestricted height of content within the block |
||
231 | * not by adding each line's height, but by getting the last line's position. |
||
232 | * This because lines could have been pushed lower by a clearing element. |
||
233 | * |
||
234 | * @return float |
||
235 | */ |
||
236 | protected function _calculate_content_height() |
||
237 | { |
||
238 | $height = 0; |
||
239 | $lines = $this->_frame->get_line_boxes(); |
||
240 | if (count($lines) > 0) { |
||
241 | $last_line = end($lines); |
||
242 | $content_box = $this->_frame->get_content_box(); |
||
243 | $height = $last_line->y + $last_line->h - $content_box["y"]; |
||
244 | } |
||
245 | return $height; |
||
246 | } |
||
247 | |||
248 | /** |
||
249 | * Determine the frame's restricted height |
||
250 | * |
||
251 | * @return array |
||
252 | */ |
||
253 | protected function _calculate_restricted_height() |
||
254 | { |
||
255 | $frame = $this->_frame; |
||
256 | $style = $frame->get_style(); |
||
257 | $content_height = $this->_calculate_content_height(); |
||
258 | $cb = $frame->get_containing_block(); |
||
259 | |||
260 | $height = $style->length_in_pt($style->height, $cb["h"]); |
||
261 | |||
262 | $top = $style->length_in_pt($style->top, $cb["h"]); |
||
263 | $bottom = $style->length_in_pt($style->bottom, $cb["h"]); |
||
264 | |||
265 | $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]); |
||
266 | $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); |
||
267 | |||
268 | if ($frame->is_absolute()) { |
||
269 | |||
270 | // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height |
||
271 | |||
272 | $dims = array($top !== "auto" ? $top : 0, |
||
273 | $style->margin_top !== "auto" ? $style->margin_top : 0, |
||
274 | $style->padding_top, |
||
275 | $style->border_top_width, |
||
276 | $height !== "auto" ? $height : 0, |
||
277 | $style->border_bottom_width, |
||
278 | $style->padding_bottom, |
||
279 | $style->margin_bottom !== "auto" ? $style->margin_bottom : 0, |
||
280 | $bottom !== "auto" ? $bottom : 0); |
||
281 | |||
282 | $sum = (float)$style->length_in_pt($dims, $cb["h"]); |
||
283 | |||
284 | $diff = $cb["h"] - $sum; |
||
285 | |||
286 | if ($diff > 0) { |
||
287 | if ($height === "auto" && $top === "auto" && $bottom === "auto") { |
||
288 | if ($margin_top === "auto") { |
||
289 | $margin_top = 0; |
||
290 | } |
||
291 | if ($margin_bottom === "auto") { |
||
292 | $margin_bottom = 0; |
||
293 | } |
||
294 | |||
295 | $height = $diff; |
||
296 | } else if ($height === "auto" && $top === "auto") { |
||
297 | if ($margin_top === "auto") { |
||
298 | $margin_top = 0; |
||
299 | } |
||
300 | if ($margin_bottom === "auto") { |
||
301 | $margin_bottom = 0; |
||
302 | } |
||
303 | |||
304 | $height = $content_height; |
||
305 | $top = $diff - $content_height; |
||
306 | } else if ($height === "auto" && $bottom === "auto") { |
||
307 | if ($margin_top === "auto") { |
||
308 | $margin_top = 0; |
||
309 | } |
||
310 | if ($margin_bottom === "auto") { |
||
311 | $margin_bottom = 0; |
||
312 | } |
||
313 | |||
314 | $height = $content_height; |
||
315 | $bottom = $diff - $content_height; |
||
316 | } else if ($top === "auto" && $bottom === "auto") { |
||
317 | if ($margin_top === "auto") { |
||
318 | $margin_top = 0; |
||
319 | } |
||
320 | if ($margin_bottom === "auto") { |
||
321 | $margin_bottom = 0; |
||
322 | } |
||
323 | |||
324 | $bottom = $diff; |
||
325 | } else if ($top === "auto") { |
||
326 | if ($margin_top === "auto") { |
||
327 | $margin_top = 0; |
||
328 | } |
||
329 | if ($margin_bottom === "auto") { |
||
330 | $margin_bottom = 0; |
||
331 | } |
||
332 | |||
333 | $top = $diff; |
||
334 | } else if ($height === "auto") { |
||
335 | if ($margin_top === "auto") { |
||
336 | $margin_top = 0; |
||
337 | } |
||
338 | if ($margin_bottom === "auto") { |
||
339 | $margin_bottom = 0; |
||
340 | } |
||
341 | |||
342 | $height = $diff; |
||
343 | } else if ($bottom === "auto") { |
||
344 | if ($margin_top === "auto") { |
||
345 | $margin_top = 0; |
||
346 | } |
||
347 | if ($margin_bottom === "auto") { |
||
348 | $margin_bottom = 0; |
||
349 | } |
||
350 | |||
351 | $bottom = $diff; |
||
352 | } else { |
||
353 | if ($style->overflow === "visible") { |
||
354 | // set all autos to zero |
||
355 | if ($margin_top === "auto") { |
||
356 | $margin_top = 0; |
||
357 | } |
||
358 | if ($margin_bottom === "auto") { |
||
359 | $margin_bottom = 0; |
||
360 | } |
||
361 | if ($top === "auto") { |
||
362 | $top = 0; |
||
363 | } |
||
364 | if ($bottom === "auto") { |
||
365 | $bottom = 0; |
||
366 | } |
||
367 | if ($height === "auto") { |
||
368 | $height = $content_height; |
||
369 | } |
||
370 | } |
||
371 | |||
372 | // FIXME: overflow hidden |
||
373 | } |
||
374 | } |
||
375 | |||
376 | } else { |
||
377 | // Expand the height if overflow is visible |
||
378 | if ($height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) { |
||
379 | $height = $content_height; |
||
380 | } |
||
381 | |||
382 | // FIXME: this should probably be moved to a seperate function as per |
||
383 | // _calculate_restricted_width |
||
384 | |||
385 | // Only handle min/max height if the height is independent of the frame's content |
||
386 | if (!($style->overflow === "visible" || ($style->overflow === "hidden" && $height === "auto"))) { |
||
387 | $min_height = $style->min_height; |
||
388 | $max_height = $style->max_height; |
||
389 | |||
390 | View Code Duplication | if (isset($cb["h"])) { |
|
391 | $min_height = $style->length_in_pt($min_height, $cb["h"]); |
||
392 | $max_height = $style->length_in_pt($max_height, $cb["h"]); |
||
393 | } else if (isset($cb["w"])) { |
||
394 | if (mb_strpos($min_height, "%") !== false) { |
||
395 | $min_height = 0; |
||
396 | } else { |
||
397 | $min_height = $style->length_in_pt($min_height, $cb["w"]); |
||
398 | } |
||
399 | |||
400 | if (mb_strpos($max_height, "%") !== false) { |
||
401 | $max_height = "none"; |
||
402 | } else { |
||
403 | $max_height = $style->length_in_pt($max_height, $cb["w"]); |
||
404 | } |
||
405 | } |
||
406 | |||
407 | View Code Duplication | if ($max_height !== "none" && $min_height > $max_height) { |
|
408 | // Swap 'em |
||
409 | list($max_height, $min_height) = array($min_height, $max_height); |
||
410 | } |
||
411 | |||
412 | if ($max_height !== "none" && $height > $max_height) { |
||
413 | $height = $max_height; |
||
414 | } |
||
415 | |||
416 | if ($height < $min_height) { |
||
417 | $height = $min_height; |
||
418 | } |
||
419 | } |
||
420 | } |
||
421 | |||
422 | return array($height, $margin_top, $margin_bottom, $top, $bottom); |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Adjust the justification of each of our lines. |
||
427 | * http://www.w3.org/TR/CSS21/text.html#propdef-text-align |
||
428 | */ |
||
429 | protected function _text_align() |
||
430 | { |
||
431 | $style = $this->_frame->get_style(); |
||
432 | $w = $this->_frame->get_containing_block("w"); |
||
433 | $width = (float)$style->length_in_pt($style->width, $w); |
||
434 | |||
435 | switch ($style->text_align) { |
||
436 | default: |
||
437 | case "left": |
||
438 | foreach ($this->_frame->get_line_boxes() as $line) { |
||
439 | if (!$line->left) { |
||
440 | continue; |
||
441 | } |
||
442 | |||
443 | foreach ($line->get_frames() as $frame) { |
||
444 | if ($frame instanceof BlockFrameDecorator) { |
||
445 | continue; |
||
446 | } |
||
447 | $frame->set_position($frame->get_position("x") + $line->left); |
||
448 | } |
||
449 | } |
||
450 | return; |
||
451 | |||
452 | View Code Duplication | case "right": |
|
453 | foreach ($this->_frame->get_line_boxes() as $line) { |
||
454 | // Move each child over by $dx |
||
455 | $dx = $width - $line->w - $line->right; |
||
456 | |||
457 | foreach ($line->get_frames() as $frame) { |
||
458 | // Block frames are not aligned by text-align |
||
459 | if ($frame instanceof BlockFrameDecorator) { |
||
460 | continue; |
||
461 | } |
||
462 | |||
463 | $frame->set_position($frame->get_position("x") + $dx); |
||
464 | } |
||
465 | } |
||
466 | break; |
||
467 | |||
468 | case "justify": |
||
469 | // We justify all lines except the last one |
||
470 | $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards) |
||
471 | $last_line = array_pop($lines); |
||
472 | |||
473 | foreach ($lines as $i => $line) { |
||
474 | if ($line->br) { |
||
475 | unset($lines[$i]); |
||
476 | } |
||
477 | } |
||
478 | |||
479 | // One space character's width. Will be used to get a more accurate spacing |
||
480 | $space_width = $this->get_dompdf()->getFontMetrics()->getTextWidth(" ", $style->font_family, $style->font_size); |
||
481 | |||
482 | foreach ($lines as $line) { |
||
483 | View Code Duplication | if ($line->left) { |
|
484 | foreach ($line->get_frames() as $frame) { |
||
485 | if (!$frame instanceof TextFrameDecorator) { |
||
486 | continue; |
||
487 | } |
||
488 | |||
489 | $frame->set_position($frame->get_position("x") + $line->left); |
||
490 | } |
||
491 | } |
||
492 | |||
493 | // Set the spacing for each child |
||
494 | if ($line->wc > 1) { |
||
495 | $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1); |
||
496 | } else { |
||
497 | $spacing = 0; |
||
498 | } |
||
499 | |||
500 | $dx = 0; |
||
501 | foreach ($line->get_frames() as $frame) { |
||
502 | if (!$frame instanceof TextFrameDecorator) { |
||
503 | continue; |
||
504 | } |
||
505 | |||
506 | $text = $frame->get_text(); |
||
507 | $spaces = mb_substr_count($text, " "); |
||
508 | |||
509 | $char_spacing = (float)$style->length_in_pt($style->letter_spacing); |
||
510 | $_spacing = $spacing + $char_spacing; |
||
511 | |||
512 | $frame->set_position($frame->get_position("x") + $dx); |
||
513 | $frame->set_text_spacing($_spacing); |
||
514 | |||
515 | $dx += $spaces * $_spacing; |
||
516 | } |
||
517 | |||
518 | // The line (should) now occupy the entire width |
||
519 | $line->w = $width; |
||
520 | } |
||
521 | |||
522 | // Adjust the last line if necessary |
||
523 | View Code Duplication | if ($last_line->left) { |
|
524 | foreach ($last_line->get_frames() as $frame) { |
||
525 | if ($frame instanceof BlockFrameDecorator) { |
||
526 | continue; |
||
527 | } |
||
528 | $frame->set_position($frame->get_position("x") + $last_line->left); |
||
529 | } |
||
530 | } |
||
531 | break; |
||
532 | |||
533 | case "center": |
||
534 | View Code Duplication | case "centre": |
|
535 | foreach ($this->_frame->get_line_boxes() as $line) { |
||
536 | // Centre each line by moving each frame in the line by: |
||
537 | $dx = ($width + $line->left - $line->w - $line->right) / 2; |
||
538 | |||
539 | foreach ($line->get_frames() as $frame) { |
||
540 | // Block frames are not aligned by text-align |
||
541 | if ($frame instanceof BlockFrameDecorator) { |
||
542 | continue; |
||
543 | } |
||
544 | |||
545 | $frame->set_position($frame->get_position("x") + $dx); |
||
546 | } |
||
547 | } |
||
548 | break; |
||
549 | } |
||
550 | } |
||
551 | |||
552 | /** |
||
553 | * Align inline children vertically. |
||
554 | * Aligns each child vertically after each line is reflowed |
||
555 | */ |
||
556 | function vertical_align() |
||
557 | { |
||
558 | $canvas = null; |
||
559 | |||
560 | foreach ($this->_frame->get_line_boxes() as $line) { |
||
561 | |||
562 | $height = $line->h; |
||
563 | |||
564 | foreach ($line->get_frames() as $frame) { |
||
565 | $style = $frame->get_style(); |
||
566 | $isInlineBlock = ( |
||
567 | '-dompdf-image' === $style->display |
||
568 | || 'inline-block' === $style->display |
||
569 | || 'inline-table' === $style->display |
||
570 | ); |
||
571 | if (!$isInlineBlock && $style->display !== "inline") { |
||
572 | continue; |
||
573 | } |
||
574 | |||
575 | if (!isset($canvas)) { |
||
576 | $canvas = $frame->get_root()->get_dompdf()->get_canvas(); |
||
577 | } |
||
578 | |||
579 | $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size); |
||
580 | $y_offset = 0; |
||
581 | |||
582 | //FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?) |
||
583 | if($isInlineBlock) { |
||
584 | $lineFrames = $line->get_frames(); |
||
585 | if (count($lineFrames) == 1) { |
||
586 | continue; |
||
587 | } |
||
588 | $frameBox = $frame->get_frame()->get_border_box(); |
||
589 | $imageHeightDiff = $height * 0.8 - (float)$frameBox['h']; |
||
590 | |||
591 | $align = $frame->get_style()->vertical_align; |
||
592 | if (in_array($align, Style::$vertical_align_keywords) === true) { |
||
593 | switch ($align) { |
||
594 | case "middle": |
||
595 | $y_offset = $imageHeightDiff / 2; |
||
596 | break; |
||
597 | |||
598 | case "sub": |
||
599 | $y_offset = 0.3 * $height + $imageHeightDiff; |
||
600 | break; |
||
601 | |||
602 | case "super": |
||
603 | $y_offset = -0.2 * $height + $imageHeightDiff; |
||
604 | break; |
||
605 | |||
606 | case "text-top": // FIXME: this should be the height of the frame minus the height of the text |
||
607 | $y_offset = $height - (float)$style->length_in_pt($style->line_height, $style->font_size); |
||
0 ignored issues
–
show
|
|||
608 | break; |
||
609 | |||
610 | case "top": |
||
611 | break; |
||
612 | |||
613 | case "text-bottom": // FIXME: align bottom of image with the descender? |
||
614 | case "bottom": |
||
615 | $y_offset = 0.3 * $height + $imageHeightDiff; |
||
616 | break; |
||
617 | |||
618 | case "baseline": |
||
619 | default: |
||
620 | $y_offset = $imageHeightDiff; |
||
621 | break; |
||
622 | } |
||
623 | } else { |
||
624 | $y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - (float)$frameBox['h']; |
||
625 | } |
||
626 | } else { |
||
627 | $parent = $frame->get_parent(); |
||
628 | if ($parent instanceof TableCellFrameDecorator) { |
||
629 | $align = "baseline"; |
||
630 | } else { |
||
631 | $align = $parent->get_style()->vertical_align; |
||
632 | } |
||
633 | if (in_array($align, Style::$vertical_align_keywords) === true) { |
||
634 | switch ($align) { |
||
635 | case "middle": |
||
636 | $y_offset = ($height * 0.8 - $baseline) / 2; |
||
637 | break; |
||
638 | |||
639 | case "sub": |
||
640 | $y_offset = $height * 0.8 - $baseline * 0.5; |
||
641 | break; |
||
642 | |||
643 | case "super": |
||
644 | $y_offset = $height * 0.8 - $baseline * 1.4; |
||
645 | break; |
||
646 | |||
647 | case "text-top": |
||
648 | case "top": // Not strictly accurate, but good enough for now |
||
649 | break; |
||
650 | |||
651 | case "text-bottom": |
||
652 | case "bottom": |
||
653 | $y_offset = $height * 0.8 - $baseline; |
||
654 | break; |
||
655 | |||
656 | case "baseline": |
||
657 | default: |
||
658 | $y_offset = $height * 0.8 - $baseline; |
||
659 | break; |
||
660 | } |
||
661 | } else { |
||
662 | $y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size); |
||
663 | } |
||
664 | } |
||
665 | |||
666 | if ($y_offset !== 0) { |
||
667 | $frame->move(0, $y_offset); |
||
668 | } |
||
669 | } |
||
670 | } |
||
671 | } |
||
672 | |||
673 | /** |
||
674 | * @param Frame $child |
||
675 | */ |
||
676 | function process_clear(Frame $child) |
||
677 | { |
||
678 | $child_style = $child->get_style(); |
||
679 | $root = $this->_frame->get_root(); |
||
680 | |||
681 | // Handle "clear" |
||
682 | if ($child_style->clear !== "none") { |
||
683 | //TODO: this is a WIP for handling clear/float frames that are in between inline frames |
||
684 | if ($child->get_prev_sibling() !== null) { |
||
685 | $this->_frame->add_line(); |
||
686 | } |
||
687 | if ($child_style->float !== "none" && $child->get_next_sibling()) { |
||
688 | $this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1); |
||
689 | } |
||
690 | |||
691 | $lowest_y = $root->get_lowest_float_offset($child); |
||
692 | |||
693 | // If a float is still applying, we handle it |
||
694 | if ($lowest_y) { |
||
695 | if ($child->is_in_flow()) { |
||
696 | $line_box = $this->_frame->get_current_line_box(); |
||
697 | $line_box->y = $lowest_y + $child->get_margin_height(); |
||
698 | $line_box->left = 0; |
||
699 | $line_box->right = 0; |
||
700 | } |
||
701 | |||
702 | $child->move(0, $lowest_y - $child->get_position("y")); |
||
703 | } |
||
704 | } |
||
705 | } |
||
706 | |||
707 | /** |
||
708 | * @param Frame $child |
||
709 | * @param float $cb_x |
||
710 | * @param float $cb_w |
||
711 | */ |
||
712 | function process_float(Frame $child, $cb_x, $cb_w) |
||
713 | { |
||
714 | $child_style = $child->get_style(); |
||
715 | $root = $this->_frame->get_root(); |
||
716 | |||
717 | // Handle "float" |
||
718 | if ($child_style->float !== "none") { |
||
719 | $root->add_floating_frame($child); |
||
720 | |||
721 | // Remove next frame's beginning whitespace |
||
722 | $next = $child->get_next_sibling(); |
||
723 | if ($next && $next instanceof TextFrameDecorator) { |
||
724 | $next->set_text(ltrim($next->get_text())); |
||
725 | } |
||
726 | |||
727 | $line_box = $this->_frame->get_current_line_box(); |
||
728 | list($old_x, $old_y) = $child->get_position(); |
||
729 | |||
730 | $float_x = $cb_x; |
||
731 | $float_y = $old_y; |
||
732 | $float_w = $child->get_margin_width(); |
||
733 | |||
734 | if ($child_style->clear === "none") { |
||
735 | switch ($child_style->float) { |
||
736 | case "left": |
||
737 | $float_x += $line_box->left; |
||
738 | break; |
||
739 | case "right": |
||
740 | $float_x += ($cb_w - $line_box->right - $float_w); |
||
741 | break; |
||
742 | } |
||
743 | } else { |
||
744 | if ($child_style->float === "right") { |
||
745 | $float_x += ($cb_w - $float_w); |
||
746 | } |
||
747 | } |
||
748 | |||
749 | if ($cb_w < $float_x + $float_w - $old_x) { |
||
750 | // TODO handle when floating elements don't fit |
||
751 | } |
||
752 | |||
753 | $line_box->get_float_offsets(); |
||
754 | |||
755 | if ($child->_float_next_line) { |
||
756 | $float_y += $line_box->h; |
||
757 | } |
||
758 | |||
759 | $child->set_position($float_x, $float_y); |
||
760 | $child->move($float_x - $old_x, $float_y - $old_y, true); |
||
761 | } |
||
762 | } |
||
763 | |||
764 | /** |
||
765 | * @param BlockFrameDecorator $block |
||
766 | * @return mixed|void |
||
767 | */ |
||
768 | function reflow(BlockFrameDecorator $block = null) |
||
769 | { |
||
770 | |||
771 | // Check if a page break is forced |
||
772 | $page = $this->_frame->get_root(); |
||
773 | $page->check_forced_page_break($this->_frame); |
||
774 | |||
775 | // Bail if the page is full |
||
776 | if ($page->is_full()) { |
||
777 | return; |
||
778 | } |
||
779 | |||
780 | // Generated content |
||
781 | $this->_set_content(); |
||
782 | |||
783 | // Collapse margins if required |
||
784 | $this->_collapse_margins(); |
||
785 | |||
786 | $style = $this->_frame->get_style(); |
||
787 | $cb = $this->_frame->get_containing_block(); |
||
788 | |||
789 | if ($style->position === "fixed") { |
||
790 | $cb = $this->_frame->get_root()->get_containing_block(); |
||
791 | } |
||
792 | |||
793 | // Determine the constraints imposed by this frame: calculate the width |
||
794 | // of the content area: |
||
795 | list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width(); |
||
796 | |||
797 | // Store the calculated properties |
||
798 | $style->width = $w; |
||
799 | $style->margin_left = $left_margin; |
||
800 | $style->margin_right = $right_margin; |
||
801 | $style->left = $left; |
||
802 | $style->right = $right; |
||
803 | |||
804 | // Update the position |
||
805 | $this->_frame->position(); |
||
806 | list($x, $y) = $this->_frame->get_position(); |
||
807 | |||
808 | // Adjust the first line based on the text-indent property |
||
809 | $indent = (float)$style->length_in_pt($style->text_indent, $cb["w"]); |
||
810 | $this->_frame->increase_line_width($indent); |
||
811 | |||
812 | // Determine the content edge |
||
813 | $top = (float)$style->length_in_pt(array($style->margin_top, |
||
814 | $style->padding_top, |
||
815 | $style->border_top_width), $cb["h"]); |
||
816 | |||
817 | $bottom = (float)$style->length_in_pt(array($style->border_bottom_width, |
||
818 | $style->margin_bottom, |
||
819 | $style->padding_bottom), $cb["h"]); |
||
820 | |||
821 | $cb_x = $x + (float)$left_margin + (float)$style->length_in_pt(array($style->border_left_width, |
||
822 | $style->padding_left), $cb["w"]); |
||
823 | |||
824 | $cb_y = $y + $top; |
||
825 | |||
826 | $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y; |
||
827 | |||
828 | // Set the y position of the first line in this block |
||
829 | $line_box = $this->_frame->get_current_line_box(); |
||
830 | $line_box->y = $cb_y; |
||
831 | $line_box->get_float_offsets(); |
||
832 | |||
833 | // Set the containing blocks and reflow each child |
||
834 | View Code Duplication | foreach ($this->_frame->get_children() as $child) { |
|
835 | |||
836 | // Bail out if the page is full |
||
837 | if ($page->is_full()) { |
||
838 | break; |
||
839 | } |
||
840 | |||
841 | $child->set_containing_block($cb_x, $cb_y, $w, $cb_h); |
||
842 | |||
843 | $this->process_clear($child); |
||
844 | |||
845 | $child->reflow($this->_frame); |
||
846 | |||
847 | // Don't add the child to the line if a page break has occurred |
||
848 | if ($page->check_page_break($child)) { |
||
849 | break; |
||
850 | } |
||
851 | |||
852 | $this->process_float($child, $cb_x, $w); |
||
853 | } |
||
854 | |||
855 | // Determine our height |
||
856 | list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height(); |
||
857 | $style->height = $height; |
||
858 | $style->margin_top = $margin_top; |
||
859 | $style->margin_bottom = $margin_bottom; |
||
860 | $style->top = $top; |
||
861 | $style->bottom = $bottom; |
||
862 | |||
863 | $orig_style = $this->_frame->get_original_style(); |
||
864 | |||
865 | $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto")); |
||
866 | |||
867 | // Absolute positioning measurement |
||
868 | if ($needs_reposition) { |
||
869 | if ($orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto")) { |
||
870 | $width = 0; |
||
871 | foreach ($this->_frame->get_line_boxes() as $line) { |
||
872 | $width = max($line->w, $width); |
||
873 | } |
||
874 | $style->width = $width; |
||
875 | } |
||
876 | |||
877 | $style->left = $orig_style->left; |
||
878 | $style->right = $orig_style->right; |
||
879 | } |
||
880 | |||
881 | // Calculate inline-block / float auto-widths |
||
882 | if (($style->display === "inline-block" || $style->float !== 'none') && $orig_style->width === 'auto') { |
||
883 | $width = 0; |
||
884 | |||
885 | foreach ($this->_frame->get_line_boxes() as $line) { |
||
886 | $line->recalculate_width(); |
||
887 | |||
888 | $width = max($line->w, $width); |
||
889 | } |
||
890 | |||
891 | if ($width === 0) { |
||
892 | foreach ($this->_frame->get_children() as $child) { |
||
893 | $width += $child->calculate_auto_width(); |
||
894 | } |
||
895 | } |
||
896 | |||
897 | $style->width = $width; |
||
898 | } |
||
899 | |||
900 | $this->_text_align(); |
||
901 | $this->vertical_align(); |
||
902 | |||
903 | // Absolute positioning |
||
904 | if ($needs_reposition) { |
||
905 | list($x, $y) = $this->_frame->get_position(); |
||
906 | $this->_frame->position(); |
||
907 | list($new_x, $new_y) = $this->_frame->get_position(); |
||
908 | $this->_frame->move($new_x - $x, $new_y - $y, true); |
||
909 | } |
||
910 | |||
911 | if ($block && $this->_frame->is_in_flow()) { |
||
912 | $block->add_frame_to_line($this->_frame); |
||
913 | |||
914 | // May be inline-block |
||
915 | if ($style->display === "block") { |
||
916 | $block->add_line(); |
||
917 | } |
||
918 | } |
||
919 | } |
||
920 | |||
921 | /** |
||
922 | * Determine current frame width based on contents |
||
923 | * |
||
924 | * @return float |
||
925 | */ |
||
926 | public function calculate_auto_width() |
||
927 | { |
||
928 | $width = 0; |
||
929 | |||
930 | foreach ($this->_frame->get_line_boxes() as $line) { |
||
931 | $line_width = 0; |
||
932 | |||
933 | foreach ($line->get_frames() as $frame) { |
||
934 | if ($frame->get_original_style()->width == 'auto') { |
||
935 | $line_width += $frame->calculate_auto_width(); |
||
936 | } else { |
||
937 | $line_width += $frame->get_margin_width(); |
||
938 | } |
||
939 | } |
||
940 | |||
941 | $width = max($line_width, $width); |
||
942 | } |
||
943 | |||
944 | $this->_frame->get_style()->width = $width; |
||
945 | |||
946 | return $this->_frame->get_margin_width(); |
||
947 | } |
||
948 | } |
||
949 |
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.