You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

68 lines
2.4 KiB

/**
* Rasterize public/og-image.svg → public/og-image.png with a true Playfair Display wordmark.
* ImageMagick/Inkscape copy the SVG to /tmp, so @font-face + file URLs often never load;
* we outline "Imwald" with opentype.js so the PNG is font-independent.
*
* Wordmark fill + weight are read from the `#og-imwald` <text> in the SVG so PNG matches
* the green-tinged off-white you see in the browser (rasterizers often look harsher than live text).
*/
import { execFileSync } from 'node:child_process'
import { readFileSync, writeFileSync, unlinkSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import opentype from 'opentype.js'
const root = join(dirname(fileURLToPath(import.meta.url)), '..')
const svgPath = join(root, 'public/og-image.svg')
const fontPath = join(root, 'public/fonts/PlayfairDisplay-wght.ttf')
const outPng = join(root, 'public/og-image.png')
const tmpSvg = join(root, 'public/.og-image.raster.svg')
function parseOgImwaldFromSvg(svg) {
const defaults = { fill: '#d4ebe0', weight: 700, letterSpacing: 0.018 }
const idPos = svg.indexOf('id="og-imwald"')
if (idPos < 0) return defaults
const textOpen = svg.lastIndexOf('<text', idPos)
const textClose = svg.indexOf('</text>', idPos)
if (textOpen < 0 || textClose < 0) return defaults
const t = svg.slice(textOpen, textClose + '</text>'.length)
const fill = t.match(/\bfill="([^"]+)"/)?.[1] ?? defaults.fill
const weight = parseInt(t.match(/font-weight="(\d+)"/)?.[1] ?? String(defaults.weight), 10)
return {
fill,
weight: Number.isFinite(weight) ? weight : defaults.weight,
letterSpacing: defaults.letterSpacing
}
}
let svg = readFileSync(svgPath, 'utf8')
const { fill: imwaldFill, weight: imwaldWght, letterSpacing } = parseOgImwaldFromSvg(svg)
const font = opentype.loadSync(fontPath)
const pathObj = font.getPath('Imwald', 72, 300, 108, {
variation: { wght: imwaldWght },
letterSpacing
})
let pathTag = pathObj.toSVG(2)
if (!pathTag.includes('fill=')) {
pathTag = pathTag.replace('<path ', `<path fill="${imwaldFill}" `)
}
svg = svg.replace(/<text[^>]*id="og-imwald"[^>]*>[\s\S]*?<\/text>/, pathTag)
writeFileSync(tmpSvg, svg, 'utf8')
try {
execFileSync('convert', ['-background', 'none', '-density', '150', tmpSvg, outPng], {
stdio: 'inherit',
cwd: root
})
} finally {
try {
unlinkSync(tmpSvg)
} catch {
/* ignore */
}
}
console.info('[og:image] wrote', outPng)