UNPKG

@cf-wasm/og

Version:

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

1 lines 14.9 kB
{"version":3,"file":"font.cjs","names":["cache","FetchError","FONT_CACHE_MAP"],"sources":["../../src/core/font.ts"],"sourcesContent":["import { cache } from './cache';\nimport { FetchError } from './errors';\nimport { FONT_CACHE_MAP } from './maps';\nimport type { FontStyle, FontWeight } from './satori';\nimport type { MayBePromise } from './types';\n\nexport type FontDisplay = 'auto' | 'block' | 'swap' | 'fallback' | 'optional';\n\nexport type FontSubset =\n\t| 'arabic'\n\t| 'bengali'\n\t| 'chinese-simplified'\n\t| 'cyrillic'\n\t| 'cyrillic-ext'\n\t| 'devanagari'\n\t| 'ethiopic'\n\t| 'greek'\n\t| 'greek-ext'\n\t| 'gujarati'\n\t| 'gurmukhi'\n\t| 'hebrew'\n\t| 'japanese'\n\t| 'kannada'\n\t| 'khmer'\n\t| 'korean'\n\t| 'lao'\n\t| 'latin'\n\t| 'latin-ext'\n\t| 'malayalam'\n\t| 'menu'\n\t| 'myanmar'\n\t| 'oriya'\n\t| 'sinhala'\n\t| 'tamil'\n\t| 'telugu'\n\t| 'thai'\n\t| 'tibetan'\n\t| 'vietnamese';\n\n/** Default user agent for loading css */\nexport const GOOGLE_FONT_CSS_DEFAULT_USER_AGENT =\n\t'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';\n\n/** Google fonts utils */\nclass GoogleFontsUtils {\n\t/** Default Google font css loader */\n\tprivate _defaultCssLoader: (\n\t\tcssUrl: string,\n\t\tuserAgent: string,\n\t) => MayBePromise<string> = async (cssUrl, userAgent) => {\n\t\tconst cssResponse = await cache.serve(cssUrl, () =>\n\t\t\tfetch(cssUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\t'User-Agent': userAgent,\n\t\t\t\t},\n\t\t\t})\n\t\t\t\t.then((res) => {\n\t\t\t\t\tif (!res.ok) {\n\t\t\t\t\t\tthrow new FetchError(\n\t\t\t\t\t\t\t`Response was not successful (status: \\`${res.status}\\`, statusText: \\`${res.statusText}\\`)`,\n\t\t\t\t\t\t\t{ response: res },\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn res;\n\t\t\t\t})\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tthrow new FetchError(\n\t\t\t\t\t\t`An error ocurred while fetching \\`${cssUrl}\\``,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcause: e,\n\t\t\t\t\t\t\tresponse: e instanceof FetchError ? e.response : undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t);\n\n\t\treturn cssResponse.text();\n\t};\n\n\t/** Google font css loader */\n\tprivate _cssLoader: (\n\t\tcssUrl: string,\n\t\tuserAgent: string,\n\t) => MayBePromise<string | undefined> = this._defaultCssLoader;\n\n\t/**\n\t * Loads Google font css\n\t *\n\t * @param cssUrl The Google font css url\n\t * @param userAgent The user agent header to be used\n\t *\n\t * @returns On success, the css as string\n\t */\n\tasync loadCss(\n\t\tcssUrl: string,\n\t\tuserAgent = GOOGLE_FONT_CSS_DEFAULT_USER_AGENT,\n\t): Promise<string> {\n\t\tlet css = await this._cssLoader(cssUrl, userAgent);\n\t\tif (typeof css === 'undefined') {\n\t\t\tcss = await this._defaultCssLoader(cssUrl, userAgent);\n\t\t} else if (typeof css !== 'string') {\n\t\t\tthrow new Error(\n\t\t\t\t'Google font css loader return value must resolve to string',\n\t\t\t);\n\t\t}\n\t\treturn css;\n\t}\n\n\t/**\n\t * Sets Google font css loader\n\t *\n\t * @param loader The {@link GoogleFontCssLoader}\n\t */\n\tsetCssLoader(\n\t\tloader: (\n\t\t\tcssUrl: string,\n\t\t\tuserAgent: string,\n\t\t) => MayBePromise<string | undefined>,\n\t) {\n\t\tif (typeof loader !== 'function') {\n\t\t\tthrow new TypeError('Argument 1 must be a function.');\n\t\t}\n\t\tthis._cssLoader = loader;\n\t}\n}\n\nexport const googleFonts = new GoogleFontsUtils();\n\n/** An interface representing options for {@link loadGoogleFont} function */\nexport interface LoadGoogleFontOptions {\n\t/** The font style to load */\n\tstyle?: FontStyle;\n\n\t/** The font weight to load */\n\tweight?: string | number;\n\n\t/** The font subset to load */\n\tsubset?: FontSubset;\n\n\t/** The `font-display` */\n\tdisplay?: FontDisplay;\n\n\ttext?: string;\n}\n\n/** Constructs Google font css url */\nexport function constructGoogleFontCssUrl(\n\tfamily: string,\n\t{\n\t\tstyle = 'normal',\n\t\tweight = 400,\n\t\tsubset = 'latin',\n\t\tdisplay,\n\t\ttext,\n\t}: LoadGoogleFontOptions = {},\n): string {\n\tif (typeof family !== 'string' || family.trim().length === 0) {\n\t\tthrow new Error('Not a valid font family name was provided');\n\t}\n\n\tconst params: Record<string, string> = {\n\t\tfamily: `${family.replaceAll(' ', '+')}:${style === 'italic' ? 'ital,' : ''}wght@${style === 'italic' ? '1,' : ''}${weight}`,\n\t};\n\n\tif (text) {\n\t\tparams.text = encodeURIComponent(text);\n\t} else {\n\t\tparams.subset = subset;\n\t}\n\n\tif (typeof display === 'string') {\n\t\tparams.display = display;\n\t}\n\n\tconst cssUrl = `https://fonts.googleapis.com/css2?${Object.keys(params)\n\t\t.map((key) => `${key}=${params[key]}`)\n\t\t.join('&')}`;\n\n\treturn cssUrl;\n}\n\n/**\n * A helper function for loading google fonts as {@link ArrayBuffer}\n *\n * @param family The name of the font family\n * @param param1 Options\n *\n * @returns A promise which resolved to {@link ArrayBuffer}\n */\nexport async function loadGoogleFont(\n\tfamily: string,\n\toptions: LoadGoogleFontOptions = {},\n): Promise<ArrayBuffer> {\n\tconst cssUrl = constructGoogleFontCssUrl(family, options);\n\n\tconst fromMap = FONT_CACHE_MAP.get(cssUrl);\n\n\tif (fromMap) {\n\t\treturn fromMap;\n\t}\n\n\tconst css = await googleFonts.loadCss(cssUrl);\n\n\tconst fontUrl = css.match(\n\t\t/src: url\\((.+)\\) format\\('(opentype|truetype)'\\)/,\n\t)?.[1];\n\n\tif (!fontUrl) {\n\t\tthrow new Error('The css does not content source for truetype font.');\n\t}\n\n\tconst fontResponse = await cache.serve(fontUrl, () =>\n\t\tfetch(fontUrl)\n\t\t\t.then((res) => {\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\tthrow new FetchError(\n\t\t\t\t\t\t`Response was not successful (status: ${res.status}, statusText: ${res.statusText})`,\n\t\t\t\t\t\t{ response: res },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn res;\n\t\t\t})\n\t\t\t.catch((e) => {\n\t\t\t\tthrow new FetchError(`An error ocurred while fetching ${fontUrl}`, {\n\t\t\t\t\tcause: e,\n\t\t\t\t\tresponse: e instanceof FetchError ? e.response : undefined,\n\t\t\t\t});\n\t\t\t}),\n\t);\n\n\tconst buffer = await fontResponse.arrayBuffer();\n\n\tFONT_CACHE_MAP.set(cssUrl, buffer);\n\n\treturn buffer;\n}\n\nexport interface BaseFontOptions {\n\tweight?: FontWeight;\n\tstyle?: FontStyle;\n}\n\nexport interface BaseFont {\n\tname: string;\n\tstyle: FontStyle;\n\tweight: FontWeight;\n\tget data(): Promise<Buffer | ArrayBuffer>;\n}\n\n/** An interface representing options for {@link CustomFont} */\nexport interface CustomFontOptions extends BaseFontOptions {\n\tlang?: string;\n}\n\n/** A helper class to load Custom font */\nexport class CustomFont implements BaseFont {\n\tprotected input:\n\t\t| MayBePromise<Buffer | ArrayBuffer>\n\t\t| (() => MayBePromise<Buffer | ArrayBuffer>);\n\n\tprivate promise?: Promise<Buffer | ArrayBuffer>;\n\n\ttype: 'custom';\n\tname: string;\n\tstyle: FontStyle;\n\tweight: FontWeight;\n\tlang: string | undefined;\n\n\t/**\n\t * Creates an instance of {@link CustomFont}\n\t *\n\t * @param name The name of the font (can be used for font-family css property)\n\t * @param input Font data as `ArrayBuffer` or a promise which resolves to `ArrayBuffer`\n\t * @param options\n\t */\n\tconstructor(\n\t\tname: string,\n\t\tinput:\n\t\t\t| MayBePromise<Buffer | ArrayBuffer>\n\t\t\t| (() => MayBePromise<Buffer | ArrayBuffer>),\n\t\t{ style = 'normal', weight = 400, lang }: CustomFontOptions = {},\n\t) {\n\t\tthis.type = 'custom';\n\t\tthis.name = name;\n\t\tthis.style = style;\n\t\tthis.weight = weight;\n\t\tthis.input = input;\n\t\tthis.lang = lang;\n\t}\n\n\t/**\n\t * A promise which resolves to font data as `ArrayBuffer`\n\t */\n\tget data(): Promise<Buffer | ArrayBuffer> {\n\t\tconst fallback = async () =>\n\t\t\ttypeof this.input === 'function' ? this.input() : this.input;\n\t\tthis.promise = this.promise?.then(null, fallback) ?? fallback();\n\t\treturn this.promise;\n\t}\n}\n\n/** An interface representing options for {@link GoogleFont} */\nexport interface GoogleFontOptions extends BaseFontOptions {\n\t/** The name of the font (can be used for font-family css property) */\n\tname?: string;\n\n\t/** Loads font only for particular text (for better performance) */\n\ttext?: string;\n\n\tsubset?: FontSubset;\n}\n\n/** A helper class to load Google font */\nexport class GoogleFont implements BaseFont {\n\tprivate promise?: Promise<ArrayBuffer>;\n\n\ttype: 'google';\n\tname: string;\n\tstyle: FontStyle;\n\tweight: FontWeight;\n\tsubset: FontSubset;\n\n\t/** The font family name */\n\tfamily: string;\n\n\t/** Text for which the font is loaded */\n\ttext: string | undefined;\n\n\t/**\n\t * Creates an instance of {@link GoogleFont}\n\t *\n\t * @param family The name of font family to load\n\t * @param options The {@link GoogleFontOptions}\n\t */\n\tconstructor(\n\t\tfamily: string,\n\t\t{\n\t\t\tname,\n\t\t\tstyle = 'normal',\n\t\t\tweight = 400,\n\t\t\tsubset = 'latin',\n\t\t\ttext,\n\t\t}: GoogleFontOptions = {},\n\t) {\n\t\tthis.type = 'google';\n\t\tthis.name = name || family;\n\t\tthis.style = style;\n\t\tthis.weight = weight;\n\t\tthis.subset = subset;\n\t\tthis.family = family;\n\t\tthis.text = text;\n\t}\n\n\t/** A promise which resolves to font data as `ArrayBuffer` */\n\tget data(): Promise<ArrayBuffer> {\n\t\tconst fallback = async () =>\n\t\t\tloadGoogleFont(this.family, {\n\t\t\t\tstyle: this.style,\n\t\t\t\tweight: this.weight,\n\t\t\t\tsubset: this.subset,\n\t\t\t\ttext: this.text,\n\t\t\t});\n\t\tthis.promise = this.promise?.then(null, fallback) ?? fallback();\n\t\treturn this.promise;\n\t}\n\n\t/**\n\t * Checks whether font can load buffer\n\t *\n\t * @returns On success, true otherwise the error object thrown\n\t */\n\tasync canLoad() {\n\t\ttry {\n\t\t\tawait this.data;\n\t\t} catch (e) {\n\t\t\treturn e;\n\t\t}\n\t\treturn true;\n\t}\n}\n\nexport interface FontBuffer {\n\tdata: MayBePromise<Buffer | ArrayBuffer>;\n}\n\nexport type FontInput = MayBePromise<\n\tResponse | Buffer | ArrayBuffer | FontBuffer\n>;\n\n/** Default font utils */\nclass DefaultFont {\n\tprivate _input?: FontInput | (() => FontInput);\n\tprivate _data?: Buffer | ArrayBuffer;\n\tprivate _shouldResolve = true;\n\n\t/**\n\t * Sets default font for image rendering\n\t *\n\t * @param input {@link FontInput}\n\t */\n\tset(input: FontInput | (() => FontInput)) {\n\t\tif (!input) {\n\t\t\tthrow new TypeError('Argument 1 type is not acceptable');\n\t\t}\n\t\tthis._shouldResolve = true;\n\t\tthis._input = input;\n\t}\n\n\t/**\n\t * Gets default font buffer for image rendering if it is set\n\t *\n\t * @returns A Promise which resolves to ArrayBuffer if default font is set,\n\t * otherwise undefined\n\t */\n\tasync get(): Promise<Buffer | ArrayBuffer | undefined> {\n\t\tlet buffer: Buffer | ArrayBuffer | undefined;\n\n\t\tif (this._shouldResolve && this._input) {\n\t\t\tconst input =\n\t\t\t\ttypeof this._input === 'function'\n\t\t\t\t\t? await this._input()\n\t\t\t\t\t: await this._input;\n\t\t\tif (input instanceof Response) {\n\t\t\t\tbuffer = await input.arrayBuffer();\n\t\t\t} else if ('data' in input) {\n\t\t\t\tbuffer = await input.data;\n\t\t\t} else {\n\t\t\t\tbuffer = input;\n\t\t\t}\n\t\t} else if (this._data) {\n\t\t\tbuffer = this._data;\n\t\t}\n\n\t\tthis._data = buffer;\n\t\tthis._shouldResolve = false;\n\n\t\treturn buffer;\n\t}\n\n\t/**\n\t * Check whether default font is set or not\n\t *\n\t * @returns true if default font is set, otherwise false\n\t */\n\thas(): boolean {\n\t\treturn Boolean(this._input);\n\t}\n}\n\nexport const defaultFont = new DefaultFont();\n"],"mappings":"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"}