Skip to content

Commit c2074ea

Browse files
authored
fix: replace wordwrap to support utf8 words (#337)
1 parent 56db25c commit c2074ea

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

src/card.php

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,46 @@ function getRequestedTheme(array $params): array
110110
return $theme;
111111
}
112112

113+
/**
114+
* Wraps a string to a given number of characters
115+
*
116+
* Similar to `wordwrap()`, but uses regex and does not break with certain non-ascii characters
117+
*
118+
* @param string $string The input string
119+
* @param int $width The number of characters at which the string will be wrapped
120+
* @param string $break The line is broken using the optional `break` parameter
121+
* @param bool $cut_long_words If the `cut_long_words` parameter is set to true, the string is
122+
* the string is always wrapped at or before the specified width. So if you have
123+
* a word that is larger than the given width, it is broken apart.
124+
* When false the function does not split the word even if the width is smaller
125+
* than the word width.
126+
* @return string The given string wrapped at the specified length
127+
*/
128+
function utf8WordWrap($string, $width = 75, $break = "\n", $cut_long_words = false)
129+
{
130+
// match anything 1 to $width chars long followed by whitespace or EOS
131+
$string = preg_replace("/(.{1,$width})(?:\s|$)/uS", "$1$break", $string);
132+
// split words that are too long after being broken up
133+
if ($cut_long_words) {
134+
$string = preg_replace("/(\S{" . $width . "})(?=\S)/u", "$1$break", $string);
135+
}
136+
// trim any trailing line breaks
137+
return rtrim($string, $break);
138+
}
139+
140+
/**
141+
* Get the length of a string with utf8 characters
142+
*
143+
* Similar to `strlen()`, but uses regex and does not break with certain non-ascii characters
144+
*
145+
* @param string $string The input string
146+
* @return int The length of the string
147+
*/
148+
function utf8Strlen($string)
149+
{
150+
return preg_match_all("/./us", $string, $matches);
151+
}
152+
113153
/**
114154
* Split lines of text using <tspan> elements if it contains a newline or exceeds a maximum number of characters
115155
*
@@ -122,18 +162,19 @@ function getRequestedTheme(array $params): array
122162
function splitLines(string $text, int $maxChars, int $line1Offset): string
123163
{
124164
// if too many characters, insert \n before a " " or "-" if possible
125-
if (mb_strlen($text) > $maxChars && strpos($text, "\n") === false) {
165+
if (utf8Strlen($text) > $maxChars && strpos($text, "\n") === false) {
126166
// prefer splitting at " - " if possible
127167
if (strpos($text, " - ") !== false) {
128168
$text = str_replace(" - ", "\n- ", $text);
129169
}
130-
// otherwise, use word wrap to split at " "
170+
// otherwise, use word wrap to split at spaces
131171
else {
132-
$text = wordwrap($text, $maxChars, "\n", true);
172+
$text = utf8WordWrap($text, $maxChars, "\n", true);
133173
}
134174
}
175+
$text = htmlspecialchars($text);
135176
return preg_replace(
136-
"/^(.*)\n(.*)$/",
177+
"/^(.*)\n(.*)/",
137178
"<tspan x='81.5' dy='{$line1Offset}'>$1</tspan><tspan x='81.5' dy='16'>$2</tspan>",
138179
$text
139180
);

src/translations.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
],
127127
"vi" => [
128128
"Total Contributions" => "Tổng số đóng góp",
129-
"Current Streak" => "Chuỗi đóng góp hiện tại",
129+
"Current Streak" => "Chuỗi đóng góp\nhiện tại",
130130
"Longest Streak" => "Chuỗi đóng góp lớn nhất",
131131
"Present" => "Hiện tại",
132132
],

tests/RenderTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,13 @@ public function testSplitLines(): void
117117
$this->assertEquals("Total Contributions", splitLines("Total Contributions", 24, -9));
118118
// Check label that is too long, split
119119
$this->assertEquals(
120-
"<tspan x='81.5' dy='-9'>Chuỗi đóng góp</tspan><tspan x='81.5' dy='16'>hiện tại</tspan>",
120+
"<tspan x='81.5' dy='-9'>Chuỗi đóng góp hiện</tspan><tspan x='81.5' dy='16'>tại</tspan>",
121121
splitLines("Chuỗi đóng góp hiện tại", 22, -9)
122122
);
123123
// Check label with manually inserted line break, split
124124
$this->assertEquals(
125-
"<tspan x='81.5' dy='-9'>Chuỗi đóng</tspan><tspan x='81.5' dy='16'>góp hiện tại</tspan>",
126-
splitLines("Chuỗi đóng\ngóp hiện tại", 22, -9)
125+
"<tspan x='81.5' dy='-9'>Chuỗi đóng góp</tspan><tspan x='81.5' dy='16'>hiện tại</tspan>",
126+
splitLines("Chuỗi đóng góp\nhiện tại", 22, -9)
127127
);
128128
// Check date range label, no split
129129
$this->assertEquals("Mar 28, 2019 – Apr 12, 2019", splitLines("Mar 28, 2019 – Apr 12, 2019", 28, 0));

0 commit comments

Comments
 (0)