Skip to content

Commit

Permalink
Merge pull request #535 from filipedeschamps/dynamic-thumbnails
Browse files Browse the repository at this point in the history
Thumbnails dinâmicas dos conteúdos
  • Loading branch information
filipedeschamps authored Jul 22, 2022
2 parents 6f406d4 + fae743f commit db0284a
Show file tree
Hide file tree
Showing 17 changed files with 977 additions and 13 deletions.
Binary file added fonts/NotoEmoji-Bold.ttf
Binary file not shown.
Binary file added fonts/Roboto-Bold.ttf
Binary file not shown.
Binary file added fonts/Roboto-Regular.ttf
Binary file not shown.
180 changes: 180 additions & 0 deletions models/thumbnail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,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,
});
4 changes: 3 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ module.exports = {
eslint: {
ignoreDuringBuilds: true,
},
experimental: { nftTracing: true },
experimental: {
nftTracing: true,
},
compiler: {
styledComponents: true,
},
Expand Down
Loading

1 comment on commit db0284a

@vercel
Copy link

@vercel vercel bot commented on db0284a Jul 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

tabnews – ./

tabnews.com.br
www.tabnews.com.br
tabnews-tabnews.vercel.app
tabnews-git-main-tabnews.vercel.app

Please sign in to comment.