| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- import fs from 'node:fs/promises'
- import { createWriteStream } from 'node:fs'
- import { basename, join } from 'node:path'
- import { tmpdir } from 'node:os'
- import { execSync } from 'node:child_process'
- import yauzl from 'yauzl'
- // brew install woff2
- const families = [
- {
- folder: 'dm-mono',
- url: 'https://github.com/googlefonts/dm-mono',
- fonts: [
- 'https://github.com/googlefonts/dm-mono/raw/refs/heads/main/exports/DMMono-Italic.ttf',
- 'https://github.com/googlefonts/dm-mono/raw/refs/heads/main/exports/DMMono-Medium.ttf',
- 'https://github.com/googlefonts/dm-mono/raw/refs/heads/main/exports/DMMono-MediumItalic.ttf',
- 'https://github.com/googlefonts/dm-mono/raw/refs/heads/main/exports/DMMono-Regular.ttf',
- ],
- },
- {
- folder: 'inter',
- url: 'https://github.com/rsms/inter/releases/tag/v4.1',
- archive:
- 'https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip',
- fonts: ['web/Inter-Regular.woff2', 'web/Inter-SemiBold.woff2'],
- license:
- 'https://raw.githubusercontent.com/rsms/inter/refs/heads/master/LICENSE.txt',
- },
- {
- folder: 'lato',
- url: 'https://www.latofonts.com/',
- archive: 'https://www.latofonts.com/files/Lato2OFLWeb.zip',
- fonts: [
- 'Lato2OFLWeb/LatoLatin/fonts/LatoLatin-Bold.woff2',
- 'Lato2OFLWeb/LatoLatin/fonts/LatoLatin-BoldItalic.woff2',
- 'Lato2OFLWeb/LatoLatin/fonts/LatoLatin-Italic.woff2',
- 'Lato2OFLWeb/LatoLatin/fonts/LatoLatin-Regular.woff2',
- ],
- },
- {
- folder: 'merriweather',
- url: 'https://github.com/SorkinType/Merriweather',
- fonts: [
- 'https://github.com/SorkinType/Merriweather/raw/refs/heads/master/fonts/webfonts/Merriweather-Bold.woff2',
- 'https://github.com/SorkinType/Merriweather/raw/refs/heads/master/fonts/webfonts/Merriweather-BoldItalic.woff2',
- 'https://github.com/SorkinType/Merriweather/raw/refs/heads/master/fonts/webfonts/Merriweather-Italic.woff2',
- 'https://github.com/SorkinType/Merriweather/raw/refs/heads/master/fonts/webfonts/Merriweather-Regular.woff2',
- ],
- license:
- 'https://github.com/SorkinType/Merriweather/raw/refs/heads/master/OFL.txt',
- },
- {
- folder: 'noto-sans',
- url: 'https://notofonts.github.io/#latin-greek-cyrillic',
- fonts: [
- 'https://github.com/notofonts/notofonts.github.io/raw/refs/heads/main/fonts/NotoSans/full/ttf/NotoSans-Italic.ttf',
- 'https://github.com/notofonts/notofonts.github.io/raw/refs/heads/main/fonts/NotoSans/full/ttf/NotoSans-Medium.ttf',
- 'https://github.com/notofonts/notofonts.github.io/raw/refs/heads/main/fonts/NotoSans/full/ttf/NotoSans-MediumItalic.ttf',
- 'https://github.com/notofonts/notofonts.github.io/raw/refs/heads/main/fonts/NotoSans/full/ttf/NotoSans-Regular.ttf',
- 'https://github.com/notofonts/notofonts.github.io/raw/refs/heads/main/fonts/NotoSans/full/ttf/NotoSans-SemiBold.ttf',
- 'https://github.com/notofonts/notofonts.github.io/raw/refs/heads/main/fonts/NotoSans/full/ttf/NotoSans-SemiBoldItalic.ttf',
- ],
- license:
- 'https://raw.githubusercontent.com/notofonts/latin-greek-cyrillic/refs/heads/main/OFL.txt',
- },
- {
- folder: 'noto-serif',
- url: 'https://notofonts.github.io/#latin-greek-cyrillic',
- fonts: [
- 'https://github.com/notofonts/notofonts.github.io/raw/refs/heads/main/fonts/NotoSerif/unhinted/slim-variable-ttf/NotoSerif%5Bwght%5D.ttf',
- 'https://github.com/notofonts/notofonts.github.io/raw/refs/heads/main/fonts/NotoSerif/unhinted/slim-variable-ttf/NotoSerif-Italic%5Bwght%5D.ttf',
- ],
- license:
- 'https://raw.githubusercontent.com/notofonts/latin-greek-cyrillic/refs/heads/main/OFL.txt',
- },
- {
- folder: 'open-dyslexic-mono',
- url: 'https://github.com/antijingoist/opendyslexic',
- fonts: [
- 'https://github.com/antijingoist/open-dyslexic/blob/master/otf/OpenDyslexicMono-Regular.otf',
- ],
- license: 'https://github.com/antijingoist/opendyslexic/blob/master/OFL.txt',
- },
- {
- folder: 'open-sans',
- url: 'https://github.com/googlefonts/opensans',
- fonts: [
- 'https://github.com/googlefonts/opensans/raw/refs/heads/main/fonts/ttf/OpenSans-Bold.ttf',
- 'https://github.com/googlefonts/opensans/raw/refs/heads/main/fonts/ttf/OpenSans-Light.ttf',
- 'https://github.com/googlefonts/opensans/raw/refs/heads/main/fonts/ttf/OpenSans-Regular.ttf',
- 'https://github.com/googlefonts/opensans/raw/refs/heads/main/fonts/ttf/OpenSans-SemiBold.ttf',
- ],
- license:
- 'https://raw.githubusercontent.com/googlefonts/opensans/refs/heads/main/OFL.txt',
- },
- {
- folder: 'source-code-pro',
- url: 'https://github.com/adobe-fonts/source-code-pro',
- fonts: [
- 'https://github.com/adobe-fonts/source-code-pro/raw/refs/heads/release/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2',
- ],
- license:
- 'https://raw.githubusercontent.com/adobe-fonts/source-code-pro/refs/heads/release/LICENSE.md',
- },
- {
- folder: 'STIXTwoMath',
- url: 'https://github.com/stipub/stixfonts',
- fonts: [
- 'https://github.com/stipub/stixfonts/raw/refs/heads/master/fonts/static_otf_woff2/STIXTwoMath-Regular.woff2',
- ],
- license:
- 'https://raw.githubusercontent.com/stipub/stixfonts/refs/heads/master/OFL.txt',
- },
- ]
- const fetchFile = async (url, path) => {
- console.log(`${url}\n${path}`)
- const response = await fetch(url)
- if (!response.ok) {
- throw new Error(response.statusText)
- }
- await fs.writeFile(path, response.body)
- }
- for (const family of families) {
- if (!family.folder) {
- throw new Error('Missing family information')
- }
- await fs.mkdir(family.folder, { recursive: true })
- const fonts = new Set(family.fonts)
- if (family.archive) {
- const dir = await fs.mkdtemp(join(tmpdir(), 'fonts-'))
- const filename = decodeURIComponent(basename(family.archive))
- const path = `${dir}/${filename}`
- await fetchFile(family.archive, path)
- await new Promise((resolve, reject) => {
- yauzl.open(path, { lazyEntries: true }, (err, zipfile) => {
- if (err) {
- reject(err)
- } else {
- zipfile.on('entry', entry => {
- if (fonts.has(entry.fileName)) {
- console.log(entry.fileName)
- zipfile.openReadStream(entry, (err, readStream) => {
- if (err) {
- reject(err)
- } else {
- const path = `${family.folder}/${basename(entry.fileName)}`
- const output = createWriteStream(path)
- readStream.on('end', async () => {
- output.close()
- if (path.endsWith('.ttf')) {
- execSync(`woff2_compress "${path}"`)
- await fs.unlink(path)
- }
- zipfile.readEntry()
- })
- readStream.pipe(output)
- }
- })
- } else {
- zipfile.readEntry()
- }
- })
- zipfile.on('error', reject)
- zipfile.on('end', resolve)
- zipfile.readEntry()
- }
- })
- })
- await fs.unlink(path)
- } else {
- for (const url of fonts) {
- const filename = decodeURIComponent(basename(url))
- const path = `${family.folder}/${filename}`
- await fetchFile(url, path)
- if (path.endsWith('.ttf')) {
- execSync(`woff2_compress "${path}"`)
- await fs.unlink(path)
- }
- }
- }
- if (family.license) {
- const url = family.license
- const filename = decodeURIComponent(basename(url))
- const path = `${family.folder}/${filename}`
- await fetchFile(url, path)
- }
- }
|