@cf-wasm/og
Version:
Generate Open Graph Images dynamically from HTML/CSS without a browser.
139 lines (138 loc) • 4.06 kB
JavaScript
const require_maps = require("./maps.cjs");
const require_font = require("./font.cjs");
const require_asset = require("./asset.cjs");
const require_resvg = require("./resvg.cjs");
const require_satori = require("./satori.cjs");
//#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) => {
const next = () => require_asset.loadDynamicAsset(languageCode, segment, renderOptions?.emoji);
let result;
if (options.loadAdditionalAsset) result = await options.loadAdditionalAsset(languageCode, segment, next);
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 ?? require_font.defaultFont.get());
if (fallbackFont) defaultFonts.push(new require_font.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 require_font.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 = require_maps.SATORI_FONT_CACHE_MAP.get(key);
if (fromMap) return fromMap;
require_maps.SATORI_FONT_CACHE_MAP.set(key, font);
return font;
}));
};
promises.fonts = promises.fonts?.then(null, fallback) ?? 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 () => {
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 require_satori.satori(element, satoriOptions),
height: satoriOptions.height,
width: satoriOptions.width,
type: "image/svg+xml"
};
};
promises.svg = promises.svg?.then(null, fallback) ?? 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 () => {
const fallback = async () => {
const resvg = await require_resvg.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(null, fallback) ?? fallback();
return promises.png;
};
return {
asSvg,
asPng
};
}
//#endregion
exports.RENDER_DEFAULT_OPTIONS = RENDER_DEFAULT_OPTIONS;
exports.render = render;
//# sourceMappingURL=render.cjs.map