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.
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';
}
}
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With