Executando verificação de segurança...
1

Aproveitando a API do Google Fonts para Otimizar o Carregamento de Fontes

Como já havia comentado nas Dicas para um site nota 100.
O Google Fonts é amplamente conhecido e usado em muitos sistemas, mas você sabia que ele possui uma API aberta? Com a API do Google Fonts, é possível carregar apenas os estilos e caracteres específicos necessários para o seu site, como fontes em itálico, o que pode otimizar o carregamento.

No exemplo a seguir, você pode definir apenas os caracteres em itálico usados no seu site:

const base = "https://fonts.googleapis.com/css2"
const chars = 'Olá mundo';

const text = encodeURIComponent();
const italic = true;
const fontWeight = "400";
const family = `Roboto:${italic ? ("ital,wght@1," + fontWeight ) : ("wght@" + fontWeight)}`
const queryArgs = {
    display: "swap",
    text,
};
const url =  base + "?family=" + family + "&" + new URLSearchParams(queryArgs).toString();

//https://fonts.googleapis.com/css2?family=Roboto:ital,wght@1,400&display=swap&text=Ol%25C3%25A1mundo

Esse código gera o link para importar o font-face desejado:

@font-face {
  font-family: 'Roboto';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/l/font?kit={URL_PARA_ARQUIVO_DA_FONTE}) format('{FORMATO_DO_ARQUIVO}');
}

Para melhorar o carregamento da página, você pode adicionar o preconnect ao CDN do Google Fonts (https://fonts.gstatic.com) e usar preload para carregar o arquivo da fonte:

<link href="https://fonts.gstatic.com/l/font?kit={URL_PARA_ARQUIVO_DA_FONTE}" rel="preload" crossorigin="anonymous" as="font" type="font/{FORMATO_DO_ARQUIVO}" fetchpriority="high">

Extra : Como extrair as fontes de uma página estática a partir do style tag :

Abaixo uma pequena implementação simplificada (porém funcional) para extrair os caracteres e suas fontes do html estático utlizando a biblioteca Cheerio.

GoogleFontsClient.js

Este é o cliente para solicitar o font-face à API do Google Fonts e extrair o link do arquivo de fonte para realizar o preload e o CSS que será adicionado ao cabeçalho da página:

class GoogleFontsClient {
    static generateFontUrl(charset, fontFamily) {
        const fontUrls = [];

        Object.keys(charset).forEach((fontWeight) => {
            const text = charset[fontWeight];
            const baseChars = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split("");

            text.forEach((char) => {
                if (!baseChars.includes(char)) {
                    baseChars.push(char);
                }
            });

            let charSet = baseChars.join("");
            let chars = '';
            try {
                chars = encodeURIComponent(charSet.trim().replace(/[^\p{L}\p{N}\p{P}\p{Z}^$~0123456789]/gu, '').replace(/\s/g, ''));
            } catch (e) {
                chars = null;
            }

            if (chars.length > 0) {
                if (fontWeight.indexOf(':ital') !== -1) {
                    fontWeight = fontWeight.replace(":ital", "");
                    fontUrls.push('https://fonts.googleapis.com/css2?family=' + fontFamily + ':ital,wght@1,' + fontWeight + '&display=swap&text=' + chars);
                } else {
                    fontUrls.push('https://fonts.googleapis.com/css2?family=' + fontFamily + ':wght@' + fontWeight + '&display=swap&text=' + chars);
                }
            }
        });
        return fontUrls;
    }

    static async getMinifiedFont(charset, fontFamily) {
        if (!fontFamily) {
            return [];
        }
        fontFamily = fontFamily.replaceAll('"', '').replaceAll(" ", "+");

        if (charset.length === 0) {
            return [];
        }
        const fontUrls = GoogleFontsClient.generateFontUrl(charset, fontFamily);

        const fontFaces = [];
        for (const url of fontUrls) {
            const { css, preload } = await GoogleFontsClient.downloadFont(url);
            if (!css) continue;
            fontFaces.push({ url, css, preload });
        }

        return fontFaces;
    }

    static async downloadFont(url) {
        const response = await fetch(url);

        if (!response.ok || response.status !== 200) {
            return {};
        }
        const text = await response.text();
        let regex = /https:\/\/fonts\.gstatic\.com\/[^\s)]+/g;
        let matches = text.match(regex);

        if (!matches) {
            return {
                css: text,
                preload: new Set()
            };
        }

        const uniqueMatches = Array.from(new Set(matches));

        return {
            css: text,
            preload: uniqueMatches
        };
    }
}

module.exports = { GoogleFontsClient };


FontExtractor.js

Esse arquivo extrai os caracteres a partir do atributo style de uma tag no HTML, verificando os elementos para identificar caracteres em itálico ou negrito:

const cheerio = require('cheerio');

class FontExtractor {
  static async extract(htmlContent, { defaultFontFamily = 'Roboto' }) {
    const $ = cheerio.load(htmlContent);
    const fontStyles = {};

    $('*').each((i, tag) => {
      if (tag.attribs.style && tag.attribs.style.indexOf('font-family') !== -1) {
        const styleRules = FontExtractor.parseRules(tag.attribs.style.trim());
        if (styleRules) {
          FontExtractor.fetchFonts(fontStyles, styleRules, $, tag);
        }
      }
    });

    return FontExtractor.formatFontStyles(fontStyles);
  }

  static parseRules(rules) {
    return rules.split(';').reduce((obj, rule) => {
      const [key, value] = rule.split(': ').map(rule => rule.trim());
      obj[key] = value;
      return obj;
    }, {});
  }

  static fetchFonts(fontStyles, styleRules, $, tag, text = '') {
    const recursiveHandler = (child) => {
      const children = $(child).children().toArray();

      if (children.length > 0) {
        children.forEach(rChild => recursiveHandler(rChild));
      }

      let fontFamily = styleRules['font-family'];
      let italicChars = '';
      let boldChars = '';

      if (child.name === 'em' || $(child).attr('style')?.includes('italic')) {
        italicChars = $(child).text() + text.replace(/\s/g, '');
      }

      if (child.name === 'strong' || $(child).attr('style')?.includes('bold')) {
        boldChars = $(child).text() + text.replace(/\s/g, '');
      }

      const isNormal = !(italicChars || boldChars);

      if (italicChars) {
        FontExtractor.getFonts(fontStyles, { 'font-family': fontFamily }, italicChars, 'italic');
      }

      if (boldChars) {
        FontExtractor.getFonts(fontStyles, { 'font-family': fontFamily }, boldChars, 'bold');
      }

      if (isNormal) {
        FontExtractor.getFonts(fontStyles, { 'font-family': fontFamily }, $(child).text() + text.replace(/\s/g, ''), 'normal');
      }
    };

    recursiveHandler(tag);
  }

  static getFonts(fontStyles, styleRules, text, type = 'normal') {
    const fontFamily = styleRules['font-family'];
    const textChars = new Set(text.trim().replace(/\s/g, '').split(""));

    if (!fontStyles[fontFamily]) {
      fontStyles[fontFamily] = {
        normal: new Set(),
        bold: new Set(),
        italic: new Set(),
        'bold-italic': new Set(),
      };
    }

    for (const char of textChars) {
      fontStyles[fontFamily][type].add(char);
    }
  }

  static formatFontStyles(fontStyles) {
    return Object.keys(fontStyles).map(fontFamily => ({
      charset: {
        '400': Array.from(fontStyles[fontFamily].normal),
        '700': Array.from(fontStyles[fontFamily].bold),
        '400:ital': Array.from(fontStyles[fontFamily].italic),
        '700:ital': Array.from(fontStyles[fontFamily]['bold-italic']),
      },
      source: 'google-fonts',
      fontFamily,
    }));
  }
}

module.exports = { FontExtractor };

index.js

const { GoogleFontsClient } = require('./GoogleFontsClient');
const { FontExtractor } = require('./FontExtractor');
const fonts = FontExtractor.extract(htmlString);

 for (const font of fonts) {
    const {charset, fontFamily} = font;
    const data = await GoogleFontsClient.getMinifiedFont(charset, fontFamily);
    result.push(data);
};

package.json


{
  "name": "google-fonts-extractor",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "author": "[email protected]",
  "dependencies": {
    "cheerio": "^1.0.0-rc.12",
  }
}
Carregando publicação patrocinada...