function get_min_max_width() { $style = $this->_frame->get_style(); $this->_block_parent = $this->_frame->find_block_parent(); $line_width = $this->_frame->get_containing_block("w"); $str = $text = $this->_frame->get_text(); $size = $style->font_size; $font = $style->font_family; $word_spacing = $style->length_in_pt($style->word_spacing); $char_spacing = $style->length_in_pt($style->letter_spacing); switch ($style->white_space) { default: case "normal": $str = preg_replace(self::$_whitespace_pattern, " ", $str); case "pre-wrap": case "pre-line": // Find the longest word (i.e. minimum length) // This technique (using arrays & an anonymous function) is actually // faster than doing a single-pass character by character scan. Heh, // yes I took the time to bench it ;) $words = array_flip(preg_split("/[\\s-]+/u", $str, -1, PREG_SPLIT_DELIM_CAPTURE)); array_walk($words, create_function('&$val,$str', '$val = Font_Metrics::get_text_width($str, "' . addslashes($font) . '", ' . $size . ', ' . $word_spacing . ', ' . $char_spacing . ');')); arsort($words); $min = reset($words); break; case "pre": $lines = array_flip(preg_split("/\n/u", $str)); array_walk($lines, create_function('&$val,$str', '$val = Font_Metrics::get_text_width($str, "' . addslashes($font) . '", ' . $size . ', ' . $word_spacing . ', ' . $char_spacing . ');')); arsort($lines); $min = reset($lines); break; case "nowrap": $min = Font_Metrics::get_text_width($this->_collapse_white_space($str), $font, $size, $word_spacing, $char_spacing); break; } switch ($style->white_space) { default: case "normal": case "nowrap": $str = preg_replace(self::$_whitespace_pattern, " ", $text); break; case "pre-line": //XXX: Is this correct? $str = preg_replace("/[ \t]+/u", " ", $text); case "pre-wrap": // Find the longest word (i.e. minimum length) $lines = array_flip(preg_split("/\n/", $text)); array_walk($lines, create_function('&$val,$str', '$val = Font_Metrics::get_text_width($str, "' . $font . '", ' . $size . ', ' . $word_spacing . ', ' . $char_spacing . ');')); arsort($lines); reset($lines); $str = key($lines); break; } $max = Font_Metrics::get_text_width($str, $font, $size, $word_spacing, $char_spacing); $delta = $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); $min += $delta; $max += $delta; return array($min, $max, "min" => $min, "max" => $max); }
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; } $break_word = $style->word_wrap === "break-word"; // The first word has overflowed. Force it onto the line if ($current_line_width == 0 && $width == 0) { $s = ""; $last_width = 0; if ($break_word) { for ($j = 0; $j < strlen($word); $j++) { $s .= $word[$j]; $_width = Font_Metrics::get_text_width($s, $font, $size, $word_spacing, $char_spacing); if ($_width > $available_width) { break; } $last_width = $_width; } } if ($break_word && $last_width > 0) { $width += $last_width; $str .= substr($s, 0, -1); } else { $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; }
if (self::$_buggy_splittext) { // workaround to solve DOMText::spliText() bug parsing multibyte strings $node = $this->_frame->get_node(); $txt0 = $node->substringData(0, $offset); $txt1 = $node->substringData($offset, mb_strlen($node->textContent) - 1); $node->replaceData(0, mb_strlen($node->textContent), $txt0); $split = $node->parentNode->appendChild(new DOMText($txt1)); } else { $split = $this->_frame->get_node()->splitText($offset); } $deco = $this->copy($split); $p = $this->get_parent(); $p->insert_child_after($deco, $this, false); if ($p instanceof Inline_Frame_Decorator) { $p->split($deco); } return $deco; } //........................................................................ function delete_text($offset, $count) { $this->_frame->get_node()->deleteData($offset, $count); } //........................................................................ function set_text($text) { $this->_frame->get_node()->data = $text; } } Text_Frame_Decorator::$_buggy_splittext = PHP_VERSION_ID < 50207;
if (self::$_buggy_splittext) { // workaround to solve DOMText::spliText() bug parsing multibyte strings $node = $this->_frame->get_node(); $txt0 = $node->substringData(0, $offset); $txt1 = $node->substringData($offset, mb_strlen($node->textContent) - 1); $node->replaceData(0, mb_strlen($node->textContent), $txt0); $split = $node->parentNode->appendChild(new DOMText($txt1)); } else { $split = $this->_frame->get_node()->splitText($offset); } $deco = $this->copy($split); $p = $this->get_parent(); $p->insert_child_after($deco, $this, false); if ($p instanceof Inline_Frame_Decorator) { $p->split($deco); } return $deco; } //........................................................................ function delete_text($offset, $count) { $this->_frame->get_node()->deleteData($offset, $count); } //........................................................................ function set_text($text) { $this->_frame->get_node()->data = $text; } } Text_Frame_Decorator::$_buggy_splittext = version_compare(PHP_VERSION, '5.2.6', '<=');