diff --git a/tools/corpus/acquire.test.ts b/tools/corpus/acquire.test.ts index 5d47cec..10ec0cb 100644 --- a/tools/corpus/acquire.test.ts +++ b/tools/corpus/acquire.test.ts @@ -1,14 +1,18 @@ import { describe, expect, test } from "bun:test"; -import { readFileSync } from "node:fs"; +import { readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; -import { - collectGitHubTreeFonts, - type GitHubTreeEntry, - SOURCE_RELEASES, -} from "./acquire"; +import { collectGitHubTreeFonts, type GitHubTreeEntry } from "./acquire"; +import { SOURCE_RELEASES } from "./sources"; const joined = (...parts: string[]) => parts.join(""); +const tsFilesIn = (dir: string): string[] => + readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { + const path = join(dir, entry.name); + if (entry.isDirectory()) return tsFilesIn(path); + return entry.name.endsWith(".ts") ? [path] : []; + }); + describe("source acquisition catalog", () => { test("has unique source ids and https release URLs", () => { expect(SOURCE_RELEASES.length).toBeGreaterThan(0); @@ -213,7 +217,12 @@ describe("source acquisition catalog", () => { }); test("does not include private paths or measurement environment details", () => { - const script = readFileSync(join(import.meta.dir, "acquire.ts"), "utf8"); + const text = [ + join(import.meta.dir, "acquire.ts"), + ...tsFilesIn(join(import.meta.dir, "sources")), + ] + .map((path) => readFileSync(path, "utf8")) + .join("\n"); for (const needle of [ joined("/", "Users", "/"), joined("/", "Applications", "/"), @@ -221,7 +230,7 @@ describe("source acquisition catalog", () => { joined("or", "acle"), "macOS", ]) - expect(script.includes(needle), `script contains "${needle}"`).toBe( + expect(text.includes(needle), `corpus source contains "${needle}"`).toBe( false, ); }); diff --git a/tools/corpus/acquire.ts b/tools/corpus/acquire.ts index 7c69d3f..469c7c5 100644 --- a/tools/corpus/acquire.ts +++ b/tools/corpus/acquire.ts @@ -1,6 +1,13 @@ import { createHash } from "node:crypto"; import { mkdirSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; +import { + type ArchiveSource, + type GitHubTreeSource, + type LicenseFamily, + SOURCE_RELEASES, +} from "./sources"; +import { LICENSE_FILE_NAMES } from "./sources/licenses"; import { ACQUIRE_FONT_EXTENSIONS, type ArchiveFormat, @@ -12,12 +19,13 @@ import { requireArchiveTool, } from "./src/archive"; -const GUST_LICENSE_URL = - "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-LICENSE.txt/at_download/file"; - -// Pinned google/fonts commit. The Google tree source resolves against this SHA -// so acquisitions stay reproducible even as the upstream branch moves. -const GOOGLE_FONTS_COMMIT = "c89741abbf4eeabce432c3ed2fd7dc28b022701e"; +export type { + ArchiveSource, + GitHubTreeSource, + LicenseFamily, + SourceRelease, +} from "./sources"; +export { SOURCE_RELEASES } from "./sources"; const githubRawUrl = (repo: string, commit: string, path: string): string => `https://raw.githubusercontent.com/${repo}/${commit}/${path @@ -28,497 +36,6 @@ const githubRawUrl = (repo: string, commit: string, path: string): string => const githubTreeUrl = (repo: string, commit: string): string => `https://api.github.com/repos/${repo}/git/trees/${commit}?recursive=1`; -const LICENSE_FILE_NAMES = new Set([ - "LICENSE", - "LICENSE.md", - "LICENSE.txt", - "LICENCE.txt", - "OFL.txt", - "UFL.txt", -]); - -export type LicenseFamily = - | "GUST-FL" - | "OFL-1.1" - | "AGPL-3.0-FE" - | "Apache-2.0" - | "UFL-1.0" - | "GPL-2.0-FE" - | "Bitstream-Vera-DejaVu"; - -/** Fields shared by every acquisition source, regardless of how it is fetched. */ -interface BaseSource { - sourceId: string; - family: string; - project: string; - targetFamilies: string[]; -} - -interface LicensedSource extends BaseSource { - licenseFamily: LicenseFamily; - licenseUrl: string; -} - -/** A source delivered as a single archive containing many font members. */ -export interface ArchiveSource extends LicensedSource { - /** Optional for archive sources: an absent `kind` is treated as "archive". */ - kind?: "archive"; - /** Archive container format. Absent is treated as "zip". */ - archiveFormat?: ArchiveFormat; - downloadUrl: string; - expectedFiles: string[]; -} - -/** A source discovered from a pinned GitHub tree, then downloaded as raw files. */ -export interface GitHubTreeSource extends BaseSource { - kind: "github-tree"; - repo: string; - commit: string; - licenseDirs: Record; -} - -export type SourceRelease = ArchiveSource | GitHubTreeSource; - -export const SOURCE_RELEASES: SourceRelease[] = [ - { - sourceId: "tex-gyre-adventor", - family: "TeX Gyre Adventor", - project: "TeX Gyre", - licenseFamily: "GUST-FL", - downloadUrl: - "https://www.gust.org.pl/projects/e-foundry/tex-gyre/adventor/tg_adventor-otf-2_609-31_03_2026.zip", - licenseUrl: GUST_LICENSE_URL, - expectedFiles: [ - "texgyreadventor-regular.otf", - "texgyreadventor-bold.otf", - "texgyreadventor-italic.otf", - "texgyreadventor-bolditalic.otf", - ], - targetFamilies: ["Century Gothic", "ITC Avant Garde Gothic"], - }, - { - sourceId: "tex-gyre-bonum", - family: "TeX Gyre Bonum", - project: "TeX Gyre", - licenseFamily: "GUST-FL", - downloadUrl: - "https://www.gust.org.pl/projects/e-foundry/tex-gyre/bonum/tg_bonum-otf-2_609-31_03_2026.zip", - licenseUrl: GUST_LICENSE_URL, - expectedFiles: [ - "texgyrebonum-regular.otf", - "texgyrebonum-bold.otf", - "texgyrebonum-italic.otf", - "texgyrebonum-bolditalic.otf", - ], - targetFamilies: ["Bookman Old Style", "ITC Bookman"], - }, - { - sourceId: "tex-gyre-chorus", - family: "TeX Gyre Chorus", - project: "TeX Gyre", - licenseFamily: "GUST-FL", - downloadUrl: - "https://www.gust.org.pl/projects/e-foundry/tex-gyre/chorus/tg_chorus-otf-2_609-31_03_2026.zip", - licenseUrl: GUST_LICENSE_URL, - expectedFiles: ["texgyrechorus-mediumitalic.otf"], - targetFamilies: ["Monotype Corsiva", "ITC Zapf Chancery"], - }, - { - sourceId: "tex-gyre-cursor", - family: "TeX Gyre Cursor", - project: "TeX Gyre", - licenseFamily: "GUST-FL", - downloadUrl: - "https://www.gust.org.pl/projects/e-foundry/tex-gyre/cursor/tg_cursor-otf-2_609-31_03_2026.zip", - licenseUrl: GUST_LICENSE_URL, - expectedFiles: [ - "texgyrecursor-regular.otf", - "texgyrecursor-bold.otf", - "texgyrecursor-italic.otf", - "texgyrecursor-bolditalic.otf", - ], - targetFamilies: ["Courier New", "Courier"], - }, - { - sourceId: "tex-gyre-heros", - family: "TeX Gyre Heros", - project: "TeX Gyre", - licenseFamily: "GUST-FL", - downloadUrl: - "https://www.gust.org.pl/projects/e-foundry/tex-gyre/heros/tg_heros-otf-2_609-31_03_2026.zip", - licenseUrl: GUST_LICENSE_URL, - expectedFiles: [ - "texgyreheros-regular.otf", - "texgyreheros-bold.otf", - "texgyreheros-italic.otf", - "texgyreheros-bolditalic.otf", - ], - targetFamilies: ["Arial", "Helvetica", "Arial Narrow"], - }, - { - sourceId: "tex-gyre-pagella", - family: "TeX Gyre Pagella", - project: "TeX Gyre", - licenseFamily: "GUST-FL", - downloadUrl: - "https://www.gust.org.pl/projects/e-foundry/tex-gyre/pagella/tg_pagella-otf-2_609-31_03_2026.zip", - licenseUrl: GUST_LICENSE_URL, - expectedFiles: [ - "texgyrepagella-regular.otf", - "texgyrepagella-bold.otf", - "texgyrepagella-italic.otf", - "texgyrepagella-bolditalic.otf", - ], - targetFamilies: ["Palatino Linotype", "Book Antiqua"], - }, - { - sourceId: "tex-gyre-schola", - family: "TeX Gyre Schola", - project: "TeX Gyre", - licenseFamily: "GUST-FL", - downloadUrl: - "https://www.gust.org.pl/projects/e-foundry/tex-gyre/schola/tg_schola-otf-2_609-31_03_2026.zip", - licenseUrl: GUST_LICENSE_URL, - expectedFiles: [ - "texgyreschola-regular.otf", - "texgyreschola-bold.otf", - "texgyreschola-italic.otf", - "texgyreschola-bolditalic.otf", - ], - targetFamilies: ["Century Schoolbook", "New Century Schoolbook"], - }, - { - sourceId: "tex-gyre-termes", - family: "TeX Gyre Termes", - project: "TeX Gyre", - licenseFamily: "GUST-FL", - downloadUrl: - "https://www.gust.org.pl/projects/e-foundry/tex-gyre/termes/tg_termes-otf-2_609-31_03_2026.zip", - licenseUrl: GUST_LICENSE_URL, - expectedFiles: [ - "texgyretermes-regular.otf", - "texgyretermes-bold.otf", - "texgyretermes-italic.otf", - "texgyretermes-bolditalic.otf", - ], - targetFamilies: ["Times New Roman", "Times"], - }, - - { - sourceId: "urw-base35", - family: "URW Base 35", - project: "URW Base 35", - licenseFamily: "AGPL-3.0-FE", - downloadUrl: - "https://github.com/ArtifexSoftware/urw-base35-fonts/archive/refs/tags/20200910.zip", - licenseUrl: - "https://raw.githubusercontent.com/ArtifexSoftware/urw-base35-fonts/20200910/LICENSE", - expectedFiles: [ - "NimbusRoman-Regular.otf", - "NimbusSans-Regular.otf", - "NimbusMonoPS-Regular.otf", - "URWBookman-Light.otf", - "P052-Roman.otf", - "C059-Roman.otf", - ], - targetFamilies: [ - "Times New Roman", - "Arial", - "Helvetica", - "Courier New", - "Bookman Old Style", - "Palatino Linotype", - "Century Schoolbook", - ], - }, - - { - sourceId: "source-serif-4", - family: "Source Serif 4", - project: "Adobe Source", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/adobe-fonts/source-serif/releases/download/4.005R/source-serif-4.005_Desktop.zip", - licenseUrl: - "https://raw.githubusercontent.com/adobe-fonts/source-serif/release/LICENSE.md", - expectedFiles: [ - "SourceSerif4-Regular.otf", - "SourceSerif4-Bold.otf", - "SourceSerif4-It.otf", - "SourceSerif4-BoldIt.otf", - ], - targetFamilies: ["Georgia", "Cambria"], - }, - { - sourceId: "source-sans-3", - family: "Source Sans 3", - project: "Adobe Source", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/adobe-fonts/source-sans/releases/download/3.052R/OTF-source-sans-3.052R.zip", - licenseUrl: - "https://raw.githubusercontent.com/adobe-fonts/source-sans/release/LICENSE.md", - expectedFiles: [ - "SourceSans3-Regular.otf", - "SourceSans3-Bold.otf", - "SourceSans3-It.otf", - "SourceSans3-BoldIt.otf", - ], - targetFamilies: ["Calibri", "Segoe UI"], - }, - { - sourceId: "source-code-pro", - family: "Source Code Pro", - project: "Adobe Source", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/adobe-fonts/source-code-pro/releases/download/2.042R-u/1.062R-i/1.026R-vf/OTF-source-code-pro-2.042R-u_1.062R-i.zip", - licenseUrl: - "https://raw.githubusercontent.com/adobe-fonts/source-code-pro/release/LICENSE.md", - expectedFiles: [ - "SourceCodePro-Regular.otf", - "SourceCodePro-Bold.otf", - "SourceCodePro-It.otf", - "SourceCodePro-BoldIt.otf", - ], - targetFamilies: ["Consolas", "Courier New"], - }, - - { - sourceId: "sil-charis", - family: "Charis SIL", - project: "SIL", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/silnrsi/font-charis/releases/download/v7.000/Charis-7.000.zip", - licenseUrl: - "https://raw.githubusercontent.com/silnrsi/font-charis/v7.000/OFL.txt", - expectedFiles: [ - "Charis-Regular.ttf", - "Charis-Bold.ttf", - "Charis-Italic.ttf", - "Charis-BoldItalic.ttf", - ], - targetFamilies: ["Charter", "Bitstream Charter"], - }, - { - sourceId: "sil-gentium", - family: "Gentium Plus", - project: "SIL", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/silnrsi/font-gentium/releases/download/v7.000/Gentium-7.000.zip", - licenseUrl: - "https://raw.githubusercontent.com/silnrsi/font-gentium/v7.000/OFL.txt", - expectedFiles: [ - "Gentium-Regular.ttf", - "Gentium-Bold.ttf", - "Gentium-Italic.ttf", - "Gentium-BoldItalic.ttf", - ], - targetFamilies: ["Times New Roman"], - }, - { - sourceId: "sil-doulos", - family: "Doulos SIL", - project: "SIL", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/silnrsi/font-doulos/releases/download/v7.000/DoulosSIL-7.000.zip", - licenseUrl: - "https://raw.githubusercontent.com/silnrsi/font-doulos/v7.000/OFL.txt", - expectedFiles: ["DoulosSIL-Regular.ttf"], - targetFamilies: ["Times New Roman"], - }, - { - sourceId: "sil-andika", - family: "Andika", - project: "SIL", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/silnrsi/font-andika/releases/download/v7.000/Andika-7.000.zip", - licenseUrl: - "https://raw.githubusercontent.com/silnrsi/font-andika/v7.000/OFL.txt", - expectedFiles: [ - "Andika-Regular.ttf", - "Andika-Bold.ttf", - "Andika-Italic.ttf", - "Andika-BoldItalic.ttf", - ], - targetFamilies: ["Verdana", "Tahoma"], - }, - - { - sourceId: "noto-sans", - family: "Noto Sans", - project: "Noto", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSans-v2.015/NotoSans-v2.015.zip", - licenseUrl: - "https://raw.githubusercontent.com/notofonts/latin-greek-cyrillic/main/OFL.txt", - expectedFiles: [ - "NotoSans-Regular.otf", - "NotoSans-Bold.otf", - "NotoSans-Italic.otf", - "NotoSans-BoldItalic.otf", - ], - targetFamilies: ["Calibri", "Segoe UI"], - }, - { - sourceId: "noto-serif", - family: "Noto Serif", - project: "Noto", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSerif-v2.015/NotoSerif-v2.015.zip", - licenseUrl: - "https://raw.githubusercontent.com/notofonts/latin-greek-cyrillic/main/OFL.txt", - expectedFiles: [ - "NotoSerif-Regular.otf", - "NotoSerif-Bold.otf", - "NotoSerif-Italic.otf", - "NotoSerif-BoldItalic.otf", - ], - targetFamilies: ["Georgia", "Cambria"], - }, - { - sourceId: "noto-sans-mono", - family: "Noto Sans Mono", - project: "Noto", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSansMono-v2.014/NotoSansMono-v2.014.zip", - licenseUrl: - "https://raw.githubusercontent.com/notofonts/latin-greek-cyrillic/main/OFL.txt", - expectedFiles: ["NotoSansMono-Regular.otf", "NotoSansMono-Bold.otf"], - targetFamilies: ["Consolas", "Courier New"], - }, - - { - sourceId: "liberation-fonts", - family: "Liberation", - project: "Liberation", - licenseFamily: "OFL-1.1", - archiveFormat: "tar.gz", - downloadUrl: - "https://github.com/liberationfonts/liberation-fonts/files/7261482/liberation-fonts-ttf-2.1.5.tar.gz", - licenseUrl: - "https://raw.githubusercontent.com/liberationfonts/liberation-fonts/2.1.5/LICENSE", - expectedFiles: [ - "LiberationMono-Regular.ttf", - "LiberationMono-Bold.ttf", - "LiberationMono-Italic.ttf", - "LiberationMono-BoldItalic.ttf", - "LiberationSans-Regular.ttf", - "LiberationSans-Bold.ttf", - "LiberationSans-Italic.ttf", - "LiberationSans-BoldItalic.ttf", - "LiberationSerif-Regular.ttf", - "LiberationSerif-Bold.ttf", - "LiberationSerif-Italic.ttf", - "LiberationSerif-BoldItalic.ttf", - ], - targetFamilies: ["Arial", "Helvetica", "Times New Roman", "Courier New"], - }, - { - sourceId: "liberation-sans-narrow", - family: "Liberation Sans Narrow", - project: "Liberation", - licenseFamily: "GPL-2.0-FE", - archiveFormat: "tar.gz", - downloadUrl: - "https://github.com/liberationfonts/liberation-sans-narrow/files/2579431/liberation-narrow-fonts-ttf-1.07.6.tar.gz", - licenseUrl: - "https://raw.githubusercontent.com/liberationfonts/liberation-sans-narrow/1.07.6/License.txt", - expectedFiles: [ - "LiberationSansNarrow-Regular.ttf", - "LiberationSansNarrow-Bold.ttf", - "LiberationSansNarrow-Italic.ttf", - "LiberationSansNarrow-BoldItalic.ttf", - ], - targetFamilies: ["Arial Narrow"], - }, - { - sourceId: "selawik", - family: "Selawik", - project: "Selawik", - licenseFamily: "OFL-1.1", - downloadUrl: - "https://github.com/microsoft/Selawik/releases/download/1.01/Selawik_Release.zip", - licenseUrl: - "https://raw.githubusercontent.com/microsoft/Selawik/1.01/LICENSE.txt", - expectedFiles: [ - "selawk.ttf", - "selawk.woff", - "selawk.woff2", - "selawkb.ttf", - "selawkb.woff", - "selawkb.woff2", - "selawkl.ttf", - "selawkl.woff", - "selawkl.woff2", - "selawksb.ttf", - "selawksb.woff", - "selawksb.woff2", - "selawksl.ttf", - "selawksl.woff", - "selawksl.woff2", - ], - targetFamilies: ["Segoe UI"], - }, - { - sourceId: "dejavu", - family: "DejaVu", - project: "DejaVu", - licenseFamily: "Bitstream-Vera-DejaVu", - downloadUrl: - "https://github.com/dejavu-fonts/dejavu-fonts/releases/download/version_2_37/dejavu-fonts-ttf-2.37.zip", - licenseUrl: - "https://raw.githubusercontent.com/dejavu-fonts/dejavu-fonts/version_2_37/LICENSE", - expectedFiles: [ - "DejaVuMathTeXGyre.ttf", - "DejaVuSans.ttf", - "DejaVuSans-Bold.ttf", - "DejaVuSans-BoldOblique.ttf", - "DejaVuSans-ExtraLight.ttf", - "DejaVuSans-Oblique.ttf", - "DejaVuSansCondensed.ttf", - "DejaVuSansCondensed-Bold.ttf", - "DejaVuSansCondensed-BoldOblique.ttf", - "DejaVuSansCondensed-Oblique.ttf", - "DejaVuSansMono.ttf", - "DejaVuSansMono-Bold.ttf", - "DejaVuSansMono-BoldOblique.ttf", - "DejaVuSansMono-Oblique.ttf", - "DejaVuSerif.ttf", - "DejaVuSerif-Bold.ttf", - "DejaVuSerif-BoldItalic.ttf", - "DejaVuSerif-Italic.ttf", - "DejaVuSerifCondensed.ttf", - "DejaVuSerifCondensed-Bold.ttf", - "DejaVuSerifCondensed-BoldItalic.ttf", - "DejaVuSerifCondensed-Italic.ttf", - ], - targetFamilies: ["Verdana", "Tahoma"], - }, - - { - kind: "github-tree", - sourceId: "google-fonts", - family: "Google Fonts", - project: "Google Fonts", - repo: "google/fonts", - commit: GOOGLE_FONTS_COMMIT, - licenseDirs: { - apache: "Apache-2.0", - ofl: "OFL-1.1", - ufl: "UFL-1.0", - }, - targetFamilies: ["Document font discovery"], - }, -]; - interface FileSnapshot { name: string; /** For archives: the in-archive member path. For cached files: the cache-relative path. */ diff --git a/tools/corpus/sources/adobe.ts b/tools/corpus/sources/adobe.ts new file mode 100644 index 0000000..c3d0b63 --- /dev/null +++ b/tools/corpus/sources/adobe.ts @@ -0,0 +1,55 @@ +import type { ArchiveSource } from "./types"; + +export const ADOBE_SOURCES: ArchiveSource[] = [ + { + sourceId: "source-serif-4", + family: "Source Serif 4", + project: "Adobe Source", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/adobe-fonts/source-serif/releases/download/4.005R/source-serif-4.005_Desktop.zip", + licenseUrl: + "https://raw.githubusercontent.com/adobe-fonts/source-serif/release/LICENSE.md", + expectedFiles: [ + "SourceSerif4-Regular.otf", + "SourceSerif4-Bold.otf", + "SourceSerif4-It.otf", + "SourceSerif4-BoldIt.otf", + ], + targetFamilies: ["Georgia", "Cambria"], + }, + { + sourceId: "source-sans-3", + family: "Source Sans 3", + project: "Adobe Source", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/adobe-fonts/source-sans/releases/download/3.052R/OTF-source-sans-3.052R.zip", + licenseUrl: + "https://raw.githubusercontent.com/adobe-fonts/source-sans/release/LICENSE.md", + expectedFiles: [ + "SourceSans3-Regular.otf", + "SourceSans3-Bold.otf", + "SourceSans3-It.otf", + "SourceSans3-BoldIt.otf", + ], + targetFamilies: ["Calibri", "Segoe UI"], + }, + { + sourceId: "source-code-pro", + family: "Source Code Pro", + project: "Adobe Source", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/adobe-fonts/source-code-pro/releases/download/2.042R-u/1.062R-i/1.026R-vf/OTF-source-code-pro-2.042R-u_1.062R-i.zip", + licenseUrl: + "https://raw.githubusercontent.com/adobe-fonts/source-code-pro/release/LICENSE.md", + expectedFiles: [ + "SourceCodePro-Regular.otf", + "SourceCodePro-Bold.otf", + "SourceCodePro-It.otf", + "SourceCodePro-BoldIt.otf", + ], + targetFamilies: ["Consolas", "Courier New"], + }, +]; diff --git a/tools/corpus/sources/dejavu.ts b/tools/corpus/sources/dejavu.ts new file mode 100644 index 0000000..4bd501b --- /dev/null +++ b/tools/corpus/sources/dejavu.ts @@ -0,0 +1,39 @@ +import type { ArchiveSource } from "./types"; + +export const DEJAVU_SOURCES: ArchiveSource[] = [ + { + sourceId: "dejavu", + family: "DejaVu", + project: "DejaVu", + licenseFamily: "Bitstream-Vera-DejaVu", + downloadUrl: + "https://github.com/dejavu-fonts/dejavu-fonts/releases/download/version_2_37/dejavu-fonts-ttf-2.37.zip", + licenseUrl: + "https://raw.githubusercontent.com/dejavu-fonts/dejavu-fonts/version_2_37/LICENSE", + expectedFiles: [ + "DejaVuMathTeXGyre.ttf", + "DejaVuSans.ttf", + "DejaVuSans-Bold.ttf", + "DejaVuSans-BoldOblique.ttf", + "DejaVuSans-ExtraLight.ttf", + "DejaVuSans-Oblique.ttf", + "DejaVuSansCondensed.ttf", + "DejaVuSansCondensed-Bold.ttf", + "DejaVuSansCondensed-BoldOblique.ttf", + "DejaVuSansCondensed-Oblique.ttf", + "DejaVuSansMono.ttf", + "DejaVuSansMono-Bold.ttf", + "DejaVuSansMono-BoldOblique.ttf", + "DejaVuSansMono-Oblique.ttf", + "DejaVuSerif.ttf", + "DejaVuSerif-Bold.ttf", + "DejaVuSerif-BoldItalic.ttf", + "DejaVuSerif-Italic.ttf", + "DejaVuSerifCondensed.ttf", + "DejaVuSerifCondensed-Bold.ttf", + "DejaVuSerifCondensed-BoldItalic.ttf", + "DejaVuSerifCondensed-Italic.ttf", + ], + targetFamilies: ["Verdana", "Tahoma"], + }, +]; diff --git a/tools/corpus/sources/google-fonts.ts b/tools/corpus/sources/google-fonts.ts new file mode 100644 index 0000000..3b5b1bf --- /dev/null +++ b/tools/corpus/sources/google-fonts.ts @@ -0,0 +1,20 @@ +import type { GitHubTreeSource } from "./types"; + +const GOOGLE_FONTS_COMMIT = "c89741abbf4eeabce432c3ed2fd7dc28b022701e"; + +export const GOOGLE_FONT_SOURCES: GitHubTreeSource[] = [ + { + kind: "github-tree", + sourceId: "google-fonts", + family: "Google Fonts", + project: "Google Fonts", + repo: "google/fonts", + commit: GOOGLE_FONTS_COMMIT, + licenseDirs: { + apache: "Apache-2.0", + ofl: "OFL-1.1", + ufl: "UFL-1.0", + }, + targetFamilies: ["Document font discovery"], + }, +]; diff --git a/tools/corpus/sources/index.ts b/tools/corpus/sources/index.ts new file mode 100644 index 0000000..bcdc652 --- /dev/null +++ b/tools/corpus/sources/index.ts @@ -0,0 +1,29 @@ +import { ADOBE_SOURCES } from "./adobe"; +import { DEJAVU_SOURCES } from "./dejavu"; +import { GOOGLE_FONT_SOURCES } from "./google-fonts"; +import { LIBERATION_SOURCES } from "./liberation"; +import { NOTO_SOURCES } from "./noto"; +import { SELAWIK_SOURCES } from "./selawik"; +import { SIL_SOURCES } from "./sil"; +import { TEX_GYRE_SOURCES } from "./tex-gyre"; +import type { SourceRelease } from "./types"; +import { URW_SOURCES } from "./urw"; + +export type { + ArchiveSource, + GitHubTreeSource, + LicenseFamily, + SourceRelease, +} from "./types"; + +export const SOURCE_RELEASES: SourceRelease[] = [ + ...TEX_GYRE_SOURCES, + ...URW_SOURCES, + ...ADOBE_SOURCES, + ...SIL_SOURCES, + ...NOTO_SOURCES, + ...LIBERATION_SOURCES, + ...SELAWIK_SOURCES, + ...DEJAVU_SOURCES, + ...GOOGLE_FONT_SOURCES, +]; diff --git a/tools/corpus/sources/liberation.ts b/tools/corpus/sources/liberation.ts new file mode 100644 index 0000000..7f3c93c --- /dev/null +++ b/tools/corpus/sources/liberation.ts @@ -0,0 +1,48 @@ +import type { ArchiveSource } from "./types"; + +export const LIBERATION_SOURCES: ArchiveSource[] = [ + { + sourceId: "liberation-fonts", + family: "Liberation", + project: "Liberation", + licenseFamily: "OFL-1.1", + archiveFormat: "tar.gz", + downloadUrl: + "https://github.com/liberationfonts/liberation-fonts/files/7261482/liberation-fonts-ttf-2.1.5.tar.gz", + licenseUrl: + "https://raw.githubusercontent.com/liberationfonts/liberation-fonts/2.1.5/LICENSE", + expectedFiles: [ + "LiberationMono-Regular.ttf", + "LiberationMono-Bold.ttf", + "LiberationMono-Italic.ttf", + "LiberationMono-BoldItalic.ttf", + "LiberationSans-Regular.ttf", + "LiberationSans-Bold.ttf", + "LiberationSans-Italic.ttf", + "LiberationSans-BoldItalic.ttf", + "LiberationSerif-Regular.ttf", + "LiberationSerif-Bold.ttf", + "LiberationSerif-Italic.ttf", + "LiberationSerif-BoldItalic.ttf", + ], + targetFamilies: ["Arial", "Helvetica", "Times New Roman", "Courier New"], + }, + { + sourceId: "liberation-sans-narrow", + family: "Liberation Sans Narrow", + project: "Liberation", + licenseFamily: "GPL-2.0-FE", + archiveFormat: "tar.gz", + downloadUrl: + "https://github.com/liberationfonts/liberation-sans-narrow/files/2579431/liberation-narrow-fonts-ttf-1.07.6.tar.gz", + licenseUrl: + "https://raw.githubusercontent.com/liberationfonts/liberation-sans-narrow/1.07.6/License.txt", + expectedFiles: [ + "LiberationSansNarrow-Regular.ttf", + "LiberationSansNarrow-Bold.ttf", + "LiberationSansNarrow-Italic.ttf", + "LiberationSansNarrow-BoldItalic.ttf", + ], + targetFamilies: ["Arial Narrow"], + }, +]; diff --git a/tools/corpus/sources/licenses.ts b/tools/corpus/sources/licenses.ts new file mode 100644 index 0000000..972ca45 --- /dev/null +++ b/tools/corpus/sources/licenses.ts @@ -0,0 +1,11 @@ +export const GUST_LICENSE_URL = + "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-LICENSE.txt/at_download/file"; + +export const LICENSE_FILE_NAMES = new Set([ + "LICENSE", + "LICENSE.md", + "LICENSE.txt", + "LICENCE.txt", + "OFL.txt", + "UFL.txt", +]); diff --git a/tools/corpus/sources/noto.ts b/tools/corpus/sources/noto.ts new file mode 100644 index 0000000..abda47d --- /dev/null +++ b/tools/corpus/sources/noto.ts @@ -0,0 +1,50 @@ +import type { ArchiveSource } from "./types"; + +export const NOTO_SOURCES: ArchiveSource[] = [ + { + sourceId: "noto-sans", + family: "Noto Sans", + project: "Noto", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSans-v2.015/NotoSans-v2.015.zip", + licenseUrl: + "https://raw.githubusercontent.com/notofonts/latin-greek-cyrillic/main/OFL.txt", + expectedFiles: [ + "NotoSans-Regular.otf", + "NotoSans-Bold.otf", + "NotoSans-Italic.otf", + "NotoSans-BoldItalic.otf", + ], + targetFamilies: ["Calibri", "Segoe UI"], + }, + { + sourceId: "noto-serif", + family: "Noto Serif", + project: "Noto", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSerif-v2.015/NotoSerif-v2.015.zip", + licenseUrl: + "https://raw.githubusercontent.com/notofonts/latin-greek-cyrillic/main/OFL.txt", + expectedFiles: [ + "NotoSerif-Regular.otf", + "NotoSerif-Bold.otf", + "NotoSerif-Italic.otf", + "NotoSerif-BoldItalic.otf", + ], + targetFamilies: ["Georgia", "Cambria"], + }, + { + sourceId: "noto-sans-mono", + family: "Noto Sans Mono", + project: "Noto", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSansMono-v2.014/NotoSansMono-v2.014.zip", + licenseUrl: + "https://raw.githubusercontent.com/notofonts/latin-greek-cyrillic/main/OFL.txt", + expectedFiles: ["NotoSansMono-Regular.otf", "NotoSansMono-Bold.otf"], + targetFamilies: ["Consolas", "Courier New"], + }, +]; diff --git a/tools/corpus/sources/selawik.ts b/tools/corpus/sources/selawik.ts new file mode 100644 index 0000000..6a47773 --- /dev/null +++ b/tools/corpus/sources/selawik.ts @@ -0,0 +1,32 @@ +import type { ArchiveSource } from "./types"; + +export const SELAWIK_SOURCES: ArchiveSource[] = [ + { + sourceId: "selawik", + family: "Selawik", + project: "Selawik", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/microsoft/Selawik/releases/download/1.01/Selawik_Release.zip", + licenseUrl: + "https://raw.githubusercontent.com/microsoft/Selawik/1.01/LICENSE.txt", + expectedFiles: [ + "selawk.ttf", + "selawk.woff", + "selawk.woff2", + "selawkb.ttf", + "selawkb.woff", + "selawkb.woff2", + "selawkl.ttf", + "selawkl.woff", + "selawkl.woff2", + "selawksb.ttf", + "selawksb.woff", + "selawksb.woff2", + "selawksl.ttf", + "selawksl.woff", + "selawksl.woff2", + ], + targetFamilies: ["Segoe UI"], + }, +]; diff --git a/tools/corpus/sources/sil.ts b/tools/corpus/sources/sil.ts new file mode 100644 index 0000000..c50715a --- /dev/null +++ b/tools/corpus/sources/sil.ts @@ -0,0 +1,67 @@ +import type { ArchiveSource } from "./types"; + +export const SIL_SOURCES: ArchiveSource[] = [ + { + sourceId: "sil-charis", + family: "Charis SIL", + project: "SIL", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/silnrsi/font-charis/releases/download/v7.000/Charis-7.000.zip", + licenseUrl: + "https://raw.githubusercontent.com/silnrsi/font-charis/v7.000/OFL.txt", + expectedFiles: [ + "Charis-Regular.ttf", + "Charis-Bold.ttf", + "Charis-Italic.ttf", + "Charis-BoldItalic.ttf", + ], + targetFamilies: ["Charter", "Bitstream Charter"], + }, + { + sourceId: "sil-gentium", + family: "Gentium Plus", + project: "SIL", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/silnrsi/font-gentium/releases/download/v7.000/Gentium-7.000.zip", + licenseUrl: + "https://raw.githubusercontent.com/silnrsi/font-gentium/v7.000/OFL.txt", + expectedFiles: [ + "Gentium-Regular.ttf", + "Gentium-Bold.ttf", + "Gentium-Italic.ttf", + "Gentium-BoldItalic.ttf", + ], + targetFamilies: ["Times New Roman"], + }, + { + sourceId: "sil-doulos", + family: "Doulos SIL", + project: "SIL", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/silnrsi/font-doulos/releases/download/v7.000/DoulosSIL-7.000.zip", + licenseUrl: + "https://raw.githubusercontent.com/silnrsi/font-doulos/v7.000/OFL.txt", + expectedFiles: ["DoulosSIL-Regular.ttf"], + targetFamilies: ["Times New Roman"], + }, + { + sourceId: "sil-andika", + family: "Andika", + project: "SIL", + licenseFamily: "OFL-1.1", + downloadUrl: + "https://github.com/silnrsi/font-andika/releases/download/v7.000/Andika-7.000.zip", + licenseUrl: + "https://raw.githubusercontent.com/silnrsi/font-andika/v7.000/OFL.txt", + expectedFiles: [ + "Andika-Regular.ttf", + "Andika-Bold.ttf", + "Andika-Italic.ttf", + "Andika-BoldItalic.ttf", + ], + targetFamilies: ["Verdana", "Tahoma"], + }, +]; diff --git a/tools/corpus/sources/tex-gyre.ts b/tools/corpus/sources/tex-gyre.ts new file mode 100644 index 0000000..23c2aa6 --- /dev/null +++ b/tools/corpus/sources/tex-gyre.ts @@ -0,0 +1,128 @@ +import { GUST_LICENSE_URL } from "./licenses"; +import type { ArchiveSource } from "./types"; + +export const TEX_GYRE_SOURCES: ArchiveSource[] = [ + { + sourceId: "tex-gyre-adventor", + family: "TeX Gyre Adventor", + project: "TeX Gyre", + licenseFamily: "GUST-FL", + downloadUrl: + "https://www.gust.org.pl/projects/e-foundry/tex-gyre/adventor/tg_adventor-otf-2_609-31_03_2026.zip", + licenseUrl: GUST_LICENSE_URL, + expectedFiles: [ + "texgyreadventor-regular.otf", + "texgyreadventor-bold.otf", + "texgyreadventor-italic.otf", + "texgyreadventor-bolditalic.otf", + ], + targetFamilies: ["Century Gothic", "ITC Avant Garde Gothic"], + }, + { + sourceId: "tex-gyre-bonum", + family: "TeX Gyre Bonum", + project: "TeX Gyre", + licenseFamily: "GUST-FL", + downloadUrl: + "https://www.gust.org.pl/projects/e-foundry/tex-gyre/bonum/tg_bonum-otf-2_609-31_03_2026.zip", + licenseUrl: GUST_LICENSE_URL, + expectedFiles: [ + "texgyrebonum-regular.otf", + "texgyrebonum-bold.otf", + "texgyrebonum-italic.otf", + "texgyrebonum-bolditalic.otf", + ], + targetFamilies: ["Bookman Old Style", "ITC Bookman"], + }, + { + sourceId: "tex-gyre-chorus", + family: "TeX Gyre Chorus", + project: "TeX Gyre", + licenseFamily: "GUST-FL", + downloadUrl: + "https://www.gust.org.pl/projects/e-foundry/tex-gyre/chorus/tg_chorus-otf-2_609-31_03_2026.zip", + licenseUrl: GUST_LICENSE_URL, + expectedFiles: ["texgyrechorus-mediumitalic.otf"], + targetFamilies: ["Monotype Corsiva", "ITC Zapf Chancery"], + }, + { + sourceId: "tex-gyre-cursor", + family: "TeX Gyre Cursor", + project: "TeX Gyre", + licenseFamily: "GUST-FL", + downloadUrl: + "https://www.gust.org.pl/projects/e-foundry/tex-gyre/cursor/tg_cursor-otf-2_609-31_03_2026.zip", + licenseUrl: GUST_LICENSE_URL, + expectedFiles: [ + "texgyrecursor-regular.otf", + "texgyrecursor-bold.otf", + "texgyrecursor-italic.otf", + "texgyrecursor-bolditalic.otf", + ], + targetFamilies: ["Courier New", "Courier"], + }, + { + sourceId: "tex-gyre-heros", + family: "TeX Gyre Heros", + project: "TeX Gyre", + licenseFamily: "GUST-FL", + downloadUrl: + "https://www.gust.org.pl/projects/e-foundry/tex-gyre/heros/tg_heros-otf-2_609-31_03_2026.zip", + licenseUrl: GUST_LICENSE_URL, + expectedFiles: [ + "texgyreheros-regular.otf", + "texgyreheros-bold.otf", + "texgyreheros-italic.otf", + "texgyreheros-bolditalic.otf", + ], + targetFamilies: ["Arial", "Helvetica", "Arial Narrow"], + }, + { + sourceId: "tex-gyre-pagella", + family: "TeX Gyre Pagella", + project: "TeX Gyre", + licenseFamily: "GUST-FL", + downloadUrl: + "https://www.gust.org.pl/projects/e-foundry/tex-gyre/pagella/tg_pagella-otf-2_609-31_03_2026.zip", + licenseUrl: GUST_LICENSE_URL, + expectedFiles: [ + "texgyrepagella-regular.otf", + "texgyrepagella-bold.otf", + "texgyrepagella-italic.otf", + "texgyrepagella-bolditalic.otf", + ], + targetFamilies: ["Palatino Linotype", "Book Antiqua"], + }, + { + sourceId: "tex-gyre-schola", + family: "TeX Gyre Schola", + project: "TeX Gyre", + licenseFamily: "GUST-FL", + downloadUrl: + "https://www.gust.org.pl/projects/e-foundry/tex-gyre/schola/tg_schola-otf-2_609-31_03_2026.zip", + licenseUrl: GUST_LICENSE_URL, + expectedFiles: [ + "texgyreschola-regular.otf", + "texgyreschola-bold.otf", + "texgyreschola-italic.otf", + "texgyreschola-bolditalic.otf", + ], + targetFamilies: ["Century Schoolbook", "New Century Schoolbook"], + }, + { + sourceId: "tex-gyre-termes", + family: "TeX Gyre Termes", + project: "TeX Gyre", + licenseFamily: "GUST-FL", + downloadUrl: + "https://www.gust.org.pl/projects/e-foundry/tex-gyre/termes/tg_termes-otf-2_609-31_03_2026.zip", + licenseUrl: GUST_LICENSE_URL, + expectedFiles: [ + "texgyretermes-regular.otf", + "texgyretermes-bold.otf", + "texgyretermes-italic.otf", + "texgyretermes-bolditalic.otf", + ], + targetFamilies: ["Times New Roman", "Times"], + }, +]; diff --git a/tools/corpus/sources/types.ts b/tools/corpus/sources/types.ts new file mode 100644 index 0000000..5110170 --- /dev/null +++ b/tools/corpus/sources/types.ts @@ -0,0 +1,38 @@ +import type { ArchiveFormat } from "../src/archive"; + +export type LicenseFamily = + | "GUST-FL" + | "OFL-1.1" + | "AGPL-3.0-FE" + | "Apache-2.0" + | "UFL-1.0" + | "GPL-2.0-FE" + | "Bitstream-Vera-DejaVu"; + +interface BaseSource { + sourceId: string; + family: string; + project: string; + targetFamilies: string[]; +} + +interface LicensedSource extends BaseSource { + licenseFamily: LicenseFamily; + licenseUrl: string; +} + +export interface ArchiveSource extends LicensedSource { + kind?: "archive"; + archiveFormat?: ArchiveFormat; + downloadUrl: string; + expectedFiles: string[]; +} + +export interface GitHubTreeSource extends BaseSource { + kind: "github-tree"; + repo: string; + commit: string; + licenseDirs: Record; +} + +export type SourceRelease = ArchiveSource | GitHubTreeSource; diff --git a/tools/corpus/sources/urw.ts b/tools/corpus/sources/urw.ts new file mode 100644 index 0000000..1870849 --- /dev/null +++ b/tools/corpus/sources/urw.ts @@ -0,0 +1,31 @@ +import type { ArchiveSource } from "./types"; + +export const URW_SOURCES: ArchiveSource[] = [ + { + sourceId: "urw-base35", + family: "URW Base 35", + project: "URW Base 35", + licenseFamily: "AGPL-3.0-FE", + downloadUrl: + "https://github.com/ArtifexSoftware/urw-base35-fonts/archive/refs/tags/20200910.zip", + licenseUrl: + "https://raw.githubusercontent.com/ArtifexSoftware/urw-base35-fonts/20200910/LICENSE", + expectedFiles: [ + "NimbusRoman-Regular.otf", + "NimbusSans-Regular.otf", + "NimbusMonoPS-Regular.otf", + "URWBookman-Light.otf", + "P052-Roman.otf", + "C059-Roman.otf", + ], + targetFamilies: [ + "Times New Roman", + "Arial", + "Helvetica", + "Courier New", + "Bookman Old Style", + "Palatino Linotype", + "Century Schoolbook", + ], + }, +];