// Shared SVG-to-PNG rasterization for the brand render scripts. The SVG is // drawn into a canvas in a headless Chrome page, which reports the PNG and // its own opaque-pixel count via --dump-dom — Chrome's one-shot screenshot // mode intermittently captures blank or partially painted frames, so the // callers verify dimensions and opaque coverage before writing anything. import { execFileSync } from 'node:child_process' import { writeFileSync } from 'node:fs' import { join } from 'node:path' import { pathToFileURL } from 'node:url' /** Rasterize svgText at width x height; returns { opaque, png }. */ export function rasterize(chrome, work, name, svgText, width, height) { const svgUri = `data:image/svg+xml;base64,${Buffer.from(svgText).toString('base64')}` const wrapper = join(work, `wrap-${name}.html`) writeFileSync( wrapper, ``, ) const dom = execFileSync( chrome, [ '--headless=new', '--disable-gpu', '--no-sandbox', `--user-data-dir=${join(work, `profile-${name}`)}`, '--virtual-time-budget=5000', '--dump-dom', pathToFileURL(wrapper).href, ], // stderr ignored: headless Chrome logs harmless GPU-init noise there. { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'] }, ) const m = dom.match(/RESULT (\d+) (data:image\/png;base64,[A-Za-z0-9+/=]+)/) if (!m) throw new Error(`${name}: no render result in page output`) return { opaque: Number(m[1]), png: Buffer.from(m[2].split(',')[1], 'base64') } }