mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-25 22:19:28 -04:00
127 lines
4.1 KiB
TypeScript
127 lines
4.1 KiB
TypeScript
import { createCanvas, loadImage, registerFont } from 'canvas';
|
|
import SQL from 'sql-template-strings';
|
|
|
|
import { PermissionAreas } from '#shared/helpers.ts';
|
|
import {
|
|
availableGenders,
|
|
availableNumeri,
|
|
resolveFirstDeclension,
|
|
iconUnicodesByGender,
|
|
longIdentifierByGender,
|
|
numeri,
|
|
symbolsByNumeri,
|
|
} from '#shared/nouns.ts';
|
|
import { getLocale, loadConfig, loadNounsData, loadTranslator } from '~~/server/data.ts';
|
|
import { registerLocaleFont } from '~~/server/localeFont.ts';
|
|
import { buildNoun, parseNounRow } from '~~/server/nouns.ts';
|
|
import type { NounRow } from '~~/server/nouns.ts';
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const locale = getLocale(event);
|
|
const [config, translator, nounsData] =
|
|
await Promise.all([loadConfig(locale), loadTranslator(locale), loadNounsData(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 nounRow = await db.get<NounRow>(SQL`
|
|
SELECT * FROM nouns
|
|
WHERE locale = ${locale}
|
|
AND id = ${id}
|
|
AND approved >= ${isGranted(PermissionAreas.Nouns) ? 0 : 1}
|
|
AND deleted = 0
|
|
`);
|
|
|
|
if (!nounRow) {
|
|
throw createError({
|
|
status: 404,
|
|
statusMessage: 'Not Found',
|
|
});
|
|
}
|
|
|
|
const noun = buildNoun(parseNounRow(nounRow), config, nounsData);
|
|
|
|
const genders = availableGenders(config);
|
|
|
|
let maxItems = 0;
|
|
genders.forEach((gender) => {
|
|
let items = 0;
|
|
for (const numerus of numeri) {
|
|
items += noun.words[gender]?.[numerus]?.length ?? 0;
|
|
}
|
|
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((gender, column) => {
|
|
let i = 0;
|
|
for (const numerus of availableNumeri(config)) {
|
|
const symbol = symbolsByNumeri[numerus];
|
|
noun.words[gender]?.[numerus]?.forEach((word) => {
|
|
context.fillText(
|
|
`${symbol} ${resolveFirstDeclension(word, numerus, nounsData)}`,
|
|
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);
|
|
});
|