@cf-wasm/og
Version:
Generate Open Graph Images dynamically from HTML/CSS without a browser.
143 lines (142 loc) • 4.95 kB
JavaScript
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