block_frame_reflower.cls.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  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. /**
  10. * Reflows block frames
  11. *
  12. * @access private
  13. * @package dompdf
  14. */
  15. class Block_Frame_Reflower extends Frame_Reflower {
  16. // Minimum line width to justify, as fraction of available width
  17. const MIN_JUSTIFY_WIDTH = 0.80;
  18. /**
  19. * @var Block_Frame_Decorator
  20. */
  21. protected $_frame;
  22. function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); }
  23. /**
  24. * Calculate the ideal used value for the width property as per:
  25. * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
  26. *
  27. * @param float $width
  28. * @return array
  29. */
  30. protected function _calculate_width($width) {
  31. $frame = $this->_frame;
  32. $style = $frame->get_style();
  33. $w = $frame->get_containing_block("w");
  34. if ( $style->position === "fixed" ) {
  35. $w = $frame->get_parent()->get_containing_block("w");
  36. }
  37. $rm = $style->length_in_pt($style->margin_right, $w);
  38. $lm = $style->length_in_pt($style->margin_left, $w);
  39. $left = $style->length_in_pt($style->left, $w);
  40. $right = $style->length_in_pt($style->right, $w);
  41. // Handle 'auto' values
  42. $dims = array($style->border_left_width,
  43. $style->border_right_width,
  44. $style->padding_left,
  45. $style->padding_right,
  46. $width !== "auto" ? $width : 0,
  47. $rm !== "auto" ? $rm : 0,
  48. $lm !== "auto" ? $lm : 0);
  49. // absolutely positioned boxes take the 'left' and 'right' properties into account
  50. if ( $frame->is_absolute() ) {
  51. $absolute = true;
  52. $dims[] = $left !== "auto" ? $left : 0;
  53. $dims[] = $right !== "auto" ? $right : 0;
  54. }
  55. else {
  56. $absolute = false;
  57. }
  58. $sum = $style->length_in_pt($dims, $w);
  59. // Compare to the containing block
  60. $diff = $w - $sum;
  61. if ( $diff > 0 ) {
  62. if ( $absolute ) {
  63. // resolve auto properties: see
  64. // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
  65. if ( $width === "auto" && $left === "auto" && $right === "auto" ) {
  66. if ( $lm === "auto" ) $lm = 0;
  67. if ( $rm === "auto" ) $rm = 0;
  68. // Technically, the width should be "shrink-to-fit" i.e. based on the
  69. // preferred width of the content... a little too costly here as a
  70. // special case. Just get the width to take up the slack:
  71. $left = 0;
  72. $right = 0;
  73. $width = $diff;
  74. }
  75. else if ( $width === "auto" ) {
  76. if ( $lm === "auto" ) $lm = 0;
  77. if ( $rm === "auto" ) $rm = 0;
  78. if ( $left === "auto" ) $left = 0;
  79. if ( $right === "auto" ) $right = 0;
  80. $width = $diff;
  81. }
  82. else if ( $left === "auto" ) {
  83. if ( $lm === "auto" ) $lm = 0;
  84. if ( $rm === "auto" ) $rm = 0;
  85. if ( $right === "auto" ) $right = 0;
  86. $left = $diff;
  87. }
  88. else if ( $right === "auto" ) {
  89. if ( $lm === "auto" ) $lm = 0;
  90. if ( $rm === "auto" ) $rm = 0;
  91. $right = $diff;
  92. }
  93. }
  94. else {
  95. // Find auto properties and get them to take up the slack
  96. if ( $width === "auto" ) {
  97. $width = $diff;
  98. }
  99. else if ( $lm === "auto" && $rm === "auto" ) {
  100. $lm = $rm = round($diff / 2);
  101. }
  102. else if ( $lm === "auto" ) {
  103. $lm = $diff;
  104. }
  105. else if ( $rm === "auto" ) {
  106. $rm = $diff;
  107. }
  108. }
  109. }
  110. else if ($diff < 0) {
  111. // We are over constrained--set margin-right to the difference
  112. $rm = $diff;
  113. }
  114. return array(
  115. "width" => $width,
  116. "margin_left" => $lm,
  117. "margin_right" => $rm,
  118. "left" => $left,
  119. "right" => $right,
  120. );
  121. }
  122. /**
  123. * Call the above function, but resolve max/min widths
  124. *
  125. * @throws DOMPDF_Exception
  126. * @return array
  127. */
  128. protected function _calculate_restricted_width() {
  129. $frame = $this->_frame;
  130. $style = $frame->get_style();
  131. $cb = $frame->get_containing_block();
  132. if ( $style->position === "fixed" ) {
  133. $cb = $frame->get_root()->get_containing_block();
  134. }
  135. //if ( $style->position === "absolute" )
  136. // $cb = $frame->find_positionned_parent()->get_containing_block();
  137. if ( !isset($cb["w"]) ) {
  138. throw new DOMPDF_Exception("Box property calculation requires containing block width");
  139. }
  140. // Treat width 100% as auto
  141. if ( $style->width === "100%" ) {
  142. $width = "auto";
  143. }
  144. else {
  145. $width = $style->length_in_pt($style->width, $cb["w"]);
  146. }
  147. extract($this->_calculate_width($width));
  148. // Handle min/max width
  149. $min_width = $style->length_in_pt($style->min_width, $cb["w"]);
  150. $max_width = $style->length_in_pt($style->max_width, $cb["w"]);
  151. if ( $max_width !== "none" && $min_width > $max_width ) {
  152. list($max_width, $min_width) = array($min_width, $max_width);
  153. }
  154. if ( $max_width !== "none" && $width > $max_width ) {
  155. extract($this->_calculate_width($max_width));
  156. }
  157. if ( $width < $min_width ) {
  158. extract($this->_calculate_width($min_width));
  159. }
  160. return array($width, $margin_left, $margin_right, $left, $right);
  161. }
  162. /**
  163. * Determine the unrestricted height of content within the block
  164. * not by adding each line's height, but by getting the last line's position.
  165. * This because lines could have been pushed lower by a clearing element.
  166. *
  167. * @return float
  168. */
  169. protected function _calculate_content_height() {
  170. $lines = $this->_frame->get_line_boxes();
  171. $height = 0;
  172. foreach ($lines as $line) {
  173. $height += $line->h;
  174. }
  175. /*
  176. $first_line = reset($lines);
  177. $last_line = end($lines);
  178. $height2 = $last_line->y + $last_line->h - $first_line->y;
  179. */
  180. return $height;
  181. }
  182. /**
  183. * Determine the frame's restricted height
  184. *
  185. * @return array
  186. */
  187. protected function _calculate_restricted_height() {
  188. $frame = $this->_frame;
  189. $style = $frame->get_style();
  190. $content_height = $this->_calculate_content_height();
  191. $cb = $frame->get_containing_block();
  192. $height = $style->length_in_pt($style->height, $cb["h"]);
  193. $top = $style->length_in_pt($style->top, $cb["h"]);
  194. $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
  195. $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
  196. $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
  197. if ( $frame->is_absolute() ) {
  198. // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
  199. $dims = array($top !== "auto" ? $top : 0,
  200. $style->margin_top !== "auto" ? $style->margin_top : 0,
  201. $style->padding_top,
  202. $style->border_top_width,
  203. $height !== "auto" ? $height : 0,
  204. $style->border_bottom_width,
  205. $style->padding_bottom,
  206. $style->margin_bottom !== "auto" ? $style->margin_bottom : 0,
  207. $bottom !== "auto" ? $bottom : 0);
  208. $sum = $style->length_in_pt($dims, $cb["h"]);
  209. $diff = $cb["h"] - $sum;
  210. if ( $diff > 0 ) {
  211. if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) {
  212. if ( $margin_top === "auto" ) $margin_top = 0;
  213. if ( $margin_bottom === "auto" ) $margin_bottom = 0;
  214. $height = $diff;
  215. }
  216. else if ( $height === "auto" && $top === "auto" ) {
  217. if ( $margin_top === "auto" ) $margin_top = 0;
  218. if ( $margin_bottom === "auto" ) $margin_bottom = 0;
  219. $height = $content_height;
  220. $top = $diff - $content_height;
  221. }
  222. else if ( $height === "auto" && $bottom === "auto" ) {
  223. if ( $margin_top === "auto" ) $margin_top = 0;
  224. if ( $margin_bottom === "auto" ) $margin_bottom = 0;
  225. $height = $content_height;
  226. $bottom = $diff - $content_height;
  227. }
  228. else if ( $top === "auto" && $bottom === "auto" ) {
  229. if ( $margin_top === "auto" ) $margin_top = 0;
  230. if ( $margin_bottom === "auto" ) $margin_bottom = 0;
  231. $bottom = $diff;
  232. }
  233. else if ( $top === "auto" ) {
  234. if ( $margin_top === "auto" ) $margin_top = 0;
  235. if ( $margin_bottom === "auto" ) $margin_bottom = 0;
  236. $top = $diff;
  237. }
  238. else if ( $height === "auto" ) {
  239. if ( $margin_top === "auto" ) $margin_top = 0;
  240. if ( $margin_bottom === "auto" ) $margin_bottom = 0;
  241. $height = $diff;
  242. }
  243. else if ( $bottom === "auto" ) {
  244. if ( $margin_top === "auto" ) $margin_top = 0;
  245. if ( $margin_bottom === "auto" ) $margin_bottom = 0;
  246. $bottom = $diff;
  247. }
  248. else {
  249. if ( $style->overflow === "visible" ) {
  250. // set all autos to zero
  251. if ( $margin_top === "auto" ) $margin_top = 0;
  252. if ( $margin_bottom === "auto" ) $margin_bottom = 0;
  253. if ( $top === "auto" ) $top = 0;
  254. if ( $bottom === "auto" ) $bottom = 0;
  255. if ( $height === "auto" ) $height = $content_height;
  256. }
  257. // FIXME: overflow hidden
  258. }
  259. }
  260. }
  261. else {
  262. // Expand the height if overflow is visible
  263. if ( $height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) {
  264. $height = $content_height;
  265. }
  266. // FIXME: this should probably be moved to a seperate function as per
  267. // _calculate_restricted_width
  268. // Only handle min/max height if the height is independent of the frame's content
  269. if ( !($style->overflow === "visible" ||
  270. ($style->overflow === "hidden" && $height === "auto")) ) {
  271. $min_height = $style->min_height;
  272. $max_height = $style->max_height;
  273. if ( isset($cb["h"]) ) {
  274. $min_height = $style->length_in_pt($min_height, $cb["h"]);
  275. $max_height = $style->length_in_pt($max_height, $cb["h"]);
  276. }
  277. else if ( isset($cb["w"]) ) {
  278. if ( mb_strpos($min_height, "%") !== false ) {
  279. $min_height = 0;
  280. }
  281. else {
  282. $min_height = $style->length_in_pt($min_height, $cb["w"]);
  283. }
  284. if ( mb_strpos($max_height, "%") !== false ) {
  285. $max_height = "none";
  286. }
  287. else {
  288. $max_height = $style->length_in_pt($max_height, $cb["w"]);
  289. }
  290. }
  291. if ( $max_height !== "none" && $min_height > $max_height ) {
  292. // Swap 'em
  293. list($max_height, $min_height) = array($min_height, $max_height);
  294. }
  295. if ( $max_height !== "none" && $height > $max_height ) {
  296. $height = $max_height;
  297. }
  298. if ( $height < $min_height ) {
  299. $height = $min_height;
  300. }
  301. }
  302. }
  303. return array($height, $margin_top, $margin_bottom, $top, $bottom);
  304. }
  305. /**
  306. * Adjust the justification of each of our lines.
  307. * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
  308. */
  309. protected function _text_align() {
  310. $style = $this->_frame->get_style();
  311. $w = $this->_frame->get_containing_block("w");
  312. $width = $style->length_in_pt($style->width, $w);
  313. switch ($style->text_align) {
  314. default:
  315. case "left":
  316. foreach ($this->_frame->get_line_boxes() as $line) {
  317. if ( !$line->left ) {
  318. continue;
  319. }
  320. foreach($line->get_frames() as $frame) {
  321. if ( $frame instanceof Block_Frame_Decorator) {
  322. continue;
  323. }
  324. $frame->set_position( $frame->get_position("x") + $line->left );
  325. }
  326. }
  327. return;
  328. case "right":
  329. foreach ($this->_frame->get_line_boxes() as $line) {
  330. // Move each child over by $dx
  331. $dx = $width - $line->w - $line->right;
  332. foreach($line->get_frames() as $frame) {
  333. // Block frames are not aligned by text-align
  334. if ($frame instanceof Block_Frame_Decorator) {
  335. continue;
  336. }
  337. $frame->set_position( $frame->get_position("x") + $dx );
  338. }
  339. }
  340. break;
  341. case "justify":
  342. // We justify all lines except the last one
  343. $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards)
  344. array_pop($lines);
  345. foreach($lines as $i => $line) {
  346. if ( $line->br ) {
  347. unset($lines[$i]);
  348. }
  349. }
  350. // One space character's width. Will be used to get a more accurate spacing
  351. $space_width = Font_Metrics::get_text_width(" ", $style->font_family, $style->font_size);
  352. foreach ($lines as $line) {
  353. if ( $line->left ) {
  354. foreach ( $line->get_frames() as $frame ) {
  355. if ( !$frame instanceof Text_Frame_Decorator ) {
  356. continue;
  357. }
  358. $frame->set_position( $frame->get_position("x") + $line->left );
  359. }
  360. }
  361. // Only set the spacing if the line is long enough. This is really
  362. // just an aesthetic choice ;)
  363. //if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) {
  364. // Set the spacing for each child
  365. if ( $line->wc > 1 ) {
  366. $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1);
  367. }
  368. else {
  369. $spacing = 0;
  370. }
  371. $dx = 0;
  372. foreach($line->get_frames() as $frame) {
  373. if ( !$frame instanceof Text_Frame_Decorator ) {
  374. continue;
  375. }
  376. $text = $frame->get_text();
  377. $spaces = mb_substr_count($text, " ");
  378. $char_spacing = $style->length_in_pt($style->letter_spacing);
  379. $_spacing = $spacing + $char_spacing;
  380. $frame->set_position( $frame->get_position("x") + $dx );
  381. $frame->set_text_spacing($_spacing);
  382. $dx += $spaces * $_spacing;
  383. }
  384. // The line (should) now occupy the entire width
  385. $line->w = $width;
  386. //}
  387. }
  388. break;
  389. case "center":
  390. case "centre":
  391. foreach ($this->_frame->get_line_boxes() as $line) {
  392. // Centre each line by moving each frame in the line by:
  393. $dx = ($width + $line->left - $line->w - $line->right ) / 2;
  394. foreach ($line->get_frames() as $frame) {
  395. // Block frames are not aligned by text-align
  396. if ($frame instanceof Block_Frame_Decorator) {
  397. continue;
  398. }
  399. $frame->set_position( $frame->get_position("x") + $dx );
  400. }
  401. }
  402. break;
  403. }
  404. }
  405. /**
  406. * Align inline children vertically.
  407. * Aligns each child vertically after each line is reflowed
  408. */
  409. function vertical_align() {
  410. $canvas = null;
  411. foreach ( $this->_frame->get_line_boxes() as $line ) {
  412. $height = $line->h;
  413. foreach ( $line->get_frames() as $frame ) {
  414. $style = $frame->get_style();
  415. if ( $style->display !== "inline" ) {
  416. continue;
  417. }
  418. $align = $frame->get_parent()->get_style()->vertical_align;
  419. if ( !isset($canvas) ) {
  420. $canvas = $frame->get_root()->get_dompdf()->get_canvas();
  421. }
  422. $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size);
  423. $y_offset = 0;
  424. switch ($align) {
  425. case "baseline":
  426. $y_offset = $height*0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning
  427. break;
  428. case "middle":
  429. $y_offset = ($height*0.8 - $baseline) / 2;
  430. break;
  431. case "sub":
  432. $y_offset = 0.3 * $height;
  433. break;
  434. case "super":
  435. $y_offset = -0.2 * $height;
  436. break;
  437. case "text-top":
  438. case "top": // Not strictly accurate, but good enough for now
  439. break;
  440. case "text-bottom":
  441. case "bottom":
  442. $y_offset = $height*0.8 - $baseline;
  443. break;
  444. }
  445. if ( $y_offset ) {
  446. $frame->move(0, $y_offset);
  447. }
  448. }
  449. }
  450. }
  451. /**
  452. * @param Frame $child
  453. */
  454. function process_clear(Frame $child){
  455. $enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
  456. if ( !$enable_css_float ) {
  457. return;
  458. }
  459. $child_style = $child->get_style();
  460. $root = $this->_frame->get_root();
  461. // Handle "clear"
  462. if ( $child_style->clear !== "none" ) {
  463. $lowest_y = $root->get_lowest_float_offset($child);
  464. // If a float is still applying, we handle it
  465. if ( $lowest_y ) {
  466. if ( $child->is_in_flow() ) {
  467. $line_box = $this->_frame->get_current_line_box();
  468. $line_box->y = $lowest_y + $child->get_margin_height();
  469. $line_box->left = 0;
  470. $line_box->right = 0;
  471. }
  472. $child->move(0, $lowest_y - $child->get_position("y"));
  473. }
  474. }
  475. }
  476. /**
  477. * @param Frame $child
  478. * @param float $cb_x
  479. * @param float $cb_w
  480. */
  481. function process_float(Frame $child, $cb_x, $cb_w){
  482. $enable_css_float = $this->_frame->get_dompdf()->get_option("enable_css_float");
  483. if ( !$enable_css_float ) {
  484. return;
  485. }
  486. $child_style = $child->get_style();
  487. $root = $this->_frame->get_root();
  488. // Handle "float"
  489. if ( $child_style->float !== "none" ) {
  490. $root->add_floating_frame($child);
  491. // Remove next frame's beginning whitespace
  492. $next = $child->get_next_sibling();
  493. if ( $next && $next instanceof Text_Frame_Decorator) {
  494. $next->set_text(ltrim($next->get_text()));
  495. }
  496. $line_box = $this->_frame->get_current_line_box();
  497. list($old_x, $old_y) = $child->get_position();
  498. $float_x = $cb_x;
  499. $float_y = $old_y;
  500. $float_w = $child->get_margin_width();
  501. if ( $child_style->clear === "none" ) {
  502. switch( $child_style->float ) {
  503. case "left":
  504. $float_x += $line_box->left;
  505. break;
  506. case "right":
  507. $float_x += ($cb_w - $line_box->right - $float_w);
  508. break;
  509. }
  510. }
  511. else {
  512. if ( $child_style->float === "right" ) {
  513. $float_x += ($cb_w - $float_w);
  514. }
  515. }
  516. if ( $cb_w < $float_x + $float_w - $old_x ) {
  517. // TODO handle when floating elements don't fit
  518. }
  519. $line_box->get_float_offsets();
  520. if ( $child->_float_next_line ) {
  521. $float_y += $line_box->h;
  522. }
  523. $child->set_position($float_x, $float_y);
  524. $child->move($float_x - $old_x, $float_y - $old_y, true);
  525. }
  526. }
  527. /**
  528. * @param Frame_Decorator $block
  529. */
  530. function reflow(Block_Frame_Decorator $block = null) {
  531. // Check if a page break is forced
  532. $page = $this->_frame->get_root();
  533. $page->check_forced_page_break($this->_frame);
  534. // Bail if the page is full
  535. if ( $page->is_full() ) {
  536. return;
  537. }
  538. // Generated content
  539. $this->_set_content();
  540. // Collapse margins if required
  541. $this->_collapse_margins();
  542. $style = $this->_frame->get_style();
  543. $cb = $this->_frame->get_containing_block();
  544. if ( $style->position === "fixed" ) {
  545. $cb = $this->_frame->get_root()->get_containing_block();
  546. }
  547. // Determine the constraints imposed by this frame: calculate the width
  548. // of the content area:
  549. list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
  550. // Store the calculated properties
  551. $style->width = $w . "pt";
  552. $style->margin_left = $left_margin."pt";
  553. $style->margin_right = $right_margin."pt";
  554. $style->left = $left ."pt";
  555. $style->right = $right . "pt";
  556. // Update the position
  557. $this->_frame->position();
  558. list($x, $y) = $this->_frame->get_position();
  559. // Adjust the first line based on the text-indent property
  560. $indent = $style->length_in_pt($style->text_indent, $cb["w"]);
  561. $this->_frame->increase_line_width($indent);
  562. // Determine the content edge
  563. $top = $style->length_in_pt(array($style->margin_top,
  564. $style->padding_top,
  565. $style->border_top_width), $cb["h"]);
  566. $bottom = $style->length_in_pt(array($style->border_bottom_width,
  567. $style->margin_bottom,
  568. $style->padding_bottom), $cb["h"]);
  569. $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width,
  570. $style->padding_left), $cb["w"]);
  571. $cb_y = $y + $top;
  572. $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
  573. // Set the y position of the first line in this block
  574. $line_box = $this->_frame->get_current_line_box();
  575. $line_box->y = $cb_y;
  576. $line_box->get_float_offsets();
  577. // Set the containing blocks and reflow each child
  578. foreach ( $this->_frame->get_children() as $child ) {
  579. // Bail out if the page is full
  580. if ( $page->is_full() ) {
  581. break;
  582. }
  583. $child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
  584. $this->process_clear($child);
  585. $child->reflow($this->_frame);
  586. // Don't add the child to the line if a page break has occurred
  587. if ( $page->check_page_break($child) ) {
  588. break;
  589. }
  590. $this->process_float($child, $cb_x, $w);
  591. }
  592. // Determine our height
  593. list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
  594. $style->height = $height;
  595. $style->margin_top = $margin_top;
  596. $style->margin_bottom = $margin_bottom;
  597. $style->top = $top;
  598. $style->bottom = $bottom;
  599. $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"));
  600. // Absolute positioning measurement
  601. if ( $needs_reposition ) {
  602. $orig_style = $this->_frame->get_original_style();
  603. if ( $orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto") ) {
  604. $width = 0;
  605. foreach ($this->_frame->get_line_boxes() as $line) {
  606. $width = max($line->w, $width);
  607. }
  608. $style->width = $width;
  609. }
  610. $style->left = $orig_style->left;
  611. $style->right = $orig_style->right;
  612. }
  613. $this->_text_align();
  614. $this->vertical_align();
  615. // Absolute positioning
  616. if ( $needs_reposition ) {
  617. list($x, $y) = $this->_frame->get_position();
  618. $this->_frame->position();
  619. list($new_x, $new_y) = $this->_frame->get_position();
  620. $this->_frame->move($new_x-$x, $new_y-$y, true);
  621. }
  622. if ( $block && $this->_frame->is_in_flow() ) {
  623. $block->add_frame_to_line($this->_frame);
  624. // May be inline-block
  625. if ( $style->display === "block" ) {
  626. $block->add_line();
  627. }
  628. }
  629. }
  630. }