Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
// From https://github.com/sw-yx/swyxdotio/blob/60b088cea0439d3e2536a78dc922af3146ba40fd/screenshot-plugin/screenshot.js
const puppeteer = require('puppeteer')
const fs = require('fs')
const path = require('path')
module.exports = screenshot
async function screenshot(PostArray) {
const headless = true
// const headless = false // for debug
const browser = await puppeteer.launch({ headless })
const page = await browser.newPage()
page.setViewport({ width: 1200, height: 628 })
const getHtml = require('./template')
console.log('taking screenshots...')
for (const post of PostArray) {
const [slug, text] = post
const html = getHtml({
fileType: 'jpg',
text,
theme: 'light',
md: true,
fontSize: Math.min(20, Math.max(7, Math.floor(100 / text.length))) + 'vw'
})
await page.setContent(html)
const filePath = path.resolve(`static/og_image/${slug}.png`)
ensureDirectoryExistence(filePath)
await page.screenshot({ path: filePath })
}
if (headless) {
await browser.close()
}
}
function ensureDirectoryExistence(filePath) {
var dirname = path.dirname(filePath)
if (fs.existsSync(dirname)) {
return true
}
ensureDirectoryExistence(dirname)
fs.mkdirSync(dirname)
}
const { readFileSync } = require('fs')
const marked = require('marked')
const twemoji = require('twemoji')
const twOptions = { folder: 'svg', ext: '.svg' }
const emojify = text => twemoji.parse(text, twOptions)
const rglr = readFileSync(`${__dirname}/_fonts/Inter-Regular.woff2`).toString(
'base64'
)
const bold = readFileSync(`${__dirname}/_fonts/Inter-Bold.woff2`).toString(
'base64'
)
const mono = readFileSync(`${__dirname}/_fonts/Vera-Mono.woff2`).toString(
'base64'
)
function getCss(theme, fontSize) {
let background = 'white'
let foreground = 'black'
let radial = 'lightgray'
if (theme === 'dark') {
background = 'black'
foreground = 'white'
radial = 'dimgray'
}
return `
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: normal;
src: url(data:font/woff2;charset=utf-8;base64,${rglr}) format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: bold;
src: url(data:font/woff2;charset=utf-8;base64,${bold}) format('woff2');
}
@font-face {
font-family: 'Vera';
font-style: normal;
font-weight: normal;
src: url(data:font/woff2;charset=utf-8;base64,${mono}) format("woff2");
}
.bodywrapper {
background: ${background};
background-image: url("https://www.swyx.io/swyx-og-card-blank.png");
background-size: cover;
padding-left: 1rem;
object-fit: cover;
height: 600px;
width: 1200px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
code {
color: #D400FF;
font-family: 'Vera';
white-space: pre-wrap;
letter-spacing: -5px;
}
code:before, code:after {
content: '\`';
}
.logo-wrapper {
display: flex;
align-items: center;
align-content: center;
justify-content: center;
justify-items: center;
}
.logo {
/*margin: 0 75px;*/
}
.plus {
color: #BBB;
font-family: Times New Roman, Verdana;
font-size: 100px;
}
.spacer {
display: flex;
flex-direction: row;
}
.emoji {
height: 1em;
width: 1em;
margin: 0 .05em 0 .1em;
vertical-align: -0.1em;
}
p {
margin: 0;
font-weight: bold;
}
.heading {
font-family: 'Inter', sans-serif;
font-size: ${sanitizeHtml(fontSize)};
font-style: normal;
color: ${foreground};
line-height: 1.25;
width: 60vw;
}
.footer {
width: 45vw;
font-size: 3rem;
display: flex;
justify-content: space-between;
}
`
}
module.exports = function getHtml(parsedReq) {
const { text, theme, md, fontSize } = parsedReq
return `<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>Generated Image</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
${getCss(theme, fontSize)}
</style>
<body>
<div class="bodywrapper">
<div class="heading">${emojify(
md ? marked(text) : sanitizeHtml(text)
)}</div>
<div class="footer">
<div>swyx.io</div>
<div>@swyx</div>
</div>
</div>
</body>
</html>`
}
function getImage(src, width = 'auto', height = '225') {
return `<img
class="logo"
alt="Generated Image"
src="${sanitizeHtml(src)}"
width="${sanitizeHtml(width)}"
height="${sanitizeHtml(height)}"
/>`
}
function getPlusSign(i) {
return i === 0 ? '' : '<div class="plus">+</div>'
}
const entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;'
}
function sanitizeHtml(html) {
return String(html).replace(/[&<>"'\/]/g, key => entityMap[key])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment