PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.17
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.17
1.10.27 1.10.26 1.10.25 1.10.24 1.10.23 1.10.22 1.10.21 1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 All 171 releases
woocommerce-pos / vendor_prefixed / dompdf / dompdf / src / FrameReflower / Block.php

Block.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.17, at vendor_prefixed/dompdf/dompdf/src/FrameReflower/Block.php

773 lines 33.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @package dompdf
5 * @link https://github.com/dompdf/dompdf
6 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7 */
8 namespace WCPOS\Vendor\Dompdf\FrameReflower;
9
10 use WCPOS\Vendor\Dompdf\FrameDecorator\AbstractFrameDecorator;
11 use WCPOS\Vendor\Dompdf\FrameDecorator\Block as BlockFrameDecorator;
12 use WCPOS\Vendor\Dompdf\FrameDecorator\TableCell as TableCellFrameDecorator;
13 use WCPOS\Vendor\Dompdf\FrameDecorator\Text as TextFrameDecorator;
14 use WCPOS\Vendor\Dompdf\Exception;
15 use WCPOS\Vendor\Dompdf\Css\Style;
16 use WCPOS\Vendor\Dompdf\Helpers;
17 /**
18 * Reflows block frames
19 *
20 * @package dompdf
21 */
22 class Block extends AbstractFrameReflower
23 {
24 // Minimum line width to justify, as fraction of available width
25 const MIN_JUSTIFY_WIDTH = 0.8;
26 /**
27 * Frame for this reflower
28 *
29 * @var BlockFrameDecorator
30 */
31 protected $_frame;
32 function __construct(BlockFrameDecorator $frame)
33 {
34 parent::__construct($frame);
35 }
36 /**
37 * Calculate the ideal used value for the width property as per:
38 * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
39 *
40 * @param float $width
41 *
42 * @return array
43 */
44 protected function _calculate_width($width)
45 {
46 $frame = $this->_frame;
47 $style = $frame->get_style();
48 $absolute = $frame->is_absolute();
49 $cb = $frame->get_containing_block();
50 $w = $cb["w"];
51 $rm = $style->length_in_pt($style->margin_right, $w);
52 $lm = $style->length_in_pt($style->margin_left, $w);
53 $left = $style->length_in_pt($style->left, $w);
54 $right = $style->length_in_pt($style->right, $w);
55 // Handle 'auto' values
56 $dims = [$style->border_left_width, $style->border_right_width, $style->padding_left, $style->padding_right, $width !== "auto" ? $width : 0, $rm !== "auto" ? $rm : 0, $lm !== "auto" ? $lm : 0];
57 // absolutely positioned boxes take the 'left' and 'right' properties into account
58 if ($absolute) {
59 $dims[] = $left !== "auto" ? $left : 0;
60 $dims[] = $right !== "auto" ? $right : 0;
61 }
62 $sum = (float) $style->length_in_pt($dims, $w);
63 // Compare to the containing block
64 $diff = $w - $sum;
65 if ($absolute) {
66 // Absolutely positioned
67 // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
68 if ($width === "auto" || $left === "auto" || $right === "auto") {
69 // "all of the three are 'auto'" logic + otherwise case
70 if ($lm === "auto") {
71 $lm = 0;
72 }
73 if ($rm === "auto") {
74 $rm = 0;
75 }
76 $block_parent = $frame->find_block_parent();
77 $parent_content = $block_parent->get_content_box();
78 $line = $block_parent->get_current_line_box();
79 // TODO: This is the in-flow inline position. Use the in-flow
80 // block position if the original display type is block-level
81 $inflow_x = $parent_content["x"] - $cb["x"] + $line->left + $line->w;
82 if ($width === "auto" && $left === "auto" && $right === "auto") {
83 // rule 3, per instruction preceding rule set
84 // shrink-to-fit width
85 $left = $inflow_x;
86 [$min, $max] = $this->get_min_max_child_width();
87 $width = \min(\max($min, $diff - $left), $max);
88 $right = $diff - $left - $width;
89 } elseif ($width === "auto" && $left === "auto") {
90 // rule 1
91 // shrink-to-fit width
92 [$min, $max] = $this->get_min_max_child_width();
93 $width = \min(\max($min, $diff), $max);
94 $left = $diff - $width;
95 } elseif ($width === "auto" && $right === "auto") {
96 // rule 3
97 // shrink-to-fit width
98 [$min, $max] = $this->get_min_max_child_width();
99 $width = \min(\max($min, $diff), $max);
100 $right = $diff - $width;
101 } elseif ($left === "auto" && $right === "auto") {
102 // rule 2
103 $left = $inflow_x;
104 $right = $diff - $left;
105 } elseif ($left === "auto") {
106 // rule 4
107 $left = $diff;
108 } elseif ($width === "auto") {
109 // rule 5
110 $width = \max($diff, 0);
111 } else {
112 // $right === "auto"
113 // rule 6
114 $right = $diff;
115 }
116 } else {
117 // "none of the three are 'auto'" logic described in paragraph preceding the rules
118 if ($diff >= 0) {
119 if ($lm === "auto" && $rm === "auto") {
120 $lm = $rm = $diff / 2;
121 } elseif ($lm === "auto") {
122 $lm = $diff;
123 } elseif ($rm === "auto") {
124 $rm = $diff;
125 }
126 } else {
127 // over-constrained, solve for right
128 $right = $right + $diff;
129 if ($lm === "auto") {
130 $lm = 0;
131 }
132 if ($rm === "auto") {
133 $rm = 0;
134 }
135 }
136 }
137 } elseif ($style->float !== "none" || $style->display === "inline-block") {
138 // Shrink-to-fit width for float and inline block
139 // https://www.w3.org/TR/CSS21/visudet.html#float-width
140 // https://www.w3.org/TR/CSS21/visudet.html#inlineblock-width
141 if ($width === "auto") {
142 [$min, $max] = $this->get_min_max_child_width();
143 $width = \min(\max($min, $diff), $max);
144 }
145 if ($lm === "auto") {
146 $lm = 0;
147 }
148 if ($rm === "auto") {
149 $rm = 0;
150 }
151 } else {
152 // Block-level, normal flow
153 // https://www.w3.org/TR/CSS21/visudet.html#blockwidth
154 if ($diff >= 0) {
155 // Find auto properties and get them to take up the slack
156 if ($width === "auto") {
157 $width = $diff;
158 if ($lm === "auto") {
159 $lm = 0;
160 }
161 if ($rm === "auto") {
162 $rm = 0;
163 }
164 } elseif ($lm === "auto" && $rm === "auto") {
165 $lm = $rm = $diff / 2;
166 } elseif ($lm === "auto") {
167 $lm = $diff;
168 } elseif ($rm === "auto") {
169 $rm = $diff;
170 }
171 } else {
172 // We are over constrained--set margin-right to the difference
173 $rm = (float) $rm + $diff;
174 if ($width === "auto") {
175 $width = 0;
176 }
177 if ($lm === "auto") {
178 $lm = 0;
179 }
180 }
181 }
182 return ["width" => $width, "margin_left" => $lm, "margin_right" => $rm, "left" => $left, "right" => $right];
183 }
184 /**
185 * Call the above function, but resolve max/min widths
186 *
187 * @throws Exception
188 * @return array
189 */
190 protected function _calculate_restricted_width()
191 {
192 $frame = $this->_frame;
193 $style = $frame->get_style();
194 $cb = $frame->get_containing_block();
195 if (!isset($cb["w"])) {
196 throw new Exception("Box property calculation requires containing block width");
197 }
198 $width = $style->length_in_pt($style->width, $cb["w"]);
199 $values = $this->_calculate_width($width);
200 $margin_left = $values["margin_left"];
201 $margin_right = $values["margin_right"];
202 $width = $values["width"];
203 $left = $values["left"];
204 $right = $values["right"];
205 // Handle min/max width
206 // https://www.w3.org/TR/CSS21/visudet.html#min-max-widths
207 $min_width = $this->resolve_min_width($cb["w"]);
208 $max_width = $this->resolve_max_width($cb["w"]);
209 if ($width > $max_width) {
210 $values = $this->_calculate_width($max_width);
211 $margin_left = $values["margin_left"];
212 $margin_right = $values["margin_right"];
213 $width = $values["width"];
214 $left = $values["left"];
215 $right = $values["right"];
216 }
217 if ($width < $min_width) {
218 $values = $this->_calculate_width($min_width);
219 $margin_left = $values["margin_left"];
220 $margin_right = $values["margin_right"];
221 $width = $values["width"];
222 $left = $values["left"];
223 $right = $values["right"];
224 }
225 return [$width, $margin_left, $margin_right, $left, $right];
226 }
227 /**
228 * Determine the unrestricted height of content within the block
229 * not by adding each line's height, but by getting the last line's position.
230 * This because lines could have been pushed lower by a clearing element.
231 *
232 * @return float
233 */
234 protected function _calculate_content_height() : float
235 {
236 $height = 0.0;
237 $lines = $this->_frame->get_line_boxes();
238 if (\count($lines) > 0) {
239 $last_line = \end($lines);
240 $content_box = $this->_frame->get_content_box();
241 $height = $last_line->y + $last_line->h - $content_box["y"];
242 }
243 return $height;
244 }
245 /**
246 * Determine the frame's restricted height
247 *
248 * @return array
249 */
250 protected function _calculate_restricted_height()
251 {
252 $frame = $this->_frame;
253 $style = $frame->get_style();
254 $content_height = $this->_calculate_content_height();
255 $cb = $frame->get_containing_block();
256 $height = $style->length_in_pt($style->height, $cb["h"]);
257 $margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
258 $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
259 $top = $style->length_in_pt($style->top, $cb["h"]);
260 $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
261 if ($frame->is_absolute()) {
262 // Absolutely positioned
263 // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
264 $h_dims = [$top !== "auto" ? $top : 0, $height !== "auto" ? $height : 0, $bottom !== "auto" ? $bottom : 0];
265 $w_dims = [$style->margin_top !== "auto" ? $style->margin_top : 0, $style->padding_top, $style->border_top_width, $style->border_bottom_width, $style->padding_bottom, $style->margin_bottom !== "auto" ? $style->margin_bottom : 0];
266 $sum = (float) $style->length_in_pt($h_dims, $cb["h"]) + (float) $style->length_in_pt($w_dims, $cb["w"]);
267 $diff = $cb["h"] - $sum;
268 if ($height === "auto" || $top === "auto" || $bottom === "auto") {
269 // "all of the three are 'auto'" logic + otherwise case
270 if ($margin_top === "auto") {
271 $margin_top = 0;
272 }
273 if ($margin_bottom === "auto") {
274 $margin_bottom = 0;
275 }
276 $block_parent = $frame->find_block_parent();
277 $current_line = $block_parent->get_current_line_box();
278 // TODO: This is the in-flow inline position. Use the in-flow
279 // block position if the original display type is block-level
280 $inflow_y = $current_line->y - $cb["y"];
281 if ($height === "auto" && $top === "auto" && $bottom === "auto") {
282 // rule 3, per instruction preceding rule set
283 $top = $inflow_y;
284 $height = $content_height;
285 $bottom = $diff - $top - $height;
286 } elseif ($height === "auto" && $top === "auto") {
287 // rule 1
288 $height = $content_height;
289 $top = $diff - $height;
290 } elseif ($height === "auto" && $bottom === "auto") {
291 // rule 3
292 $height = $content_height;
293 $bottom = $diff - $height;
294 } elseif ($top === "auto" && $bottom === "auto") {
295 // rule 2
296 $top = $inflow_y;
297 $bottom = $diff - $top;
298 } elseif ($top === "auto") {
299 // rule 4
300 $top = $diff;
301 } elseif ($height === "auto") {
302 // rule 5
303 $height = \max($diff, 0);
304 } else {
305 // $bottom === "auto"
306 // rule 6
307 $bottom = $diff;
308 }
309 } else {
310 // "none of the three are 'auto'" logic described in paragraph preceding the rules
311 if ($diff >= 0) {
312 if ($margin_top === "auto" && $margin_bottom === "auto") {
313 $margin_top = $margin_bottom = $diff / 2;
314 } elseif ($margin_top === "auto") {
315 $margin_top = $diff;
316 } elseif ($margin_bottom === "auto") {
317 $margin_bottom = $diff;
318 }
319 } else {
320 // over-constrained, solve for bottom
321 $bottom = $bottom + $diff;
322 if ($margin_top === "auto") {
323 $margin_top = 0;
324 }
325 if ($margin_bottom === "auto") {
326 $margin_bottom = 0;
327 }
328 }
329 }
330 } else {
331 // https://www.w3.org/TR/CSS21/visudet.html#normal-block
332 // https://www.w3.org/TR/CSS21/visudet.html#block-root-margin
333 if ($height === "auto") {
334 $height = $content_height;
335 }
336 if ($margin_top === "auto") {
337 $margin_top = 0;
338 }
339 if ($margin_bottom === "auto") {
340 $margin_bottom = 0;
341 }
342 // Handle min/max height
343 // https://www.w3.org/TR/CSS21/visudet.html#min-max-heights
344 $min_height = $this->resolve_min_height($cb["h"]);
345 $max_height = $this->resolve_max_height($cb["h"]);
346 $height = Helpers::clamp($height, $min_height, $max_height);
347 }
348 // TODO: Need to also take min/max height into account for absolute
349 // positioning, using similar logic to the `_calculate_width`/
350 // `calculate_restricted_width` split above. The non-absolute case
351 // can simply clamp height within min/max, as margins and offsets are
352 // not affected
353 return [$height, $margin_top, $margin_bottom, $top, $bottom];
354 }
355 /**
356 * Adjust the justification of each of our lines.
357 * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
358 */
359 protected function _text_align()
360 {
361 $style = $this->_frame->get_style();
362 $w = $this->_frame->get_containing_block("w");
363 $width = (float) $style->length_in_pt($style->width, $w);
364 $text_indent = (float) $style->length_in_pt($style->text_indent, $w);
365 switch ($style->text_align) {
366 default:
367 case "left":
368 foreach ($this->_frame->get_line_boxes() as $line) {
369 if (!$line->inline) {
370 continue;
371 }
372 $line->trim_trailing_ws();
373 if ($line->left) {
374 foreach ($line->frames_to_align() as $frame) {
375 $frame->move($line->left, 0);
376 }
377 }
378 }
379 break;
380 case "right":
381 foreach ($this->_frame->get_line_boxes() as $i => $line) {
382 if (!$line->inline) {
383 continue;
384 }
385 $line->trim_trailing_ws();
386 $indent = $i === 0 ? $text_indent : 0;
387 $dx = $width - $line->w - $line->right - $indent;
388 foreach ($line->frames_to_align() as $frame) {
389 $frame->move($dx, 0);
390 }
391 }
392 break;
393 case "justify":
394 // We justify all lines except the last one, unless the frame
395 // has been split, in which case the actual last line is part of
396 // the split-off frame
397 $lines = $this->_frame->get_line_boxes();
398 $last_line_index = $this->_frame->is_split ? null : \count($lines) - 1;
399 foreach ($lines as $i => $line) {
400 if (!$line->inline) {
401 continue;
402 }
403 $line->trim_trailing_ws();
404 if ($line->left) {
405 foreach ($line->frames_to_align() as $frame) {
406 $frame->move($line->left, 0);
407 }
408 }
409 if ($line->br || $i === $last_line_index) {
410 continue;
411 }
412 $frames = $line->get_frames();
413 $other_frame_count = 0;
414 foreach ($frames as $frame) {
415 if (!$frame instanceof TextFrameDecorator) {
416 $other_frame_count++;
417 }
418 }
419 $word_count = $line->wc + $other_frame_count;
420 // Set the spacing for each child
421 if ($word_count > 1) {
422 $indent = $i === 0 ? $text_indent : 0;
423 $spacing = ($width - $line->get_width() - $indent) / ($word_count - 1);
424 } else {
425 $spacing = 0;
426 }
427 $dx = 0;
428 foreach ($frames as $frame) {
429 if ($frame instanceof TextFrameDecorator) {
430 $text = $frame->get_text();
431 $spaces = \mb_substr_count($text, " ");
432 $frame->move($dx, 0);
433 $frame->set_text_spacing($spacing);
434 $dx += $spaces * $spacing;
435 } else {
436 $frame->move($dx, 0);
437 }
438 }
439 // The line (should) now occupy the entire width
440 $line->w = $width;
441 }
442 break;
443 case "center":
444 case "centre":
445 foreach ($this->_frame->get_line_boxes() as $i => $line) {
446 if (!$line->inline) {
447 continue;
448 }
449 $line->trim_trailing_ws();
450 $indent = $i === 0 ? $text_indent : 0;
451 $dx = ($width + $line->left - $line->w - $line->right - $indent) / 2;
452 foreach ($line->frames_to_align() as $frame) {
453 $frame->move($dx, 0);
454 }
455 }
456 break;
457 }
458 }
459 /**
460 * Align inline children vertically.
461 * Aligns each child vertically after each line is reflowed
462 */
463 function vertical_align()
464 {
465 $fontMetrics = $this->get_dompdf()->getFontMetrics();
466 foreach ($this->_frame->get_line_boxes() as $line) {
467 $height = $line->h;
468 // Move all markers to the top of the line box
469 foreach ($line->get_list_markers() as $marker) {
470 $x = $marker->get_position("x");
471 $marker->set_position($x, $line->y);
472 }
473 foreach ($line->frames_to_align() as $frame) {
474 $style = $frame->get_style();
475 $isInlineBlock = $style->display !== "inline" && $style->display !== "-dompdf-list-bullet";
476 $baseline = $fontMetrics->getFontBaseline($style->font_family, $style->font_size);
477 $y_offset = 0;
478 //FIXME: The 0.8 ratio applied to the height is arbitrary (used to accommodate descenders?)
479 if ($isInlineBlock) {
480 // Workaround: Skip vertical alignment if the frame is the
481 // only one one the line, excluding empty text frames, which
482 // may be the result of trailing white space
483 // FIXME: This special case should be removed once vertical
484 // alignment is properly fixed
485 $skip = \true;
486 foreach ($line->get_frames() as $other) {
487 if ($other !== $frame && !($other->is_text_node() && $other->get_node()->nodeValue === "")) {
488 $skip = \false;
489 break;
490 }
491 }
492 if ($skip) {
493 continue;
494 }
495 $marginHeight = $frame->get_margin_height();
496 $imageHeightDiff = $height * 0.8 - $marginHeight;
497 $align = $frame->get_style()->vertical_align;
498 if (\in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, \true)) {
499 switch ($align) {
500 case "middle":
501 $y_offset = $imageHeightDiff / 2;
502 break;
503 case "sub":
504 $y_offset = 0.3 * $height + $imageHeightDiff;
505 break;
506 case "super":
507 $y_offset = -0.2 * $height + $imageHeightDiff;
508 break;
509 case "text-top":
510 // FIXME: this should be the height of the frame minus the height of the text
511 $y_offset = $height - $style->line_height;
512 break;
513 case "top":
514 break;
515 case "text-bottom":
516 // FIXME: align bottom of image with the descender?
517 case "bottom":
518 $y_offset = 0.3 * $height + $imageHeightDiff;
519 break;
520 case "baseline":
521 default:
522 $y_offset = $imageHeightDiff;
523 break;
524 }
525 } else {
526 $y_offset = $baseline - (float) $style->length_in_pt($align, $style->font_size) - $marginHeight;
527 }
528 } else {
529 $parent = $frame->get_parent();
530 if ($parent instanceof TableCellFrameDecorator) {
531 $align = "baseline";
532 } else {
533 $align = $parent->get_style()->vertical_align;
534 }
535 if (\in_array($align, Style::VERTICAL_ALIGN_KEYWORDS, \true)) {
536 switch ($align) {
537 case "middle":
538 $y_offset = ($height * 0.8 - $baseline) / 2;
539 break;
540 case "sub":
541 $y_offset = $height * 0.8 - $baseline * 0.5;
542 break;
543 case "super":
544 $y_offset = $height * 0.8 - $baseline * 1.4;
545 break;
546 case "text-top":
547 case "top":
548 // Not strictly accurate, but good enough for now
549 break;
550 case "text-bottom":
551 case "bottom":
552 $y_offset = $height * 0.8 - $baseline;
553 break;
554 case "baseline":
555 default:
556 $y_offset = $height * 0.8 - $baseline;
557 break;
558 }
559 } else {
560 $y_offset = $height * 0.8 - $baseline - (float) $style->length_in_pt($align, $style->font_size);
561 }
562 }
563 if ($y_offset !== 0) {
564 $frame->move(0, $y_offset);
565 }
566 }
567 }
568 }
569 /**
570 * @param AbstractFrameDecorator $child
571 */
572 function process_clear(AbstractFrameDecorator $child)
573 {
574 $child_style = $child->get_style();
575 $root = $this->_frame->get_root();
576 // Handle "clear"
577 if ($child_style->clear !== "none") {
578 //TODO: this is a WIP for handling clear/float frames that are in between inline frames
579 if ($child->get_prev_sibling() !== null) {
580 $this->_frame->add_line();
581 }
582 if ($child_style->float !== "none" && $child->get_next_sibling()) {
583 $this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1);
584 }
585 $lowest_y = $root->get_lowest_float_offset($child);
586 // If a float is still applying, we handle it
587 if ($lowest_y) {
588 if ($child->is_in_flow()) {
589 $line_box = $this->_frame->get_current_line_box();
590 $line_box->y = $lowest_y + $child->get_margin_height();
591 $line_box->left = 0;
592 $line_box->right = 0;
593 }
594 $child->move(0, $lowest_y - $child->get_position("y"));
595 }
596 }
597 }
598 /**
599 * @param AbstractFrameDecorator $child
600 * @param float $cb_x
601 * @param float $cb_w
602 */
603 function process_float(AbstractFrameDecorator $child, $cb_x, $cb_w)
604 {
605 $child_style = $child->get_style();
606 $root = $this->_frame->get_root();
607 // Handle "float"
608 if ($child_style->float !== "none") {
609 $root->add_floating_frame($child);
610 // Remove next frame's beginning whitespace
611 $next = $child->get_next_sibling();
612 if ($next && $next instanceof TextFrameDecorator) {
613 $next->set_text(\ltrim($next->get_text()));
614 }
615 $line_box = $this->_frame->get_current_line_box();
616 list($old_x, $old_y) = $child->get_position();
617 $float_x = $cb_x;
618 $float_y = $old_y;
619 $float_w = $child->get_margin_width();
620 if ($child_style->clear === "none") {
621 switch ($child_style->float) {
622 case "left":
623 $float_x += $line_box->left;
624 break;
625 case "right":
626 $float_x += $cb_w - $line_box->right - $float_w;
627 break;
628 }
629 } else {
630 if ($child_style->float === "right") {
631 $float_x += $cb_w - $float_w;
632 }
633 }
634 if ($cb_w < $float_x + $float_w - $old_x) {
635 // TODO handle when floating elements don't fit
636 }
637 $line_box->get_float_offsets();
638 if ($child->_float_next_line) {
639 $float_y += $line_box->h;
640 }
641 $child->set_position($float_x, $float_y);
642 $child->move($float_x - $old_x, $float_y - $old_y, \true);
643 }
644 }
645 /**
646 * @param BlockFrameDecorator|null $block
647 */
648 function reflow(?BlockFrameDecorator $block = null)
649 {
650 // Check if a page break is forced
651 $page = $this->_frame->get_root();
652 $page->check_forced_page_break($this->_frame);
653 // Bail if the page is full
654 if ($page->is_full()) {
655 return;
656 }
657 $this->determine_absolute_containing_block();
658 // Counters and generated content
659 $this->_set_content();
660 // Inherit any dangling list markers
661 if ($block && $this->_frame->is_in_flow()) {
662 $this->_frame->inherit_dangling_markers($block);
663 }
664 // Collapse margins if required
665 $this->_collapse_margins();
666 $style = $this->_frame->get_style();
667 $cb = $this->_frame->get_containing_block();
668 // Determine the constraints imposed by this frame: calculate the width
669 // of the content area:
670 [$width, $margin_left, $margin_right, $left, $right] = $this->_calculate_restricted_width();
671 // Store the calculated properties
672 $style->set_used("width", $width);
673 $style->set_used("margin_left", $margin_left);
674 $style->set_used("margin_right", $margin_right);
675 $style->set_used("left", $left);
676 $style->set_used("right", $right);
677 $margin_top = $style->length_in_pt($style->margin_top, $cb["w"]);
678 $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["w"]);
679 $auto_top = $style->top === "auto";
680 $auto_margin_top = $margin_top === "auto";
681 // Update the position
682 $this->_frame->position();
683 [$x, $y] = $this->_frame->get_position();
684 // Adjust the first line based on the text-indent property
685 $indent = (float) $style->length_in_pt($style->text_indent, $cb["w"]);
686 $this->_frame->increase_line_width($indent);
687 // Determine the content edge
688 $top = (float) $style->length_in_pt([$margin_top !== "auto" ? $margin_top : 0, $style->border_top_width, $style->padding_top], $cb["w"]);
689 $bottom = (float) $style->length_in_pt([$margin_bottom !== "auto" ? $margin_bottom : 0, $style->border_bottom_width, $style->padding_bottom], $cb["w"]);
690 $cb_x = $x + (float) $margin_left + (float) $style->length_in_pt([$style->border_left_width, $style->padding_left], $cb["w"]);
691 $cb_y = $y + $top;
692 $height = $style->length_in_pt($style->height, $cb["h"]);
693 if ($height === "auto") {
694 $height = $cb["h"] + $cb["y"] - $bottom - $cb_y;
695 }
696 // Set the y position of the first line in this block
697 $line_box = $this->_frame->get_current_line_box();
698 $line_box->y = $cb_y;
699 $line_box->get_float_offsets();
700 // Set the containing blocks and reflow each child
701 foreach ($this->_frame->get_children() as $child) {
702 $child->set_containing_block($cb_x, $cb_y, $width, $height);
703 $this->process_clear($child);
704 $child->reflow($this->_frame);
705 // Check for a page break before the child
706 $page->check_page_break($child);
707 // Don't add the child to the line if a page break has occurred
708 // before it (possibly via a descendant), in which case it has been
709 // reset, including its position
710 if ($page->is_full() && $child->get_position("x") === null) {
711 break;
712 }
713 $this->process_float($child, $cb_x, $width);
714 }
715 // Stop reflow if a page break has occurred before the frame, in which
716 // case it has been reset, including its position
717 if ($page->is_full() && $this->_frame->get_position("x") === null) {
718 return;
719 }
720 // Determine our height
721 [$height, $margin_top, $margin_bottom, $top, $bottom] = $this->_calculate_restricted_height();
722 $style->set_used("height", $height);
723 $style->set_used("margin_top", $margin_top);
724 $style->set_used("margin_bottom", $margin_bottom);
725 $style->set_used("top", $top);
726 $style->set_used("bottom", $bottom);
727 if ($this->_frame->is_absolute()) {
728 if ($auto_top) {
729 $this->_frame->move(0, $top);
730 }
731 if ($auto_margin_top) {
732 $this->_frame->move(0, $margin_top, \true);
733 }
734 }
735 $this->_text_align();
736 $this->vertical_align();
737 // Handle relative positioning
738 foreach ($this->_frame->get_children() as $child) {
739 $this->position_relative($child);
740 }
741 if ($block && $this->_frame->is_in_flow()) {
742 $block->add_frame_to_line($this->_frame);
743 if ($this->_frame->is_block_level()) {
744 $block->add_line();
745 }
746 }
747 }
748 public function get_min_max_content_width() : array
749 {
750 // TODO: While the containing block is not set yet on the frame, it can
751 // already be determined in some cases due to fixed dimensions on the
752 // ancestor forming the containing block. In such cases, percentage
753 // values could be resolved here
754 $style = $this->_frame->get_style();
755 $width = $style->width;
756 $fixed_width = $width !== "auto" && !Helpers::is_percent($width);
757 // If the frame has a specified width, then we don't need to check
758 // its children
759 if ($fixed_width) {
760 $min = (float) $style->length_in_pt($width, 0);
761 $max = $min;
762 } else {
763 [$min, $max] = $this->get_min_max_child_width();
764 }
765 // Handle min/max width style properties
766 $min_width = $this->resolve_min_width(null);
767 $max_width = $this->resolve_max_width(null);
768 $min = Helpers::clamp($min, $min_width, $max_width);
769 $max = Helpers::clamp($max, $min_width, $max_width);
770 return [$min, $max];
771 }
772 }
773