protected function _line_break($text) { $style = $this->_frame->get_style(); $size = $style->font_size; $font = $style->font_family; $current_line = $this->_block_parent->get_current_line_box(); // Determine the available width $line_width = $this->_frame->get_containing_block("w"); $current_line_width = $current_line->left + $current_line->w + $current_line->right; $available_width = $line_width - $current_line_width; // split the text into words $words = preg_split('/([\\s-]+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE); $wc = count($words); // Account for word-spacing $word_spacing = $style->length_in_pt($style->word_spacing); $char_spacing = $style->length_in_pt($style->letter_spacing); // Determine the frame width including margin, padding & border $text_width = Font_Metrics::get_text_width($text, $font, $size, $word_spacing, $char_spacing); $mbp_width = $style->length_in_pt(array($style->margin_left, $style->border_left_width, $style->padding_left, $style->padding_right, $style->border_right_width, $style->margin_right), $line_width); $frame_width = $text_width + $mbp_width; // Debugging: // pre_r("Text: '" . htmlspecialchars($text). "'"); // pre_r("width: " .$frame_width); // pre_r("textwidth + delta: $text_width + $mbp_width"); // pre_r("font-size: $size"); // pre_r("cb[w]: " .$line_width); // pre_r("available width: " . $available_width); // pre_r("current line width: " . $current_line_width); // pre_r($words); if ($frame_width <= $available_width) { return false; } // Determine the split point $width = 0; $str = ""; reset($words); // @todo support <shy>, <wbr> for ($i = 0; $i < $wc; $i += 2) { $word = $words[$i] . (isset($words[$i + 1]) ? $words[$i + 1] : ""); $word_width = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing); if ($width + $word_width + $mbp_width > $available_width) { break; } $width += $word_width; $str .= $word; } // The first word has overflowed. Force it onto the line if ($current_line_width == 0 && $width == 0) { $width += $word_width; $str .= $word; } $offset = mb_strlen($str); // More debugging: // pre_var_dump($str); // pre_r("Width: ". $width); // pre_r("Offset: " . $offset); return $offset; }
/** * @param Frame $child * @param float $cb_x * @param float $cb_w */ function process_float(Frame $child, $cb_x, $cb_w) { $enable_css_float = $this->_frame->get_dompdf()->get_option("enable_css_float"); if (!$enable_css_float) { return; } $child_style = $child->get_style(); $root = $this->_frame->get_root(); // Handle "float" if ($child_style->float !== "none") { $root->add_floating_frame($child); // Remove next frame's beginning whitespace $next = $child->get_next_sibling(); if ($next && $next instanceof Text_Frame_Decorator) { $next->set_text(ltrim($next->get_text())); } $line_box = $this->_frame->get_current_line_box(); list($old_x, $old_y) = $child->get_position(); $float_x = $cb_x; $float_y = $old_y; $float_w = $child->get_margin_width(); if ($child_style->clear === "none") { switch ($child_style->float) { case "left": $float_x += $line_box->left; break; case "right": $float_x += $cb_w - $line_box->right - $float_w; break; } } else { if ($child_style->float === "right") { $float_x += $cb_w - $float_w; } } if ($cb_w < $float_x + $float_w - $old_x) { // TODO handle when floating elements don't fit } $line_box->get_float_offsets(); if ($child->_float_next_line) { $float_y += $line_box->h; } $child->set_position($float_x, $float_y); $child->move($float_x - $old_x, $float_y - $old_y, true); } }
function reflow(Frame_Decorator $block = null) { // Check if a page break is forced $page = $this->_frame->get_root(); $page->check_forced_page_break($this->_frame); // Bail if the page is full if ($page->is_full()) { return; } // Generated content $this->_set_content(); // Collapse margins if required $this->_collapse_margins(); $style = $this->_frame->get_style(); $cb = $this->_frame->get_containing_block(); if ($style->position === "fixed") { $cb = $this->_frame->get_root()->get_containing_block(); } // Determine the constraints imposed by this frame: calculate the width // of the content area: list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width(); // Store the calculated properties $style->width = $w . "pt"; $style->margin_left = $left_margin . "pt"; $style->margin_right = $right_margin . "pt"; $style->left = $left . "pt"; $style->right = $right . "pt"; // Update the position $this->_frame->position(); list($x, $y) = $this->_frame->get_position(); // Adjust the first line based on the text-indent property $indent = $style->length_in_pt($style->text_indent, $cb["w"]); $this->_frame->increase_line_width($indent); // Determine the content edge $top = $style->length_in_pt(array($style->margin_top, $style->padding_top, $style->border_top_width), $cb["h"]); $bottom = $style->length_in_pt(array($style->border_bottom_width, $style->margin_bottom, $style->padding_bottom), $cb["h"]); $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width, $style->padding_left), $cb["w"]); $cb_y = $y + $top; $cb_h = $cb["h"] + $cb["y"] - $bottom - $cb_y; // Set the y position of the first line in this block $this->_frame->set_current_line($cb_y); $this->_frame->get_current_line_box()->get_float_offsets(); // Set the containing blocks and reflow each child foreach ($this->_frame->get_children() as $child) { // Bail out if the page is full if ($page->is_full()) { break; } $child->set_containing_block($cb_x, $cb_y, $w, $cb_h); $this->process_clear($child); $child->reflow($this->_frame); // Don't add the child to the line if a page break has occurred if ($page->check_page_break($child)) { break; } $this->process_float($child, $cb_x, $w); } // Determine our height list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height(); $style->height = $height; $style->margin_top = $margin_top; $style->margin_bottom = $margin_bottom; $style->top = $top; $style->bottom = $bottom; $needs_reposition = $style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"); // Absolute positioning measurement if ($needs_reposition) { $orig_style = $this->_frame->get_original_style(); if ($orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto")) { $width = 0; foreach ($this->_frame->get_line_boxes() as $line) { $width = max($line->w, $width); } $style->width = $width; } $style->left = $orig_style->left; $style->right = $orig_style->right; } $this->_text_align(); $this->vertical_align(); // Absolute positioning if ($needs_reposition) { list($x, $y) = $this->_frame->get_position(); $this->_frame->position(); list($new_x, $new_y) = $this->_frame->get_position(); $this->_frame->move($new_x - $x, $new_y - $y, true); } if ($block && $this->_frame->is_in_flow()) { $block->add_frame_to_line($this->_frame); // May be inline-block if ($style->display === "block") { $block->add_line(); } } }
function reflow(Frame_Decorator $block = null) { // Check if a page break is forced $page = $this->_frame->get_root(); $page->check_forced_page_break($this->_frame); // Bail if the page is full if ($page->is_full()) { return; } // Generated content $this->_set_content(); // Collapse margins if required $this->_collapse_margins(); $style = $this->_frame->get_style(); $cb = $this->_frame->get_containing_block(); if ($style->counter_increment && ($increment = $style->counter_increment) !== "none") { $this->_frame->increment_counter($increment); } if ($style->position === "fixed") { $cb = $this->_frame->get_root()->get_containing_block(); } // Determine the constraints imposed by this frame: calculate the width // of the content area: list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width(); // Store the calculated properties $style->width = $w . "pt"; $style->margin_left = $left_margin . "pt"; $style->margin_right = $right_margin . "pt"; $style->left = $left . "pt"; $style->right = $right . "pt"; // Update the position $this->_frame->position(); list($x, $y) = $this->_frame->get_position(); // Adjust the first line based on the text-indent property $indent = $style->length_in_pt($style->text_indent, $cb["w"]); $this->_frame->increase_line_width($indent); // Determine the content edge $top = $style->length_in_pt(array($style->margin_top, $style->padding_top, $style->border_top_width), $cb["h"]); $bottom = $style->length_in_pt(array($style->border_bottom_width, $style->margin_bottom, $style->padding_bottom), $cb["h"]); $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width, $style->padding_left), $cb["w"]); $cb_y = $y + $top; $cb_h = $cb["h"] + $cb["y"] - $bottom - $cb_y; // Set the y position of the first line in this block $this->_frame->set_current_line($cb_y); $this->_floating_children = array(); // Set the containing blocks and reflow each child foreach ($this->_frame->get_children() as $child) { // Bail out if the page is full if ($page->is_full()) { break; } $this->get_float_offsets($child, $w); $child->set_containing_block($cb_x, $cb_y, $w, $cb_h); $child->reflow($this->_frame); // << must check in the reflower for offsets in new lines after split !! // Don't add the child to the line if a page break has occurred if ($page->check_page_break($child)) { break; } $child_style = $child->get_style(); if (DOMPDF_ENABLE_CSS_FLOAT && $child_style->float !== "none") { $this->_floating_children[] = $child; // Remove next frame's beginning whitespace $next = $child->get_next_sibling(); if ($next && $next instanceof Text_Frame_Decorator) { $next->set_text(ltrim($next->get_text())); } $float_x = $cb_x; $float_y = $this->_frame->get_current_line_box()->y; switch ($child_style->float) { case "left": break; case "right": $width = $w; $float_x += $width - $child->get_margin_width(); break; } $child->set_position($float_x, $float_y); } } // Determine our height list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height(); $style->height = $height; $style->margin_top = $margin_top; $style->margin_bottom = $margin_bottom; $style->top = $top; $style->bottom = $bottom; $this->_text_align(); $this->vertical_align(); if ($block) { $block->add_frame_to_line($this->_frame); } }