composer package updates
[openemr.git] / vendor / dompdf / dompdf / src / FrameReflower / Block.php
blobef77be7cddc50bc41fd205bacf2f71574d878a63
1 <?php
2 /**
3 * @package dompdf
4 * @link http://dompdf.github.com/
5 * @author Benj Carson <benjcarson@digitaljunkies.ca>
6 * @author Fabien Ménager <fabien.menager@gmail.com>
7 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8 */
9 namespace Dompdf\FrameReflower;
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;
18 /**
19 * Reflows block frames
21 * @package dompdf
23 class Block extends AbstractFrameReflower
25 // Minimum line width to justify, as fraction of available width
26 const MIN_JUSTIFY_WIDTH = 0.80;
28 /**
29 * @var BlockFrameDecorator
31 protected $_frame;
33 function __construct(BlockFrameDecorator $frame)
35 parent::__construct($frame);
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
42 * @param float $width
44 * @return array
46 protected function _calculate_width($width)
48 $frame = $this->_frame;
49 $style = $frame->get_style();
50 $w = $frame->get_containing_block("w");
52 if ($style->position === "fixed") {
53 $w = $frame->get_parent()->get_containing_block("w");
56 $rm = $style->length_in_pt($style->margin_right, $w);
57 $lm = $style->length_in_pt($style->margin_left, $w);
59 $left = $style->length_in_pt($style->left, $w);
60 $right = $style->length_in_pt($style->right, $w);
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);
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;
80 $sum = (float)$style->length_in_pt($dims, $w);
82 // Compare to the containing block
83 $diff = $w - $sum;
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
90 if ($width === "auto" && $left === "auto" && $right === "auto") {
91 if ($lm === "auto") {
92 $lm = 0;
94 if ($rm === "auto") {
95 $rm = 0;
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;
108 if ($rm === "auto") {
109 $rm = 0;
111 if ($left === "auto") {
112 $left = 0;
114 if ($right === "auto") {
115 $right = 0;
118 $width = $diff;
119 } else if ($left === "auto") {
120 if ($lm === "auto") {
121 $lm = 0;
123 if ($rm === "auto") {
124 $rm = 0;
126 if ($right === "auto") {
127 $right = 0;
130 $left = $diff;
131 } else if ($right === "auto") {
132 if ($lm === "auto") {
133 $lm = 0;
135 if ($rm === "auto") {
136 $rm = 0;
139 $right = $diff;
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;
154 } else if ($diff < 0) {
155 // We are over constrained--set margin-right to the difference
156 $rm = $diff;
159 return array(
160 "width" => $width,
161 "margin_left" => $lm,
162 "margin_right" => $rm,
163 "left" => $left,
164 "right" => $right,
169 * Call the above function, but resolve max/min widths
171 * @throws Exception
172 * @return array
174 protected function _calculate_restricted_width()
176 $frame = $this->_frame;
177 $style = $frame->get_style();
178 $cb = $frame->get_containing_block();
180 if ($style->position === "fixed") {
181 $cb = $frame->get_root()->get_containing_block();
184 //if ( $style->position === "absolute" )
185 // $cb = $frame->find_positionned_parent()->get_containing_block();
187 if (!isset($cb["w"])) {
188 throw new Exception("Box property calculation requires containing block width");
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"]);
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'];
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"]);
209 if ($max_width !== "none" && $min_width > $max_width) {
210 list($max_width, $min_width) = array($min_width, $max_width);
213 if ($max_width !== "none" && $width > $max_width) {
214 extract($this->_calculate_width($max_width));
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'];
226 return array($width, $margin_left, $margin_right, $left, $right);
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.
234 * @return float
236 protected function _calculate_content_height()
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"];
245 return $height;
249 * Determine the frame's restricted height
251 * @return array
253 protected function _calculate_restricted_height()
255 $frame = $this->_frame;
256 $style = $frame->get_style();
257 $content_height = $this->_calculate_content_height();
258 $cb = $frame->get_containing_block();
260 $height = $style->length_in_pt($style->height, $cb["h"]);
262 $top = $style->length_in_pt($style->top, $cb["h"]);
263 $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
265 $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
266 $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
268 if ($frame->is_absolute()) {
270 // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
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);
282 $sum = (float)$style->length_in_pt($dims, $cb["h"]);
284 $diff = $cb["h"] - $sum;
286 if ($diff > 0) {
287 if ($height === "auto" && $top === "auto" && $bottom === "auto") {
288 if ($margin_top === "auto") {
289 $margin_top = 0;
291 if ($margin_bottom === "auto") {
292 $margin_bottom = 0;
295 $height = $diff;
296 } else if ($height === "auto" && $top === "auto") {
297 if ($margin_top === "auto") {
298 $margin_top = 0;
300 if ($margin_bottom === "auto") {
301 $margin_bottom = 0;
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;
310 if ($margin_bottom === "auto") {
311 $margin_bottom = 0;
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;
320 if ($margin_bottom === "auto") {
321 $margin_bottom = 0;
324 $bottom = $diff;
325 } else if ($top === "auto") {
326 if ($margin_top === "auto") {
327 $margin_top = 0;
329 if ($margin_bottom === "auto") {
330 $margin_bottom = 0;
333 $top = $diff;
334 } else if ($height === "auto") {
335 if ($margin_top === "auto") {
336 $margin_top = 0;
338 if ($margin_bottom === "auto") {
339 $margin_bottom = 0;
342 $height = $diff;
343 } else if ($bottom === "auto") {
344 if ($margin_top === "auto") {
345 $margin_top = 0;
347 if ($margin_bottom === "auto") {
348 $margin_bottom = 0;
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;
358 if ($margin_bottom === "auto") {
359 $margin_bottom = 0;
361 if ($top === "auto") {
362 $top = 0;
364 if ($bottom === "auto") {
365 $bottom = 0;
367 if ($height === "auto") {
368 $height = $content_height;
372 // FIXME: overflow hidden
376 } else {
377 // Expand the height if overflow is visible
378 if ($height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) {
379 $height = $content_height;
382 // FIXME: this should probably be moved to a seperate function as per
383 // _calculate_restricted_width
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;
390 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"]);
400 if (mb_strpos($max_height, "%") !== false) {
401 $max_height = "none";
402 } else {
403 $max_height = $style->length_in_pt($max_height, $cb["w"]);
407 if ($max_height !== "none" && $min_height > $max_height) {
408 // Swap 'em
409 list($max_height, $min_height) = array($min_height, $max_height);
412 if ($max_height !== "none" && $height > $max_height) {
413 $height = $max_height;
416 if ($height < $min_height) {
417 $height = $min_height;
422 return array($height, $margin_top, $margin_bottom, $top, $bottom);
426 * Adjust the justification of each of our lines.
427 * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
429 protected function _text_align()
431 $style = $this->_frame->get_style();
432 $w = $this->_frame->get_containing_block("w");
433 $width = (float)$style->length_in_pt($style->width, $w);
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;
443 foreach ($line->get_frames() as $frame) {
444 if ($frame instanceof BlockFrameDecorator) {
445 continue;
447 $frame->set_position($frame->get_position("x") + $line->left);
450 return;
452 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;
457 foreach ($line->get_frames() as $frame) {
458 // Block frames are not aligned by text-align
459 if ($frame instanceof BlockFrameDecorator) {
460 continue;
463 $frame->set_position($frame->get_position("x") + $dx);
466 break;
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);
473 foreach ($lines as $i => $line) {
474 if ($line->br) {
475 unset($lines[$i]);
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);
482 foreach ($lines as $line) {
483 if ($line->left) {
484 foreach ($line->get_frames() as $frame) {
485 if (!$frame instanceof TextFrameDecorator) {
486 continue;
489 $frame->set_position($frame->get_position("x") + $line->left);
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;
500 $dx = 0;
501 foreach ($line->get_frames() as $frame) {
502 if (!$frame instanceof TextFrameDecorator) {
503 continue;
506 $text = $frame->get_text();
507 $spaces = mb_substr_count($text, " ");
509 $char_spacing = (float)$style->length_in_pt($style->letter_spacing);
510 $_spacing = $spacing + $char_spacing;
512 $frame->set_position($frame->get_position("x") + $dx);
513 $frame->set_text_spacing($_spacing);
515 $dx += $spaces * $_spacing;
518 // The line (should) now occupy the entire width
519 $line->w = $width;
522 // Adjust the last line if necessary
523 if ($last_line->left) {
524 foreach ($last_line->get_frames() as $frame) {
525 if ($frame instanceof BlockFrameDecorator) {
526 continue;
528 $frame->set_position($frame->get_position("x") + $last_line->left);
531 break;
533 case "center":
534 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;
539 foreach ($line->get_frames() as $frame) {
540 // Block frames are not aligned by text-align
541 if ($frame instanceof BlockFrameDecorator) {
542 continue;
545 $frame->set_position($frame->get_position("x") + $dx);
548 break;
553 * Align inline children vertically.
554 * Aligns each child vertically after each line is reflowed
556 function vertical_align()
558 $canvas = null;
560 foreach ($this->_frame->get_line_boxes() as $line) {
562 $height = $line->h;
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
571 if (!$isInlineBlock && $style->display !== "inline") {
572 continue;
575 if (!isset($canvas)) {
576 $canvas = $frame->get_root()->get_dompdf()->get_canvas();
579 $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size);
580 $y_offset = 0;
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;
588 $frameBox = $frame->get_frame()->get_border_box();
589 $imageHeightDiff = $height * 0.8 - (float)$frameBox['h'];
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;
598 case "sub":
599 $y_offset = 0.3 * $height + $imageHeightDiff;
600 break;
602 case "super":
603 $y_offset = -0.2 * $height + $imageHeightDiff;
604 break;
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->get_line_height(), $style->font_size);
608 break;
610 case "top":
611 break;
613 case "text-bottom": // FIXME: align bottom of image with the descender?
614 case "bottom":
615 $y_offset = 0.3 * $height + $imageHeightDiff;
616 break;
618 case "baseline":
619 default:
620 $y_offset = $imageHeightDiff;
621 break;
623 } else {
624 $y_offset = $baseline - (float)$style->length_in_pt($align, $style->font_size) - (float)$frameBox['h'];
626 } else {
627 $parent = $frame->get_parent();
628 if ($parent instanceof TableCellFrameDecorator) {
629 $align = "baseline";
630 } else {
631 $align = $parent->get_style()->vertical_align;
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;
639 case "sub":
640 $y_offset = $height * 0.8 - $baseline * 0.5;
641 break;
643 case "super":
644 $y_offset = $height * 0.8 - $baseline * 1.4;
645 break;
647 case "text-top":
648 case "top": // Not strictly accurate, but good enough for now
649 break;
651 case "text-bottom":
652 case "bottom":
653 $y_offset = $height * 0.8 - $baseline;
654 break;
656 case "baseline":
657 default:
658 $y_offset = $height * 0.8 - $baseline;
659 break;
661 } else {
662 $y_offset = $height * 0.8 - $baseline - (float)$style->length_in_pt($align, $style->font_size);
666 if ($y_offset !== 0) {
667 $frame->move(0, $y_offset);
674 * @param Frame $child
676 function process_clear(Frame $child)
678 $child_style = $child->get_style();
679 $root = $this->_frame->get_root();
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();
687 if ($child_style->float !== "none" && $child->get_next_sibling()) {
688 $this->_frame->set_current_line_number($this->_frame->get_current_line_number() - 1);
691 $lowest_y = $root->get_lowest_float_offset($child);
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;
702 $child->move(0, $lowest_y - $child->get_position("y"));
708 * @param Frame $child
709 * @param float $cb_x
710 * @param float $cb_w
712 function process_float(Frame $child, $cb_x, $cb_w)
714 $child_style = $child->get_style();
715 $root = $this->_frame->get_root();
717 // Handle "float"
718 if ($child_style->float !== "none") {
719 $root->add_floating_frame($child);
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()));
727 $line_box = $this->_frame->get_current_line_box();
728 list($old_x, $old_y) = $child->get_position();
730 $float_x = $cb_x;
731 $float_y = $old_y;
732 $float_w = $child->get_margin_width();
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;
743 } else {
744 if ($child_style->float === "right") {
745 $float_x += ($cb_w - $float_w);
749 if ($cb_w < $float_x + $float_w - $old_x) {
750 // TODO handle when floating elements don't fit
753 $line_box->get_float_offsets();
755 if ($child->_float_next_line) {
756 $float_y += $line_box->h;
759 $child->set_position($float_x, $float_y);
760 $child->move($float_x - $old_x, $float_y - $old_y, true);
765 * @param BlockFrameDecorator $block
766 * @return mixed|void
768 function reflow(BlockFrameDecorator $block = null)
771 // Check if a page break is forced
772 $page = $this->_frame->get_root();
773 $page->check_forced_page_break($this->_frame);
775 // Bail if the page is full
776 if ($page->is_full()) {
777 return;
780 // Generated content
781 $this->_set_content();
783 // Collapse margins if required
784 $this->_collapse_margins();
786 $style = $this->_frame->get_style();
787 $cb = $this->_frame->get_containing_block();
789 if ($style->position === "fixed") {
790 $cb = $this->_frame->get_root()->get_containing_block();
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();
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;
804 // Update the position
805 $this->_frame->position();
806 list($x, $y) = $this->_frame->get_position();
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);
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"]);
817 $bottom = (float)$style->length_in_pt(array($style->border_bottom_width,
818 $style->margin_bottom,
819 $style->padding_bottom), $cb["h"]);
821 $cb_x = $x + (float)$left_margin + (float)$style->length_in_pt(array($style->border_left_width,
822 $style->padding_left), $cb["w"]);
824 $cb_y = $y + $top;
826 $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
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();
833 // Set the containing blocks and reflow each child
834 foreach ($this->_frame->get_children() as $child) {
836 // Bail out if the page is full
837 if ($page->is_full()) {
838 break;
841 $child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
843 $this->process_clear($child);
845 $child->reflow($this->_frame);
847 // Don't add the child to the line if a page break has occurred
848 if ($page->check_page_break($child)) {
849 break;
852 $this->process_float($child, $cb_x, $w);
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;
863 $orig_style = $this->_frame->get_original_style();
865 $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"));
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);
874 $style->width = $width;
877 $style->left = $orig_style->left;
878 $style->right = $orig_style->right;
881 // Calculate inline-block / float auto-widths
882 if (($style->display === "inline-block" || $style->float !== 'none') && $orig_style->width === 'auto') {
883 $width = 0;
885 foreach ($this->_frame->get_line_boxes() as $line) {
886 $line->recalculate_width();
888 $width = max($line->w, $width);
891 if ($width === 0) {
892 foreach ($this->_frame->get_children() as $child) {
893 $width += $child->calculate_auto_width();
897 $style->width = $width;
900 $this->_text_align();
901 $this->vertical_align();
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);
911 if ($block && $this->_frame->is_in_flow()) {
912 $block->add_frame_to_line($this->_frame);
914 // May be inline-block
915 if ($style->display === "block") {
916 $block->add_line();
922 * Determine current frame width based on contents
924 * @return float
926 public function calculate_auto_width()
928 $width = 0;
930 foreach ($this->_frame->get_line_boxes() as $line) {
931 $line_width = 0;
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();
941 $width = max($line_width, $width);
944 $this->_frame->get_style()->width = $width;
946 return $this->_frame->get_margin_width();