From 96a378081b687af2fc9a42dd3b7e816c9783b7d6 Mon Sep 17 00:00:00 2001 From: Bob Vincent Date: Tue, 15 Mar 2011 06:26:45 -0400 Subject: [PATCH] Improved text_summary() function. --- modules/field/modules/text/text.module | 181 ++++++++++++++++++++++---------- 1 files changed, 125 insertions(+), 56 deletions(-) diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module index 89c605c..fbef11f 100644 --- a/modules/field/modules/text/text.module +++ b/modules/field/modules/text/text.module @@ -327,6 +327,10 @@ function _text_sanitize($instance, $langcode, $item, $column) { * place such as the end of a paragraph, a line break, or the end of a * sentence (in that order of preference). * + * @note + * This function uses strlen(), strpos(), etc. rather than their multibyte + * equivalents where doing so increases speed without affecting output. + * * @param $text * The content for which a summary will be generated. * @param $format @@ -344,7 +348,7 @@ function _text_sanitize($instance, $langcode, $item, $column) { * @return * The generated summary. */ -function text_summary($text, $format = NULL, $size = NULL) { +function text_summary($text, $format = NULL, $size = NULL, &$summary_length = NULL) { if (!isset($size)) { // What used to be called 'teaser' is now called 'summary', but @@ -368,6 +372,7 @@ function text_summary($text, $format = NULL, $size = NULL) { // We check for the presence of the PHP evaluator filter in the current // format. If the body contains PHP code, we do not split it up to prevent // parse errors. + $filters = array(); if (isset($format)) { $filters = filter_list_format($format); if (isset($filters['php_code']) && $filters['php_code']->status && strpos($text, '' => 0); - - // If no complete paragraph then treat line breaks as paragraphs. - $line_breaks = array('
' => 6, '
' => 4); - // Newline only indicates a line break if line break converter - // filter is present. - if (isset($filters['filter_autop'])) { - $line_breaks["\n"] = 1; - } - $break_points[] = $line_breaks; - - // If the first paragraph is too long, split at the end of a sentence. - $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1); - - // Iterate over the groups of break points until a break point is found. - foreach ($break_points as $points) { - // Look for each break point, starting at the end of the summary. - foreach ($points as $point => $offset) { - // The summary is already reversed, but the break point isn't. - $rpos = strpos($reversed, strrev($point)); - if ($rpos !== FALSE) { - $min_rpos = min($rpos + $offset, $min_rpos); + $filter_newline = isset($filters['filter/1']); + $body_length = strlen(body); + + $position = 0; + $length = 0; + $stack = array(); + while ($position < $body_length && $length < $size) { + $last_tag = FALSE; + $offset = strpos($body, '<', $position); + if ($offset === FALSE) { + // There are no more tags, so find the UTF-8 string length. + $additional = drupal_strlen(substr($body, $position, $body_length - $position)); + $num_chars = $body_length; + } + else { + // Count UTF-8 characters between the previous position and the next tag. + $additional = drupal_strlen(substr($body, $position, $offset - $position)); + ++$offset; // Skip the '<' character. + $num_chars = strpos($body, '>', $offset); + if ($body[$offset] == '/') { + // Found a closing tag, so pop the opening tag too. + array_pop($stack); + } + elseif ($body[$num_chars - 1] != '/') { // Skip empty tags. + // Found an opening tag; save it on the stack. + $end_name = strpos($body, ' ', $offset); + if ($end_name === FALSE || $end_name > $num_chars) { + $end_name = $num_chars; + } + $tag_name = substr($body, $offset, $end_name - $offset); + switch ($tag_name) { // Ignore empty tags that were not properly closed. + case 'br': + case 'hr': + case 'img': + case 'input': + break; + default: + $stack[] = $tag_name; + $last_tag = TRUE; + break; + } + } + // For now, we assume properly opening/closing tag boundaries. + if ($num_chars === FALSE) { + // Either the last tag was not closed or it wasn't a tag. + $num_chars = $body_length; + } + else { + ++$num_chars; // Skip the '>' character. } } + // Are there any characters to add the to result? + if ($additional) { + if ($length + $additional >= $size) { + // The last tag did not make it in. + if ($last_tag) { + array_pop($stack); + } + // There are too many characters, so search for a break-point. + $offset = $position + $size - $length; + if ($body[$offset] != ' ') while ($offset > $position) { + switch ($body[$offset - 1]) { + case "\xD8": + // Is this the Arabic equivalent of the ascii '?' character? + if (!isset($body[$offset]) || $body[$offset] != "\x9F") { + // No; this is not the right sequence. + break; + } + if ($offset + 1 == $body_length || $body[$offset + 1] == ' ') { + // Found a break-point. + break 2; + } + if ($body[$offset + 1] == '"') { + $offset += 2; + break 2; + } + break; + case '.': + case '!': + case '?': + if ($offset == $body_length || $body[$offset] == ' ') { + // Found a break-point. + break 2; + } + if ($body[$offset] == '"') { + ++$offset; + break 2; + } + break; + case "\n": + if (!$filter_newline) { + break; + } + case ' ': + // Found the (breaking) space; remove and break there. + --$offset; + break 2; + // @todo Add support for other UTF-8 spaces? + case "\xE3": + // Found the CJK ideographic full stop? + if (isset($body[$offset + 1]) && $body[$offset] == "\x80" && $body[$offset + 1] == "\x82") { + // keep this character in full + $offset += 2; + break 2; + } + break; - // If a break point was found in this group, slice and stop searching. - if ($min_rpos !== $max_rpos) { - // Don't slice with length 0. Length must be <0 to slice from RHS. - $summary = ($min_rpos === 0) ? $summary : substr($summary, 0, 0 - $min_rpos); - break; + } + --$offset; + } + $position = $offset; + break; + } + $length += $additional; } + $position = $num_chars; } - - // If the htmlcorrector filter is present, apply it to the generated summary. - if (isset($filters['filter_htmlcorrector'])) { - $summary = _filter_htmlcorrector($summary); + $summary = substr($body, 0, $position); + if (!empty($stack)) { + // If closing tags are missing, we add an ellipsis and closing tags. + $summary .= t('...'); + do { + $summary .= ''; + } while (!empty($stack)); } - return $summary; } -- 1.7.1