VulpesOS/gaia/shared/js/font_size_utils.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

198 lines
6.7 KiB
JavaScript
Executable file

/* jshint -W083 */
(function(exports) {
'use strict';
/**
* Utility functions for measuring and manipulating font sizes
*/
var FontSizeUtils = {
/**
* Keep a cache of canvas contexts with a given font.
* We do this because it is faster to create new canvases
* than to re-set the font on existing contexts repeatedly.
*/
_cachedContexts: {},
/**
* Grab or create a cached canvas context for a given fontSize/family pair.
* @todo Add font-weight as a new dimension for caching.
*
* @param {Integer} fontSize The font size of the canvas we want.
* @param {String} fontFamily The font family of the canvas we want.
* @param {String} fontStyle The style of the font (default to italic).
* @return {CanvasRenderingContext2D} A context with the specified font.
*/
_getCachedContext: function(fontSize, fontFamily, fontStyle) {
// Default to italic style since this code is only ever used
// by headers right now and header text is always italic.
fontStyle = fontStyle || 'italic';
var cache = this._cachedContexts;
var ctx = cache[fontSize] && cache[fontSize][fontFamily] ?
cache[fontSize][fontFamily][fontStyle] : null;
if (!ctx) {
var canvas = document.createElement('canvas');
canvas.setAttribute('moz-opaque', 'true');
canvas.setAttribute('width', '1');
canvas.setAttribute('height', '1');
ctx = canvas.getContext('2d', { willReadFrequently: true });
ctx.font = fontStyle + ' ' + fontSize + 'px ' + fontFamily;
// Populate the contexts cache.
if (!cache[fontSize]) {
cache[fontSize] = {};
}
if (!cache[fontSize][fontFamily]) {
cache[fontSize][fontFamily] = {};
}
cache[fontSize][fontFamily][fontStyle] = ctx;
}
return ctx;
},
/**
* Clear any current canvas contexts from the cache.
*/
resetCache: function() {
this._cachedContexts = {};
},
/**
* Get the width of a string in pixels, given its fontSize and fontFamily.
*
* @param {String} string The string we are measuring.
* @param {Integer} fontSize The size of the font to measure against.
* @param {String} fontFamily The font family to measure against.
* @param {String} fontStyle The style of the font (default to italic).
* @return {Integer} The pixel width of the string with the given font.
*/
getFontWidth: function(string, fontSize, fontFamily, fontStyle) {
var ctx = this._getCachedContext(fontSize, fontFamily, fontStyle);
return ctx.measureText(string).width;
},
/**
* Get the maximum allowable fontSize for a string such that it will
* not overflow past a maximum width.
*
* @param {String} string The string for which to check max font size.
* @param {Array} allowedSizes A list of fontSizes allowed.
* @param {String} fontFamily The font family of the string we're measuring.
* @param {Integer} maxWidth The maximum number of pixels before overflow.
* @return {Object} Dict containing fontSize, overflow and textWidth.
*/
getMaxFontSizeInfo: function(string, allowedSizes, fontFamily, maxWidth) {
var fontSize;
var resultWidth;
var i = allowedSizes.length - 1;
do {
fontSize = allowedSizes[i];
resultWidth = this.getFontWidth(string, fontSize, fontFamily);
i--;
} while (resultWidth > maxWidth && i >= 0);
return {
fontSize: fontSize,
overflow: resultWidth > maxWidth,
textWidth: resultWidth
};
},
/**
* Last result of the overflow count computation. Every time we recompute
* the overflow count we start from this value; if the string is larger
* than the previous one this will cut down the number of computations, if
* not we restart from 0.
*/
_overflowCountCache: -1,
/**
* Get the amount of characters truncated from overflow ellipses.
*
* @param {String} string The string for which to check max font size.
* @param {Integer} fontSize The font size of the string we are measuring.
* @param {String} fontFamily The font family of the string we're measuring.
* @param {Integer} maxWidth The maximum number of pixels before overflow.
*/
getOverflowCount: function(string, fontSize, fontFamily, maxWidth) {
var substring;
var resultWidth;
var overflowCount = -1;
// If the new string is longer than the cached result of the previous
// call, and if the calculation yields a coherent result then start from
// the cached value to speed up the calculation. */
if (string.length > this._overflowCountCache) {
substring = string.substr(0, string.length - this._overflowCountCache);
resultWidth = this.getFontWidth(substring, fontSize, fontFamily);
if (resultWidth > maxWidth) {
overflowCount = this._overflowCountCache;
}
}
do {
overflowCount++;
substring = string.substr(0, string.length - overflowCount);
resultWidth = this.getFontWidth(substring, fontSize, fontFamily);
} while (substring.length > 0 && resultWidth > maxWidth);
this._overflowCountCache = overflowCount;
return overflowCount;
},
/**
* Get an element's content width disregarding its box model sizing.
*
* @param {HTMLElement|Object} HTML element, or style object.
* @returns {Number} width in pixels of elements content.
*/
getContentWidth: function(style) {
var width = parseInt(style.width, 10);
if (style.boxSizing === 'border-box') {
width -= (parseInt(style.paddingRight, 10) +
parseInt(style.paddingLeft, 10));
}
return width;
},
/**
* Get an element's style properies.
*
* @param {HTMLElement} element The element from which to fetch style.
* @return {Object} A dictionary containing element's style properties.
*/
getStyleProperties: function(element) {
var style = window.getComputedStyle(element);
var contentWidth = this.getContentWidth(style);
if (isNaN(contentWidth)) {
contentWidth = 0;
}
return {
fontFamily: style.fontFamily,
contentWidth: contentWidth,
paddingRight: parseInt(style.paddingRight, 10),
paddingLeft: parseInt(style.paddingLeft, 10),
offsetLeft: element.offsetLeft
};
},
/**
* Cache and return the width of the inner window.
*
* @return {Integer} The width of the inner window in pixels.
*/
getWindowWidth: function() {
return window.innerWidth;
}
};
exports.FontSizeUtils = FontSizeUtils;
}(this));