UNPKG

@cf-wasm/og

Version:

Generate Open Graph Images dynamically from HTML/CSS without a browser.

118 lines (117 loc) 3.42 kB
// src/core/render.ts import { loadDynamicAsset } from "./asset.js"; import { CustomFont, GoogleFont, defaultFont } from "./font.js"; import { modules } from "./modules.js"; var RENDER_DEFAULT_OPTIONS = { width: 1200, height: 630, debug: false, fonts: [] }; var render = (element, options = {}) => { const promises = {}; const renderOptions = { ...RENDER_DEFAULT_OPTIONS, ...options }; const loadAdditionalAsset = async (languageCode, segment) => { const next = () => loadDynamicAsset(languageCode, segment, renderOptions?.emoji); let result = void 0; if (options.loadAdditionalAsset) { result = await options.loadAdditionalAsset(languageCode, segment, next); } result ?? (result = await next()); if (Array.isArray(result)) { return await Promise.all( result.map(async (asset) => ({ ...asset, data: await asset.data })) ); } return result; }; const getFonts = async () => { const fallback = async () => { const defaultFonts = []; const fallbackFont = await (renderOptions.defaultFont?.data ?? defaultFont.get()); if (fallbackFont) { defaultFonts.push( new CustomFont("sans serif", fallbackFont, { weight: 400, style: "normal" }) ); } else { console.warn("(@cf-wasm/og) [ WARN ] No default font specified. Using 'Noto Sans' from Google Fonts as the fallback."); defaultFonts.push( new GoogleFont("Noto Sans", { name: "sans serif", weight: 400, style: "normal" }) ); } return Promise.all( [...defaultFonts, ...renderOptions.fonts].map(async (font) => ({ ...font, data: await font.data })) ); }; promises.fonts = promises.fonts?.then(null, fallback) ?? fallback(); return promises.fonts; }; const asSvg = async () => { const fallback = async () => { const satoriFonts = await getFonts(); const satoriOptions = { ...renderOptions.satoriOptions, width: renderOptions.width, height: renderOptions.height, debug: renderOptions.debug, fonts: satoriFonts, loadAdditionalAsset }; const svg = await modules.satori.satori(element, satoriOptions); return { image: svg, height: satoriOptions.height, width: satoriOptions.width, type: "image/svg+xml" }; }; promises.svg = promises.svg?.then(null, fallback) ?? fallback(); return promises.svg; }; const asPng = async () => { const fallback = async () => { const svg = await asSvg(); const resvg = await modules.resvg.Resvg.create(svg.image, { ...renderOptions.resvgOptions, fitTo: { mode: "width", value: renderOptions.width } }); const renderedImage = resvg.render(); const result = { pixels: renderedImage.pixels, image: renderedImage.asPng(), width: renderedImage.width, height: renderedImage.height, type: "image/png" }; renderedImage.free(); resvg.free(); return result; }; promises.png = promises.png?.then(null, fallback) ?? fallback(); return promises.png; }; return { asSvg, asPng }; }; export { RENDER_DEFAULT_OPTIONS, render };