Skip to content

Commit 56db25c

Browse files
feat: Wrap long lines and allow for two-line translation strings (#336)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 6c18585 commit 56db25c

File tree

7 files changed

+68
-50
lines changed

7 files changed

+68
-50
lines changed

src/card.php

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

113+
/**
114+
* Split lines of text using <tspan> elements if it contains a newline or exceeds a maximum number of characters
115+
*
116+
* @param string $text Text to split
117+
* @param int $maxChars Maximum number of characters per line
118+
* @param int $line1Offset Offset for the first line
119+
*
120+
* @return string Original text if one line, or split text with <tspan> elements
121+
*/
122+
function splitLines(string $text, int $maxChars, int $line1Offset): string
123+
{
124+
// if too many characters, insert \n before a " " or "-" if possible
125+
if (mb_strlen($text) > $maxChars && strpos($text, "\n") === false) {
126+
// prefer splitting at " - " if possible
127+
if (strpos($text, " - ") !== false) {
128+
$text = str_replace(" - ", "\n- ", $text);
129+
}
130+
// otherwise, use word wrap to split at " "
131+
else {
132+
$text = wordwrap($text, $maxChars, "\n", true);
133+
}
134+
}
135+
return preg_replace(
136+
"/^(.*)\n(.*)$/",
137+
"<tspan x='81.5' dy='{$line1Offset}'>$1</tspan><tspan x='81.5' dy='16'>$2</tspan>",
138+
$text
139+
);
140+
}
141+
113142
/**
114143
* Generate SVG output for a stats array
115144
*
@@ -171,6 +200,16 @@ function generateCard(array $stats, array $params = null): string
171200
$longestStreakRange .= " - " . $longestStreakEnd;
172201
}
173202

203+
// if the translations contain a newline, split the text into two tspan elements
204+
$totalContributionsText = splitLines($localeTranslations["Total Contributions"], 24, -9);
205+
$currentStreakText = splitLines($localeTranslations["Current Streak"], 22, -9);
206+
$longestStreakText = splitLines($localeTranslations["Longest Streak"], 24, -9);
207+
208+
// if the ranges contain over 28 characters, split the text into two tspan elements
209+
$totalContributionsRange = splitLines($totalContributionsRange, 28, 0);
210+
$currentStreakRange = splitLines($currentStreakRange, 28, 0);
211+
$longestStreakRange = splitLines($longestStreakRange, 28, 0);
212+
174213
return "<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'
175214
style='isolation:isolate' viewBox='0 0 495 195' width='495px' height='195px' direction='{$direction}'>
176215
<style>
@@ -204,23 +243,20 @@ function generateCard(array $stats, array $params = null): string
204243
<g style='isolation:isolate'>
205244
<!-- Total Contributions Big Number -->
206245
<g transform='translate(1,48)'>
207-
<rect width='163' height='50' stroke='none' fill='none'></rect>
208246
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:700;font-size:28px;font-style:normal;fill:{$theme["sideNums"]};stroke:none; opacity: 0; animation: fadein 0.5s linear forwards 0.6s;'>
209247
{$totalContributions}
210248
</text>
211249
</g>
212250
213251
<!-- Total Contributions Label -->
214252
<g transform='translate(1,84)'>
215-
<rect width='163' height='50' stroke='none' fill='none'></rect>
216253
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:400;font-size:14px;font-style:normal;fill:{$theme["sideLabels"]};stroke:none; opacity: 0; animation: fadein 0.5s linear forwards 0.7s;'>
217-
{$localeTranslations["Total Contributions"]}
254+
{$totalContributionsText}
218255
</text>
219256
</g>
220257
221258
<!-- total contributions range -->
222259
<g transform='translate(1,114)'>
223-
<rect width='163' height='50' stroke='none' fill='none'></rect>
224260
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:400;font-size:12px;font-style:normal;fill:{$theme["dates"]};stroke:none; opacity: 0; animation: fadein 0.5s linear forwards 0.8s;'>
225261
{$totalContributionsRange}
226262
</text>
@@ -229,23 +265,20 @@ function generateCard(array $stats, array $params = null): string
229265
<g style='isolation:isolate'>
230266
<!-- Current Streak Big Number -->
231267
<g transform='translate(166,48)'>
232-
<rect width='163' height='50' stroke='none' fill='none'></rect>
233268
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:700;font-size:28px;font-style:normal;fill:{$theme["currStreakNum"]};stroke:none;animation: currstreak 0.6s linear forwards;'>
234269
{$currentStreak}
235270
</text>
236271
</g>
237272
238273
<!-- Current Streak Label -->
239274
<g transform='translate(166,108)'>
240-
<rect width='163' height='50' stroke='none' fill='none'></rect>
241275
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:700;font-size:14px;font-style:normal;fill:{$theme["currStreakLabel"]};stroke:none;opacity: 0; animation: fadein 0.5s linear forwards 0.9s;'>
242-
{$localeTranslations["Current Streak"]}
276+
{$currentStreakText}
243277
</text>
244278
</g>
245279
246280
<!-- Current Streak Range -->
247281
<g transform='translate(166,145)'>
248-
<rect width='163' height='26' stroke='none' fill='none'></rect>
249282
<text x='81.5' y='21' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:400;font-size:12px;font-style:normal;fill:{$theme["dates"]};stroke:none;opacity: 0; animation: fadein 0.5s linear forwards 0.9s;'>
250283
{$currentStreakRange}
251284
</text>
@@ -265,23 +298,20 @@ function generateCard(array $stats, array $params = null): string
265298
<g style='isolation:isolate'>
266299
<!-- Longest Streak Big Number -->
267300
<g transform='translate(331,48)'>
268-
<rect width='163' height='50' stroke='none' fill='none'></rect>
269301
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:700;font-size:28px;font-style:normal;fill:{$theme["sideNums"]};stroke:none; opacity: 0; animation: fadein 0.5s linear forwards 1.2s;'>
270302
{$longestStreak}
271303
</text>
272304
</g>
273305
274306
<!-- Longest Streak Label -->
275307
<g transform='translate(331,84)'>
276-
<rect width='163' height='50' stroke='none' fill='none'></rect>
277308
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:400;font-size:14px;font-style:normal;fill:{$theme["sideLabels"]};stroke:none;opacity: 0; animation: fadein 0.5s linear forwards 1.3s;'>
278-
{$localeTranslations["Longest Streak"]}
309+
{$longestStreakText}
279310
</text>
280311
</g>
281312
282313
<!-- Longest Streak Range -->
283314
<g transform='translate(331,114)'>
284-
<rect width='163' height='50' stroke='none' fill='none'></rect>
285315
<text x='81.5' y='32' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:400;font-size:12px;font-style:normal;fill:{$theme["dates"]};stroke:none;opacity: 0; animation: fadein 0.5s linear forwards 1.4s;'>
286316
{$longestStreakRange}
287317
</text>
@@ -328,7 +358,6 @@ function generateErrorCard(string $message, array $params = null): string
328358
<g style='isolation:isolate'>
329359
<!-- Error Label -->
330360
<g transform='translate(166,108)'>
331-
<rect width='163' height='50' stroke='none' fill='none'></rect>
332361
<text x='81.5' y='50' dy='0.25em' stroke-width='0' text-anchor='middle' style='font-family:Segoe UI, Ubuntu, sans-serif;font-weight:400;font-size:14px;font-style:normal;fill:{$theme["sideLabels"]};stroke:none;'>
333362
{$message}
334363
</text>

tests/RenderTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,30 @@ public function testJsonRender(): void
107107
$expected = file_get_contents("tests/expected/test_stats.json");
108108
$this->assertEquals($expected, $render);
109109
}
110+
111+
/**
112+
* Test split lines function
113+
*/
114+
public function testSplitLines(): void
115+
{
116+
// Check normal label, no split
117+
$this->assertEquals("Total Contributions", splitLines("Total Contributions", 24, -9));
118+
// Check label that is too long, split
119+
$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>",
121+
splitLines("Chuỗi đóng góp hiện tại", 22, -9)
122+
);
123+
// Check label with manually inserted line break, split
124+
$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)
127+
);
128+
// Check date range label, no split
129+
$this->assertEquals("Mar 28, 2019 – Apr 12, 2019", splitLines("Mar 28, 2019 – Apr 12, 2019", 28, 0));
130+
// Check date range label that is too long, split
131+
$this->assertEquals(
132+
"<tspan x='81.5' dy='0'>19 de dez. de 2021</tspan><tspan x='81.5' dy='16'>- 14 de mar.</tspan>",
133+
splitLines("19 de dez. de 2021 - 14 de mar.", 24, 0)
134+
);
135+
}
110136
}

tests/expected/test_border_radius_card.svg

Lines changed: 0 additions & 9 deletions
Loading

tests/expected/test_card.svg

Lines changed: 0 additions & 9 deletions
Loading

0 commit comments

Comments
 (0)