UNPKG

@ogs-gmbh/ngx-utils

Version:

A lightweight collection of utility functions and helpers for Angular applications

75 lines 10.4 kB
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=