-
Notifications
You must be signed in to change notification settings - Fork 4
/
index.js
98 lines (85 loc) · 2.56 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
const http = require('http');
const {parse} = require('url');
const opentype = require('opentype.js');
const layer = require('color-composite');
// rgba(27,31,35,0.12)
const BOX_SHADOW = {
space: 'rgb',
values: [27, 31, 35],
alpha: 0.12
};
const FONT_SIZE = 12;
const X_PADDING = 4;
const Y_PADDING = 3;
const font = opentype.loadSync('./SF-Pro-Text-Semibold.otf');
const lineHeight = FONT_SIZE + 2 * Y_PADDING;
const canvasHeight = lineHeight + 1;
const server = http.createServer((req, res) => {
const {color, text} = parse(req.url, true).query;
if (isNotUndefined(color) && isNotUndefined(text)) {
const rgb = hexToRGB(color);
if (isNotUndefined(rgb)) {
res.setHeader('Content-Type', 'image/svg+xml');
return res.end(
svg(
text,
calcWidth(text),
stringifyHex(rgb),
calcShadow(rgb),
isDarkColor(rgb) ? '#fff' : '#000'
)
);
}
}
res.statusCode = 400;
res.end();
});
server.listen(8000);
function svg(text, width, fg, bg, fill) {
const paddedWidth = width + 2 * X_PADDING;
return `<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="${paddedWidth}" height="${canvasHeight}">
<rect x="0" y="1" width="${paddedWidth}" height="${lineHeight}" rx="3" fill="${bg}" />
<rect x="0" y="0" width="${paddedWidth}" height="${lineHeight}" rx="3" fill="${fg}" />
<text fill="${fill}" x="${X_PADDING}" dy="0.39em" y="9" textLength="${width}" style="font-family:-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;font-size:12px;font-weight:600;text-rendering:geometricPrecision;">${text}</text>
</svg>
`;
}
function hexToRGB(hex) {
if (hex.length !== 6) {
return void 0;
}
const num = parseInt(hex, 16);
if (Number.isNaN(num)) {
return void 0;
}
return {
space: 'rgb',
values: [num >> 16, (num >> 8) & 255, num & 255],
alpha: 1
};
}
// http://24ways.org/2010/calculating-color-contrast
// This is the same lightness algorithm as used on GitHub
function isDarkColor(color) {
const [r, g, b] = color.values;
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
// Note: the value 150 is hardcoded into GitHub
return yiq < 150;
}
function stringifyHex(color) {
const [r, g, b] = color.values;
return `#${(b | (g << 8) | (r << 16) | (1 << 24)).toString(16).slice(1)}`;
}
function calcShadow(color) {
return stringifyHex(layer([BOX_SHADOW, color]));
}
function calcWidth(text) {
return font.getAdvanceWidth(text, FONT_SIZE, {
kerning: true,
features: false,
hinting: false
});
}
function isNotUndefined(x) {
return x !== void 0;
}