列出 Google Fonts API 中的所有可变字体?

huangapple go评论103阅读模式
英文:

List all variable fonts from Google Fonts API?

问题

我需要获取通过Google Fonts API可用的所有可变字体的列表。

我可以从这个端点获取所有字体名称。有一些参数可以添加,但我认为其中没有可变字体过滤器:
https://www.googleapis.com/webfonts/v1/webfonts?key=API_KEY&sort=popularity

我不认为我可以在调用API后过滤结果。在这里,"Open Sans"是一个可变字体,但我在响应中看不到任何指示这一点的信息。

英文:

I need to get a list of all variable fonts available through the Google Fonts API.

I can get all the font names from this endpoint. There are some parameters you can add but I don't think that a variable font filter is among them:
https://www.googleapis.com/webfonts/v1/webfonts?key=API_KEY&sort=popularity

I don't think I can filter the results after making the API call. Here "Open Sans" is a variable font but I don't see anything to indicate this in the response.

{
      "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"
    },

答案1

得分: 1

有一个网站列出了所有的可变字体。在开发者控制台(F12)中,我看到它正在下载这个文件:

https://fonts.google.com/metadata/fonts

这个文件包含了所有的字体。如果一个字体的“axes”数组不为空,那么它肯定是一个可变字体。也许你需要检查其他属性名称(我对可变字体不是很熟悉)。

英文:

There is this site which lists all variable fonts. In the developer console (F12) I saw it downloading this file:

https://fonts.google.com/metadata/fonts

This file contains all fonts. If the "axes" array of a font is not empty then it's definitely a variable font. Maybe you have to check other property names (I am not that familiar with variable fonts).

答案2

得分: 1

开放API:添加能力参数“VF”

如果您使用开发者API,您还需要设置参数**capability=VF**以检索轴数据:

https://www.googleapis.com/webfonts/v1/webfonts?capability=VF&sort=style&key=${apiKey}

其他能力选项:

  • WOFF2:返回woff2字体文件URL
  • CAPABILITY_UNSPECIFIED:返回ttf/truetype字体文件URL

您可以结合这些选项获取woff2可变字体URL或truetype:

https://www.googleapis.com/webfonts/v1/webfonts?capability=VF&capability=WOFF2&sort=style&key=${apiKey}

然而,这不会将列表过滤为仅可变字体。因此,您需要手动过滤项目数组。

(async () => {
    // 获取字体JSON
    let listObj = await (await fetch(dataAPIStatic)).json();
    // 过滤带有轴属性的项目
    let itemsVariable = listObj.items.filter(item => item.axes && item.axes.length);
    output.value = JSON.stringify(itemsVariable, null, ' ');
})();

从元数据JSON检索数据

如果您没有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}

API

{tag: 'wdth', start: 100, end: 200}

因此,您需要调整您的过滤脚本。

示例:加载元数据或API数据助手

let apiKey = "您的API密钥";
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";

// 设置查询选项
let options = {
    // apiKey: apiKey,
    format: 'woff2',
    variableFonts: true,
    staticURL: dataAPIStatic
};

// 初始化
(async () => {
    let googleFontList = await getGoogleFontList(options);

    // 生成自动填充
    populateDatalist(datalistFonts, googleFontList);

    // 获取URL
    let googleFontUrls = await getGoogleFontUrls(googleFontList);
    showLinkList(cssUrls, googleFontUrls);

    // 过滤字体
    inputFonts.addEventListener('change', async e => {
        let family = e.currentTarget.value;
        let familyQuery = family.replaceAll(' ', '+');
        let item = googleFontList.filter(item => item.family === family);

        // 更新链接
        googleFontUrls = await getGoogleFontUrls(item);
        showLinkList(cssUrls, googleFontUrls);
    });
})();
英文:

Open API: add capability parameter "VF"

If you're using the open developer API, you also need to set the parameter capability=VF to retrieve axes data:

https://www.googleapis.com/webfonts/v1/webfonts?capability=VF&sort=style&key=${apiKey}

Other capability options:

  • WOFF2: returns woff2 font file URLs
  • CAPABILITY_UNSPECIFIED: returns ttf/truetype font file URLs

You can combine these options to get either woff2 variable font URLs or truetype:

https://www.googleapis.com/webfonts/v1/webfonts?capability=VF&capability=WOFF2&sort=style&key=${apiKey}

However, this won't filter the list to variable fonts only. So you need to filter the items array manually.

(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, ' ');
})()

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-js -->

/**
 * static copy of
 * https://www.googleapis.com/webfonts/v1/webfonts?capability=VF&amp;capability=CAPABILITY_UNSPECIFIED&amp;sort=style&amp;key=APIKEY
 */
let dataAPIStatic = &quot;https://cdn.jsdelivr.net/gh/herrstrietzel/fonthelpers@main/json/gfontsAPI.json&quot;;


(async() =&gt; {
  // fetch font JSON
  let listObj = await (await fetch(dataAPIStatic)).json();
  // filter items with axes properties
  let itemsVariable = listObj.items.filter(item =&gt; item.axes &amp;&amp; item.axes.length);
  output.value = JSON.stringify(itemsVariable, null, &#39; &#39;);

})()

<!-- language: lang-css -->

textarea {
  width: 100%;
  min-height: 75vh;
}

<!-- language: lang-html -->

&lt;textarea id=&quot;output&quot;&gt;&lt;/textarea&gt;

<!-- end snippet -->

Retrieve data from meta JSON

If you don't have an API key you can also get the required data from the aforementioned meta URL https://fonts.google.com/metadata/fonts.

But you can't retrieve the data directly via javaScript fetch as the URL doesn't allow cross origin access.

As a workaround you can host your own copy locally or on a cdn.
Obviously, this JSON won't update automatically, so you need to update the file once in a while.

The meta JSON also has a different structure.
E.g. axes min and max values are stored like this:

Meta

{tag: &#39;wdth&#39;, min: 100.0, max: 200.0, defaultValue: 100.0}

API

{tag: &#39;wdth&#39;, start: 100, end: 200}

So you need to adapt your filtering script.

Example: load meta or API data helper

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-js -->

let apiKey = &quot;your APi key&quot;;
let dataAPIStatic = &quot;https://cdn.jsdelivr.net/gh/herrstrietzel/fonthelpers@main/json/gfontsAPI.json&quot;;
let dataMetaStatic = &quot;https://cdn.jsdelivr.net/gh/herrstrietzel/fonthelpers@main/json/gfontsMeta.json&quot;;


// set query options
let options = {
  //apiKey: apiKey,
  format: &#39;woff2&#39;,
  variableFonts: true,
  staticURL: dataAPIStatic
};

// init
(async() =&gt; {
  let googleFontList = await getGoogleFontList(options);

  // generate autofill
  populateDatalist(datalistFonts, googleFontList)

  // get URLs
  let googleFontUrls = await getGoogleFontUrls(googleFontList);
  showLinkList(cssUrls, googleFontUrls)


  // filter fonts
  inputFonts.addEventListener(&#39;change&#39;, async e =&gt; {
    let family = e.currentTarget.value;
    let familyQuery = family.replaceAll(&#39; &#39;, &#39;+&#39;);
    let item = googleFontList.filter(item =&gt; item.family === family);

    // update links
    googleFontUrls = await getGoogleFontUrls(item);
    showLinkList(cssUrls, googleFontUrls)
  });
})();


async function getGoogleFontList(options = {
  format: &#39;woff2&#39;,
  variableFonts: true,
  staticURL: &#39;&#39;
}) {
  let {
    apiKey,
    format,
    variableFonts,
    staticURL
  } = options;

  let capabilities = [];
  if (variableFonts) {
    capabilities.push(&#39;capability=VF&#39;);
  }
  if (format) {
    let fontFormat = (format === &#39;ttf&#39; || format === &#39;truetype&#39;) ? &#39;CAPABILITY_UNSPECIFIED&#39; : &#39;WOFF2&#39;;
    capabilities.push(`capability=${fontFormat}`);
  }

  let capabilityQuery = capabilities.join(&#39;&amp;&#39;);
  let apiURL = `https://www.googleapis.com/webfonts/v1/webfonts?${capabilityQuery}&amp;sort=style&amp;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 =&gt; item.axes &amp;&amp; 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 =&gt; item.axes &amp;&amp; item.axes.length);
  }

  if (listTypeMeta &amp;&amp; variableFonts) {
    for (let i = 0; i &lt; items.length; i++) {
      let item = items[i];
      let axes = item.axes;
      for (let a = 0; a &lt; 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 =&gt; {
    let isVariable = font.axes &amp;&amp; font.axes.length ? true : false;
    //console.log(isVariable);
    let family = font.family;
    let familyQuery = family.replaceAll(&#39; &#39;, &#39;+&#39;);
    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 =&gt; {
          return weight === &#39;regular&#39; ? &#39;400&#39; : weight.replaceAll(&#39;italic&#39;, &#39;i&#39;)
        });
      } else {
        fonts = Object.keys(font.fonts);
      }

      let styles = [];
      let italics = fonts.map(weight =&gt; {
        return weight.includes(&#39;i&#39;) ? weight : &#39;&#39;
      }).filter(Boolean);
      let weights = fonts.map(weight =&gt; {
        return !weight.includes(&#39;i&#39;) ? weight : &#39;&#39;
      }).filter(Boolean);

      if (italics.length) {
        styles = fonts.map(weight =&gt; {
          return weight.includes(&#39;i&#39;) === false ? `0,${weight.replaceAll(&#39;i&#39;, &#39;&#39;)}` : `1,${weight.replaceAll(&#39;i&#39;, &#39;&#39;)}`
        }).filter(Boolean);
      } else {
        styles = fonts.map(weight =&gt; {
          return `${weight}`
        }).filter(Boolean);
      }

      if (italics.length &amp;&amp; !queryPre.includes(&#39;ital&#39;)) {
        queryPre.push(&#39;ital&#39;)
      }
      if (weights.length &amp;&amp; !queryPre.includes(&#39;wght&#39;)) {
        queryPre.push(&#39;wght&#39;)
      }

      query = queryPre.join(&#39;,&#39;) + &#39;@&#39; + styles.join(&#39;;&#39;)
      urls.push(`${gfontBase}${familyQuery}:${query}`);
    }
  })

  return urls;
}


function getAxes(axes) {
  // sort axes alphabetically
  axes = [axes.filter(item =&gt; item.tag.toLowerCase() === item.tag), axes.filter(item =&gt; 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 =&gt; {
      return val.tag
    }).join(&#39;,&#39;) + &#39;@&#39; + axes.map(val =&gt; {
      return val.min + &#39;..&#39; + val.max
    }).join(&#39;,&#39;);
  } else {
    axesQuery = axes.map(val =&gt; {
      return val.tag
    }).join(&#39;,&#39;) + &#39;@&#39; + axes.map(val =&gt; {
      return val.start + &#39;..&#39; + val.end
    }).join(&#39;,&#39;);
  }
  return axesQuery;
}


function showLinkList(target, urls) {
  target.innerHTML = &#39;&#39;;
  let urlList = &#39;&#39;;
  urls.forEach(url =&gt; {
    urlList += `&lt;li class=&quot;liUrl&quot;&gt;&lt;a href=&quot;${url}&quot;&gt;${url}&lt;/li&gt;`;
  })
  target.insertAdjacentHTML(&#39;beforeend&#39;, urlList)
}


function populateDatalist(target, list) {
  let fonts = list;
  let datalistOptions = &#39;&#39;;
  fonts.forEach(font =&gt; {
    let option = document.createElement(&#39;option&#39;);
    //only VF
    if (font.axes) {
      datalistOptions += `&lt;option &gt;${font.family}&lt;/option&gt;`;
    }
  });
  target.insertAdjacentHTML(&#39;beforeend&#39;, datalistOptions);
}

<!-- language: lang-css -->

textarea {
  width: 100%;
  min-height: 20em;
}

<!-- language: lang-html -->

&lt;h1&gt;Get variable font CSS URLS&lt;/h1&gt;

&lt;p&gt;&lt;input type=&quot;text&quot; list=&quot;datalistFonts&quot; id=&quot;inputFonts&quot; placeholder=&quot;Search google font&quot;&gt;&lt;/p&gt;
&lt;datalist id=&quot;datalistFonts&quot;&gt;
    &lt;/datalist&gt;

&lt;h3&gt;CSS Urls&lt;/h3&gt;
&lt;ol id=&quot;cssUrls&quot;&gt;
&lt;/ol&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年2月8日 20:35:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/75385876.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定