I am trying to figure out how to take this Google Font API JSON metadata, which lists all the fonts like this:
{
"family": "ABeeZee",
"id": "abeezee",
"subsets": ["latin", "latin-ext"],
"weights": [400],
"styles": ["italic", "normal"],
"unicodeRange": {
"latin-ext": "U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF",
"latin": "U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD"
},
"variants": {
"400": {
"italic": {
"latin-ext": {
"url": {
"woff2": "https://fonts.gstatic.com/s/abeezee/v22/esDT31xSG-6AGleN2tCUnJ8DKpE.woff2",
"woff": "https://fonts.gstatic.com/s/abeezee/v22/esDT31xSG-6AGleN2tCUnJ8FOJKuGPLB.woff",
"truetype": "https://fonts.gstatic.com/s/abeezee/v22/esDT31xSG-6AGleN2tCklQ.ttf"
}
},
"latin": {
"url": {
"woff2": "https://fonts.gstatic.com/s/abeezee/v22/esDT31xSG-6AGleN2tCUkp8D.woff2",
"woff": "https://fonts.gstatic.com/s/abeezee/v22/esDT31xSG-6AGleN2tCUkp8FOJKuGA.woff",
"truetype": "https://fonts.gstatic.com/s/abeezee/v22/esDT31xSG-6AGleN2tCklQ.ttf"
}
}
},
"normal": {
"latin-ext": {
"url": {
"woff2": "https://fonts.gstatic.com/s/abeezee/v22/esDR31xSG-6AGleN2tukkIcH.woff2",
"woff": "https://fonts.gstatic.com/s/abeezee/v22/esDR31xSG-6AGleN2tuklpUEGpCeGQ.woff",
"truetype": "https://fonts.gstatic.com/s/abeezee/v22/esDR31xSG-6AGleN6tI.ttf"
}
},
"latin": {
"url": {
"woff2": "https://fonts.gstatic.com/s/abeezee/v22/esDR31xSG-6AGleN2tWkkA.woff2",
"woff": "https://fonts.gstatic.com/s/abeezee/v22/esDR31xSG-6AGleN2tWklpUEGpA.woff",
"truetype": "https://fonts.gstatic.com/s/abeezee/v22/esDR31xSG-6AGleN6tI.ttf"
}
}
}
}
},
"defSubset": "latin",
"lastModified": "2022-09-22",
"version": "v22",
"category": "sans-serif"
}
And convert it into a URL request using the CSS2 API, like this (e.g. for Crimson Pro Bold & Bold Italic):
https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,700;1,700
What I don't get is the exact meaning of the 0 and 1 in that URL, which is not present in the JSON metadata I linked to. How do I figure out what 0 or 1 value I need from the JSON?
This link https://fonts.google.com/knowledge/glossary/italic_axis says the spec defines the "ital" italic axis as defaulting to 0 (the minimum), maxing out at 1, with a step of 0.1. What does that even mean? Is 0 no italics and 1 is full italics?
If so, what does ital,wght@0,700;1,700
mean?
ital
and wght
, and two items on the right 0,700
and 1,700
.How do I figure out when I should add a 0 or 1 to the URL I'm going to generate from this JSON metadata?
It seems to get more complicated too, as per this example:
Recursive:slnt,wght,CASL,CRSV,MONO@-15..0,300..1000,0..1,0..1,0..1
So basically, I would like to know how to fetch any font properly using the CSS2 API, given the JSON metadata above. I only care about getting the 4 main fonts: regular, bold, italic, and bold italic, when they are available FYI.
In fact, the ital
query parameter can be considered a boolean value.
You can't interpolate between upright designs and true italics – so the documentation claiming there are intermediate steps of 0.1 is wrong – it is not a proper design axis like wght
, wdth
or slnt
. Variable fonts also allow more fine grained controls (if meticulously defined by the font designer) to get a intermediate design: so it's not just simple linear interpolation (... but most of the time it is based on control point interpolation).
font-style
and font-weight
(in CSS lingo) are grouped and separated by a ;
(semicolon)font-style
and font-weight
are separated by a ,
(comma)ital,wght@0,700;1,700
translates to load font in:
Your second example queries a variable font – note the ..
separators to define a axis range
Recursive:slnt,wght,CASL,CRSV,MONO@-15..0,300..1000,0..1,0..1,0..1
You can interpolate in a "slant" axis which has a similar effect (also commonly referred to as "oblique" – the main geometry is retained of a glyph). True italics however are separate font files - that's why you need separate tuples.
You may have a look at this example generating google font API queries for variable (if available) and static fonts:
const baseUrl = `https://fonts.googleapis.com/css2?family=`;
let fontAPiJson =
"https://cdn.jsdelivr.net/gh/herrstrietzel/fonthelpers@main/json/gfontsMeta.json";
// default selection
let fontFamily = "Open Sans";
(async() => {
let fetched = await fetch(fontAPiJson);
// convert tp parsable JSON
let metaTxt = await (await fetched).text();
let fontItems = JSON.parse("[" + metaTxt + "]")[0].familyMetadataList;
//console.log(fontItems)
/**
* populate font list
*/
populateFontList(fontItems);
function populateFontList(fontItems) {
let options = "";
fontItems.forEach((font) => {
options += `<option>${font.family}</option>`;
});
fontsDataOptions.innerHTML = options;
}
/**
* update fonts
*/
inputFont.addEventListener("input", (e) => {
fontFamily = e.currentTarget.value;
getFontItemUrls(fontItems, fontFamily);
});
/**
* init
*/
inputFont.value = fontFamily;
inputFont.dispatchEvent(new Event("input"));
//console.log(fontItems )
function getFontItemUrls(fontItems, fontFamily) {
let fontItem = fontItems.filter((item) => item.family === fontFamily)[0];
let googleQueryParams = getGoogleFontQueryMeta(fontItem);
let urlStatic = googleQueryParams.static ?
baseUrl + googleQueryParams.static :
"";
let urlVariable = googleQueryParams.variable ?
baseUrl + googleQueryParams.variable :
"";
a_meta.href = urlVariable;
a_meta.textContent = urlVariable;
a_meta_static.href = urlStatic;
a_meta_static.textContent = urlStatic;
}
})();
/**
* compatible with
* https://fonts.google.com/metadata/fonts
*/
function getGoogleFontQueryMeta(fontItem, variable = true) {
//console.log(item);
let fontFamily = fontItem.family;
let fontfamilyQuery = fontFamily.replaceAll(" ", "+");
// prepended tuples
let queryPre = [];
let queryParams = {
variable: "",
static: ""
};
// count weights
let styles = Object.keys(fontItem.fonts);
let weightsItalic = [];
let weightsRegular = [];
styles.forEach((style) => {
if (style.includes("i")) {
weightsItalic.push(parseFloat(style));
} else {
weightsRegular.push(parseFloat(style));
}
});
// is variable
let axes = fontItem.axes;
let isVF = axes && axes.length ? true : false;
if (isVF) {
//console.log(axes, ranges)
// sort axes alphabetically - case sensitive ([a-z],[A-Z])
axes = [
axes.filter((item) => item.tag.toLowerCase() === item.tag),
axes.filter((item) => item.tag.toUpperCase() === item.tag)
].flat();
let ranges = axes.map((val) => {
return val.min + ".." + val.max;
});
// italic and regular
if (weightsItalic.length && weightsRegular.length) {
queryPre.push("ital");
rangeArr = [];
for (let i = 0; i < 2; i++) {
rangeArr.push(`${i},${ranges.join(",")}`);
}
}
// only italic
else if (weightsItalic.length && !weightsRegular.length) {
queryPre.push("ital");
rangeArr = [];
rangeArr.push(`${1},${ranges.join(",")}`);
}
// only regular
else {
rangeArr = [];
rangeArr.push(`${ranges.join(",")}`);
}
// add axes tags to pre query string
axes.map((val) => {
return queryPre.push(val.tag);
});
queryParams.variable =
fontfamilyQuery +
":" +
queryPre.join(",") +
"@" +
rangeArr.join(";") +
"&display=swap";
}
/**
* get static
*/
queryPre = [];
if (weightsItalic.length) {
queryPre.push("ital");
queryPre.push("wght");
} else if (!weightsItalic.length && weightsRegular.length) {
queryPre.push("wght");
}
let query = queryPre.join(",") + "@";
// italic and regular
if (weightsItalic.length && weightsRegular.length) {
query +=
weightsRegular
.map((val) => {
return "0," + val;
})
.join(";") +
";" +
weightsItalic
.map((val) => {
return "1," + val;
})
.join(";");
}
// only italic
else if (weightsItalic.length && !weightsRegular.length) {
query += weightsItalic
.map((val) => {
return "1," + val;
})
.join(";");
}
// only regular
else {
query += weightsRegular
.map((val) => {
return val;
})
.join(";");
}
// generate URL query
queryParams.static = fontfamilyQuery + ":" + query + "&display=swap";
return queryParams;
}
body {
font-family: sans-serif
}
legend {
font-weight: bold;
}
input {
width: 100%
}
a {
word-break: break-all
}
<fieldset>
<legend>Get font CSS </legend>
<input id="inputFont" type="text" list="fontsDataOptions" placeholder="Search for google font">
<datalist id="fontsDataOptions"></datalist>
</fieldset>
<p><strong>Variable font URL: </strong>
<a id="a_meta" href=""></a>
</p>
<p><strong>Static font URL:</strong>
<a id="a_meta_static" href=""></a>
</p>
In the above example I'm using a static copy of the meta json data - so it's not up-to-date.
Worth noting axis identifiers must be ordered alpha-numerically so [a-z][A-Z] [0-9]
https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,400;0,700;1,400;1,700
works!
https://fonts.googleapis.com/css2?family=Crimson+Pro:ital,wght@0,700;0,400;1,400;1,700
doesn't work!
So the API query structure is quite unforgiving.
Frankly, I prefer the quite similar approach using the developers API. as it also gives you the ability to get truetype fonts or ignore variable fonts.