-
-
Save Uvacoder/a61c963cef282505e04cee1dce8d8a77 to your computer and use it in GitHub Desktop.
Automatic OG images from https://www.swyx.io/writing/jamstack-og-images/
This file contains hidden or 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 hidden or 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