Skip to content

Commit 208a708

Browse files
authored
fix(svg): escape ampersands in Google Fonts import URLs (JhaSourav07#1706)
## Description Fixes JhaSourav07#1705 This PR fixes an XML/SVG syntax issue in the auto-theme SVG generators. When a custom Google Font is requested, `generateAutoThemeSVG` and `generateAutoThemeMonthlySVG` generate a Google Fonts `@import` URL containing a raw `&display=swap` parameter inside an SVG `<style>` block. Because SVG is XML-based and the stylesheet is not wrapped in CDATA, raw ampersands are invalid XML and can cause parsing or rendering failures in strict SVG/XML parsers. ### Changes * Replaced raw `&display=swap` with `&amp;display=swap` * Updated: * `generateAutoThemeSVG` * `generateAutoThemeMonthlySVG` * Aligned implementation with other SVG generators in the codebase that already use XML-safe escaped URLs ### Before ```css @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); ``` ### After ```css @import url('https://fonts.googleapis.com/css2?family=Roboto&amp;display=swap'); ``` This preserves existing functionality while ensuring generated SVG output remains valid XML. ## Pillar * [ ] 🎨 Pillar 1 — New Theme Design * [ ] 📐 Pillar 2 — Geometric SVG Improvement * [ ] 🕐 Pillar 3 — Timezone Logic Optimization * [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview N/A (No visual changes) This change only affects XML escaping within generated SVG `<style>` blocks and does not alter the rendered output. ## Checklist before requesting a review: * [x] I have read the `CONTRIBUTING.md` file. * [x] I have tested these changes locally (`localhost:3000/api/streak?user=YOUR_USERNAME`). * [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). * [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). * [ ] I have updated `README.md` if I added a new theme or URL parameter. * [x] I have started the repo. * [x] I have made sure that i have only one commit to merge in this PR. * [x] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). * [ ] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents d0c5107 + 28d06db commit 208a708

3 files changed

Lines changed: 5 additions & 5 deletions

File tree

app/api/streak/route.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,7 @@ describe('GET /api/streak', () => {
12081208
const body = await response.text();
12091209

12101210
expect(response.status).toBe(200);
1211-
expect(body).toContain('family=Inter&display=swap');
1211+
expect(body).toContain('family=Inter&amp;display=swap');
12121212
expect(body).toContain('"Inter", sans-serif');
12131213
});
12141214
});

lib/svg/generator.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ describe('generateSVG', () => {
506506
);
507507

508508
expect(svg).toContain(
509-
"@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');"
509+
"@import url('https://fonts.googleapis.com/css2?family=Inter&amp;display=swap');"
510510
);
511511
expect(svg).toContain('font-family: "Inter", sans-serif;');
512512
});
@@ -966,7 +966,7 @@ describe('generateMonthlySVG', () => {
966966
} as unknown as BadgeParams);
967967

968968
expect(svg).toContain(
969-
"@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');"
969+
"@import url('https://fonts.googleapis.com/css2?family=Inter&amp;display=swap');"
970970
);
971971
expect(svg).toContain('font-family: "Inter", sans-serif;');
972972
});

lib/svg/generator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ function generateAutoThemeSVG(
552552
const statsFont = selectedFont || '"Space Grotesk", sans-serif';
553553
const googleFontUrlPart = sanitizedFont ? sanitizeGoogleFontUrl(sanitizedFont) : null;
554554
const googleFontsImport = googleFontUrlPart
555-
? `@import url('https://fonts.googleapis.com/css2?family=${googleFontUrlPart}&display=swap');`
555+
? `@import url('https://fonts.googleapis.com/css2?family=${googleFontUrlPart}&amp;display=swap');`
556556
: '';
557557
const sf = getSizeScale(params.size);
558558
const radius = sanitizeRadius(params.radius, 8) * sf;
@@ -1033,7 +1033,7 @@ function generateAutoThemeMonthlySVG(stats: MonthlyStats, params: BadgeParams):
10331033
const statsFont = selectedFont || '"Space Grotesk", sans-serif';
10341034
const googleFontUrlPart = sanitizedFont ? sanitizeGoogleFontUrl(sanitizedFont) : null;
10351035
const googleFontsImport = googleFontUrlPart
1036-
? `@import url('https://fonts.googleapis.com/css2?family=${googleFontUrlPart}&display=swap');`
1036+
? `@import url('https://fonts.googleapis.com/css2?family=${googleFontUrlPart}&amp;display=swap');`
10371037
: '';
10381038
const parsedRadius = Number(params.radius);
10391039
const radius = Math.max(0, Math.min(Number.isNaN(parsedRadius) ? 8 : parsedRadius, 50));

0 commit comments

Comments
 (0)