103 lines
3.6 KiB
TypeScript

import { createCanvas, loadImage, registerFont } from 'canvas';
import SQL from 'sql-template-strings';
import { getLocale, loadConfig, loadTranslator } from '~/server/data.ts';
import { registerLocaleFont } from '~/server/localeFont.ts';
import type { NounRow } from '~/server/nouns.ts';
import { availableGenders, iconUnicodesByGender, longIdentifierByGender } from '~/src/nouns.ts';
export default defineEventHandler(async (event) => {
const locale = getLocale(event);
const [config, translator] = await Promise.all([loadConfig(locale), loadTranslator(locale)]);
checkIsConfigEnabledOr404(await loadConfig(locale), 'nouns');
const { isGranted } = await useAuthentication(event);
if (!getRouterParam(event, 'id')?.endsWith('.png')) {
throw createError({
status: 404,
statusMessage: 'Not Found',
});
}
const id = getRouterParam(event, 'id')!.replace(/\.png$/, '');
const db = useDatabase();
const noun = await db.get<NounRow>(SQL`
SELECT * FROM nouns
WHERE locale = ${locale}
AND id = ${id}
AND approved >= ${isGranted('nouns') ? 0 : 1}
AND deleted = 0
`);
if (!noun) {
throw createError({
status: 404,
statusMessage: 'Not Found',
});
}
const genders = availableGenders(config);
let maxItems = 0;
genders.forEach((form) => {
let items = 0;
for (const key of ['', 'Pl'] as const) {
items += noun[`${form}${key}`].split('|').filter((x) => x.length).length;
}
if (items > maxItems) {
maxItems = items;
}
});
const padding = 48;
const width = genders.length * 400;
const height = padding * 2.5 + (maxItems + 1) * 48 + padding;
const mime = 'image/png';
const fontName = registerLocaleFont(config, 'fontHeadings', ['regular', 'bold']);
registerFont('node_modules/@fortawesome/fontawesome-pro/webfonts/fa-light-300.ttf', { family: 'FontAwesome', weight: 'regular' });
const canvas = createCanvas(width, height);
const context = canvas.getContext('2d');
const bg = await loadImage('public/bg.png');
context.drawImage(bg, 0, 0, width, height);
context.font = `bold 64pt '${fontName}'`;
genders.forEach((gender, column) => {
context.font = '24pt FontAwesome';
context.fillText(iconUnicodesByGender[gender], column * (width - 2 * padding) / genders.length + padding, padding * 1.5);
context.font = `bold 24pt '${fontName}'`;
const header = translator.translate(`nouns.${longIdentifierByGender[gender]}`);
context.fillText(header, column * (width - 2 * padding) / genders.length + padding + 36, padding * 1.5);
});
context.font = `24pt '${fontName}'`;
genders.forEach((form, column) => {
let i = 0;
for (const [key, symbol] of [['', '⋅'], ['Pl', '⁖']] as const) {
noun[`${form}${key}`].split('|').filter((x) => x.length)
.forEach((part) => {
context.fillText(`${symbol} ${part}`, column * (width - 2 * padding) / genders.length + padding, padding * 2.5 + i * 48);
i++;
});
}
});
context.fillStyle = '#C71585';
context.font = '16pt FontAwesome';
context.fillText('\uf02c', padding, height - padding + 12);
context.font = `16pt '${fontName}'`;
context.fillText(
`${translator.translate('domain')}/${config.nouns.routeMain || config.nouns.route}`,
padding + 36,
height - padding + 10,
);
setResponseHeader(event, 'content-type', mime);
return canvas.toBuffer(mime);
});