A metadata generator that fetches and parses the Google Fonts API to be primarily used for the Fontsource monorepo.
Install the package from npm
:
npm install google-font-metadata
The project exports the following data:
import {
APIv1,
APIv2,
APIVariable,
APIIconStatic,
APIIconVariable,
APILicense,
APIRegistry,
} from "google-font-metadata";
const {
APIv1,
APIv2,
APIVariable,
APIIconStatic,
APIIconVariable,
APILicense,
APIRegistry,
} = require("google-font-metadata");
console.dir(APIv2);
Uses the Google Fonts CSS APIv1 that includes different font files for each subset, but does NOT include unicode-range values. This isn't usually recommended.
It exports data/google-fonts-v1.json
.
{
...
"abel": {
"family": "Abel",
"id": "abel",
"subsets": ["latin"],
"weights": [400],
"styles": ["normal"],
"variants": {
"400": {
"normal": {
"latin": {
"url": {
"woff2": "https://fonts.gstatic.com/s/abel/v18/MwQ5bhbm2POE2V9BPQ.woff2",
"woff": "https://fonts.gstatic.com/s/abel/v18/MwQ5bhbm2POE2V9BOw.woff",
"truetype": "https://fonts.gstatic.com/s/abel/v18/MwQ5bhbm2POE2V9BOA.ttf"
}
}
}
}
},
"defSubset": "latin",
"lastModified": "2022-04-20",
"version": "v18",
"category": "sans-serif"
},
...
}
Uses the Google Fonts CSS APIv2 and includes the unicode-range values for every subset. However, the API serves ttf
files with ALL subsets included in one file and therefore all links for those file types in the same subset lead to the same link for each weight and style. woff2
and woff
files are individually split per subset.
Exports data/google-fonts-v2.json
.
{
...
"abel": {
"family": "Abel",
"id": "abel",
"subsets": ["latin"],
"weights": [400],
"styles": ["normal"],
"unicodeRange": {
"latin": "U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD"
},
"variants": {
"400": {
"normal": {
"latin": {
"url": {
"woff2": "https://fonts.gstatic.com/s/abel/v18/MwQ5bhbm2POE2V9BPQ.woff2",
"woff": "https://fonts.gstatic.com/s/abel/v18/MwQ5bhbm2POE6Vs.woff",
"truetype": "https://fonts.gstatic.com/s/abel/v18/MwQ5bhbm2POE6Vg.ttf"
}
}
}
}
},
"defSubset": "latin",
"lastModified": "2022-04-20",
"version": "v18",
"category": "sans-serif"
},
...
}
Note that fonts with large glyphsets such as the Japanese, Korean or Chinese language, are divided into many smaller numbered subsets that utilize the unicode-range @fontface selector. An example is Noto Sans JP which returns the following:
{
...
"noto-sans-jp": {
"family": "Noto Sans JP",
"id": "noto-sans-jp",
"subsets": ["japanese", "latin"],
"weights": [100, 300, 400, 500, 700, 900],
"styles": ["normal"],
"unicodeRange": {
"[0]": "U+25ee8,...,U+2f9f4",
"[1]": "U+1f235-1f23b,...,U+25ed8",
...
"[119]": "U+20,...,U+ff0e"
},
"variants": {
"100": {
"normal": {
"[0]": {
"url": {
"woff2": "https://fonts.gstatic.com/s/notosansjp/v42/-F6ofjtqLzI2JPCgQBnw7HFQoggPkENvl4B0ZLgOquiXidBa3qHiDcp2RQ.0.woff2",
"woff": "https://fonts.gstatic.com/s/notosansjp/v42/-F62fjtqLzI2JPCgQBnw7HFoxQII2lcnk-AFfrgQrvWXpdFg3KXxAMsKMbdN.0.woff",
"opentype": "https://fonts.gstatic.com/s/notosansjp/v42/-F6ofjtqLzI2JPCgQBnw7HFQoggM.otf"
}
},
...,
},
...,
}
}
}
...
}
Scrapes the Google Fonts directory and uses the Google Fonts API to generate all the relevant axis definitions and download variant metadata. You can learn more variable font axis' here.
There are 3 default variants:
wght
- Only links to font files that only have thewght
axis.standard
- A default set of fonts that includeswght, wdth, slnt, opsz
axis' if available.full
- Links to font files that have all the axis' included within them.
Furthermore, a variant is generated for each unique axis in the font, e.g. if wdth
exists, variants.wdth.normal.latin
will exist. Note that the wght
axis is also included in each unique custom variant.
Note that standard
or full
variants may not exist if there are no relevant axes in the font for that classification. This is to prevent duplicate variants with different names.
Exports data/variable.json
.
{
...
"akshar": {
"family": "Akshar",
"id": "akshar",
"axes": {
"wght": { "default": "400", "min": "300", "max": "700", "step": "1" }
},
"variants": {
"wght": {
"normal": {
"devanagari": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCpR5lhOc.woff2",
"latin-ext": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCqh5lhOc.woff2",
"latin": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCpB5l.woff2"
}
},
"full": {
"normal": {
"devanagari": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCpR5lhOc.woff2",
"latin-ext": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCqh5lhOc.woff2",
"latin": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCpB5l.woff2"
}
},
"standard": {
"normal": {
"devanagari": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCpR5lhOc.woff2",
"latin-ext": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCqh5lhOc.woff2",
"latin": "https://fonts.gstatic.com/s/akshar/v5/Yq6V-LyHWTfz9rGCpB5l.woff2"
}
}
}
},
...
}
Note that certain fonts such as Inter or Recursive have the SLNT axis, meaning their font-style
in CSS won't be normal
or italic
on property full
but oblique x deg x deg
. Refer to the CSS test fixture for Recursive. While still showing as normal
in metadata, it is up to the developer to include the oblique
style if they are generating CSS using the min
and max
values from recursive.axes.slnt
property.
These are arrays of generated objects from the npx gfm generate [key]
command. It is unlikely you will use this.
import { APIDirect, APIVariableDirect } from "google-font-metadata";
const { APIDirect, APIVariableDirect } = require("google-font-metadata");
Exports data/api-response.json
and data/variable-response.json
respectively.
Scrapes the Google Fonts Attribution page and returns a readable object.
{
...
"abel": {
"id": "abel",
"authors": {
"copyright": "Copyright 2011, Matthew Desmond with Reserved Font Name Abel.",
"website": "http://www.madtype.com",
"email": "mattdesmond@gmail.com"
},
"license": {
"type": "SIL Open Font License, 1.1",
"url": "http://scripts.sil.org/OFL"
},
"original": "Copyright (c) 2011, Matthew Desmond (http://www.madtype.com | mattdesmond@gmail.com), with Reserved Font Name Abel."
},
...
}
Exports data/licenses.json
{
...
{
"name": "Thick Stroke",
"tag": "XOPQ",
"min": -1000,
"max": 2000,
"default": 88,
"precision": 0
},
...
}
Exports data/axis-registry.json
You can refer to src/index.ts
and src/data.ts
to see all exports.
You can use the gfm
CLI tool to update the metadata with fresh results from the Google APIs.
npx gfm generate [key]
- Fetches the default Google Fonts API which can be used for parsing later. This has to be called before npx gfm parse
.
Flags:
-n, --normal
- Only fetch the normal Google Developer API for APIv1 and APIv2.-v, --variable
- Only scrape the variable axis page for APIVariable. Notekey
does not need to be given if this option is passed.
You are able to get a Google Fonts API key
value from here. Alternatively, you can use a .env
file with API_KEY=keyvalue
instead of providing a key argument in the command.
npx gfm parse
- Parses through the Google Fonts CSS API and generate full metadata using the generate
command data.
Flags:
-1, --v1
- Only parse and update APIv1.-2, --v2
- Only parse and update APIv2.-v, --variable
- Only parse and update APIVariable.-l, --license
- Only parse and update APILicense.-f, --force
- This skips the cache and force parses every font.--no-validate
- This skips invokingnpx gfm validate
after finishing parsing.
npx gfm validate
- Helper command to validate your existing metadata with a schema. This is automatically invoked with npx gfm parse
.
Flags:
-1, --v1
- Only validate APIv1.-2, --v2
- Only validate APIv2.-v, --variable
- Only validate APIVariable.