Original: https://github.com/fabian-hiller/valibot/blob/main/website/src/routes/og-image/index.ts
Fonts: https://github.com/fabian-hiller/valibot/tree/main/website/src/fonts
This script "OG image" created for the website Qit.tools.
import type { RequestHandler } from "@builder.io/qwik-city";
import { fetchFont, html, ImageResponse } from "og-img";
const U200D = String.fromCharCode(8205);
const UFE0Fg = /\uFE0F/g;
/**
* Calculates the icon code based on the character.
*
* @param {string} char - The character to calculate the icon code for.
* @return {string} The icon code calculated based on the character.
*/
function getIconCode(char: string): string {
return toCodePoint(char.indexOf(U200D) < 0 ? char.replace(UFE0Fg, "") : char);
}
/**
* Converts a string of Unicode surrogates into a string of hexadecimal code points.
*
* @param {string} unicodeSurrogates - The string of Unicode surrogates to convert.
* @return {string} The string of hexadecimal code points.
*/
function toCodePoint(unicodeSurrogates: string): string {
const r = [];
let c = 0,
p = 0,
i = 0;
while (i < unicodeSurrogates.length) {
c = unicodeSurrogates.charCodeAt(i++);
if (p) {
r.push((65536 + ((p - 55296) << 10) + (c - 56320)).toString(16));
p = 0;
} else if (55296 <= c && c <= 56319) {
p = c;
} else {
r.push(c.toString(16));
}
}
return r.join("-");
}
/**
* Generates a URL for a Twemoji SVG image based on the given code.
*
* @param {string} code - The code representing the Twemoji.
* @return {string} The URL of the Twemoji SVG image.
*/
export const apis = {
twemoji: (code: string) =>
"https://cdnjs.cloudflare.com/ajax/libs/twemoji/15.1.0/svg/" +
code.toLowerCase() +
".svg",
openmoji: "https://cdn.jsdelivr.net/npm/@svgmoji/openmoji@2.0.0/svg/",
blobmoji: "https://cdn.jsdelivr.net/npm/@svgmoji/blob@2.0.0/svg/",
noto: "https://cdn.jsdelivr.net/gh/svgmoji/svgmoji/packages/svgmoji__noto/svg/",
fluent: (code: string) =>
"https://cdn.jsdelivr.net/gh/shuding/fluentui-emoji-unicode/assets/" +
code.toLowerCase() +
"_color.svg",
fluentFlat: (code: string) =>
"https://cdn.jsdelivr.net/gh/shuding/fluentui-emoji-unicode/assets/" +
code.toLowerCase() +
"_flat.svg",
};
/**
* Asynchronously loads emoji based on the provided code and type.
*
* @param {string} code - The code representing the emoji.
* @param {keyof typeof apis} [type] - The type of emoji API to use.
* @return {Promise<string>} The loaded emoji as text.
*/
async function loadEmoji(
code: string,
type?: keyof typeof apis,
): Promise<string> {
if (!type || !apis[type]) {
type = "twemoji";
}
const api = apis[type];
if (typeof api === "function") {
const r = await fetch(api(code));
return await r.text();
}
const r_1 = await fetch(`${api}${code.toUpperCase()}.svg`);
return await r_1.text();
}
/**
* Asynchronously loads an additional asset based on the provided code and segment.
*
* @param {string} code - The code representing the asset.
* @param {string} segment - The segment of the asset.
* @return {Promise<string>} The loaded asset code or the asset itself as a data URL.
*/
async function loadAdditionalAsset(
code: string,
segment: string,
): Promise<string> {
// console.log(code, segment, Buffer.from(segment).toString('base64'));
// console.log(getIconCode(segment));
// console.log(await loadEmoji(getIconCode(segment)));
if (code === "emoji") {
return (
`data:image/svg+xml;base64,` + btoa(await loadEmoji(getIconCode(segment)))
);
}
return code;
}
export const onGet: RequestHandler = async ({ cacheControl, send, url }) => {
// Disable caching
cacheControl("no-cache");
// Get data from search params
const title = url.searchParams.get("title");
const description = url.searchParams.get("description");
const path = url.searchParams.get("path");
// Create icon and font directory URL
const iconUrl = import.meta.env.PUBLIC_WEBSITE_URL + "/pwa-192x192.png";
const fontDirUrl = import.meta.env.PUBLIC_WEBSITE_URL + "/fonts";
// Create Lexend 400 font object
const lexend400 = {
name: "Lexend",
data: await fetchFont(fontDirUrl + "/lexend-400.ttf"),
style: "normal",
weight: 400,
} as const;
// Create Lexend 500 font object
const lexend500 = {
name: "Lexend",
data: await fetchFont(fontDirUrl + "/lexend-500.ttf"),
style: "normal",
weight: 500,
} as const;
// Create Lexend Exa 500 font object
const lexendExa500 = {
name: "Lexend Exa",
data: await fetchFont(fontDirUrl + "/lexend-exa-500.ttf"),
style: "normal",
weight: 500,
} as const;
// If title is available, return image with text
if (title) {
send(
new ImageResponse(
html`
<div
tw="flex h-full w-full flex-col justify-between bg-indigo-900 p-16"
style="font-family: 'Lexend';background-image: linear-gradient( 120deg, #0f172a 15%, #172554 );"
>
<div tw="flex items-center justify-between">
<div tw="flex items-center">
<img tw="w-16 h-16" src="${iconUrl}" />
<div tw="flex items-end">
<div tw="text-5xl font-medium text-slate-300 ml-2">Qit.</div>
<div class="ml-1 flex pb-0 text-base text-slate-300">
tools
</div>
</div>
</div>
<div
tw="max-w-[70%] text-3xl text-slate-400"
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
>
qit.tools${path ? path : ""}
</div>
</div>
<div tw="flex flex-col">
<h1
tw="max-w-[90%] text-5xl font-medium leading-normal text-slate-200"
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
>
${title}
</h1>
<p
tw="text-3xl text-slate-400 leading-normal"
style="${description ? "" : "display: none"}}"
>
${description
? description.length > 170
? description.slice(0, 170).trimEnd() + "..."
: description
: ""}
</p>
</div>
</div>
`,
{
width: 1200,
height: 630,
fonts: [lexend400, lexend500, lexendExa500],
loadAdditionalAsset,
},
),
);
// Otherwise, return image just with logo
} else {
send(
new ImageResponse(
html`
<div
tw="flex h-full w-full items-center justify-center bg-slate-900"
style="font-family: 'Lexend Exa';background-image: linear-gradient( 120deg, #0f172a 15%, #172554 );"
>
<div tw="flex items-center">
<img tw="w-36 h-36" src="${iconUrl}" />
<div tw="flex items-end">
<div tw="text-9xl font-medium text-slate-300 ml-10">Qit.</div>
<div class="ml-2 flex pb-2 text-2xl text-slate-300">tools</div>
</div>
</div>
</div>
`,
{
width: 1200,
height: 630,
fonts: [lexendExa500],
loadAdditionalAsset,
},
),
);
}
};