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