diff --git a/artifacts/api-server/scripts/derive-name-letters.ts b/artifacts/api-server/scripts/derive-name-letters.ts new file mode 100644 index 000000000..3523eabb2 --- /dev/null +++ b/artifacts/api-server/scripts/derive-name-letters.ts @@ -0,0 +1,38 @@ +/** + * Regenerates `lib/api-zod/src/personalNameLetters.generated.ts`: the letters + * the name rule admits, derived from the shipped caption fonts. Run after a + * caption face is added, removed or replaced: + * + * npx tsx artifacts/api-server/scripts/derive-name-letters.ts + * + * `nameLetterDerivation.test.ts` fails until the file matches the fonts. + */ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { CAPTION_FONTS } from "@workspace/api-zod"; +import { deriveNameLetters, toRanges } from "../src/lib/fontCoverage"; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const fonts = path.resolve(here, "..", "src", "assets", "fonts"); +const out = path.resolve(here, "..", "..", "..", "lib", "api-zod", "src", "personalNameLetters.generated.ts"); + +const hex = (cp: number) => `0x${cp.toString(16).toUpperCase().padStart(4, "0")}`; +const letters = deriveNameLetters(CAPTION_FONTS.map((f) => fs.readFileSync(path.join(fonts, f.file)))); +const body = toRanges(letters).map(([a, b]) => ` [${hex(a)}, ${hex(b)}],`).join("\n"); + +fs.writeFileSync(out, `/** + * GENERATED by artifacts/api-server/scripts/derive-name-letters.ts from the + * shipped caption fonts. Do not edit by hand: change a font, then re-run it. + * + * The letters a personal name may hold: every Latin-script letter that all + * caption faces draw, in both cases (${letters.length} letters). + * + * @purpose The name rule's admitted letters as code-point ranges, derived from the caption font files. + * @subsystem accounts-auth + */ +export const PERSONAL_NAME_LETTER_RANGES: readonly (readonly [number, number])[] = [ +${body} +]; +`); +console.log(`${letters.length} letters written to ${path.relative(process.cwd(), out)}`); diff --git a/artifacts/api-server/src/__tests__/captionFonts.test.ts b/artifacts/api-server/src/__tests__/captionFonts.test.ts new file mode 100644 index 000000000..a367d1d91 --- /dev/null +++ b/artifacts/api-server/src/__tests__/captionFonts.test.ts @@ -0,0 +1,113 @@ +/** + * The caption fonts (names-and-fonts plan): every face in the shared catalogue + * ships with the built server and with the web app as the same file, registers, + * and draws differently from every other face; a new request can only name a + * catalogue face; a stored recipe from before the catalogue renders byte for + * byte as it did; and a face that failed to register refuses to render rather + * than saving a meme in a fallback face. + * + * @purpose Proves each caption font ships to the server build and the web app as one identical file, registers and renders distinctly, that requests accept catalogue ids only while stored legacy font strings render exactly as before, and that an unregistered face refuses to render. + * @subsystem meme-studio + */ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { GlobalFonts, createCanvas } from "@napi-rs/canvas"; +import { CAPTION_FONTS, CAPTION_FONT_IDS } from "@workspace/api-zod"; + +import { captionCanvasFont, fontFileCandidates, generateMemeBuffer, withCaptionFontUnregisteredForTest, type TextOptions } from "../lib/memeGenerator.js"; +import { StoredTextOptionsSchema, TextOptionsSchema } from "../lib/validators/memeBuilder.js"; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const artifactDir = path.resolve(here, "..", ".."); +const serverFonts = path.join(artifactDir, "src", "assets", "fonts"); +const webFonts = path.resolve(artifactDir, "..", "overhype-me", "public", "fonts", "caption"); + +const sha = (b: Buffer) => crypto.createHash("sha256").update(b).digest("hex"); + +function background(): Buffer { + const c = createCanvas(320, 320); + const g = c.getContext("2d"); + g.fillStyle = "#336699"; + g.fillRect(0, 0, 320, 320); + g.fillStyle = "#cc9933"; + g.fillRect(40, 40, 120, 200); + return c.toBuffer("image/png"); +} + +const render = (opts: TextOptions) => generateMemeBuffer({ type: "image", imageData: background() }, "x", opts, "square"); + +describe("caption fonts", () => { + it("each face is one file, identical on the server and in the web app, under src/assets where the build copies it", () => { + for (const font of CAPTION_FONTS) { + const server = path.join(serverFonts, font.file); + assert.ok(fs.existsSync(server), `${font.file} is in src/assets/fonts`); + assert.equal(sha(fs.readFileSync(path.join(webFonts, font.file))), sha(fs.readFileSync(server)), `${font.file} matches in the web app`); + const [built] = fontFileCandidates(path.join(artifactDir, "dist"), font.file); + assert.ok(!path.relative(path.join(artifactDir, "dist", "assets"), built!).startsWith(".."), "the built path is inside dist/assets"); + } + }); + + it("every face renders, and no two faces draw the same caption", async () => { + const seen = new Map(); + for (const font of CAPTION_FONTS) { + const hash = sha(await render({ topText: "Zoë Nguyễn counted", bottomText: "to infinity twice", fontFamily: font.id })); + assert.ok(!seen.has(hash), `${font.id} draws differently from ${seen.get(hash)}`); + seen.set(hash, font.id); + } + }); + + it("draws a catalogue face at its own fixed weight, whatever bold says", () => { + for (const font of CAPTION_FONTS) { + for (const bold of [true, false, undefined]) { + assert.equal(captionCanvasFont({ fontFamily: font.id, bold }, 40), `${font.weight} 40px "${font.family}", sans-serif`); + } + } + }); + + it("a request names a catalogue face or nothing; a stored recipe may hold an old string", () => { + for (const id of CAPTION_FONT_IDS) assert.ok(TextOptionsSchema.safeParse({ fontFamily: id }).success, id); + assert.ok(TextOptionsSchema.safeParse({}).success); + for (const bad of ["Impact", "'Bebas Neue', Impact, sans-serif", "OH Block", "Comic Sans MS"]) { + assert.equal(TextOptionsSchema.safeParse({ fontFamily: bad }).success, false, `${bad} is refused on a request`); + assert.ok(StoredTextOptionsSchema.safeParse({ fontFamily: bad }).success, `${bad} is kept on a stored recipe`); + } + }); + + // A stored recipe keeps its face: the renderer gives it the exact canvas + // font string the code before the catalogue built (memeGenerator.ts at main + // 61a2d8b3: `${italic}${bold}${size}px "${fontFamily ?? "Impact"}", sans-serif`, + // bold by default), Impact is still Anton, and the old picker's stacks still + // draw in Anton, so no new caption face can capture them. Machine-independent + // by design: hard-coded image hashes from one machine failed on CI's. + it("a stored recipe with an old font string, or none, renders as it always has", async () => { + for (const fontFamily of [undefined, "Impact", "'Bebas Neue', Impact, sans-serif", "Anton"]) { + for (const bold of [true, false, undefined]) { + for (const italic of [true, false]) { + const before = `${italic ? "italic " : ""}${(bold ?? true) ? "bold " : ""}40px "${fontFamily ?? "Impact"}", sans-serif`; + assert.equal(captionCanvasFont({ fontFamily, bold, italic }, 40), before, JSON.stringify({ fontFamily, bold, italic })); + } + } + } + const text = { topText: "Zoë counted", bottomText: "to infinity twice" }; + assert.equal(sha(await render(text)), sha(await render({ ...text, fontFamily: "Impact" })), "no font is Impact, as before"); + assert.ok(GlobalFonts.has("Impact") && GlobalFonts.has("Anton"), "Anton is still registered as Impact"); + // The canvas reads "Impact" inside the old picker's stack, so those + // recipes have always drawn in Anton; they still do. + assert.equal( + sha(await render({ ...text, fontFamily: "'Bebas Neue', Impact, sans-serif" })), + sha(await render({ ...text, fontFamily: "Impact" })), + "an old picker stack still draws in Anton, not a caption face", + ); + }); + + it("a face that did not register refuses to render instead of saving a fallback face", async () => { + await withCaptionFontUnregisteredForTest("script", async () => { + await assert.rejects(render({ topText: "Zoë", fontFamily: "script" }), /caption font "script" is not available/); + }); + assert.ok((await render({ topText: "Zoë", fontFamily: "script" })).length > 0, "it renders once registered again"); + }); +}); diff --git a/artifacts/api-server/src/__tests__/memeFontPackaging.test.ts b/artifacts/api-server/src/__tests__/memeFontPackaging.test.ts index 382e2d91a..9d39cc87a 100644 --- a/artifacts/api-server/src/__tests__/memeFontPackaging.test.ts +++ b/artifacts/api-server/src/__tests__/memeFontPackaging.test.ts @@ -16,7 +16,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { GlobalFonts, createCanvas } from "@napi-rs/canvas"; -import { antonFontCandidates, generateMemeBuffer } from "../lib/memeGenerator.js"; +import { fontFileCandidates, generateMemeBuffer } from "../lib/memeGenerator.js"; const here = path.dirname(fileURLToPath(import.meta.url)); const artifactDir = path.resolve(here, "..", ".."); @@ -25,7 +25,7 @@ const distDir = path.join(artifactDir, "dist"); describe("meme font packaging", () => { it("the built layout's font path is a file the build copies from src/assets", () => { - const [built] = antonFontCandidates(distDir); + const [built] = fontFileCandidates(distDir, "Anton-Regular.ttf"); const underDistAssets = path.relative(path.join(distDir, "assets"), built!); assert.ok(!underDistAssets.startsWith(".."), `built candidate ${built} is inside dist/assets`); const source = path.join(srcAssets, underDistAssets); @@ -33,7 +33,7 @@ describe("meme font packaging", () => { }); it("the source-tree path finds the same file", () => { - const candidates = antonFontCandidates(path.join(artifactDir, "src", "lib")); + const candidates = fontFileCandidates(path.join(artifactDir, "src", "lib"), "Anton-Regular.ttf"); assert.ok(candidates.some((c) => fs.existsSync(c)), `one of ${candidates.join(", ")} exists`); }); diff --git a/artifacts/api-server/src/__tests__/nameRuleFontCoverage.test.ts b/artifacts/api-server/src/__tests__/nameRuleFontCoverage.test.ts index e0398da41..d9896ecdc 100644 --- a/artifacts/api-server/src/__tests__/nameRuleFontCoverage.test.ts +++ b/artifacts/api-server/src/__tests__/nameRuleFontCoverage.test.ts @@ -1,47 +1,76 @@ /** - * The name rule admits only characters the meme font draws (David, - * 2026-11-02: English-script names only, no fallback fonts). This draws every - * character the rule admits in Anton and compares it with the missing-glyph - * box, so a widened rule that admits a character Anton lacks fails here. + * The name rule admits only letters every caption font draws (David, + * 2026-11-02: no fallback fonts; names-and-fonts plan, 2026-11-03: the letters + * are derived from the eleven caption faces). Two checks hold the rule to the + * shipped files: * - * @purpose Proves every character the shared name rule admits renders as a real glyph in the bundled Anton font rather than as the missing-glyph box. + * - the pinned letters (`personalNameLetters.generated.ts`) equal the letters + * derived from the font files' own character maps, in both directions, so a + * supported letter can't be silently left out and an unsupported one can't + * be let in; + * - every admitted character, and its uppercase (captions are usually drawn in + * capitals), is drawn in every face as something other than that face's + * missing-glyph box. + * + * @purpose Proves the name rule's letters equal those derived from the shipped caption fonts, and that every admitted character and its uppercase renders as a real glyph in every caption face. * @subsystem meme-studio */ import { describe, it } from "node:test"; import assert from "node:assert/strict"; +import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { GlobalFonts, createCanvas } from "@napi-rs/canvas"; -import { hasOnlyAllowedNameChars } from "@workspace/api-zod"; +import { CAPTION_FONTS, hasOnlyAllowedNameChars } from "@workspace/api-zod"; +import { PERSONAL_NAME_LETTER_RANGES } from "../../../../lib/api-zod/src/personalNameLetters.generated"; +import { deriveNameLetters } from "../lib/fontCoverage.js"; const here = path.dirname(fileURLToPath(import.meta.url)); -GlobalFonts.registerFromPath(path.resolve(here, "..", "assets", "fonts", "Anton-Regular.ttf"), "AntonCoverage"); +const fontDir = path.resolve(here, "..", "assets", "fonts"); +const hex = (cp: number) => `U+${cp.toString(16).toUpperCase().padStart(4, "0")}`; -function bitmap(ch: string): string { - const c = createCanvas(48, 48); - const ctx = c.getContext("2d"); - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, 48, 48); - ctx.fillStyle = "#fff"; - ctx.font = "32px AntonCoverage"; - ctx.fillText(ch, 8, 38); - return Buffer.from(ctx.getImageData(0, 0, 48, 48).data).toString("base64"); -} +describe("the name rule and the caption fonts", () => { + it("admits exactly the letters every caption face draws in both cases", () => { + const derived = deriveNameLetters(CAPTION_FONTS.map((f) => fs.readFileSync(path.join(fontDir, f.file)))); + const pinned: number[] = []; + for (const [a, b] of PERSONAL_NAME_LETTER_RANGES) for (let cp = a; cp <= b; cp++) pinned.push(cp); + const missing = derived.filter((cp) => !pinned.includes(cp)).map(hex); + const extra = pinned.filter((cp) => !derived.includes(cp)).map(hex); + assert.deepEqual({ missing, extra }, { missing: [], extra: [] }, + "the pinned letters differ from the fonts; run artifacts/api-server/scripts/derive-name-letters.ts"); + }); -describe("the name rule and the meme font", () => { - it("every admitted letter and digit has a glyph in Anton", () => { - const missing = bitmap("\u{10FFFD}"); - const blank = bitmap(" "); - const bad: string[] = []; - let checked = 0; - for (let cp = 0x21; cp <= 0x24f; cp++) { + it("every admitted character and its uppercase has a real glyph in every face", () => { + const admitted: string[] = []; + for (let cp = 0x21; cp <= 0x2ffff; cp++) { + if (cp >= 0xd800 && cp <= 0xdfff) continue; const ch = String.fromCodePoint(cp); - if (!hasOnlyAllowedNameChars(ch)) continue; - checked++; - const b = bitmap(ch); - if (b === missing || b === blank) bad.push(`U+${cp.toString(16).toUpperCase().padStart(4, "0")} ${ch}`); + if (hasOnlyAllowedNameChars(ch)) admitted.push(ch); + } + assert.ok(admitted.length > 340, `checked ${admitted.length} admitted characters`); + const bad: string[] = []; + for (const font of CAPTION_FONTS) { + const family = `Coverage ${font.id}`; + assert.ok(GlobalFonts.registerFromPath(path.join(fontDir, font.file), family), `${font.file} registers`); + const bitmap = (ch: string) => { + const c = createCanvas(64, 64); + const ctx = c.getContext("2d"); + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, 64, 64); + ctx.fillStyle = "#fff"; + ctx.font = `${font.weight} 36px "${family}"`; + ctx.fillText(ch, 8, 48); + return Buffer.from(ctx.getImageData(0, 0, 64, 64).data).toString("base64"); + }; + const missingGlyph = bitmap("\u{10FFFD}"); + const blank = bitmap(" "); + for (const ch of admitted) { + for (const form of new Set([ch, ch.toUpperCase()])) { + const b = bitmap(form); + if (b === missingGlyph || b === blank) bad.push(`${font.id}: ${form} (${Array.from(form, (c) => hex(c.codePointAt(0)!)).join(" ")})`); + } + } } - assert.ok(checked > 180, `checked ${checked} admitted characters`); - assert.deepEqual(bad, [], `admitted but not drawn by Anton: ${bad.join(", ")}`); + assert.deepEqual(bad, [], `admitted but not drawn: ${bad.join(", ")}`); }); }); diff --git a/artifacts/api-server/src/__tests__/personalNameRule.test.ts b/artifacts/api-server/src/__tests__/personalNameRule.test.ts index 2dfeb69aa..3cefa5bda 100644 --- a/artifacts/api-server/src/__tests__/personalNameRule.test.ts +++ b/artifacts/api-server/src/__tests__/personalNameRule.test.ts @@ -1,10 +1,10 @@ /** - * @purpose Runs the shared name table against the rule, and proves a provider name is cleaned rather than refused and that typed input is filtered without cutting a paste. + * @purpose Runs the shared name table against the rule, and proves a provider name is kept only whole (never stripped, spaced out or cut) and that typed input is filtered without cutting a paste. * @subsystem accounts-auth */ import { describe, it } from "node:test"; import assert from "node:assert/strict"; -import { checkPersonalName, cleanProvidedPersonalName, filterNameInput } from "@workspace/api-zod"; +import { acceptProvidedPersonalName, checkPersonalName, filterNameInput } from "@workspace/api-zod"; import { PERSONAL_NAME_ACCEPTED, PERSONAL_NAME_REFUSED } from "../../../../lib/api-zod/src/personalNameRule.fixtures"; describe("the shared name rule", () => { @@ -20,10 +20,19 @@ describe("the shared name rule", () => { } }); +// Names-and-fonts plan: a Google or Apple name is kept whole or not at all, +// so nothing garbled is ever stored ("Nguyễn" was once stored "Nguy n"). describe("a provider-supplied name", () => { - it("is cleaned, not refused", () => assert.equal(cleanProvidedPersonalName("Ada 😀 (Countess)"), "Ada Countess")); - it("is cut at 50 graphemes", () => assert.equal(cleanProvidedPersonalName("b".repeat(60)), "b".repeat(50))); - it("is dropped when nothing is left", () => assert.equal(cleanProvidedPersonalName("😀😀"), null)); + it("is kept whole when the rule admits it", () => assert.equal(acceptProvidedPersonalName("Nguyễn"), "Nguyễn")); + it("is normalized like a typed one", () => assert.equal(acceptProvidedPersonalName(" O\u2019Brien "), "O'Brien")); + it("is not stored at all when any character is outside the rule", () => { + for (const raw of ["Иван", "John 王", "Ada 😀 (Countess)"]) assert.equal(acceptProvidedPersonalName(raw), null, raw); + }); + it("is not stored, rather than cut, when it is over 50 characters", () => assert.equal(acceptProvidedPersonalName("b".repeat(60)), null)); + it("stays absent when the provider sent none", () => { + assert.equal(acceptProvidedPersonalName(null), null); + assert.equal(acceptProvidedPersonalName(""), null); + }); }); describe("typed input", () => { diff --git a/artifacts/api-server/src/__tests__/routes.google.callback.test.ts b/artifacts/api-server/src/__tests__/routes.google.callback.test.ts index 32da2b0a9..5918da726 100644 --- a/artifacts/api-server/src/__tests__/routes.google.callback.test.ts +++ b/artifacts/api-server/src/__tests__/routes.google.callback.test.ts @@ -88,7 +88,11 @@ function applyFakeGoogleEnv(): () => void { // Google's ID token carries `email_verified` as a boolean; a real sign-in has // it true. `emailVerified` lets a test send an unverified or absent claim. -function makeFakeTokens(email: string, emailVerified: boolean | "absent" = true): TokenEndpointResponse & TokenEndpointResponseHelpers { +function makeFakeTokens( + email: string, + emailVerified: boolean | "absent" = true, + names: { given: string; family: string } = { given: "Test", family: "User" }, +): TokenEndpointResponse & TokenEndpointResponseHelpers { return { access_token: "fake-access-token", token_type: "bearer", @@ -97,8 +101,8 @@ function makeFakeTokens(email: string, emailVerified: boolean | "absent" = true) sub: `google|${randomUUID()}`, email, ...(emailVerified === "absent" ? {} : { email_verified: emailVerified }), - given_name: "Test", - family_name: "User", + given_name: names.given, + family_name: names.family, picture: null, aud: "fake-google-client-id", iss: "https://accounts.google.com", @@ -474,4 +478,31 @@ describe("GET /api/callback/google — the provider's verified-email claim", () await signIn(verified, true); assert.equal((await row(verified))?.emailVerifiedAt?.toISOString(), earlier.toISOString()); }); + + // Names-and-fonts plan: a provider name is stored whole or not at all, and a + // later sign-in never replaces a stored name. + it("stores a provider name only whole, and never overwrites a stored one", async () => { + const restoreEnv = applyFakeGoogleEnv(); + _setClientDiscoveryForTest(makeFakeDiscovery()); + const email = `${USER_PREFIX}${randomUUID()}@test.local`; + const signIn = async (given: string, family: string) => { + _setAuthCodeGrantForTest(async () => makeFakeTokens(email, true, { given, family })); + const state = randomUUID(); + await _storePendingStateForTest(state, { codeVerifier: "v", nonce: "n", returnTo: "/", isPopup: false }); + const res = await request(makeApp()).get("/api/callback/google").query({ code: "c", state }); + assert.equal(res.status, 302, `sign-in proceeds (${res.status})`); + const [row] = await db.select().from(usersTable).where(eq(usersTable.email, email)); + return row!; + }; + try { + const first = await signIn("Иван", "Nguyễn"); + assert.equal(first.firstName, null, "a name outside the rule is not stored, not garbled"); + assert.equal(first.lastName, "Nguyễn", "a name the rule admits is kept whole"); + const again = await signIn("Ada", "Lovelace"); + assert.equal(again.firstName, "Ada", "a missing name can be filled by a later sign-in"); + assert.equal(again.lastName, "Nguyễn", "a stored name is never overwritten"); + } finally { + restoreEnv(); + } + }); }); diff --git a/artifacts/api-server/src/assets/fonts/AlfaSlabOne-Regular.ttf b/artifacts/api-server/src/assets/fonts/AlfaSlabOne-Regular.ttf new file mode 100644 index 000000000..0158f1846 Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/AlfaSlabOne-Regular.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/AmaticSC-Bold.ttf b/artifacts/api-server/src/assets/fonts/AmaticSC-Bold.ttf new file mode 100644 index 000000000..96a7ab619 Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/AmaticSC-Bold.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/Bangers-Regular.ttf b/artifacts/api-server/src/assets/fonts/Bangers-Regular.ttf new file mode 100644 index 000000000..9b0f8c1f1 Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/Bangers-Regular.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/BlackOpsOne-Regular.ttf b/artifacts/api-server/src/assets/fonts/BlackOpsOne-Regular.ttf new file mode 100644 index 000000000..672d8e285 Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/BlackOpsOne-Regular.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/Lobster-Regular.ttf b/artifacts/api-server/src/assets/fonts/Lobster-Regular.ttf new file mode 100644 index 000000000..f30b6f66c Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/Lobster-Regular.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/OHRounded-Bold.ttf b/artifacts/api-server/src/assets/fonts/OHRounded-Bold.ttf new file mode 100644 index 000000000..c21f5a832 Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/OHRounded-Bold.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/OHSans-ExtraBold.ttf b/artifacts/api-server/src/assets/fonts/OHSans-ExtraBold.ttf new file mode 100644 index 000000000..4d0bb82be Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/OHSans-ExtraBold.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/OHSerif-Bold.ttf b/artifacts/api-server/src/assets/fonts/OHSerif-Bold.ttf new file mode 100644 index 000000000..31479da1d Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/OHSerif-Bold.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/PatrickHand-Regular.ttf b/artifacts/api-server/src/assets/fonts/PatrickHand-Regular.ttf new file mode 100644 index 000000000..fb45ccdbd Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/PatrickHand-Regular.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/VT323-Regular.ttf b/artifacts/api-server/src/assets/fonts/VT323-Regular.ttf new file mode 100644 index 000000000..afa69098f Binary files /dev/null and b/artifacts/api-server/src/assets/fonts/VT323-Regular.ttf differ diff --git a/artifacts/api-server/src/assets/fonts/licenses/alfaslabone-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/alfaslabone-OFL.txt new file mode 100644 index 000000000..211557706 --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/alfaslabone-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Alfa Slab One Project Authors (http://www.jmsole.cl | info@jmsole.cl), with Reserved Font Name "Alfa Slab". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/amaticsc-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/amaticsc-OFL.txt new file mode 100644 index 000000000..f77af8218 --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/amaticsc-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2015 The Amatic SC Project Authors (https://github.com/googlefonts/AmaticSC) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/anton-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/anton-OFL.txt new file mode 100644 index 000000000..93feddee3 --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/anton-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Anton Project Authors (https://github.com/googlefonts/AntonFont.git) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/baloo2-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/baloo2-OFL.txt new file mode 100644 index 000000000..72adb492d --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/baloo2-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2019 The Baloo 2 Project Authors (https://github.com/EkType/Baloo2) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/bangers-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/bangers-OFL.txt new file mode 100644 index 000000000..bf717d4be --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/bangers-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Bangers Project Authors (https://github.com/googlefonts/bangers) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/blackopsone-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/blackopsone-OFL.txt new file mode 100644 index 000000000..525b8ec85 --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/blackopsone-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Black-Ops Project Authors (https://github.com/googlefonts/googlefonts-project-template) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/lobster-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/lobster-OFL.txt new file mode 100644 index 000000000..a41143009 --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/lobster-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Lobster Project Authors (https://github.com/impallari/The-Lobster-Font), with Reserved Font Name "Lobster". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/montserrat-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/montserrat-OFL.txt new file mode 100644 index 000000000..fe576a2ee --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/montserrat-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2024 The Montserrat.Git Project Authors (https://github.com/JulietaUla/Montserrat.git) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/artifacts/api-server/src/assets/fonts/licenses/patrickhand-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/patrickhand-OFL.txt new file mode 100644 index 000000000..4d5f9447f --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/patrickhand-OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2010-2012 Patrick Wagesreiter (mail@patrickwagesreiter.at) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/playfairdisplay-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/playfairdisplay-OFL.txt new file mode 100644 index 000000000..131538908 --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/playfairdisplay-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2017 The Playfair Display Project Authors (https://github.com/clauseggers/Playfair-Display), with Reserved Font Name "Playfair Display" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/assets/fonts/licenses/vt323-OFL.txt b/artifacts/api-server/src/assets/fonts/licenses/vt323-OFL.txt new file mode 100644 index 000000000..bcb092f7e --- /dev/null +++ b/artifacts/api-server/src/assets/fonts/licenses/vt323-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2011, The VT323 Project Authors (peter.hull@oikoi.com) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/api-server/src/lib/fontCoverage.ts b/artifacts/api-server/src/lib/fontCoverage.ts new file mode 100644 index 000000000..a3a557d4d --- /dev/null +++ b/artifacts/api-server/src/lib/fontCoverage.ts @@ -0,0 +1,96 @@ +/** + * Which characters a font file draws, read from the file's own character map + * (its `cmap` table), and the name rule's letters derived from them + * (names-and-fonts plan). The rule admits a letter only when every caption + * face draws it and every code point of its uppercase, the form an all-caps + * caption draws; so the admitted set is computed from the shipped files and + * pinned in `lib/api-zod/src/personalNameLetters.generated.ts`, and a test + * fails when the two disagree in either direction. + * + * @purpose Reads the set of code points a TrueType font maps to a glyph, and derives the name rule's admitted letters (Latin-script letters that every caption face draws in both cases) from the shipped font files. + * @subsystem accounts-auth + */ + +/** Code points the font maps to a real glyph (not glyph 0). */ +export function fontCodePoints(file: Buffer): Set { + const numTables = file.readUInt16BE(4); + let cmap = -1; + for (let i = 0; i < numTables; i++) { + const rec = 12 + i * 16; + if (file.toString("latin1", rec, rec + 4) === "cmap") cmap = file.readUInt32BE(rec + 8); + } + if (cmap < 0) throw new Error("font has no cmap table"); + const subtables: { platform: number; encoding: number; offset: number }[] = []; + const count = file.readUInt16BE(cmap + 2); + for (let i = 0; i < count; i++) { + const rec = cmap + 4 + i * 8; + subtables.push({ platform: file.readUInt16BE(rec), encoding: file.readUInt16BE(rec + 2), offset: cmap + file.readUInt32BE(rec + 4) }); + } + const pick = (format: number) => subtables.find((t) => (t.platform === 3 || t.platform === 0) && file.readUInt16BE(t.offset) === format); + const out = new Set(); + const f12 = pick(12); + if (f12) { + const groups = file.readUInt32BE(f12.offset + 12); + for (let g = 0; g < groups; g++) { + const at = f12.offset + 16 + g * 12; + const start = file.readUInt32BE(at); + const end = file.readUInt32BE(at + 4); + const glyph = file.readUInt32BE(at + 8); + for (let c = start; c <= end; c++) if (glyph + (c - start) !== 0) out.add(c); + } + return out; + } + const f4 = pick(4); + if (!f4) throw new Error("font has no Unicode cmap subtable of format 4 or 12"); + const segs = file.readUInt16BE(f4.offset + 6) / 2; + const ends = f4.offset + 14; + const starts = ends + segs * 2 + 2; + const deltas = starts + segs * 2; + const ranges = deltas + segs * 2; + for (let i = 0; i < segs; i++) { + const end = file.readUInt16BE(ends + i * 2); + const start = file.readUInt16BE(starts + i * 2); + const delta = file.readInt16BE(deltas + i * 2); + const rangeAt = ranges + i * 2; + const range = file.readUInt16BE(rangeAt); + for (let c = start; c <= end && c !== 0xffff; c++) { + let glyph: number; + if (range === 0) glyph = (c + delta) & 0xffff; + else { + const g = file.readUInt16BE(rangeAt + range + 2 * (c - start)); + glyph = g === 0 ? 0 : (g + delta) & 0xffff; + } + if (glyph !== 0) out.add(c); + } + } + return out; +} + +const LATIN_LETTER = /^\p{L}$/u; +const LATIN_SCRIPT = /^\p{Script=Latin}$/u; + +/** The Latin-script letters every font draws, together with their uppercase forms. */ +export function deriveNameLetters(fonts: readonly Buffer[]): number[] { + const maps = fonts.map(fontCodePoints); + const inAll = (cp: number) => maps.every((m) => m.has(cp)); + const letters: number[] = []; + for (let cp = 0; cp <= 0x2ffff; cp++) { + if (cp >= 0xd800 && cp <= 0xdfff) continue; + const ch = String.fromCodePoint(cp); + if (!LATIN_LETTER.test(ch) || !LATIN_SCRIPT.test(ch)) continue; + if (!inAll(cp)) continue; + if (Array.from(ch.toUpperCase()).every((u) => inAll(u.codePointAt(0)!))) letters.push(cp); + } + return letters; +} + +/** Code points as `[start, end]` runs. */ +export function toRanges(cps: readonly number[]): [number, number][] { + const out: [number, number][] = []; + for (const cp of cps) { + const last = out[out.length - 1]; + if (last && cp === last[1] + 1) last[1] = cp; + else out.push([cp, cp]); + } + return out; +} diff --git a/artifacts/api-server/src/lib/memeGenerator.ts b/artifacts/api-server/src/lib/memeGenerator.ts index 159cf6278..92ffc461c 100644 --- a/artifacts/api-server/src/lib/memeGenerator.ts +++ b/artifacts/api-server/src/lib/memeGenerator.ts @@ -7,6 +7,11 @@ import { MEME_ASPECT_RATIOS, DEFAULT_MEME_ASPECT_RATIO, type MemeAspectRatio, + CAPTION_FONTS, + captionFontById, + captionFontStack, + isCaptionFontId, + type CaptionFontId, } from "@workspace/api-zod"; import { hasUnresolvedFactTokens } from "./renderCanonical"; import { logger } from "./logger"; @@ -14,59 +19,93 @@ import { logger } from "./logger"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); /** - * Where the renderer looks for Anton, given the directory this module runs - * from. The font lives in `src/assets/fonts/`, which the build copies to - * `dist/assets/` (`build.mjs`), so one candidate serves the bundle (whose - * `__dirname` is `dist/`) and the other the source tree (`src/lib/`). Until - * 2026-11-01 the file sat in `src/lib/assets/`, which the build never copies, - * so built servers drew captions in a fallback face. + * Where the renderer looks for a bundled font file, given the directory this + * module runs from. The fonts live in `src/assets/fonts/`, which the build + * copies to `dist/assets/` (`build.mjs`), so one candidate serves the bundle + * (whose `__dirname` is `dist/`) and the other the source tree (`src/lib/`). + * Until 2026-11-01 Anton sat in `src/lib/assets/`, which the build never + * copies, so built servers drew captions in a fallback face. */ -export function antonFontCandidates(dir: string): string[] { +export function fontFileCandidates(dir: string, file: string): string[] { return [ - path.resolve(dir, "assets/fonts/Anton-Regular.ttf"), // built layout: dist/ → dist/assets - path.resolve(dir, "..", "assets/fonts/Anton-Regular.ttf"), // src/lib → src/assets + path.resolve(dir, "assets/fonts", file), // built layout: dist/ → dist/assets + path.resolve(dir, "..", "assets/fonts", file), // src/lib → src/assets ]; } -/** - * Resolve and lazily register the bundled Anton font as both "Anton" and - * "Impact" so that meme captions render with a chunky condensed display - * face matching the client-side LivePreview (which asks for Impact). - * Linux containers do not ship with Impact; without this the previous - * default fell back to a generic sans-serif and produced a render that - * did not match the studio preview at all. - */ -const ANTON_FONT_PATH = (() => { - const candidates = antonFontCandidates(__dirname); +function resolveFontFile(file: string): string { + const candidates = fontFileCandidates(__dirname, file); for (const candidate of candidates) { try { if (fs.statSync(candidate).isFile()) return candidate; } catch { /* try next */ } } return candidates[0]!; -})(); +} + +/** Caption faces that registered, by catalogue id. */ +const registeredCaptionFonts = new Set(); + +/** + * Test seam: run `fn` as if `id` had failed to register. The registry is + * process-wide and the test runner shares one process, so a test cannot + * observe a real registration failure without one. + */ +export async function withCaptionFontUnregisteredForTest(id: CaptionFontId, fn: () => Promise): Promise { + ensureFontsRegistered(); + const had = registeredCaptionFonts.delete(id); + try { + return await fn(); + } finally { + if (had) registeredCaptionFonts.add(id); + } +} let fontsRegistered = false; function ensureFontsRegistered() { if (fontsRegistered) return; // registerFromPath reports a missing or unreadable file by returning null, - // not by throwing, so the result is checked rather than caught. - let registered = false; - try { - registered = GlobalFonts.registerFromPath(ANTON_FONT_PATH, "Anton") != null; - // Alias Anton as Impact so callers that ask for "Impact" (matching the - // client-side LivePreview's font stack) get a visually equivalent face. - registered = GlobalFonts.registerFromPath(ANTON_FONT_PATH, "Impact") != null && registered; - } catch { - registered = false; + // not by throwing, so each result is checked rather than caught. + const register = (file: string, family: string): boolean => { + try { + return GlobalFonts.registerFromPath(resolveFontFile(file), family) != null; + } catch { + return false; + } + }; + // Stored recipes from before the caption-font catalogue name "Impact" (or + // nothing, which means Impact), and Anton has always stood in for it. They + // keep exactly that. + const anton = register("Anton-Regular.ttf", "Anton") && register("Anton-Regular.ttf", "Impact"); + if (!anton) { + logger.warn({ file: "Anton-Regular.ttf" }, "meme font Anton could not be registered; legacy captions use a fallback face"); } - if (!registered) { - // Non-fatal: the canvas falls back to a system sans-serif. Logged, because - // that fallback went unnoticed on every built server until the font was - // moved where the build copies it. - logger.warn({ path: ANTON_FONT_PATH }, "meme font Anton could not be registered; captions use a fallback face"); + for (const font of CAPTION_FONTS) { + if (register(font.file, font.family)) registeredCaptionFonts.add(font.id); + else logger.error({ font: font.id, file: font.file }, "caption font could not be registered; memes in it will not render"); } fontsRegistered = true; } +/** + * The canvas font for a caption. A catalogue id draws its own face at its one + * weight, and refuses when that face did not register, rather than storing a + * meme in a fallback face that does not match its preview (the PR #831 + * failure). Anything else is a stored recipe from before the catalogue and + * resolves exactly as it always has. + */ +export function captionCanvasFont(options: Pick | undefined, fontSize: number): string { + const italic = options?.italic ? "italic " : ""; + const fontFamily = options?.fontFamily; + if (isCaptionFontId(fontFamily)) { + if (!registeredCaptionFonts.has(fontFamily)) { + throw new Error(`caption font "${fontFamily}" is not available on this server`); + } + const font = captionFontById(fontFamily); + return `${italic}${font.weight} ${fontSize}px ${captionFontStack(font)}`; + } + const bold = (options?.bold ?? true) ? "bold " : ""; + return `${italic}${bold}${fontSize}px "${fontFamily ?? "Impact"}", sans-serif`; +} + /** * Maximum longest-edge resolution for photo-backed meme renders. * Matches the client-side upload cap so the server never produces @@ -304,19 +343,15 @@ export async function generateMemeBuffer( const fontSize = Math.min(Math.max(options?.fontSize ?? defaultSize, 14), 100); const textColor = options?.color ?? "#ffffff"; const textAlign = options?.align ?? "center"; - const fontFamily = options?.fontFamily ?? "Impact"; const textEffect = options?.textEffect ?? "outline"; const outlineColor = options?.outlineColor ?? "#000000"; const outlineWidthVal = options?.outlineWidth ?? 5; const allCaps = options?.allCaps ?? true; - const isBold = options?.bold ?? true; - const isItalic = options?.italic ?? false; const textOpacity = options?.opacity ?? 1; const padding = 40; const maxW = logicalW - padding * 2 - sidebarW; - const fontStyle = `${isItalic ? "italic " : ""}${isBold ? "bold " : ""}`; - const fontStr = `${fontStyle}${fontSize}px "${fontFamily}", sans-serif`; + const fontStr = captionCanvasFont(options, fontSize); const textAreaLeft = padding + sidebarW; const textAreaRight = logicalW - padding; diff --git a/artifacts/api-server/src/lib/validators/memeBuilder.ts b/artifacts/api-server/src/lib/validators/memeBuilder.ts index 46d6fd6fe..512767647 100644 --- a/artifacts/api-server/src/lib/validators/memeBuilder.ts +++ b/artifacts/api-server/src/lib/validators/memeBuilder.ts @@ -21,7 +21,7 @@ */ import { z } from "zod"; -import { checkPersonalName } from "@workspace/api-zod"; +import { CAPTION_FONT_IDS, checkPersonalName } from "@workspace/api-zod"; import { PRONOUN_WORD_CAP_UPPER_BOUND, sanitizePronounsWithCap } from "./personalName"; /** @@ -63,7 +63,7 @@ export const NameSchema = z.string().transform((v, ctx) => { return checked.value; }); -export const TextOptionsSchema = z.object({ +const TextOptionsFields = z.object({ fontSize: z.number().int().min(14).max(100).optional(), color: z.string().regex(/^#[0-9a-fA-F]{3,8}$/).optional(), align: z.enum(["left", "center", "right"]).optional(), @@ -74,6 +74,8 @@ export const TextOptionsSchema = z.object({ // percentage of the image height (#707). Stripped as unknown keys before. topY: z.number().min(0).max(100).optional(), bottomY: z.number().min(0).max(100).optional(), + // A stored recipe from before the caption-font catalogue holds a CSS font + // string here; a new request names a catalogue face (below). fontFamily: z.string().max(50).optional(), outlineColor: z.string().regex(/^#[0-9a-fA-F]{3,8}$/).optional(), textEffect: z.enum(["shadow", "outline", "none"]).optional(), @@ -82,8 +84,20 @@ export const TextOptionsSchema = z.object({ bold: z.boolean().optional(), italic: z.boolean().optional(), opacity: z.number().min(0).max(1).optional(), +}); + +/** + * Text options on a render or save request. The caption font is a catalogue id + * (names-and-fonts plan): the face the preview drew is the face the server + * draws, and no new meme can name a font the server does not ship. + */ +export const TextOptionsSchema = TextOptionsFields.extend({ + fontFamily: z.enum(CAPTION_FONT_IDS).optional(), }).optional(); +/** Text options as a stored recipe holds them, old font strings included. */ +export const StoredTextOptionsSchema = TextOptionsFields.optional(); + // A meme's background is the user's own photo: an upload, or the profile photo // (`identity`). Stock photos went in launch Phase 4, removal 6, and the // gradient templates in removal 5. diff --git a/artifacts/api-server/src/routes/auth.ts b/artifacts/api-server/src/routes/auth.ts index dd3b4507c..e821f31bb 100644 --- a/artifacts/api-server/src/routes/auth.ts +++ b/artifacts/api-server/src/routes/auth.ts @@ -13,7 +13,7 @@ import { import * as oidc from "openid-client"; import * as Sentry from "@sentry/node"; import { Router, type IRouter, type Request, type Response } from "express"; -import { GetCurrentAuthUserResponse, cleanProvidedPersonalName } from "@workspace/api-zod"; +import { GetCurrentAuthUserResponse, acceptProvidedPersonalName } from "@workspace/api-zod"; import { db, usersTable, oauthPendingStatesTable } from "@workspace/db"; import { eq, and, lt } from "drizzle-orm"; import { @@ -202,10 +202,12 @@ async function upsertUser( : ((claims.family_name as string) || null); // The visitor did not type these, so a name that fails the shared rule is - // cleaned to what it admits (or dropped), never a reason to refuse the - // sign-in, and the denylist is not applied (#709). - const oidcFirstName = cleanProvidedPersonalName(rawFirstName); - const oidcLastName = cleanProvidedPersonalName(rawLastName); + // never a reason to refuse the sign-in, and the denylist is not applied + // (#709). It is kept only whole: one the rule can't hold is not stored, + // never stored garbled, and onboarding asks for the name in English letters + // (names-and-fonts plan). + const oidcFirstName = acceptProvidedPersonalName(rawFirstName); + const oidcLastName = acceptProvidedPersonalName(rawLastName); const existingFirstName = existing[0]?.firstName ?? null; const existingLastName = existing[0]?.lastName ?? null; diff --git a/artifacts/api-server/src/testing/legacyFixture/shapes.ts b/artifacts/api-server/src/testing/legacyFixture/shapes.ts index 576221aef..b8ff23f8e 100644 --- a/artifacts/api-server/src/testing/legacyFixture/shapes.ts +++ b/artifacts/api-server/src/testing/legacyFixture/shapes.ts @@ -22,7 +22,7 @@ import { visualRenderApprovalWaiverSchema, } from "@workspace/api-zod"; -import { ImageSourceSchema, TextOptionsSchema } from "../../lib/validators/memeBuilder"; +import { ImageSourceSchema, StoredTextOptionsSchema } from "../../lib/validators/memeBuilder"; /** * A stored meme recipe: the live sources plus the two older code wrote and the @@ -409,7 +409,7 @@ export const SHAPES: Record = { photoUrl: "stock-data-url", photographerName: "prose", }), - textOptions: plain(TextOptionsSchema, { + textOptions: plain(StoredTextOptionsSchema, { color: "keep", outlineColor: "keep", fontFamily: "keep", diff --git a/artifacts/overhype-me/index.html b/artifacts/overhype-me/index.html index 96f40f8e3..529d2c01a 100644 --- a/artifacts/overhype-me/index.html +++ b/artifacts/overhype-me/index.html @@ -5,6 +5,8 @@ Overhype.me + + diff --git a/artifacts/overhype-me/public/fonts/caption/AlfaSlabOne-Regular.ttf b/artifacts/overhype-me/public/fonts/caption/AlfaSlabOne-Regular.ttf new file mode 100644 index 000000000..0158f1846 Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/AlfaSlabOne-Regular.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/AmaticSC-Bold.ttf b/artifacts/overhype-me/public/fonts/caption/AmaticSC-Bold.ttf new file mode 100644 index 000000000..96a7ab619 Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/AmaticSC-Bold.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/Anton-Regular.ttf b/artifacts/overhype-me/public/fonts/caption/Anton-Regular.ttf new file mode 100644 index 000000000..4d65707db Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/Anton-Regular.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/Bangers-Regular.ttf b/artifacts/overhype-me/public/fonts/caption/Bangers-Regular.ttf new file mode 100644 index 000000000..9b0f8c1f1 Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/Bangers-Regular.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/BlackOpsOne-Regular.ttf b/artifacts/overhype-me/public/fonts/caption/BlackOpsOne-Regular.ttf new file mode 100644 index 000000000..672d8e285 Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/BlackOpsOne-Regular.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/Lobster-Regular.ttf b/artifacts/overhype-me/public/fonts/caption/Lobster-Regular.ttf new file mode 100644 index 000000000..f30b6f66c Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/Lobster-Regular.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/OHRounded-Bold.ttf b/artifacts/overhype-me/public/fonts/caption/OHRounded-Bold.ttf new file mode 100644 index 000000000..c21f5a832 Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/OHRounded-Bold.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/OHSans-ExtraBold.ttf b/artifacts/overhype-me/public/fonts/caption/OHSans-ExtraBold.ttf new file mode 100644 index 000000000..4d0bb82be Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/OHSans-ExtraBold.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/OHSerif-Bold.ttf b/artifacts/overhype-me/public/fonts/caption/OHSerif-Bold.ttf new file mode 100644 index 000000000..31479da1d Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/OHSerif-Bold.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/PatrickHand-Regular.ttf b/artifacts/overhype-me/public/fonts/caption/PatrickHand-Regular.ttf new file mode 100644 index 000000000..fb45ccdbd Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/PatrickHand-Regular.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/VT323-Regular.ttf b/artifacts/overhype-me/public/fonts/caption/VT323-Regular.ttf new file mode 100644 index 000000000..afa69098f Binary files /dev/null and b/artifacts/overhype-me/public/fonts/caption/VT323-Regular.ttf differ diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/alfaslabone-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/alfaslabone-OFL.txt new file mode 100644 index 000000000..211557706 --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/alfaslabone-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Alfa Slab One Project Authors (http://www.jmsole.cl | info@jmsole.cl), with Reserved Font Name "Alfa Slab". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/amaticsc-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/amaticsc-OFL.txt new file mode 100644 index 000000000..f77af8218 --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/amaticsc-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2015 The Amatic SC Project Authors (https://github.com/googlefonts/AmaticSC) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/anton-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/anton-OFL.txt new file mode 100644 index 000000000..93feddee3 --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/anton-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Anton Project Authors (https://github.com/googlefonts/AntonFont.git) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/baloo2-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/baloo2-OFL.txt new file mode 100644 index 000000000..72adb492d --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/baloo2-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2019 The Baloo 2 Project Authors (https://github.com/EkType/Baloo2) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/bangers-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/bangers-OFL.txt new file mode 100644 index 000000000..bf717d4be --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/bangers-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Bangers Project Authors (https://github.com/googlefonts/bangers) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/blackopsone-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/blackopsone-OFL.txt new file mode 100644 index 000000000..525b8ec85 --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/blackopsone-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Black-Ops Project Authors (https://github.com/googlefonts/googlefonts-project-template) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/lobster-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/lobster-OFL.txt new file mode 100644 index 000000000..a41143009 --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/lobster-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Lobster Project Authors (https://github.com/impallari/The-Lobster-Font), with Reserved Font Name "Lobster". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/montserrat-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/montserrat-OFL.txt new file mode 100644 index 000000000..fe576a2ee --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/montserrat-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2024 The Montserrat.Git Project Authors (https://github.com/JulietaUla/Montserrat.git) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/patrickhand-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/patrickhand-OFL.txt new file mode 100644 index 000000000..4d5f9447f --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/patrickhand-OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2010-2012 Patrick Wagesreiter (mail@patrickwagesreiter.at) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/playfairdisplay-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/playfairdisplay-OFL.txt new file mode 100644 index 000000000..131538908 --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/playfairdisplay-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2017 The Playfair Display Project Authors (https://github.com/clauseggers/Playfair-Display), with Reserved Font Name "Playfair Display" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/public/fonts/caption/licenses/vt323-OFL.txt b/artifacts/overhype-me/public/fonts/caption/licenses/vt323-OFL.txt new file mode 100644 index 000000000..bcb092f7e --- /dev/null +++ b/artifacts/overhype-me/public/fonts/caption/licenses/vt323-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2011, The VT323 Project Authors (peter.hull@oikoi.com) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/artifacts/overhype-me/src/components/meme-builder/MemeBuilder.tsx b/artifacts/overhype-me/src/components/meme-builder/MemeBuilder.tsx index 537fdefc9..108f8d44b 100644 --- a/artifacts/overhype-me/src/components/meme-builder/MemeBuilder.tsx +++ b/artifacts/overhype-me/src/components/meme-builder/MemeBuilder.tsx @@ -9,6 +9,9 @@ import { MyImagePicker } from "./parts/MyImagePicker"; import { LivePreview } from "./parts/LivePreview"; import { ActionBar } from "./parts/ActionBar"; import { TierLockedState } from "./parts/TierLockedState"; +import { CaptionFontPicker } from "./parts/CaptionFontPicker"; +import { captionFontOf, withCaptionFont } from "./captionFont"; +import { useCaptionFont } from "@/lib/captionFontLoader"; import { currentSource, resolveBackgroundUrl, @@ -53,6 +56,11 @@ export function MemeBuilder(props: MemeBuilderProps) { const backgroundUrl = useMemo(() => resolveBackgroundUrl(source, viewerCtx), [source, viewerCtx]); const debouncedBg = useDebouncedValue(backgroundUrl, 150); + // Save and download wait until the chosen caption face has loaded, so the + // meme comes out in the face the preview shows (names-and-fonts plan). + const fontId = captionFontOf(state.textOptions); + const fontReady = useCaptionFont(fontId).status === "ready"; + if (cell.invalid) { return ( + dispatch({ type: "set-text-options", textOptions: { ...state.textOptions, fontFamily: id } })} + /> + void; reject: () => void }; +let faces: Pending[] = []; + +beforeEach(() => { + resetCaptionFontsForTest(); + faces = []; + class FakeFontFace { + constructor(public family: string, public source: string, public descriptors: { weight: string }) {} + load() { + return new Promise((resolve, reject) => { + faces.push({ family: this.family, source: this.source, weight: this.descriptors.weight, resolve: () => resolve(this), reject }); + }); + } + } + vi.stubGlobal("FontFace", FakeFontFace); + Object.defineProperty(document, "fonts", { configurable: true, value: { add: vi.fn() } }); +}); +afterEach(() => vi.unstubAllGlobals()); + +const preview = (fontFamily?: string) => ( + +); +const canvas = () => screen.getByLabelText("Meme preview"); + +describe("caption fonts in the browser", () => { + it("loads each face from the server's file, under its family and weight", () => { + for (const font of CAPTION_FONTS) { + const { unmount } = render(preview(font.id)); + const face = faces.find((f) => f.family === font.family)!; + expect(face.source).toBe(`url(/fonts/caption/${font.file}) format("truetype")`); + expect(face.weight).toBe(String(font.weight)); + expect(canvas().getAttribute("data-caption-font")).toBe(font.family); + unmount(); + } + }); + + it("the preview waits for the face, then draws in it", async () => { + render(preview("script")); + expect(canvas().getAttribute("data-caption-font-status")).toBe("loading"); + await act(async () => faces[0]!.resolve()); + expect(canvas().getAttribute("data-caption-font-status")).toBe("ready"); + }); + + it("an untouched caption, and an old picker's font string, draw in the default face", () => { + render(preview()); + expect(canvas().getAttribute("data-caption-font")).toBe("OH Block"); + }); + + it("a failed load says so and can be retried, or another style picked", async () => { + render( {}} />); + expect(screen.getByTestId("caption-font-loading")).toBeTruthy(); + await act(async () => faces[0]!.reject()); + expect(screen.getByTestId("caption-font-failed")).toBeTruthy(); + fireEvent.click(screen.getByTestId("caption-font-retry")); + expect(screen.getByTestId("caption-font-loading")).toBeTruthy(); + await act(async () => faces[1]!.resolve()); + expect(screen.queryByTestId("caption-font-failed")).toBeNull(); + expect(screen.queryByTestId("caption-font-loading")).toBeNull(); + expect(screen.getAllByRole("radio")).toHaveLength(CAPTION_FONTS.length); + }); + + it("every save names the face: the chosen one, or the default", () => { + const base = { source: { kind: "self-upload", image: { kind: "library", objectPath: "/objects/x" } }, name: "Zoë", pronouns: "she/her", aspectRatio: "square" }; + const untouched = buildSaveMemePayload({ state: { ...base, textOptions: {} } as unknown as WizardRuntimeState, factId: 1 }); + expect(untouched?.textOptions?.fontFamily).toBe("block"); + const chosen = buildSaveMemePayload({ state: { ...base, textOptions: { fontFamily: "serif" } } as unknown as WizardRuntimeState, factId: 1 }); + expect(chosen?.textOptions?.fontFamily).toBe("serif"); + expect(withCaptionFont(undefined).fontFamily).toBe("block"); + }); + + it("a restored draft from the old picker previews and saves in the nearest face", () => { + window.sessionStorage.clear(); + const base = { source: { kind: "self-upload", image: { kind: "library", objectPath: "/objects/x" } }, name: "Zoë", pronouns: "she/her", aspectRatio: "square" }; + for (const [old, id, family] of [["'JetBrains Mono', monospace", "terminal", "OH Terminal"], ["'DM Sans', system-ui, sans-serif", "sans", "OH Sans"], ["'Bebas Neue', Impact, sans-serif", "block", "OH Block"]] as const) { + captureWizardState({ schemaVersion: 3, capturedAt: Date.now(), factId: "f1", entryFlow: "fact-detail", textOptions: { fontFamily: old } }); + const restored = restoreWizardState("f1")!; + const { unmount } = render(preview(restored.textOptions!.fontFamily)); + expect(canvas().getAttribute("data-caption-font")).toBe(family); + unmount(); + const payload = buildSaveMemePayload({ state: { ...base, textOptions: restored.textOptions } as unknown as WizardRuntimeState, factId: 1 }); + expect(payload?.textOptions?.fontFamily).toBe(id); + } + }); + + // Codex, PR #844: the wizard keeps its picker in a collapsed panel, so a + // failed face must be explained, with its retry, outside it. + it("in the wizard, a failed face is explained with a retry without opening Advanced options", async () => { + render( + {}} onCancel={() => {}} />, + ); + await screen.findByTestId("advanced-options-trigger", {}, { timeout: 5000 }); + expect(screen.queryByTestId("caption-font-picker")).toBeNull(); + const block = faces.find((f) => f.family === "OH Block")!; + await act(async () => block.reject()); + expect((await screen.findByTestId("caption-font-failed")).textContent).toMatch(/didn't load.*Advanced options/); + fireEvent.click(screen.getByTestId("caption-font-retry")); + const retried = faces.filter((f) => f.family === "OH Block").at(-1)!; + await act(async () => retried.resolve()); + expect(screen.queryByTestId("caption-font-failed")).toBeNull(); + expect(canvas().getAttribute("data-caption-font-status")).toBe("ready"); + }); +}); diff --git a/artifacts/overhype-me/src/components/meme-builder/captionFont.ts b/artifacts/overhype-me/src/components/meme-builder/captionFont.ts new file mode 100644 index 000000000..d9c154892 --- /dev/null +++ b/artifacts/overhype-me/src/components/meme-builder/captionFont.ts @@ -0,0 +1,19 @@ +/** + * The caption font a builder's text options name, as a catalogue id + * (names-and-fonts plan). A save or download always sends the id explicitly, + * so a new meme never depends on the server's default for an omitted font, + * which stored recipes still rely on. + * + * @purpose Resolves the builders' text options to a caption font id and stamps that id on every save and download request. + * @subsystem meme-studio + */ +import { captionFontIdFromStored, type CaptionFontId } from "@workspace/api-zod"; +import type { MemeTextOptions } from "./types"; + +export function captionFontOf(options: MemeTextOptions | undefined): CaptionFontId { + return captionFontIdFromStored(options?.fontFamily); +} + +export function withCaptionFont(options: MemeTextOptions | undefined): MemeTextOptions { + return { ...options, fontFamily: captionFontOf(options) }; +} diff --git a/artifacts/overhype-me/src/components/meme-builder/parts/CaptionFontPicker.tsx b/artifacts/overhype-me/src/components/meme-builder/parts/CaptionFontPicker.tsx new file mode 100644 index 000000000..b06d48294 --- /dev/null +++ b/artifacts/overhype-me/src/components/meme-builder/parts/CaptionFontPicker.tsx @@ -0,0 +1,75 @@ +/** + * The caption style picker, the same in both builders (names-and-fonts plan, + * question N1): one chip per catalogue face, and a line saying when the + * chosen face is still loading or failed to load, with a retry (shared with + * the wizard's save area through `CaptionFontStatus`). + * + * @purpose Lets the visitor pick one of the catalogue's caption fonts in either meme builder, and shows the chosen face's loading or failed state with a retry. + * @subsystem meme-studio + */ +import { CAPTION_FONTS, type CaptionFontId } from "@workspace/api-zod"; +import { useCaptionFont } from "@/lib/captionFontLoader"; + +/** + * Whether the chosen face is still loading or failed to load, with a retry. + * One statement of the wording, used by the picker and by the wizard, which + * keeps its picker inside a collapsed panel and so shows this beside its + * save button instead (Codex, PR #844). + */ +export function CaptionFontStatus({ id, otherStylesHint }: { id: CaptionFontId; otherStylesHint?: string }) { + const { status, retry } = useCaptionFont(id); + if (status === "loading") { + return ( +

+ Loading this caption style… +

+ ); + } + if (status === "failed") { + return ( +

+ This caption style didn't load.{" "} + {" "} + or pick another{otherStylesHint ? ` ${otherStylesHint}` : ""}. +

+ ); + } + return null; +} + +interface Props { + value: CaptionFontId; + onChange: (id: CaptionFontId) => void; + /** False where the same status is shown elsewhere on screen. */ + showStatus?: boolean; +} + +export function CaptionFontPicker({ value, onChange, showStatus = true }: Props) { + return ( +
+ + Caption style + +
+ {CAPTION_FONTS.map((f) => ( + + ))} +
+ {showStatus && } +
+ ); +} diff --git a/artifacts/overhype-me/src/components/meme-builder/parts/LivePreview.tsx b/artifacts/overhype-me/src/components/meme-builder/parts/LivePreview.tsx index d9ae5e5d9..cd44569f4 100644 --- a/artifacts/overhype-me/src/components/meme-builder/parts/LivePreview.tsx +++ b/artifacts/overhype-me/src/components/meme-builder/parts/LivePreview.tsx @@ -1,5 +1,8 @@ import { useEffect, useRef, useState } from "react"; +import { captionFontById, captionFontStack } from "@workspace/api-zod"; import { renderFactSegments } from "@/lib/render-fact"; +import { useCaptionFont } from "@/lib/captionFontLoader"; +import { captionFontOf } from "../captionFont"; import { DEFAULT_BOTTOM_Y, DEFAULT_TOP_Y, type AspectRatio, type MemeTextOptions } from "../types"; const NAME_COLOR = "#ff6b35"; @@ -67,6 +70,12 @@ export function LivePreview({ const internalCanvasRef = useRef(null); const canvasRef = externalCanvasRef ?? internalCanvasRef; const dims = ASPECT_DIMS[aspectRatio]; + // The caption is drawn only once its face has loaded, in that face at its + // fixed weight, so the preview never shows a fallback font the saved meme + // would not have (names-and-fonts plan). + const fontId = captionFontOf(textOptions); + const captionFont = captionFontById(fontId); + const { status: fontStatus } = useCaptionFont(fontId); // Cache the background image by URL so we don't recreate an `Image` on // every redraw (which would also re-fire `onload` and flicker). @@ -120,11 +129,13 @@ export function LivePreview({ ctx.save(); - const fontFamily = textOptions.fontFamily ?? "Impact, system-ui, sans-serif"; + if (fontStatus !== "ready") { + ctx.restore(); + return; + } const fontSize = textOptions.fontSize ?? Math.floor(dims.h * 0.07); - const weight = textOptions.bold ? "bold" : "normal"; const fontStyle = textOptions.italic ? "italic" : "normal"; - ctx.font = `${fontStyle} ${weight} ${fontSize}px ${fontFamily}`; + ctx.font = `${fontStyle} ${captionFont.weight} ${fontSize}px ${captionFontStack(captionFont)}`; ctx.strokeStyle = textOptions.outlineColor ?? "#000000"; ctx.lineWidth = 6; @@ -244,6 +255,8 @@ export function LivePreview({ name, pronouns, textOptions, + captionFont, + fontStatus, dims.w, dims.h, framingOffset?.x, @@ -257,6 +270,8 @@ export function LivePreview({ className="block h-auto w-full" style={canvasStyle} aria-label="Meme preview" + data-caption-font={captionFont.family} + data-caption-font-status={fontStatus} /> ); diff --git a/artifacts/overhype-me/src/components/meme-builder/wizard/step2-image/AdvancedOptionsSheet.tsx b/artifacts/overhype-me/src/components/meme-builder/wizard/step2-image/AdvancedOptionsSheet.tsx index bcd2efd19..578897d4a 100644 --- a/artifacts/overhype-me/src/components/meme-builder/wizard/step2-image/AdvancedOptionsSheet.tsx +++ b/artifacts/overhype-me/src/components/meme-builder/wizard/step2-image/AdvancedOptionsSheet.tsx @@ -2,20 +2,14 @@ import { useState } from "react"; import { Slider } from "@/components/ui/slider"; import { Switch } from "@/components/ui/switch"; import type { MemeTextOptions } from "../../types"; +import { CaptionFontPicker } from "../../parts/CaptionFontPicker"; +import { captionFontOf } from "../../captionFont"; interface Props { value: MemeTextOptions; onChange: (next: MemeTextOptions) => void; } -const FONT_OPTIONS: { id: string; label: string; family: string }[] = [ - { id: "bebas", label: "Bebas Neue", family: "'Bebas Neue', Impact, sans-serif" }, - { id: "anton", label: "Anton", family: "'Anton', Impact, sans-serif" }, - { id: "dm", label: "DM Sans", family: "'DM Sans', system-ui, sans-serif" }, - { id: "mono", label: "JetBrains Mono", family: "'JetBrains Mono', monospace" }, - { id: "impact", label: "Impact", family: "Impact, sans-serif" }, -]; - const TEXT_COLORS = [ // Neutrals "#ffffff", "#e5e5e5", "#9ca3af", "#4b5563", "#111111", @@ -61,18 +55,8 @@ export function AdvancedOptionsSheet({ value, onChange }: Props) { {isOpen && (
- + {/* Its status shows beside the save button, outside this collapsed panel. */} + set("fontFamily", id)} showStatus={false} />
diff --git a/artifacts/overhype-me/src/components/meme-builder/wizard/step2-image/Step2Image.tsx b/artifacts/overhype-me/src/components/meme-builder/wizard/step2-image/Step2Image.tsx index 820508301..cc4410f96 100644 --- a/artifacts/overhype-me/src/components/meme-builder/wizard/step2-image/Step2Image.tsx +++ b/artifacts/overhype-me/src/components/meme-builder/wizard/step2-image/Step2Image.tsx @@ -17,6 +17,9 @@ import { AdjustTextSheet } from "./AdjustTextSheet"; import { AdvancedOptionsSheet } from "./AdvancedOptionsSheet"; import { intelligentSplit } from "./sliders/splitLogic"; import { buildSaveMemePayload } from "../util/saveMemePayload"; +import { captionFontOf } from "../../captionFont"; +import { CaptionFontStatus } from "../../parts/CaptionFontPicker"; +import { useCaptionFont } from "@/lib/captionFontLoader"; interface Props { factId: string; @@ -148,6 +151,10 @@ export function Step2Image({ }, [textOptions, factText, splitIndex]); const sourceSelected = !!selfUploadImage; + // The meme is saved in the face the preview shows, so saving waits until + // that face has loaded (names-and-fonts plan). + const captionFontId = captionFontOf(textOptions); + const captionFontReady = useCaptionFont(captionFontId).status === "ready"; const handleSelfUploadImageSelect = (next: MyImageSource) => { setSelfUploadImage(next); @@ -207,7 +214,7 @@ export function Step2Image({ }; const handleMakeMyMeme = async () => { - if (!sourceSelected || saving) return; + if (!sourceSelected || saving || !captionFontReady) return; await save(); }; @@ -263,6 +270,10 @@ export function Step2Image({ + {/* Saving waits for the caption face; when it is slow or fails, say + so here, outside the collapsed Advanced options (Codex, PR #844). */} + + {saveError && (

(); +const listeners = new Set<() => void>(); + +function set(id: CaptionFontId, next: CaptionFontStatus) { + status.set(id, next); + for (const l of listeners) l(); +} + +/** Start loading a face, unless it is loading or loaded already. A failed face loads again. */ +export function loadCaptionFont(id: CaptionFontId): void { + const current = status.get(id); + if (current === "loading" || current === "ready") return; + // Without the FontFace API (an old browser, or a test environment) there is + // nothing to wait for: the canvas uses whatever the page has. + if (typeof FontFace === "undefined" || typeof document === "undefined" || !document.fonts) { + set(id, "ready"); + return; + } + const font = captionFontById(id); + set(id, "loading"); + const face = new FontFace(font.family, `url(/fonts/caption/${font.file}) format("truetype")`, { weight: String(font.weight) }); + face.load().then( + (loaded) => { + document.fonts.add(loaded); + set(id, "ready"); + }, + () => set(id, "failed"), + ); +} + +function subscribe(listener: () => void) { + listeners.add(listener); + return () => listeners.delete(listener); +} + +/** The selected face's status, loading it if needed, and a retry for a failed load. */ +export function useCaptionFont(id: CaptionFontId): { status: CaptionFontStatus; retry: () => void } { + useEffect(() => loadCaptionFont(id), [id]); + const current = useSyncExternalStore(subscribe, () => status.get(id) ?? "loading", () => "loading"); + const retry = useCallback(() => loadCaptionFont(id), [id]); + return { status: current, retry }; +} + +/** Test seam: forget every face's status. */ +export function resetCaptionFontsForTest(): void { + status.clear(); +} diff --git a/artifacts/overhype-me/src/pages/Onboard.test.tsx b/artifacts/overhype-me/src/pages/Onboard.test.tsx index 74e7be2e5..eba504c05 100644 --- a/artifacts/overhype-me/src/pages/Onboard.test.tsx +++ b/artifacts/overhype-me/src/pages/Onboard.test.tsx @@ -104,6 +104,13 @@ describe("Onboard screen", () => { expect((screen.getByRole("checkbox") as HTMLInputElement).checked).toBe(true); }); + // Names-and-fonts plan: a Google or Apple name the rule can't hold is not + // stored, so the name field asks for it in English letters. + it("asks for the name in English letters", async () => { + render(); + expect((await screen.findByTestId("onboard-name-guidance")).textContent).toMatch(/Write your name in English letters/); + }); + it("a date under 13 shows the refusal and Finish stays disabled", async () => { render(); const year = await screen.findByLabelText("Year"); diff --git a/artifacts/overhype-me/src/pages/Onboard.tsx b/artifacts/overhype-me/src/pages/Onboard.tsx index 46e5aee43..81fb75c72 100644 --- a/artifacts/overhype-me/src/pages/Onboard.tsx +++ b/artifacts/overhype-me/src/pages/Onboard.tsx @@ -465,7 +465,9 @@ export default function Onboard() {

Your name

-

Every fact uses this name. You can change it any time.

+

+ Write your name in English letters — the way you'd like it on your memes. Every fact uses this name, and you can change it any time. +

", sans-serif`. + - The browser loads the same file via `@font-face` under the same internal name. +- **The preview and the saved meme never differ in face or weight** (Astra, round 1). + - **Preview loading:** the preview measures and draws only after the selected face has loaded (`document.fonts.load`). While it loads, save and download are unavailable. If loading fails, the selection and the draft are kept, a Retry appears, and any other face can be chosen. Save and download return once the face shown matches the selected id. + - **Server registration:** if the server cannot register the selected face, the render fails with an error rather than storing an image in a fallback font. Today a registration failure logs a warning and carries on (`memeGenerator.ts:48`); the missing-packaged-font bug of PR #831 was exactly that. + - **Weight:** each catalogue entry fixes its weight, and the browser and the server both draw new ids at that weight. Today the preview treats an omitted `bold` as normal and the server treats it as bold (`LivePreview.tsx:123`, `memeGenerator.ts:307`). +- **Restored wizard drafts are moved to ids** (Astra, round 1). A wizard draft restored from storage (`wizardStorage.ts`, kept up to an hour) that holds an old picker's CSS stack has its font mapped to the nearest catalogue id before preview or save; the rest of the draft is untouched, and the picker shows the mapped choice. New API requests accept ids only. +- **Legacy font strings keep today's interpretation** (Astra, scope NF4). A stored recipe with an *omitted* `fontFamily` also keeps today's default face and weight; the new catalogue default (Anton, block) applies to new memes only. A stored recipe whose `fontFamily` is not a catalogue id resolves exactly as the renderer does today. That path is the legacy recipe render (`GET /memes/:slug/image` for an unbaked row or a missing file) and the boot backfill. Because the internal family names are new, a legacy string such as `'Bebas Neue', Impact, sans-serif` can never start selecting a newly bundled face. A test renders a legacy-string recipe before and after and compares the bytes. +- **Provider names: whole value or nothing** (Astra, scope NF3). `cleanProvidedPersonalName` is replaced by `acceptProvidedPersonalName`: + - It normalizes the provider's first or last name. + - It keeps the name only if the whole normalized value passes the shared rule, refusing both a disallowed character and an over-length name; otherwise it returns null. + - It never strips, spaces out or truncates. + - Sign-in proceeds either way, and an existing account name is never replaced. + + The correction surface is onboarding's existing required display-name field. Its guidance line reads "Write your name in English letters — the way you'd like it on your memes." No correction-state subsystem and no stored rejected text. +- **The picker in both builders** (Question N1, recommended): the wizard's sheet, and a new control in the remix builder on the meme page, both rendering the one catalogue. + +## Testing + +- **Name rule:** + - the shared table, run on both sides, gains Vietnamese, Romanian, Turkish, Polish and Czech accepts, keeps `IJ ij` accepted, and refuses `ʼn`, `ſ` and `ƒ` (its capital isn't in every face), Cyrillic, Greek and CJK; + - the decomposed input `Nguye` + U+0302 + U+0303 + `n` normalizes to `Nguyễn` and is accepted. +- **Coverage:** every admitted code point, both cases, drawn in each of the eleven faces from the *built* server's font directory, none equal to that face's missing-glyph box. +- **Fonts end to end:** + - for each id, a render through `generateMemeBuffer` differs from a render in every other id (the faces really differ); + - the preview requests the same internal family the server registers; + - an unknown id is refused by the schema; + - a legacy string on a stored recipe, and a recipe with no `fontFamily`, each render byte-identically before and after, through the live recipe render and the boot backfill; + - the effective face *and weight* match between preview and download for every id, through both builders, including an untouched default; + - a server that cannot register the selected face refuses the render; + - a restored draft holding an old picker string is mapped to an id and saves. +- **Preview loading:** the preview does not draw, and save and download stay unavailable, until the face reports loaded; a failed load shows Retry, keeps the draft, and lets another face be chosen. +- **Provider names:** + - `Nguyễn` is kept whole; + - `Иван` and `John 王` are refused whole, with nothing stored; + - a 60-character name is refused; + - a missing name stays null; + - a second sign-in never overwrites a stored name; + - E2E OAuth journeys for both Google and Apple with a refused name still reach onboarding and save the typed display name. +- **UAT:** David picks each style and downloads a meme with an accented name (human judgement of look). Everything else is a machine lane. + +## Implementation steps + +One increment, one PR, in dependency order: +1. Bundle the eleven faces (static weights) under the server's assets, where the build copies them (the PR #831 lesson), and as web fonts for the browser. +2. The catalogue in `lib/api-zod`, and the server registration. +3. The name rule's character class, with the shared table and the extended coverage test. +4. The schema (ids, plus legacy strings on stored recipes), the renderer's id resolution, and the legacy path kept as is. +5. The preview's face loading and the picker in the wizard; then the remix builder's picker (N1). +6. The provider-name acceptance. +7. Onboarding's guidance line. + +A specialist sign-in review is required (provider names), and `/simplify` runs before the PR. + +## Questions for David — answered + +- **N1 — Where the picker appears:** both builders (David, 2026-11-03: "1"). +- **N2 — The faces:** the eleven in the table above (David, 2026-11-03). diff --git a/lib/api-spec/patch-generated.mjs b/lib/api-spec/patch-generated.mjs index e3a738a90..983b13523 100644 --- a/lib/api-spec/patch-generated.mjs +++ b/lib/api-spec/patch-generated.mjs @@ -144,6 +144,7 @@ const apiZodIndexLines = [ 'export * from "./factTextEdit";', 'export * from "./stripeVerification";', 'export * from "./personalNameRule";', + 'export * from "./captionFonts";', 'export * from "./enrichmentOverrides";', "", ]; diff --git a/lib/api-zod/src/captionFonts.ts b/lib/api-zod/src/captionFonts.ts new file mode 100644 index 000000000..27cd01ada --- /dev/null +++ b/lib/api-zod/src/captionFonts.ts @@ -0,0 +1,88 @@ +/** + * The caption fonts (names-and-fonts plan, David 2026-11-03): one list the + * picker, the browser preview, request validation and the server renderer all + * read, so a meme saved in a face is drawn in exactly the face its preview + * showed. Each face is shipped twice from the same file: in the server's + * `assets/fonts/` (registered with the canvas) and in the web app's + * `public/fonts/caption/` (loaded with `@font-face`), both under the + * internal family below. + * + * The internal families are new names on purpose: no caption font string a + * stored recipe already holds (`Impact`, `'Bebas Neue', Impact, sans-serif`) + * can name one, so stored recipes keep drawing exactly as before. + * + * Every face draws every letter the name rule admits, in both cases; the rule's + * letters are derived from these files (`artifacts/api-server/scripts/derive-name-letters.ts`). + * + * @purpose The shared list of caption fonts — id, label, internal family, file and fixed weight — read by the meme builder's pickers and preview, the render request schema and the server renderer. + * @subsystem meme-studio + */ + +export interface CaptionFont { + /** Stable id carried in `textOptions.fontFamily` for new memes. */ + readonly id: string; + /** What the picker shows. */ + readonly label: string; + /** The typeface it is cut from: the internal family names don't say. */ + readonly typeface: string; + /** The family both the canvas and `@font-face` register it under. */ + readonly family: string; + /** The file, the same name in both asset directories. */ + readonly file: string; + /** The one weight it is drawn at, in the browser and on the server. */ + readonly weight: number; +} + +export const CAPTION_FONTS = [ + { id: "block", label: "Block", typeface: "Anton", family: "OH Block", file: "Anton-Regular.ttf", weight: 400 }, + { id: "sans", label: "Clean sans", typeface: "Montserrat", family: "OH Sans", file: "OHSans-ExtraBold.ttf", weight: 800 }, + { id: "serif", label: "Elegant serif", typeface: "Playfair Display", family: "OH Serif", file: "OHSerif-Bold.ttf", weight: 700 }, + { id: "slab", label: "Slab poster", typeface: "Alfa Slab One", family: "OH Slab", file: "AlfaSlabOne-Regular.ttf", weight: 400 }, + { id: "comic", label: "Comic shout", typeface: "Bangers", family: "OH Comic", file: "Bangers-Regular.ttf", weight: 400 }, + { id: "stencil", label: "Stencil", typeface: "Black Ops One", family: "OH Stencil", file: "BlackOpsOne-Regular.ttf", weight: 400 }, + { id: "rounded", label: "Rounded", typeface: "Baloo 2", family: "OH Rounded", file: "OHRounded-Bold.ttf", weight: 700 }, + { id: "hand", label: "Handwritten", typeface: "Patrick Hand", family: "OH Hand", file: "PatrickHand-Regular.ttf", weight: 400 }, + { id: "sketch", label: "Sketched capitals", typeface: "Amatic SC", family: "OH Sketch", file: "AmaticSC-Bold.ttf", weight: 700 }, + { id: "script", label: "Script", typeface: "Lobster", family: "OH Script", file: "Lobster-Regular.ttf", weight: 400 }, + { id: "terminal", label: "Retro terminal", typeface: "VT323", family: "OH Terminal", file: "VT323-Regular.ttf", weight: 400 }, +] as const satisfies readonly CaptionFont[]; + +export type CaptionFontId = (typeof CAPTION_FONTS)[number]["id"]; + +export const CAPTION_FONT_IDS = CAPTION_FONTS.map((f) => f.id) as [CaptionFontId, ...CaptionFontId[]]; + +/** New memes start in Anton, the face every meme has used so far. */ +export const DEFAULT_CAPTION_FONT_ID: CaptionFontId = "block"; + +export function isCaptionFontId(value: unknown): value is CaptionFontId { + return typeof value === "string" && (CAPTION_FONT_IDS as readonly string[]).includes(value); +} + +export function captionFontById(id: CaptionFontId): CaptionFont { + return CAPTION_FONTS.find((f) => f.id === id)!; +} + +/** The CSS / canvas `font` family list for a face. */ +export function captionFontStack(font: CaptionFont): string { + return `"${font.family}", sans-serif`; +} + +/** + * The font stacks the builder's old picker stored, mapped to the nearest + * catalogue face, so a wizard draft restored from before this change keeps a + * sensible choice and saves (plan: restored drafts are moved to ids). + */ +const LEGACY_PICKER_FONTS: Readonly> = { + "'Bebas Neue', Impact, sans-serif": "block", + "'Anton', Impact, sans-serif": "block", + "'DM Sans', system-ui, sans-serif": "sans", + "'JetBrains Mono', monospace": "terminal", + "Impact, sans-serif": "block", +}; + +/** A stored picker value as a catalogue id: an id as is, an old stack mapped, anything else the default. */ +export function captionFontIdFromStored(value: unknown): CaptionFontId { + if (isCaptionFontId(value)) return value; + if (typeof value === "string" && value in LEGACY_PICKER_FONTS) return LEGACY_PICKER_FONTS[value]!; + return DEFAULT_CAPTION_FONT_ID; +} diff --git a/lib/api-zod/src/index.ts b/lib/api-zod/src/index.ts index 877e36589..db0d50bbb 100644 --- a/lib/api-zod/src/index.ts +++ b/lib/api-zod/src/index.ts @@ -26,3 +26,4 @@ export * from "./renderScenarios"; export * from "./factTextEdit"; export * from "./stripeVerification"; export * from "./personalNameRule"; +export * from "./captionFonts"; diff --git a/lib/api-zod/src/personalNameLetters.generated.ts b/lib/api-zod/src/personalNameLetters.generated.ts new file mode 100644 index 000000000..0e3e07872 --- /dev/null +++ b/lib/api-zod/src/personalNameLetters.generated.ts @@ -0,0 +1,27 @@ +/** + * GENERATED by artifacts/api-server/scripts/derive-name-letters.ts from the + * shipped caption fonts. Do not edit by hand: change a font, then re-run it. + * + * The letters a personal name may hold: every Latin-script letter that all + * caption faces draw, in both cases (349 letters). + * + * @purpose The name rule's admitted letters as code-point ranges, derived from the caption font files. + * @subsystem accounts-auth + */ +export const PERSONAL_NAME_LETTER_RANGES: readonly (readonly [number, number])[] = [ + [0x0041, 0x005A], + [0x0061, 0x007A], + [0x00AA, 0x00AA], + [0x00BA, 0x00BA], + [0x00C0, 0x00D6], + [0x00D8, 0x00F6], + [0x00F8, 0x0148], + [0x014A, 0x017E], + [0x01A0, 0x01A1], + [0x01AF, 0x01B0], + [0x01FE, 0x01FF], + [0x0218, 0x021B], + [0x0237, 0x0237], + [0x1E80, 0x1E85], + [0x1EA0, 0x1EF9], +]; diff --git a/lib/api-zod/src/personalNameRule.fixtures.ts b/lib/api-zod/src/personalNameRule.fixtures.ts index 4cac1d70e..7eec48467 100644 --- a/lib/api-zod/src/personalNameRule.fixtures.ts +++ b/lib/api-zod/src/personalNameRule.fixtures.ts @@ -16,6 +16,13 @@ export const PERSONAL_NAME_ACCEPTED: { input: string; value: string; why: string { input: "Louis 14", value: "Louis 14", why: "digits" }, { input: "a".repeat(50), value: "a".repeat(50), why: "exactly 50 characters" }, { input: "One Two Three Four Five", value: "One Two Three Four Five", why: "no word-count limit" }, + // Every Latin letter all caption fonts draw (names-and-fonts plan, David 2026-11-03). + { input: "Nguyễn Thị Minh Khai", value: "Nguyễn Thị Minh Khai", why: "Vietnamese" }, + { input: "Nguye\u0302\u0303n", value: "Nguyễn", why: "Vietnamese typed with combining marks, composed by NFC" }, + { input: "Ștefan Țepeș", value: "Ștefan Țepeș", why: "Romanian comma-below letters" }, + { input: "Ayşe Türkoğlu İlhan", value: "Ayşe Türkoğlu İlhan", why: "Turkish" }, + { input: "Dvořák Čapek", value: "Dvořák Čapek", why: "Czech" }, + { input: "IJsbrand", value: "IJsbrand", why: "the Dutch IJ, kept from the earlier rule" }, ]; // English-script names only (David, 2026-11-02): other scripts are refused @@ -33,7 +40,11 @@ export const PERSONAL_NAME_REFUSED: { input: unknown; reason: "empty" | "charact { input: "देवनागरी", reason: "characters", why: "Devanagari" }, { input: "李小龙", reason: "characters", why: "CJK" }, { input: "Ольга", reason: "characters", why: "Cyrillic" }, - { input: "Nguyễn", reason: "characters", why: "a Latin Extended Additional letter outside the rule" }, + { input: "Ελένη", reason: "characters", why: "Greek" }, + { input: "Pfeiffer ƒ", reason: "characters", why: "ƒ, whose capital not every caption font draws" }, + { input: "ſamuel", reason: "characters", why: "the long s, which not every caption font draws" }, + { input: "\u0149on", reason: "characters", why: "ʼn, which not every caption font draws" }, + { input: "Hǎo", reason: "characters", why: "a pinyin tone letter not every caption font draws" }, { input: "a".repeat(51), reason: "length", why: "51 characters" }, { input: 42, reason: "type", why: "not text" }, ]; diff --git a/lib/api-zod/src/personalNameRule.ts b/lib/api-zod/src/personalNameRule.ts index 8516c63d8..15ea657a9 100644 --- a/lib/api-zod/src/personalNameRule.ts +++ b/lib/api-zod/src/personalNameRule.ts @@ -7,11 +7,13 @@ * * A name is one that can be written in English (David, 2026-11-02: the site * launches US-only and English-only; other scripts are an internationalization - * effort after launch). After NFC it may hold Latin letters — A–Z and the - * accented Latin letters of Latin-1 and Latin Extended-A (José, Zoë, Siobhán, - * Łukasz) — digits 0–9, space, `'`, `-` and `.`. Every one of those has a - * glyph in the bundled meme font, Anton (its charset covers U+0020–U+007E and - * U+00A0–U+0181), so every admitted name renders. Curly apostrophes become + * effort after launch). After NFC it may hold Latin letters, digits 0–9, + * space, `'`, `-` and `.`. The letters are exactly those every caption font + * draws in both cases (names-and-fonts plan, David 2026-11-03): A–Z, the + * Western and Central European accents (José, Zoë, Siobhán, Łukasz), and the + * Vietnamese, Romanian and Turkish letters (Nguyễn, Ștefan, Türkoğlu). They + * are derived from the font files into `personalNameLetters.generated.ts`, so + * every admitted name renders in every caption face. Curly apostrophes become * `'`, whitespace runs become one space, the ends are trimmed. It is at most * 50 visible characters. There is no word-count limit. * @@ -20,20 +22,24 @@ * 2026-10-21 rule admitted letters in any script; David narrowed it on * 2026-11-02.) * - * @purpose The shared personal-name rule for browser and server, English-script names only (David, 2026-11-02): normalizes a name (NFC, curly apostrophes, whitespace), refuses one with a character outside Latin letters, digits, space, apostrophe, hyphen and period or over 50 graphemes, cleans a provider-supplied name to what the rule admits, filters typed input without breaking IME composition, and owns the shared grapheme count and cut. + * @purpose The shared personal-name rule for browser and server, English-script names only (David, 2026-11-02): normalizes a name (NFC, curly apostrophes, whitespace), refuses one with a character outside the Latin letters every caption font draws, digits, space, apostrophe, hyphen and period or over 50 graphemes, accepts a provider-supplied name only whole, filters typed input without breaking IME composition, and owns the shared grapheme count and cut. * @subsystem accounts-auth */ +import { PERSONAL_NAME_LETTER_RANGES } from "./personalNameLetters.generated"; + /** The limit, in visible characters (grapheme clusters). */ export const PERSONAL_NAME_MAX_GRAPHEMES = 50; /** - * The characters the rule admits: Basic Latin letters and digits, the - * Latin-1 and Latin Extended-A letters (U+00C0–U+017F without × and ÷), - * space, apostrophe, hyphen and period. One character, and a whole string. + * The characters the rule admits: the derived letters, digits, space, + * apostrophe, hyphen and period. One character, and a whole string. */ -const ALLOWED_CHAR = /^[A-Za-z0-9À-ÖØ-öø-ſ '.-]$/; -const ALLOWED_NAME = /^[A-Za-z0-9À-ÖØ-öø-ſ '.-]*$/; +const LETTER_CLASS = PERSONAL_NAME_LETTER_RANGES.map(([a, b]) => + a === b ? `\\u{${a.toString(16)}}` : `\\u{${a.toString(16)}}-\\u{${b.toString(16)}}`, +).join(""); +const ALLOWED_CHAR = new RegExp(`^[${LETTER_CLASS}0-9 '.\\-]$`, "u"); +const ALLOWED_NAME = new RegExp(`^[${LETTER_CLASS}0-9 '.\\-]*$`, "u"); /** The short hint an input shows when it refuses a character. */ export const PERSONAL_NAME_HINT = "English letters, numbers, spaces, apostrophes, hyphens and periods only."; @@ -99,17 +105,15 @@ export function checkPersonalName(raw: unknown): PersonalNameCheck { } /** - * Clean a name the visitor did not type — a Google or Apple first or last - * name — to what the rule admits: disallowed characters become spaces and the - * result is cut at the limit. Returns null when nothing is left, so the - * sign-in proceeds without that name rather than failing. + * Accept a name the visitor did not type — a Google or Apple first or last + * name — only whole: the normalized value if it passes the rule, else null. + * Nothing is stripped, spaced out or cut, so a name is never stored garbled + * ("Nguyễn" once came out "Nguy n"); sign-in proceeds without it, and + * onboarding asks for the name in English letters (names-and-fonts plan). */ -export function cleanProvidedPersonalName(raw: unknown): string | null { - if (typeof raw !== "string") return null; - let kept = ""; - for (const c of normalizePersonalName(raw)) kept += ALLOWED_CHAR.test(c) ? c : " "; - const value = truncateGraphemes(normalizePersonalName(kept), PERSONAL_NAME_MAX_GRAPHEMES).trim(); - return checkPersonalName(value).ok ? value : null; +export function acceptProvidedPersonalName(raw: unknown): string | null { + const checked = checkPersonalName(raw); + return checked.ok ? checked.value : null; } /**