diff --git a/core/polyfills.php b/core/polyfills.php index 6e5bc820..d9040ee1 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -431,25 +431,26 @@ function clamp(int $val, ?int $min = null, ?int $max = null): int return $val; } -/** - * Original PHP code by Chirp Internet: www.chirp.com.au - * Please acknowledge use of this code by including this header. - */ function truncate(string $string, int $limit, string $break = " ", string $pad = "..."): string { - // return with no change if string is shorter than $limit - if (strlen($string) <= $limit) { + $e = "UTF-8"; + $strlen = mb_strlen($string, $e); + $padlen = mb_strlen($pad, $e); + assert($limit > $padlen, "Can't truncate to a length less than the padding length"); + + // if string is shorter or equal to limit, leave it alone + if($strlen <= $limit) { return $string; } - // is $break present between $limit and the end of the string? - if (false !== ($breakpoint = strpos($string, $break, $limit))) { - if ($breakpoint < strlen($string) - 1) { - $string = substr($string, 0, $breakpoint) . $pad; - } + // if there is a break point between 0 and $limit, truncate to that + $breakpoint = mb_strrpos($string, $break, -($strlen - $limit + $padlen), $e); + if ($breakpoint !== false) { + return mb_substr($string, 0, $breakpoint, $e) . $pad; } - return $string; + // if there is no break point, cut mid-word + return mb_substr($string, 0, $limit - $padlen, $e) . $pad; } /** diff --git a/core/tests/PolyfillsTest.php b/core/tests/PolyfillsTest.php index 2ca5328f..03f95fe6 100644 --- a/core/tests/PolyfillsTest.php +++ b/core/tests/PolyfillsTest.php @@ -85,10 +85,11 @@ class PolyfillsTest extends TestCase public function test_truncate(): void { - $this->assertEquals("test words", truncate("test words", 10)); - $this->assertEquals("test...", truncate("test...", 9)); - $this->assertEquals("test...", truncate("test...", 6)); - $this->assertEquals("te...", truncate("te...", 2)); + $this->assertEquals("test words", truncate("test words", 10), "No truncation if string is short enough"); + $this->assertEquals("test...", truncate("test words", 9), "Truncate when string is too long"); + $this->assertEquals("test...", truncate("test words", 7), "Truncate to the same breakpoint"); + $this->assertEquals("te...", truncate("test words", 5), "Breakpoints past the limit don't matter"); + $this->assertEquals("o...", truncate("oneVeryLongWord", 4), "Hard-break if there are no breakpoints"); } public function test_to_shorthand_int(): void diff --git a/ext/comment/theme.php b/ext/comment/theme.php index 9cbdc108..adf39848 100644 --- a/ext/comment/theme.php +++ b/ext/comment/theme.php @@ -210,7 +210,7 @@ class CommentListTheme extends Themelet $h_name = html_escape($comment->owner_name); $h_timestamp = autodate($comment->posted); if ($trim) { - $h_comment = strlen($tfe->stripped) > 52 ? substr($tfe->stripped, 0, 50)."..." : $tfe->stripped; + $h_comment = truncate($tfe->stripped, 50); } else { $h_comment = $tfe->formatted; } diff --git a/ext/forum/theme.php b/ext/forum/theme.php index ea7400f8..c530cfec 100644 --- a/ext/forum/theme.php +++ b/ext/forum/theme.php @@ -226,13 +226,7 @@ class ForumTheme extends Themelet foreach ($threads as $thread) { $titleSubString = $config->get_int('forumTitleSubString'); - - if ($titleSubString < strlen($thread["title"])) { - $title = substr($thread["title"], 0, $titleSubString); - $title = $title."..."; - } else { - $title = $thread["title"]; - } + $title = truncate($thread["title"], $titleSubString); $tbody->appendChild( TR( diff --git a/themes/danbooru/comment.theme.php b/themes/danbooru/comment.theme.php index 8214b9d9..73caabc6 100644 --- a/themes/danbooru/comment.theme.php +++ b/themes/danbooru/comment.theme.php @@ -104,7 +104,7 @@ class CustomCommentListTheme extends CommentListTheme $h_name = html_escape($comment->owner_name); //$h_poster_ip = html_escape($comment->poster_ip); if ($trim) { - $h_comment = strlen($tfe->stripped) > 52 ? substr($tfe->stripped, 0, 50)."..." : $tfe->stripped; + $h_comment = truncate($tfe->stripped, 50); } else { $h_comment = $tfe->formatted; } diff --git a/themes/danbooru2/comment.theme.php b/themes/danbooru2/comment.theme.php index 59bd1143..2d365088 100644 --- a/themes/danbooru2/comment.theme.php +++ b/themes/danbooru2/comment.theme.php @@ -104,7 +104,7 @@ class CustomCommentListTheme extends CommentListTheme $h_name = html_escape($comment->owner_name); //$h_poster_ip = html_escape($comment->poster_ip); if ($trim) { - $h_comment = strlen($tfe->stripped) > 52 ? substr($tfe->stripped, 0, 50)."..." : $tfe->stripped; + $h_comment = truncate($tfe->stripped, 50); } else { $h_comment = $tfe->formatted; } diff --git a/themes/futaba/comment.theme.php b/themes/futaba/comment.theme.php index 0335e032..c4ea8c88 100644 --- a/themes/futaba/comment.theme.php +++ b/themes/futaba/comment.theme.php @@ -78,7 +78,7 @@ class CustomCommentListTheme extends CommentListTheme $h_name = html_escape($comment->owner_name); //$h_poster_ip = html_escape($comment->poster_ip); if ($trim) { - $h_comment = strlen($tfe->stripped) > 52 ? substr($tfe->stripped, 0, 50)."..." : $tfe->stripped; + $h_comment = truncate($tfe->stripped, 50); } else { $h_comment = $tfe->formatted; }