-
Notifications
You must be signed in to change notification settings - Fork 401
/
Copy paththumbnail.js
180 lines (154 loc) · 6.62 KB
/
thumbnail.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import { join, resolve } from 'path';
import { renderToStaticMarkup } from 'react-dom/server';
import { renderAsync } from '@resvg/resvg-js';
import removeMarkdown from 'remove-markdown';
async function asPng(contentObject) {
const parsedContent = parseContent(contentObject);
const svg = renderToStaticMarkup(renderTemplate(parsedContent));
const renderBuffer = await renderAsync(svg, {
fitTo: {
mode: 'width',
value: 1280,
},
font: {
fontFiles: [
join(resolve('.'), 'fonts', 'Roboto-Regular.ttf'),
join(resolve('.'), 'fonts', 'Roboto-Bold.ttf'),
join(resolve('.'), 'fonts', 'NotoEmoji-Bold.ttf'),
],
loadSystemFonts: false,
defaultFontFamily: 'Roboto',
},
});
return renderBuffer.asPng();
}
export function parseContent(content) {
let title = content.title;
if (!title) {
title = removeMarkdown(content.body).substring(0, 120).replace(/\s+/g, ' ');
}
// Regex to wrap text: https://stackoverflow.com/a/51506718
if (content.parent_id) {
title = title.replace(/(?![^\n]{1,30}$)([^\n]{1,30})\s/g, '$1_').split('_');
} else {
title = title.replace(/(?![^\n]{1,24}$)([^\n]{1,24})\s/g, '$1_').split('_');
}
title = title.length <= 3 ? title : [title[0], title[1], title[2] + '...'];
let parent_title = content.parent_title;
if (content.parent_slug) {
parent_title = (parent_title ?? content.parent_username).substring(0, 60);
}
parent_title = parent_title?.length > 50 ? parent_title.substring(0, 50) + '...' : parent_title;
const date = new Date(content.published_at).toLocaleDateString('pt-BR');
// Measure author text width: https://bl.ocks.org/tophtucker/62f93a4658387bb61e4510c37e2e97cf
function measureText(string, fontSize = 32) {
const widths = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.246875,
0.2578125, 0.3203125, 0.61640625, 0.56171875, 0.7328125, 0.621875, 0.175, 0.3421875, 0.34765625, 0.43125,
0.5671875, 0.196875, 0.2765625, 0.26328125, 0.4125, 0.56171875, 0.56171875, 0.56171875, 0.56171875, 0.56171875,
0.56171875, 0.56171875, 0.56171875, 0.56171875, 0.56171875, 0.2421875, 0.21171875, 0.50859375, 0.54921875,
0.52265625, 0.47265625, 0.8984375, 0.65234375, 0.62265625, 0.6515625, 0.65625, 0.56875, 0.553125, 0.68125,
0.71328125, 0.27265625, 0.55234375, 0.62744140625, 0.53828125, 0.8734375, 0.71328125, 0.6875, 0.63125, 0.6875,
0.61640625, 0.59375, 0.596875, 0.6484375, 0.63671875, 0.8875, 0.62734375, 0.60078125, 0.59921875, 0.265625,
0.41015625, 0.265625, 0.41796875, 0.4515625, 0.309375, 0.54453125, 0.56171875, 0.5234375, 0.5640625, 0.53046875,
0.3486328125, 0.56171875, 0.55078125, 0.24296875, 0.27080078125, 0.50703125, 0.24296875, 0.8765625, 0.55234375,
0.5703125, 0.56171875, 0.56875, 0.3390625, 0.515625, 0.32734375, 0.5515625, 0.484375, 0.7515625, 0.49609375,
0.4734375, 0.49609375, 0.3390625, 0.24375, 0.3390625, 0.68046875,
];
const avg = 0.5117845394736842;
return (
string
.split('')
.map((c) => (c.charCodeAt(0) < widths.length ? widths[c.charCodeAt(0)] : avg))
.reduce((cur, acc) => acc + cur) * fontSize
);
}
return {
title,
parentTitle: parent_title,
username: content.username,
usernameWidth: measureText(content.username),
comments: content.children_deep_count,
date,
};
}
export function renderTemplate({ title, parentTitle, username, usernameWidth, comments, date }) {
function renderPostHeader(title) {
return (
<text y="54" fill="#212529" fontSize="75" fontWeight="bold">
{title.map((line, index) => (
<tspan x="60" dy={100} key={index}>
{line}
</tspan>
))}
</text>
);
}
function renderCommentHeader(title, parentTitle) {
return (
<>
{/* reference */}
<text fill="#424C56" fontSize="32">
<tspan x="60" y="123.938">
Em resposta a
</tspan>
</text>
<text fill="#424C56" fontSize="32" textDecoration="underline">
<tspan x="275" y="123.938">
{parentTitle}
</tspan>
</text>
{/* title */}
<text y="155" fill="#212529" fontSize="75" fontWeight="bold">
{title.map((line, index) => (
<tspan x={60} dy={90} key={index}>
{line}
</tspan>
))}
</text>
</>
);
}
return (
<svg width="1200" height="628" xmlns="http://www.w3.org/2000/svg">
{/* background */}
<rect width="1200" height="628" fill="#F5F5F5" />
{/* title */}
{!!parentTitle ? renderCommentHeader(title, parentTitle) : renderPostHeader(title)}
{/* tabnews icon */}
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1103.37 91.5C1110.21 91.5 1115.75 97.0405 1115.75 103.875V145.125C1115.75 151.96 1110.21 157.5 1103.37 157.5H1045.62C1038.79 157.5 1033.25 151.96 1033.25 145.125V103.875C1033.25 97.0405 1038.79 91.5 1045.62 91.5H1103.37ZM1107.5 114.702H1078.63C1076.35 114.702 1073.86 112.971 1073.06 110.835L1068.95 99.75H1045.62C1043.35 99.75 1041.5 101.597 1041.5 103.875V145.125C1041.5 147.403 1043.35 149.25 1045.62 149.25H1103.37C1105.65 149.25 1107.5 147.403 1107.5 145.125V114.702Z"
fill="#212529"
/>
{/* username container */}
<rect x="60" y="511" width={usernameWidth + 20} height="68" rx="8" fill="#C7D9EC" />
{/* username */}
<text x="70" y="555" fill="#424C56" fontSize="32">
<tspan>{username}</tspan>
</text>
{/* comments icon */}
<path
d="M774 532.333C765.163 532.333 758 538.431 758 545.951C758 549.137 759.243 552.072 761.236 554.169L758 561.667L768.084 558.653C780.744 562.117 790 554.319 790 545.951C790 538.431 782.837 532.333 774 532.333Z"
fill="#8EA1B4"
/>
{/* comments */}
<text x="800" y="555" fill="#424C56" fontSize="32">
<tspan>{comments}</tspan>
</text>
{/* calendar icon */}
<path
d="M909.667 547.333H904.333V542H909.667V547.333ZM917.667 542H912.333V547.333H917.667V542ZM901.667 550H896.333V555.333H901.667V550ZM909.667 550H904.333V555.333H909.667V550ZM901.667 542H896.333V547.333H901.667V542ZM923 531.333V549.181C923 552.369 914.136 562 909.919 562H891V531.333H923ZM920.333 539.333H893.667V559.333H908.816C914.357 559.333 912.333 551.333 912.333 551.333C912.333 551.333 920.333 553.533 920.333 548.057V539.333Z"
fill="#8EA1B4"
/>
{/* date */}
<text x="940" y="555" fill="#424C56" fontSize="32">
<tspan>{date}</tspan>
</text>
</svg>
);
}
export default Object.freeze({
asPng,
});