How to get filename of the rendered font CSS?

87 Views Asked by At

Is it possible to get the filename of the rendered font of an element?

Example:

@font-face {
  font-family: 'MyFont';
  src: url('../../public/fonts/MyFont-Bold.woff2') format('woff2');
  font-weight: bold;
  font-style: normal;
}

@font-face {
  font-family: 'MyFont';
  src: url('../../public/fonts/MyFont-Italic.woff2') format('woff2');
  font-weight: normal;
  font-style: italic;
}

.my-font-bold {
 font-family: 'MyFont';
 font-weight: bold;
 font-style: normal;
}

<p className="my-font-bold"></p>

In this example I would like to get ../../public/fonts/MyFont-Bold.woff2 of the element (because it has font-weight bold and font-family = "MyFont" thanks to the className "my-font-bold").

PS: I would like to do this to make font Vitest tests.

CodeSandbox: https://codesandbox.io/p/sandbox/magical-flower-q87vz7?file=%2Fapp%2Ffont.test.tsx%3A27%2C2

1

There are 1 best solutions below

0
mandy8055 On

In Javascript it is definitely possible. You just need to leverage the getComputedStyle to achieve that.

Steps to achieve the requirement:

  1. Get the font name of the font that p tag is using.
  2. Get all the css rules associated with the document as an array.
  3. Filter out the font-family based on step 1.
  4. If the fontFaceRule exists, extract the url and return the result else return null.
  5. Post this you can write the test for this helper function in jest.

Code implementation:

function getRenderedFontUrl(element) {
  const fontFamily = window.getComputedStyle(element).getPropertyValue('font-family').replace(/['"]/g, '');
  const cssRules = Array.from(document.styleSheets).flatMap(sheet => Array.from(sheet.cssRules));

  const fontFaceRule = cssRules.find(rule => {
    return rule.type === CSSRule.FONT_FACE_RULE && rule.style.getPropertyValue('font-family') === fontFamily;
  });
  

  if (fontFaceRule) {
    const src = fontFaceRule.style.getPropertyValue('src');
    const urlRegex = /url\((['"]?)(.*?)\1\)/g;
    const match = urlRegex.exec(src);
    return match ? match[2] : null;
  }

  return null;
}

// Usage example
const paragraphElement = document.querySelector('p');
const fontUrl = getRenderedFontUrl(paragraphElement);
console.log(fontUrl);
@font-face {
  font-family: 'MyFont';
  src: url('../../public/fonts/MyFont-Bold.woff2') format('woff2');
  font-weight: bold;
  font-style: normal;
}

@font-face {
  font-family: 'MyFont';
  src: url('../../public/fonts/MyFont-Italic.woff2') format('woff2');
  font-weight: normal;
  font-style: italic;
}

.my-font-bold {
 font-family: 'MyFont';
 font-weight: bold;
 font-style: normal;
}
<p style="font-family: 'MyFont'; font-weight: bold;">

Writing the test for the above function:

import { JSDOM } from 'jsdom';
import { getRenderedFontUrl } from './path-where-you-defined';

const { window } = new JSDOM();
global.window = window;
global.document = window.document;

describe('getRenderedFontUrl', () => {
  beforeEach(() => {
    document.head.innerHTML = `
      <style>
        @font-face {
          font-family: 'MyFont';
          src: url('../../public/fonts/MyFont.woff2') format('woff2');
          font-weight: 300;
          font-style: normal;
        }

        p {
          font-family: 'MyFont';
        }
      </style>
    `;
  });

  it('should return font URL for an element with', () => {
    const pElement = document.createElement('p');
    document.body.appendChild(pElement);

    const fontUrl = getRenderedFontUrl(pElement);
    expect(fontUrl).toBe('../../public/fonts/MyFont.woff2');
  });

  it('should return null for an element without', () => {
    const divElement = document.createElement('div');
    document.body.appendChild(divElement);

    const fontUrl = getRenderedFontUrl(divElement);
    expect(fontUrl).toBeNull();
  });
});