How to find the available font-weight in a custom font css file

241 Views Asked by At

I am working on a custom font feature where the user can upload a .css file and use it in our editor. In order to do show I need to know the font-weight present in the file, so that I can display them in the dropdown list in the editor.

Please suggest a best way to figure out the font-weight available for the custom font. As user will be uploading the file, therefore I wont be aware of the font-weights.

User Input:

/* latin */
@font-face {
  font-family: 'Poppins';
  font-style: italic;
  font-weight: 200;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/poppins/v20/pxiDyp8kv8JHgFVrJJLmv1pVF9eOYktMqg.woff2) format('woff2');
}
/* devanagari */
@font-face {
  font-family: 'Poppins';
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/poppins/v20/pxiDyp8kv8JHgFVrJJLm21lVFteOYktMqlap.woff2) format('woff2');
}
/* latin */
@font-face {
  font-family: 'Poppins';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrJJLucHtAOvWDSA.woff2) format('woff2');
}

Required Data: [200, 300, 400]

Looking for JS solution.

2

There are 2 best solutions below

0
Arif On BEST ANSWER

Thanks @herrstrietzel for the above solution.

Instead of creating a style tag with an id and then disabling it. I have used CSSStyleSheet(). Below is my code.

/**
 * this would be css content
 * retrieved via fileReader/input
 */
let cssText = `
 /* latin */
  @font-face {
    font-family: 'Poppins';
    font-style: italic;
    font-weight: 200;
    font-display: swap;
    src: url(https://fonts.gstatic.com/s/poppins/v20/pxiDyp8kv8JHgFVrJJLmv1pVF9eOYktMqg.woff2) format('woff2');
  }

  /* latin */
  @font-face {
    font-family: 'Poppins';
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrJJLucHtAOvWDSA.woff2) format('woff2');
  }

  /* latin-ext */
  @font-face {
    font-family: 'Agdasima';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url(https://fonts.gstatic.com/s/agdasima/v2/PN_0Rfyxp2f1fUCgAPCGgCza3v3xzHMj54Y.woff2) format('woff2');
  }
`;

let cssSheet = new CSSStyleSheet()
cssSheet.replaceSync(cssText)

let rules = [...cssSheet.cssRules]
let fontProps = []
rules.forEach((rule) => {
  let type = rule.type

  // type 5 is @font-face
  if (type === 5) {
    let fontFamily = rule.style.getPropertyValue("font-family")
    let fontStyle = rule.style.getPropertyValue("font-style")
    let fontWeight = rule.style.getPropertyValue("font-weight")
    fontProps.push({
      fontFamily: fontFamily,
      fontWeight: fontWeight,
      fontStyle: fontStyle
    })
  }
})
console.log(fontProps)
0
herrstrietzel On

One way to go would be to append your uploaded css text to the document stylesheets and retrieve all properties via document.styleSheets and cssRules properties.

Obviously, when appending a new stylesheet – we don't want to override the current app/page styles – so we need to disable the new stylesheet/style rules.

Example: populate <style> element and disable it

/**
 * this would be css content
 * retrieved via fileReader/input
 */
let cssText = `
 /* latin */
  @font-face {
    font-family: 'Poppins';
    font-style: italic;
    font-weight: 200;
    font-display: swap;
    src: url(https://fonts.gstatic.com/s/poppins/v20/pxiDyp8kv8JHgFVrJJLmv1pVF9eOYktMqg.woff2) format('woff2');
  }

  /* latin */
  @font-face {
    font-family: 'Poppins';
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrJJLucHtAOvWDSA.woff2) format('woff2');
  }

  body {
    font-family: Poppins;
    font-style: italic;
    background: red;
  }
`;

/**
 * populate disabled
 * style element with
 * new css rules for parsing
 */
cssTmp.textContent = cssText;

/**
 * get temporary stylesheet by id
 * and disable it
 * to prevent overriding the app's UI style
 */
let stylesheetTmp = [...document.styleSheets].filter((style) => {
  return style.ownerNode.id == "cssTmp";
})[0];

// disable stylesheet
stylesheetTmp.disabled = true;

/**
 * get @font-face rules
 */
let rules = [...stylesheetTmp.cssRules];
let fontProps = [];
rules.forEach((rule) => {
  let type = rule.type;

  // type 5 is @font-face
  if (type === 5) {
    let fontFamily = rule.style.getPropertyValue("font-family");
    let fontStyle = rule.style.getPropertyValue("font-style");
    let fontWeight = rule.style.getPropertyValue("font-weight");
    fontProps.push({
      fontFamily: fontFamily,
      fontWeight: fontWeight,
      fontStyle: fontStyle
    });
  }
});

console.log(fontProps);
<style id="cssTmp"></style>
<p>Test</p>

How it works

  1. create a <style> element with a unique ID e.g "cssTmp"
  2. Populate this element with the uploaded CSS content
    cssTmp.textContent = cssText
  3. Select the stylesheet by filtering all document stylesheets by the specified ID:
let stylesheetTmp = [...document.styleSheets].filter((style) => {
  return style.ownerNode.id == "cssTmp";
})[0];
  1. Disable this stylesheet:
stylesheetTmp.disabled = true;
  1. Loop through cssRules
    We first need to make the styles rules iterable by applying the spread operator or using Array.from() method.
let rules = [...stylesheetTmp.cssRules];  

@font-face rules can be filtered by their type – which is "5". Just add a console.log(rule) to inspect all data contained in the rule object.

Quite likely, you might need more property values including font styles (regular/italic) – we can collect this data in a previously defined array of objects.

Now you could populate a <select>'s options with the retrieved data.