Compare commits
4
Commits
ff20d1cb11
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2104680c4e | ||
|
|
3b7cc2e993 | ||
|
|
adcec5f67f | ||
|
|
e7f0aa1c12 |
Generated
+1861
-1282
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user