Created
July 4, 2020 19:54
-
-
Save DavidWells/32a0e7a62323f5047702d4789af6e7c3 to your computer and use it in GitHub Desktop.
Automatic OG images from https://www.swyx.io/writing/jamstack-og-images/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = { | |
'&': '&', | |
'<': '<', | |
'>': '>', | |
'"': '"', | |
"'": ''', | |
'/': '/' | |
} | |
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