diff options
Diffstat (limited to 'bskyogcard/src')
-rw-r--r-- | bskyogcard/src/assets/fonts/Inter-Bold.ttf (renamed from bskyogcard/src/assets/Inter-Bold.ttf) | bin | 316584 -> 316584 bytes | |||
-rw-r--r-- | bskyogcard/src/components/StarterPack.tsx | 5 | ||||
-rw-r--r-- | bskyogcard/src/context.ts | 20 | ||||
-rw-r--r-- | bskyogcard/src/logger.ts | 1 | ||||
-rw-r--r-- | bskyogcard/src/routes/starter-pack.tsx | 6 | ||||
-rw-r--r-- | bskyogcard/src/util.ts | 37 |
6 files changed, 59 insertions, 10 deletions
diff --git a/bskyogcard/src/assets/Inter-Bold.ttf b/bskyogcard/src/assets/fonts/Inter-Bold.ttf index fe23eeb9c..fe23eeb9c 100644 --- a/bskyogcard/src/assets/Inter-Bold.ttf +++ b/bskyogcard/src/assets/fonts/Inter-Bold.ttf Binary files differdiff --git a/bskyogcard/src/components/StarterPack.tsx b/bskyogcard/src/components/StarterPack.tsx index f73442190..29bb8f32a 100644 --- a/bskyogcard/src/components/StarterPack.tsx +++ b/bskyogcard/src/components/StarterPack.tsx @@ -43,6 +43,7 @@ export function StarterPack(props: { } else { imagesAcross.push(...imagesExceptCreator.slice(0, 7)) } + const isLongTitle = record ? record.name.length > 30 : false return ( <div style={{ @@ -130,7 +131,9 @@ export function StarterPack(props: { <div style={{ padding: '75px 30px 0px', - fontSize: 65, + fontSize: isLongTitle ? 55 : 65, + display: 'flex', + textAlign: 'center', }}> {record?.name || 'Starter Pack'} </div> diff --git a/bskyogcard/src/context.ts b/bskyogcard/src/context.ts index f92651caf..0c972c94d 100644 --- a/bskyogcard/src/context.ts +++ b/bskyogcard/src/context.ts @@ -1,8 +1,8 @@ -import {readFileSync} from 'node:fs' +import {readdirSync, readFileSync} from 'node:fs' +import * as path from 'node:path' +import {fileURLToPath} from 'node:url' import {AtpAgent} from '@atproto/api' -import * as path from 'path' -import {fileURLToPath} from 'url' import {Config} from './config.js' @@ -28,12 +28,14 @@ export class AppContext { static async fromConfig(cfg: Config, overrides?: Partial<AppContextOptions>) { const appviewAgent = new AtpAgent({service: cfg.service.appviewUrl}) - const fonts = [ - { - name: 'Inter', - data: readFileSync(path.join(__DIRNAME, 'assets', 'Inter-Bold.ttf')), - }, - ] + const fontDirectory = path.join(__DIRNAME, 'assets', 'fonts') + const fontFiles = readdirSync(fontDirectory) + const fonts = fontFiles.map(file => { + return { + name: path.basename(file, path.extname(file)), + data: readFileSync(path.join(fontDirectory, file)), + } + }) return new AppContext({ cfg, appviewAgent, diff --git a/bskyogcard/src/logger.ts b/bskyogcard/src/logger.ts index 04b5d9046..320206513 100644 --- a/bskyogcard/src/logger.ts +++ b/bskyogcard/src/logger.ts @@ -1,3 +1,4 @@ import {subsystemLogger} from '@atproto/common' export const httpLogger = subsystemLogger('bskyogcard') +export const renderLogger = subsystemLogger('bskyogcard:render') diff --git a/bskyogcard/src/routes/starter-pack.tsx b/bskyogcard/src/routes/starter-pack.tsx index cb3a55327..06cd6977c 100644 --- a/bskyogcard/src/routes/starter-pack.tsx +++ b/bskyogcard/src/routes/starter-pack.tsx @@ -13,6 +13,7 @@ import { } from '../components/StarterPack.js' import {AppContext} from '../context.js' import {httpLogger} from '../logger.js' +import {loadEmojiAsSvg} from '../util.js' import {handler, originVerifyMiddleware} from './util.js' export default function (ctx: AppContext, app: Express) { @@ -65,6 +66,11 @@ export default function (ctx: AppContext, app: Express) { fonts: ctx.fonts, height: STARTERPACK_HEIGHT, width: STARTERPACK_WIDTH, + loadAdditionalAsset: async (code, text) => { + if (code === 'emoji') { + return await loadEmojiAsSvg(text) + } + }, }, ) const output = await resvg.renderAsync(svg) diff --git a/bskyogcard/src/util.ts b/bskyogcard/src/util.ts new file mode 100644 index 000000000..2b86ded06 --- /dev/null +++ b/bskyogcard/src/util.ts @@ -0,0 +1,37 @@ +import twemoji from 'twemoji' + +import {renderLogger} from './logger.js' + +const U200D = String.fromCharCode(0x200d) +const UFE0F_REGEXP = /\uFE0F/g + +export async function loadEmojiAsSvg(chars: string) { + const cached = emojiCache.get(chars) + if (cached) return cached + const iconCode = twemoji.convert.toCodePoint( + chars.indexOf(U200D) < 0 ? chars.replace(UFE0F_REGEXP, '') : chars, + ) + const res = await fetch(getEmojiUrl(iconCode)) + const body = await res.arrayBuffer() + if (!res.ok) { + renderLogger.warn( + {status: res.status, err: Buffer.from(body).toString()}, + 'could not fetch emoji', + ) + return + } + const svg = + 'data:image/svg+xml;base64,' + Buffer.from(body).toString('base64') + emojiCache.set(chars, svg) + return svg +} + +const emojiCache = new Map<string, string>() + +function getEmojiUrl(code: string) { + return ( + 'https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/' + + code.toLowerCase() + + '.svg' + ) +} |