Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firefox returning empty string for font when using getComputedStyle

I've written a utility function for getting the pixel width of a string. I want the function to have the option of using the font set on a particular element, so whatever that element has been styled to, I'll be working with measurements in that font automatically.

The problem is that Firefox doesn't want to tell me what font an element is using. The following code is working fine for other browsers:

export function getTextWidth(items: string | string[], font: string | HTMLElement, fallbackFont?: string): number {
  const canvas = ((getTextWidth as any).canvas as HTMLCanvasElement ||
                  ((getTextWidth as any).canvas = document.createElement('canvas') as HTMLCanvasElement));
  const context = canvas.getContext('2d');
  let maxWidth = 0;

  if (typeof font === 'string')
    context.font = (font ? font : 'normal 12px sans-serif');
  else if (typeof font === 'object') {
    const elementFont = window.getComputedStyle(font).getPropertyValue('font');

    if (elementFont)
      context.font = elementFont;
    else if (fallbackFont)
      context.font = fallbackFont;
    else
      context.font = 'normal 12px sans-serif';
  }

  if (!Array.isArray(items))
    items = [items];

  for (const item of items) {
    const width = context.measureText(item).width;
    maxWidth = Math.max(maxWidth, width);
  }

  return maxWidth;
}

The problem is that window.getComputedStyle(font).getPropertyValue('font') is returning an empty string on Firefox, so I can't set the context to a matching font so that measureText works correctly.

I added the optional argument to my function fallbackFont, so I could pass an explicit font to fall back upon, but that's not a very satisfactory solution.

like image 683
kshetline Avatar asked Oct 20 '25 02:10

kshetline


2 Answers

It's a bit of a pain to have to get the font this way, but I discovered that Firefox will return individual aspects of an element's current font, such as font-size and font-family, separately. They can all be queried and assembled into a single font string:

  if (typeof font === 'string')
    context.font = (font ? font : 'normal 12px sans-serif');
  else if (typeof font === 'object') {
    let style = window.getComputedStyle(font);
    let elementFont = style.getPropertyValue('font');

    if (elementFont)
      context.font = elementFont;
    else {
      const fontStyle = style.getPropertyValue('font-style');
      const fontVariant = style.getPropertyValue('font-variant');
      const fontWeight = style.getPropertyValue('font-weight');
      const fontSize = style.getPropertyValue('font-size');
      const fontFamily = style.getPropertyValue('font-family');

      elementFont = (fontStyle + ' ' + fontVariant + ' ' + fontWeight + ' ' + fontSize + ' ' + fontFamily)
        .replace(/ +/g, ' ').trim();

      if (elementFont)
        context.font = elementFont;
      else if (fallbackFont)
        context.font = fallbackFont;
      else
        context.font = 'normal 12px sans-serif';
    }
  }
like image 120
kshetline Avatar answered Oct 21 '25 15:10

kshetline


Here's the function I'm using to safely retrieve the .font property from the object returned by getComputedStyle(). Tested in Chrome/Safari/Firefox. Notice that I also convert Firefox's percentage values for font-stretch to keywords because percentage values don't seem to work with Canvas for things like measureText().

function getFontFromComputedStyle (computedStyle) {
  let font = computedStyle.font;
  // Firefox returns the empty string for .font, so create the .font property manually
  if (font === '') {
    // Firefox uses percentages for font-stretch, but Canvas does not accept percentages
    // so convert to keywords, as listed at:
    //   https://developer.mozilla.org/en-US/docs/Web/CSS/font-stretch
    let fontStretchLookupTable = {
      '50%': 'ultra-condensed',
      '62.5%': 'extra-condensed',
      '75%': 'condensed',
      '87.5%': 'semi-condensed',
      '100%': 'normal',
      '112.5%': 'semi-expanded',
      '125%': 'expanded',
      '150%': 'extra-expanded',
      '200%': 'ultra-expanded'
    };
    // If the retrieved font-stretch percentage isn't found in the lookup table, use
    // 'normal' as a last resort.
    let fontStretch = fontStretchLookupTable.hasOwnProperty(computedStyle.fontStretch)
      ? fontStretchLookupTable[computedStyle.fontStretch]
      : 'normal';
    font = computedStyle.fontStyle
      + ' ' + computedStyle.fontVariant
      + ' ' + computedStyle.fontWeight
      + ' ' + fontStretch
      + ' ' + computedStyle.fontSize
      + '/' + computedStyle.lineHeight
      + ' ' + computedStyle.fontFamily;
  }
  return font;
}

Usage:

getFontFromComputedStyle(getComputedStyle(elem))
like image 40
colin moock Avatar answered Oct 21 '25 14:10

colin moock