@cf-wasm/og
Version:
Generate Open Graph Images dynamically from HTML/CSS without a browser.
200 lines (199 loc) • 7.25 kB
JavaScript
import { FONT_CACHE_MAP } from "./maps.js";
import { cache } from "./cache.js";
import { FetchError } from "./errors.js";
//#region src/core/font.ts
/** Default user agent for loading css */
const GOOGLE_FONT_CSS_DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1";
/** Google fonts utils */
var GoogleFontsUtils = class {
constructor() {
this._defaultCssLoader = async (cssUrl, userAgent) => {
return (await cache.serve(cssUrl, () => fetch(cssUrl, { headers: { "User-Agent": userAgent } }).then((res) => {
if (!res.ok) throw new FetchError(`Response was not successful (status: \`${res.status}\`, statusText: \`${res.statusText}\`)`, { response: res });
return res;
}).catch((e) => {
throw new FetchError(`An error ocurred while fetching \`${cssUrl}\``, {
cause: e,
response: e instanceof FetchError ? e.response : void 0
});
}))).text();
};
this._cssLoader = this._defaultCssLoader;
}
/**
* Loads Google font css
*
* @param cssUrl The Google font css url
* @param userAgent The user agent header to be used
*
* @returns On success, the css as string
*/
async loadCss(cssUrl, userAgent = GOOGLE_FONT_CSS_DEFAULT_USER_AGENT) {
let css = await this._cssLoader(cssUrl, userAgent);
if (typeof css === "undefined") css = await this._defaultCssLoader(cssUrl, userAgent);
else if (typeof css !== "string") throw new Error("Google font css loader return value must resolve to string");
return css;
}
/**
* Sets Google font css loader
*
* @param loader The {@link GoogleFontCssLoader}
*/
setCssLoader(loader) {
if (typeof loader !== "function") throw new TypeError("Argument 1 must be a function.");
this._cssLoader = loader;
}
};
const googleFonts = new GoogleFontsUtils();
/** Constructs Google font css url */
function constructGoogleFontCssUrl(family, { style = "normal", weight = 400, subset = "latin", display, text } = {}) {
if (typeof family !== "string" || family.trim().length === 0) throw new Error("Not a valid font family name was provided");
const params = { family: `${family.replaceAll(" ", "+")}:${style === "italic" ? "ital," : ""}wght@${style === "italic" ? "1," : ""}${weight}` };
if (text) params.text = encodeURIComponent(text);
else params.subset = subset;
if (typeof display === "string") params.display = display;
return `https://fonts.googleapis.com/css2?${Object.keys(params).map((key) => `${key}=${params[key]}`).join("&")}`;
}
/**
* A helper function for loading google fonts as {@link ArrayBuffer}
*
* @param family The name of the font family
* @param param1 Options
*
* @returns A promise which resolved to {@link ArrayBuffer}
*/
async function loadGoogleFont(family, options = {}) {
var _css$match;
const cssUrl = constructGoogleFontCssUrl(family, options);
const fromMap = FONT_CACHE_MAP.get(cssUrl);
if (fromMap) return fromMap;
const fontUrl = (_css$match = (await googleFonts.loadCss(cssUrl)).match(/src: url\((.+)\) format\('(opentype|truetype)'\)/)) === null || _css$match === void 0 ? void 0 : _css$match[1];
if (!fontUrl) throw new Error("The css does not content source for truetype font.");
const buffer = await (await cache.serve(fontUrl, () => fetch(fontUrl).then((res) => {
if (!res.ok) throw new FetchError(`Response was not successful (status: ${res.status}, statusText: ${res.statusText})`, { response: res });
return res;
}).catch((e) => {
throw new FetchError(`An error ocurred while fetching ${fontUrl}`, {
cause: e,
response: e instanceof FetchError ? e.response : void 0
});
}))).arrayBuffer();
FONT_CACHE_MAP.set(cssUrl, buffer);
return buffer;
}
/** A helper class to load Custom font */
var CustomFont = class {
/**
* Creates an instance of {@link CustomFont}
*
* @param name The name of the font (can be used for font-family css property)
* @param input Font data as `ArrayBuffer` or a promise which resolves to `ArrayBuffer`
* @param options
*/
constructor(name, input, { style = "normal", weight = 400, lang } = {}) {
this.type = "custom";
this.name = name;
this.style = style;
this.weight = weight;
this.input = input;
this.lang = lang;
}
/**
* A promise which resolves to font data as `ArrayBuffer`
*/
get data() {
var _this$promise$then, _this$promise;
const fallback = async () => typeof this.input === "function" ? this.input() : this.input;
this.promise = (_this$promise$then = (_this$promise = this.promise) === null || _this$promise === void 0 ? void 0 : _this$promise.then(null, fallback)) !== null && _this$promise$then !== void 0 ? _this$promise$then : fallback();
return this.promise;
}
};
/** A helper class to load Google font */
var GoogleFont = class {
/**
* Creates an instance of {@link GoogleFont}
*
* @param family The name of font family to load
* @param options The {@link GoogleFontOptions}
*/
constructor(family, { name, style = "normal", weight = 400, subset = "latin", text } = {}) {
this.type = "google";
this.name = name || family;
this.style = style;
this.weight = weight;
this.subset = subset;
this.family = family;
this.text = text;
}
/** A promise which resolves to font data as `ArrayBuffer` */
get data() {
var _this$promise$then2, _this$promise2;
const fallback = async () => loadGoogleFont(this.family, {
style: this.style,
weight: this.weight,
subset: this.subset,
text: this.text
});
this.promise = (_this$promise$then2 = (_this$promise2 = this.promise) === null || _this$promise2 === void 0 ? void 0 : _this$promise2.then(null, fallback)) !== null && _this$promise$then2 !== void 0 ? _this$promise$then2 : fallback();
return this.promise;
}
/**
* Checks whether font can load buffer
*
* @returns On success, true otherwise the error object thrown
*/
async canLoad() {
try {
await this.data;
} catch (e) {
return e;
}
return true;
}
};
/** Default font utils */
var DefaultFont = class {
constructor() {
this._shouldResolve = true;
}
/**
* Sets default font for image rendering
*
* @param input {@link FontInput}
*/
set(input) {
if (!input) throw new TypeError("Argument 1 type is not acceptable");
this._shouldResolve = true;
this._input = input;
}
/**
* Gets default font buffer for image rendering if it is set
*
* @returns A Promise which resolves to ArrayBuffer if default font is set,
* otherwise undefined
*/
async get() {
let buffer;
if (this._shouldResolve && this._input) {
const input = typeof this._input === "function" ? await this._input() : await this._input;
if (input instanceof Response) buffer = await input.arrayBuffer();
else if ("data" in input) buffer = await input.data;
else buffer = input;
} else if (this._data) buffer = this._data;
this._data = buffer;
this._shouldResolve = false;
return buffer;
}
/**
* Check whether default font is set or not
*
* @returns true if default font is set, otherwise false
*/
has() {
return Boolean(this._input);
}
};
const defaultFont = new DefaultFont();
//#endregion
export { CustomFont, GOOGLE_FONT_CSS_DEFAULT_USER_AGENT, GoogleFont, constructGoogleFontCssUrl, defaultFont, googleFonts, loadGoogleFont };
//# sourceMappingURL=font.js.map