UNPKG

@cf-wasm/og

Version:

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

143 lines (142 loc) 4.95 kB
import { SATORI_FONT_CACHE_MAP } from "./maps.js"; import { CustomFont, GoogleFont, defaultFont } from "./font.js"; import { loadDynamicAsset } from "./asset.js"; import { createResvg } from "./resvg.js"; import { satori } from "./satori.js"; //#region src/core/render.ts /** Default render options */ const RENDER_DEFAULT_OPTIONS = { width: 1200, height: 630, debug: false, fonts: [] }; /** * Renders {@link ReactNode} to image * * @param element The {@link ReactNode} * @param options The {@link RenderOptions} * * @returns An object containing methods for rendering the input element to image */ function render(element, options = {}) { const promises = {}; const renderOptions = { ...RENDER_DEFAULT_OPTIONS, ...options }; const loadAdditionalAsset = async (languageCode, segment) => { var _result; const next = () => loadDynamicAsset(languageCode, segment, renderOptions === null || renderOptions === void 0 ? void 0 : renderOptions.emoji); let result; if (options.loadAdditionalAsset) result = await options.loadAdditionalAsset(languageCode, segment, next); (_result = result) !== null && _result !== void 0 || (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 () => { var _promises$fonts$then, _promises$fonts; const fallback = async () => { var _renderOptions$defaul, _renderOptions$defaul2; const defaultFonts = []; const fallbackFont = await ((_renderOptions$defaul = (_renderOptions$defaul2 = renderOptions.defaultFont) === null || _renderOptions$defaul2 === void 0 ? void 0 : _renderOptions$defaul2.data) !== null && _renderOptions$defaul !== void 0 ? _renderOptions$defaul : 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 }))).then((fonts) => fonts.map((font) => { const key = JSON.stringify([ font.name, font.style || "", font.weight || "", font.lang || "", font.data.byteLength ]); const fromMap = SATORI_FONT_CACHE_MAP.get(key); if (fromMap) return fromMap; SATORI_FONT_CACHE_MAP.set(key, font); return font; })); }; promises.fonts = (_promises$fonts$then = (_promises$fonts = promises.fonts) === null || _promises$fonts === void 0 ? void 0 : _promises$fonts.then(null, fallback)) !== null && _promises$fonts$then !== void 0 ? _promises$fonts$then : fallback(); return promises.fonts; }; /** * Method to render the element as svg image * * @returns A promise which resolves to rendered svg image as {@link SvgResult} */ const asSvg = async () => { var _promises$svg$then, _promises$svg; const fallback = async () => { const satoriFonts = await getFonts(); const satoriOptions = { ...renderOptions.satoriOptions, width: renderOptions.width, height: renderOptions.height, debug: renderOptions.debug, fonts: satoriFonts, loadAdditionalAsset }; return { image: await satori(element, satoriOptions), height: satoriOptions.height, width: satoriOptions.width, type: "image/svg+xml" }; }; promises.svg = (_promises$svg$then = (_promises$svg = promises.svg) === null || _promises$svg === void 0 ? void 0 : _promises$svg.then(null, fallback)) !== null && _promises$svg$then !== void 0 ? _promises$svg$then : fallback(); return promises.svg; }; /** * Method to render the element as png image * * @returns A promise which resolves to rendered png image as {@link PngResult} */ const asPng = async () => { var _promises$png$then, _promises$png; const fallback = async () => { const resvg = await createResvg((await asSvg()).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 = (_promises$png = promises.png) === null || _promises$png === void 0 ? void 0 : _promises$png.then(null, fallback)) !== null && _promises$png$then !== void 0 ? _promises$png$then : fallback(); return promises.png; }; return { asSvg, asPng }; } //#endregion export { RENDER_DEFAULT_OPTIONS, render }; //# sourceMappingURL=render.js.map