Compare commits

...
4 Commits
Author SHA1 Message Date
echomike 2104680c4e update failed test, increase timeout
Node.js CI / build (push) Successful in 5m28s
2026-07-21 12:51:41 +02:00
echomike 3b7cc2e993 npm packages update
Node.js CI / build (push) Failing after 4m27s
2026-07-17 15:45:10 +02:00
echomike adcec5f67f horizontal padding fix
Node.js CI / build (push) Failing after 29m37s
2026-07-17 15:30:06 +02:00
echomike e7f0aa1c12 rect padding implemet
Node.js CI / build (push) Successful in 3m10s
2026-07-17 14:48:22 +02:00
6 changed files with 1881 additions and 1297 deletions
+1862 -1283
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -23,10 +23,10 @@
"express": "^5.1.0", "express": "^5.1.0",
"express-rate-limit": "^8.6.0", "express-rate-limit": "^8.6.0",
"helmet": "^8.1.0", "helmet": "^8.1.0",
"jest": "^30.4.2",
"pino": "^10.3.1" "pino": "^10.3.1"
}, },
"devDependencies": { "devDependencies": {
"jest": "^29.7.0",
"pino-pretty": "^13.1.3", "pino-pretty": "^13.1.3",
"supertest": "^7.1.3" "supertest": "^7.1.3"
} }
+8 -6
View File
@@ -42,8 +42,9 @@ router.get('/badge', async (req, res) => {
const iconSize = size; const iconSize = size;
const fontSize = calcFontSize(iconSize); const fontSize = calcFontSize(iconSize);
const textPadding = Math.round(iconSize * 0.4); const textPadding = Math.round(iconSize * 0.4);
const textHpad = 4; // horizontal padding inside text rect — enough to prevent clipping
const radius = style === 'flat' ? 3 : 0; const radius = style === 'flat' ? 3 : 0;
const textWidth = estimateTextWidth(effectiveLabel, fontSize); const textWidth = estimateTextWidth(effectiveLabel, fontSize) + textHpad * 2;
// Kinyerjük az ikon SVG-ből a width és height attribútumokat (ha vannak) // Kinyerjük az ikon SVG-ből a width és height attribútumokat (ha vannak)
let iconViewBoxWidth = iconSize; let iconViewBoxWidth = iconSize;
@@ -54,8 +55,9 @@ router.get('/badge', async (req, res) => {
if (heightMatch) iconViewBoxHeight = parseFloat(heightMatch[1]); if (heightMatch) iconViewBoxHeight = parseFloat(heightMatch[1]);
// Ikon doboz mérete // Ikon doboz mérete
const iconBoxWidth = iconViewBoxWidth + 5; const iconPad = 2;
const iconBoxHeight = iconViewBoxHeight + 5; const iconBoxWidth = iconViewBoxWidth + iconPad;
const iconBoxHeight = iconViewBoxHeight + iconPad;
// Ha a color paraméter nincs megadva, próbáljuk kinyerni az ikon színét // Ha a color paraméter nincs megadva, próbáljuk kinyerni az ikon színét
let effectiveColor = color; let effectiveColor = color;
@@ -79,12 +81,12 @@ router.get('/badge', async (req, res) => {
const fontWeight = safeFontWeight; // paraméterből const fontWeight = safeFontWeight; // paraméterből
if (labelpos === 'left') { if (labelpos === 'left') {
// Külön dobozok: padding csak a széleken kell, a dobozokon belül nem! // Külön dobozok: padding a szöveg körül!
textElem = `<rect x="0" y="0" width="${textWidth}" height="${iconBoxHeight}" rx="${radius}" fill="${safeBgLabel}"/> textElem = `<rect x="0" y="0" width="${textWidth}" height="${iconBoxHeight}" rx="${radius}" fill="${safeBgLabel}"/>
<text x="${textWidth / 2}" y="${iconBoxHeight / 2}" font-size="${fontSize}" font-family="${fontFamily}" font-weight="${fontWeight}" fill="${effectiveColor}" text-anchor="middle" dominant-baseline="middle">${safeLabel}</text>`; <text x="${textWidth / 2}" y="${iconBoxHeight / 2}" font-size="${fontSize}" font-family="${fontFamily}" font-weight="${fontWeight}" fill="${effectiveColor}" text-anchor="middle" dominant-baseline="middle">${safeLabel}</text>`;
iconGroup = ` iconGroup = `
<rect x="${textWidth}" y="0" width="${iconBoxWidth + 5}" height="${iconBoxHeight}" rx="${radius}" fill="${safeBgIcon}"/> <rect x="${textWidth}" y="0" width="${iconBoxWidth}" height="${iconBoxHeight}" rx="${radius}" fill="${safeBgIcon}"/>
<g transform="translate(${textWidth + (5 / 2)}, ${(iconBoxHeight - iconSize) / 2})">${iconSVG}</g> <g transform="translate(${textWidth + (iconPad / 2)}, ${(iconBoxHeight - iconSize) / 2})">${iconSVG}</g>
`; `;
} else if (labelpos === 'above' || labelpos === 'below') { } else if (labelpos === 'above' || labelpos === 'below') {
// Felső vagy alsó label: ikon doboz magassága = iconSize + 5, szélessége = szöveg doboz szélessége // Felső vagy alsó label: ikon doboz magassága = iconSize + 5, szélessége = szöveg doboz szélessége
+4 -3
View File
@@ -7,10 +7,11 @@ const FONT_FAMILY = 'Verdana,Geneva,DejaVu Sans,sans-serif';
const calcFontSize = (size) => Math.round(size * 0.6); const calcFontSize = (size) => Math.round(size * 0.6);
/** /**
* Rough text-width estimate based on font-size and character count. * Text-width estimate based on font-size and character count.
* Uses a 0.6 coefficient which works well for Verdana / sans-serif. * Uses a 0.7 coefficient — Verdana has wide letterforms (M, W, D are ~70% of fontSize).
* Returns only the raw text bounding box — padding is added by callers.
*/ */
const estimateTextWidth = (text, fontSize) => const estimateTextWidth = (text, fontSize) =>
text.length * fontSize * 0.6; text.length * fontSize * 0.7;
module.exports = { FONT_FAMILY, calcFontSize, estimateTextWidth }; module.exports = { FONT_FAMILY, calcFontSize, estimateTextWidth };
+1 -1
View File
@@ -36,7 +36,7 @@ describe('GET /badge', () => {
expect(res.headers['content-type']).toMatch(/image\/svg\+xml/); expect(res.headers['content-type']).toMatch(/image\/svg\+xml/);
expect(body).toContain('<svg'); expect(body).toContain('<svg');
expect(body).toContain('<rect'); expect(body).toContain('<rect');
}); }, 20000);
it('should use custom icon and label', async () => { it('should use custom icon and label', async () => {
const res = await request(createApp()).get('/badge?icon=github&label=GitHub'); const res = await request(createApp()).get('/badge?icon=github&label=GitHub');
+5 -3
View File
@@ -29,17 +29,19 @@ describe('svg-utils', () => {
describe('estimateTextWidth', () => { describe('estimateTextWidth', () => {
it('should estimate width based on text length and font size', () => { it('should estimate width based on text length and font size', () => {
const width = estimateTextWidth('Hello', 14); const width = estimateTextWidth('Hello', 14);
expect(width).toBe(5 * 14 * 0.6); // 42 // formula: text.length * fontSize * 0.7
expect(width).toBe(5 * 14 * 0.7); // 49
}); });
it('should return 0 for empty string', () => { it('should return 0 for empty string', () => {
expect(estimateTextWidth('', 14)).toBe(0); expect(estimateTextWidth('', 14)).toBe(0);
}); });
it('should scale linearly with font size', () => { it('should scale with font size', () => {
const w1 = estimateTextWidth('test', 10); const w1 = estimateTextWidth('test', 10);
const w2 = estimateTextWidth('test', 20); const w2 = estimateTextWidth('test', 20);
expect(w2).toBe(w1 * 2); expect(w1).toBe(4 * 10 * 0.7); // 28
expect(w2).toBe(4 * 20 * 0.7); // 56
}); });
}); });
}); });