我需要通过 Google Fonts API 获取所有可用可变字体的列表。
我可以从这个端点得到所有的字体名称。您可以添加一些参数,但我认为其中不包括可变字体过滤器: https://www.googleapis.com/webfonts/v1/webfonts?key=API_KEY&sort=popularity
我不认为我可以在进行 API 调用后过滤结果。这里“Open Sans”是一种可变字体,但我在响应中没有看到任何表明这一点的内容。
{
"family": "Open Sans",
"variants": [
"300",
"regular",
"500",
"600",
"700",
"800",
"300italic",
"italic",
"500italic",
"600italic",
"700italic",
"800italic"
],
"subsets": [
"cyrillic",
"cyrillic-ext",
"greek",
"greek-ext",
"hebrew",
"latin",
"latin-ext",
"vietnamese"
],
"version": "v34",
"lastModified": "2022-09-22",
"files": {
"300": "http://fonts.gstatic.com/s/opensans/v34/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0C4nY1M2xLER.ttf",
"regular": "http://fonts.gstatic.com/s/opensans/v34/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0C4nY1M2xLER.ttf",
"500": "http://fonts.gstatic.com/s/opensans/v34/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjr0C4nY1M2xLER.ttf",
"600": "http://fonts.gstatic.com/s/opensans/v34/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1y4nY1M2xLER.ttf",
"700": "http://fonts.gstatic.com/s/opensans/v34/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsg-1y4nY1M2xLER.ttf",
"800": "http://fonts.gstatic.com/s/opensans/v34/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgshZ1y4nY1M2xLER.ttf",
"300italic": "http://fonts.gstatic.com/s/opensans/v34/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkaVcUwaERZjA.ttf",
"italic": "http://fonts.gstatic.com/s/opensans/v34/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkaVcUwaERZjA.ttf",
"500italic": "http://fonts.gstatic.com/s/opensans/v34/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk_RkaVcUwaERZjA.ttf",
"600italic": "http://fonts.gstatic.com/s/opensans/v34/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjaVcUwaERZjA.ttf",
"700italic": "http://fonts.gstatic.com/s/opensans/v34/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkyFjaVcUwaERZjA.ttf",
"800italic": "http://fonts.gstatic.com/s/opensans/v34/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk0ZjaVcUwaERZjA.ttf"
},
"category": "sans-serif",
"kind": "webfonts#webfont"
},
有this网站列出了所有可变字体。在开发者控制台(F12)我看到它正在下载这个文件:
https://fonts.google.com/metadata/fonts
此文件包含所有字体。如果字体的“轴”数组不为空,那么它肯定是可变字体。也许您必须检查其他属性名称(我对可变字体不太熟悉)。
如果您使用的是开放式开发者 API,您还需要设置参数
capability=VF
以检索轴数据:
https://www.googleapis.com/webfonts/v1/webfonts?capability=VF&sort=style&key=${apiKey}
其他功能选项:
WOFF2
:返回 woff2 字体文件 URLsCAPABILITY_UNSPECIFIED
:返回 ttf/truetype 字体文件 URLs您可以组合这些选项来获取 woff2 可变字体 URL 或 truetype:
https://www.googleapis.com/webfonts/v1/webfonts?capability=VF&capability=WOFF2&sort=style&key=${apiKey}
但是,这不会将列表过滤为仅可变字体。所以你需要手动过滤项目数组。
(async ()=>{
// fetch font JSON
let listObj = await (await fetch(dataAPIStatic)).json();
// filter items with axes properties
let itemsVariable = listObj.items.filter(item => item.axes && item.axes.length);
output.value= JSON.stringify(itemsVariable, null, ' ');
})()
/**
* static copy of
* https://www.googleapis.com/webfonts/v1/webfonts?capability=VF&capability=CAPABILITY_UNSPECIFIED&sort=style&key=APIKEY
*/
let dataAPIStatic = "https://cdn.jsdelivr.net/gh/herrstrietzel/fonthelpers@main/json/gfontsAPI.json";
(async() => {
// fetch font JSON
let listObj = await (await fetch(dataAPIStatic)).json();
// filter items with axes properties
let itemsVariable = listObj.items.filter(item => item.axes && item.axes.length);
output.value = JSON.stringify(itemsVariable, null, ' ');
})()
textarea {
width: 100%;
min-height: 75vh;
}
<textarea id="output"></textarea>
如果您没有 API 密钥,您也可以从上述元 URL https://fonts.google.com/metadata/fonts.
获取所需的数据但是您不能通过 javaScript fetch 直接检索数据,因为 URL 不允许跨源访问。
作为解决方法,您可以在本地或 cdn 上托管自己的副本。
显然,这个 JSON 不会自动更新,所以你需要偶尔更新一下文件。
元 JSON 也有一个不同的结构。
例如。轴的最小值和最大值存储如下:
{tag: 'wdth', min: 100.0, max: 200.0, defaultValue: 100.0}
{tag: 'wdth', start: 100, end: 200}
所以你需要调整你的过滤脚本。
let apiKey = "your APi key";
let dataAPIStatic = "https://cdn.jsdelivr.net/gh/herrstrietzel/fonthelpers@main/json/gfontsAPI.json";
let dataMetaStatic = "https://cdn.jsdelivr.net/gh/herrstrietzel/fonthelpers@main/json/gfontsMeta.json";
// set query options
let options = {
//apiKey: apiKey,
format: 'woff2',
variableFonts: true,
staticURL: dataAPIStatic
};
// init
(async() => {
let googleFontList = await getGoogleFontList(options);
// generate autofill
populateDatalist(datalistFonts, googleFontList)
// get URLs
let googleFontUrls = await getGoogleFontUrls(googleFontList);
showLinkList(cssUrls, googleFontUrls)
// filter fonts
inputFonts.addEventListener('change', async e => {
let family = e.currentTarget.value;
let familyQuery = family.replaceAll(' ', '+');
let item = googleFontList.filter(item => item.family === family);
// update links
googleFontUrls = await getGoogleFontUrls(item);
showLinkList(cssUrls, googleFontUrls)
});
})();
async function getGoogleFontList(options = {
format: 'woff2',
variableFonts: true,
staticURL: ''
}) {
let {
apiKey,
format,
variableFonts,
staticURL
} = options;
let capabilities = [];
if (variableFonts) {
capabilities.push('capability=VF');
}
if (format) {
let fontFormat = (format === 'ttf' || format === 'truetype') ? 'CAPABILITY_UNSPECIFIED' : 'WOFF2';
capabilities.push(`capability=${fontFormat}`);
}
let capabilityQuery = capabilities.join('&');
let apiURL = `https://www.googleapis.com/webfonts/v1/webfonts?${capabilityQuery}&sort=style&key=${apiKey}`;
// switch between API and static src
let useStatic = !apiKey ? true : false;
let listUrl = useStatic ? staticURL : apiURL;
console.log(listUrl);
// fetch font JSON
let listObj = await (await fetch(listUrl)).json();
let itemsVariable = listObj.items.filter(item => item.axes && item.axes.length);
console.log(itemsVariable);
console.log(listObj);
/**
* normalize meta formating
* and API structure
*/
let listTypeMeta = listObj.familyMetadataList ? true : false;
let items = listTypeMeta ? listObj.familyMetadataList : listObj.items;
// filter variable fonts
if (variableFonts) {
items = items.filter(item => item.axes && item.axes.length);
}
if (listTypeMeta && variableFonts) {
for (let i = 0; i < items.length; i++) {
let item = items[i];
let axes = item.axes;
for (let a = 0; a < axes.length; a++) {
let axis = axes[a];
item.axes[a] = {
tag: axis.tag,
start: axis.min,
end: axis.max,
defaultValue: axis.defaultValue
}
}
}
}
return items;
}
async function getGoogleFontUrls(googleFontList) {
let urls = [];
let gfontBase = `https://fonts.googleapis.com/css2?family=`;
googleFontList.forEach(font => {
let isVariable = font.axes && font.axes.length ? true : false;
//console.log(isVariable);
let family = font.family;
let familyQuery = family.replaceAll(' ', '+');
let queryPre = [];
if (isVariable) {
let axes = getAxes(font.axes);
query = getAxesQuery(axes);
urls.push(`${gfontBase}${familyQuery}:${query}`);
}
//static weights
else {
let fonts;
// normalize API style
if (!font.fonts) {
fonts = font.variants.map(weight => {
return weight === 'regular' ? '400' : weight.replaceAll('italic', 'i')
});
} else {
fonts = Object.keys(font.fonts);
}
let styles = [];
let italics = fonts.map(weight => {
return weight.includes('i') ? weight : ''
}).filter(Boolean);
let weights = fonts.map(weight => {
return !weight.includes('i') ? weight : ''
}).filter(Boolean);
if (italics.length) {
styles = fonts.map(weight => {
return weight.includes('i') === false ? `0,${weight.replaceAll('i', '')}` : `1,${weight.replaceAll('i', '')}`
}).filter(Boolean);
} else {
styles = fonts.map(weight => {
return `${weight}`
}).filter(Boolean);
}
if (italics.length && !queryPre.includes('ital')) {
queryPre.push('ital')
}
if (weights.length && !queryPre.includes('wght')) {
queryPre.push('wght')
}
query = queryPre.join(',') + '@' + styles.join(';')
urls.push(`${gfontBase}${familyQuery}:${query}`);
}
})
return urls;
}
function getAxes(axes) {
// sort axes alphabetically
axes = [axes.filter(item => item.tag.toLowerCase() === item.tag), axes.filter(item => item.tag.toUpperCase() === item.tag)].flat();
return axes;
}
function getAxesQuery(axes, useStaticMeta) {
let axesQuery;
// we need to switch between different formats: google met takes min and max keys
if (useStaticMeta) {
axesQuery = axes.map(val => {
return val.tag
}).join(',') + '@' + axes.map(val => {
return val.min + '..' + val.max
}).join(',');
} else {
axesQuery = axes.map(val => {
return val.tag
}).join(',') + '@' + axes.map(val => {
return val.start + '..' + val.end
}).join(',');
}
return axesQuery;
}
function showLinkList(target, urls) {
target.innerHTML = '';
let urlList = '';
urls.forEach(url => {
urlList += `<li class="liUrl"><a href="${url}">${url}</li>`;
})
target.insertAdjacentHTML('beforeend', urlList)
}
function populateDatalist(target, list) {
let fonts = list;
let datalistOptions = '';
fonts.forEach(font => {
let option = document.createElement('option');
//only VF
if (font.axes) {
datalistOptions += `<option >${font.family}</option>`;
}
});
target.insertAdjacentHTML('beforeend', datalistOptions);
}
textarea {
width: 100%;
min-height: 20em;
}
<h1>Get variable font CSS URLS</h1>
<p><input type="text" list="datalistFonts" id="inputFonts" placeholder="Search google font"></p>
<datalist id="datalistFonts">
</datalist>
<h3>CSS Urls</h3>
<ol id="cssUrls">
</ol>