@ogs-gmbh/ngx-utils
Version:
A lightweight collection of utility functions and helpers for Angular applications
75 lines • 10.4 kB
JavaScript
import { Injectable } from '@angular/core';
import * as i0 from "@angular/core";
/**
* Builds a CSS font shorthand string from a computed style declaration.
*
* @param elementStyle - The computed CSS style of an element, containing font properties.
* @param options.isFontKey - If true, includes letter-spacing in the resulting string for use as a cache key.
* @returns A font shorthand string suitable for CanvasRenderingContext2D.font or as a unique font key.
*/
function getFontString(elementStyle, options = undefined) {
let fontString = `${elementStyle.fontStyle} ${elementStyle.fontVariant} ${elementStyle.fontWeight} ${elementStyle.fontSize}/${elementStyle.lineHeight} ${elementStyle.fontFamily}`;
if (options?.isFontKey) {
fontString += elementStyle.letterSpacing;
fontString.trim();
}
return fontString;
}
export class CanvasMeasurerService {
_canvas;
get canvas() {
if (this._canvas)
return this._canvas;
this._canvas = document.createElement("canvas");
return this._canvas;
}
_canvasContext = this.canvas.getContext("2d");
// Map of maps where each child-map represents a font and each child-map-entry is a string with the measured width
_stringWidthCache = new Map();
/**
* Measures text width for a given element style and string,
* caching results per font configuration.
*
* @param style - Computed CSS style of the element (font info).
* @param text - The string to measure.
* @returns The width in pixels, or undefined if measurement failed.
*/
getRenderedStringWidth(elementStyle, stringToMeasure) {
if (!elementStyle)
return undefined;
const fontString = getFontString(elementStyle, { isFontKey: true });
let fontMap = this._stringWidthCache.get(fontString);
if (!fontMap) {
fontMap = new Map();
this._stringWidthCache.set(fontString, fontMap);
}
let width = fontMap.get(stringToMeasure);
if (width)
return width;
if (this._canvasContext) {
this.setCanvasFont(elementStyle);
width = this._canvasContext.measureText(stringToMeasure).width;
fontMap.set(stringToMeasure, width);
return width;
}
return undefined;
}
setCanvasFont(elementStyle) {
if (this._canvasContext?.font) {
const fontString = getFontString(elementStyle);
if (this._canvasContext.font === fontString && this._canvasContext.letterSpacing === elementStyle.letterSpacing)
return;
this._canvasContext.font = fontString;
this._canvasContext.letterSpacing = elementStyle.letterSpacing;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CanvasMeasurerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CanvasMeasurerService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CanvasMeasurerService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FudmFzLW1lYXN1cmVyLXNlcnZpY2Uuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9kaXJlY3RpdmVzL3RleHQtY2xhbXAvc2VydmljZXMvY2FudmFzLW1lYXN1cmVyLXNlcnZpY2Uuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDOztBQUUzQzs7Ozs7O0dBTUc7QUFDSCxTQUFTLGFBQWEsQ0FBRSxZQUFpQyxFQUFFLFVBQStDLFNBQVM7SUFDakgsSUFBSSxVQUFVLEdBQVcsR0FBSSxZQUFZLENBQUMsU0FBVSxJQUFLLFlBQVksQ0FBQyxXQUFZLElBQUssWUFBWSxDQUFDLFVBQVcsSUFBSyxZQUFZLENBQUMsUUFBUyxJQUFLLFlBQVksQ0FBQyxVQUFXLElBQUssWUFBWSxDQUFDLFVBQVcsRUFBRSxDQUFDO0lBRXZNLElBQUksT0FBTyxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQ3ZCLFVBQVUsSUFBSSxZQUFZLENBQUMsYUFBYSxDQUFDO1FBQ3pDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNwQixDQUFDO0lBRUQsT0FBTyxVQUFVLENBQUM7QUFDcEIsQ0FBQztBQUtELE1BQU0sT0FBTyxxQkFBcUI7SUFDeEIsT0FBTyxDQUFnQztJQUUvQyxJQUFZLE1BQU07UUFDaEIsSUFBSSxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUV0QyxJQUFJLENBQUMsT0FBTyxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFaEQsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBQ3RCLENBQUM7SUFFTyxjQUFjLEdBQW9DLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRXZGLGtIQUFrSDtJQUMxRyxpQkFBaUIsR0FBcUMsSUFBSSxHQUFHLEVBQStCLENBQUM7SUFFckc7Ozs7Ozs7T0FPRztJQUNJLHNCQUFzQixDQUFFLFlBQTZDLEVBQUUsZUFBdUI7UUFDbkcsSUFBSSxDQUFDLFlBQVk7WUFBRSxPQUFPLFNBQVMsQ0FBQztRQUVwQyxNQUFNLFVBQVUsR0FBVyxhQUFhLENBQUMsWUFBWSxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFFNUUsSUFBSSxPQUFPLEdBQW9DLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFFdEYsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsT0FBTyxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO1lBQ3BDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ2xELENBQUM7UUFFRCxJQUFJLEtBQUssR0FBdUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUU3RCxJQUFJLEtBQUs7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUd4QixJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN4QixJQUFJLENBQUMsYUFBYSxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQ2pDLEtBQUssR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxlQUFlLENBQUMsQ0FBQyxLQUFLLENBQUM7WUFDL0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFFcEMsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBRUQsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVPLGFBQWEsQ0FBRSxZQUFpQztRQUN0RCxJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsSUFBSSxFQUFFLENBQUM7WUFDOUIsTUFBTSxVQUFVLEdBQVcsYUFBYSxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBRXZELElBQUksSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEtBQUssVUFBVSxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxLQUFLLFlBQVksQ0FBQyxhQUFhO2dCQUFFLE9BQU87WUFFeEgsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEdBQUcsVUFBVSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxHQUFHLFlBQVksQ0FBQyxhQUFhLENBQUM7UUFDakUsQ0FBQztJQUNILENBQUM7d0dBN0RVLHFCQUFxQjs0R0FBckIscUJBQXFCLGNBRnBCLE1BQU07OzRGQUVQLHFCQUFxQjtrQkFIakMsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbi8qKlxuICogQnVpbGRzIGEgQ1NTIGZvbnQgc2hvcnRoYW5kIHN0cmluZyBmcm9tIGEgY29tcHV0ZWQgc3R5bGUgZGVjbGFyYXRpb24uXG4gKlxuICogQHBhcmFtIGVsZW1lbnRTdHlsZSAtIFRoZSBjb21wdXRlZCBDU1Mgc3R5bGUgb2YgYW4gZWxlbWVudCwgY29udGFpbmluZyBmb250IHByb3BlcnRpZXMuXG4gKiBAcGFyYW0gb3B0aW9ucy5pc0ZvbnRLZXkgLSBJZiB0cnVlLCBpbmNsdWRlcyBsZXR0ZXItc3BhY2luZyBpbiB0aGUgcmVzdWx0aW5nIHN0cmluZyBmb3IgdXNlIGFzIGEgY2FjaGUga2V5LlxuICogQHJldHVybnMgQSBmb250IHNob3J0aGFuZCBzdHJpbmcgc3VpdGFibGUgZm9yIENhbnZhc1JlbmRlcmluZ0NvbnRleHQyRC5mb250IG9yIGFzIGEgdW5pcXVlIGZvbnQga2V5LlxuICovXG5mdW5jdGlvbiBnZXRGb250U3RyaW5nIChlbGVtZW50U3R5bGU6IENTU1N0eWxlRGVjbGFyYXRpb24sIG9wdGlvbnM6IHsgaXNGb250S2V5OiBib29sZWFuOyB9IHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkKTogc3RyaW5nIHtcbiAgbGV0IGZvbnRTdHJpbmc6IHN0cmluZyA9IGAkeyBlbGVtZW50U3R5bGUuZm9udFN0eWxlIH0gJHsgZWxlbWVudFN0eWxlLmZvbnRWYXJpYW50IH0gJHsgZWxlbWVudFN0eWxlLmZvbnRXZWlnaHQgfSAkeyBlbGVtZW50U3R5bGUuZm9udFNpemUgfS8keyBlbGVtZW50U3R5bGUubGluZUhlaWdodCB9ICR7IGVsZW1lbnRTdHlsZS5mb250RmFtaWx5IH1gO1xuXG4gIGlmIChvcHRpb25zPy5pc0ZvbnRLZXkpIHtcbiAgICBmb250U3RyaW5nICs9IGVsZW1lbnRTdHlsZS5sZXR0ZXJTcGFjaW5nO1xuICAgIGZvbnRTdHJpbmcudHJpbSgpO1xuICB9XG5cbiAgcmV0dXJuIGZvbnRTdHJpbmc7XG59XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIENhbnZhc01lYXN1cmVyU2VydmljZSB7XG4gIHByaXZhdGUgX2NhbnZhczogSFRNTENhbnZhc0VsZW1lbnQgfCB1bmRlZmluZWQ7XG5cbiAgcHJpdmF0ZSBnZXQgY2FudmFzICgpOiBIVE1MQ2FudmFzRWxlbWVudCB7XG4gICAgaWYgKHRoaXMuX2NhbnZhcykgcmV0dXJuIHRoaXMuX2NhbnZhcztcblxuICAgIHRoaXMuX2NhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJjYW52YXNcIik7XG5cbiAgICByZXR1cm4gdGhpcy5fY2FudmFzO1xuICB9XG5cbiAgcHJpdmF0ZSBfY2FudmFzQ29udGV4dDogQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEIHwgbnVsbCA9IHRoaXMuY2FudmFzLmdldENvbnRleHQoXCIyZFwiKTtcblxuICAvLyBNYXAgb2YgbWFwcyB3aGVyZSBlYWNoIGNoaWxkLW1hcCByZXByZXNlbnRzIGEgZm9udCBhbmQgZWFjaCBjaGlsZC1tYXAtZW50cnkgaXMgYSBzdHJpbmcgd2l0aCB0aGUgbWVhc3VyZWQgd2lkdGhcbiAgcHJpdmF0ZSBfc3RyaW5nV2lkdGhDYWNoZTogTWFwPHN0cmluZywgTWFwPHN0cmluZywgbnVtYmVyPj4gPSBuZXcgTWFwPHN0cmluZywgTWFwPHN0cmluZywgbnVtYmVyPj4oKTtcblxuICAvKipcbiAgICogTWVhc3VyZXMgdGV4dCB3aWR0aCBmb3IgYSBnaXZlbiBlbGVtZW50IHN0eWxlIGFuZCBzdHJpbmcsXG4gICAqIGNhY2hpbmcgcmVzdWx0cyBwZXIgZm9udCBjb25maWd1cmF0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gc3R5bGUgLSBDb21wdXRlZCBDU1Mgc3R5bGUgb2YgdGhlIGVsZW1lbnQgKGZvbnQgaW5mbykuXG4gICAqIEBwYXJhbSB0ZXh0IC0gVGhlIHN0cmluZyB0byBtZWFzdXJlLlxuICAgKiBAcmV0dXJucyBUaGUgd2lkdGggaW4gcGl4ZWxzLCBvciB1bmRlZmluZWQgaWYgbWVhc3VyZW1lbnQgZmFpbGVkLlxuICAgKi9cbiAgcHVibGljIGdldFJlbmRlcmVkU3RyaW5nV2lkdGggKGVsZW1lbnRTdHlsZTogQ1NTU3R5bGVEZWNsYXJhdGlvbiB8IHVuZGVmaW5lZCwgc3RyaW5nVG9NZWFzdXJlOiBzdHJpbmcpOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICAgIGlmICghZWxlbWVudFN0eWxlKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgZm9udFN0cmluZzogc3RyaW5nID0gZ2V0Rm9udFN0cmluZyhlbGVtZW50U3R5bGUsIHsgaXNGb250S2V5OiB0cnVlIH0pO1xuXG4gICAgbGV0IGZvbnRNYXA6IE1hcDxzdHJpbmcsIG51bWJlcj4gfCB1bmRlZmluZWQgPSB0aGlzLl9zdHJpbmdXaWR0aENhY2hlLmdldChmb250U3RyaW5nKTtcblxuICAgIGlmICghZm9udE1hcCkge1xuICAgICAgZm9udE1hcCA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4gICAgICB0aGlzLl9zdHJpbmdXaWR0aENhY2hlLnNldChmb250U3RyaW5nLCBmb250TWFwKTtcbiAgICB9XG5cbiAgICBsZXQgd2lkdGg6IG51bWJlciB8IHVuZGVmaW5lZCA9IGZvbnRNYXAuZ2V0KHN0cmluZ1RvTWVhc3VyZSk7XG5cbiAgICBpZiAod2lkdGgpIHJldHVybiB3aWR0aDtcblxuXG4gICAgaWYgKHRoaXMuX2NhbnZhc0NvbnRleHQpIHtcbiAgICAgIHRoaXMuc2V0Q2FudmFzRm9udChlbGVtZW50U3R5bGUpO1xuICAgICAgd2lkdGggPSB0aGlzLl9jYW52YXNDb250ZXh0Lm1lYXN1cmVUZXh0KHN0cmluZ1RvTWVhc3VyZSkud2lkdGg7XG4gICAgICBmb250TWFwLnNldChzdHJpbmdUb01lYXN1cmUsIHdpZHRoKTtcblxuICAgICAgcmV0dXJuIHdpZHRoO1xuICAgIH1cblxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBwcml2YXRlIHNldENhbnZhc0ZvbnQgKGVsZW1lbnRTdHlsZTogQ1NTU3R5bGVEZWNsYXJhdGlvbik6IHZvaWQge1xuICAgIGlmICh0aGlzLl9jYW52YXNDb250ZXh0Py5mb250KSB7XG4gICAgICBjb25zdCBmb250U3RyaW5nOiBzdHJpbmcgPSBnZXRGb250U3RyaW5nKGVsZW1lbnRTdHlsZSk7XG5cbiAgICAgIGlmICh0aGlzLl9jYW52YXNDb250ZXh0LmZvbnQgPT09IGZvbnRTdHJpbmcgJiYgdGhpcy5fY2FudmFzQ29udGV4dC5sZXR0ZXJTcGFjaW5nID09PSBlbGVtZW50U3R5bGUubGV0dGVyU3BhY2luZykgcmV0dXJuO1xuXG4gICAgICB0aGlzLl9jYW52YXNDb250ZXh0LmZvbnQgPSBmb250U3RyaW5nO1xuICAgICAgdGhpcy5fY2FudmFzQ29udGV4dC5sZXR0ZXJTcGFjaW5nID0gZWxlbWVudFN0eWxlLmxldHRlclNwYWNpbmc7XG4gICAgfVxuICB9XG59XG4iXX0=