all files / common/core/text-metrics/ text-metrics.service.ts

100% Statements 65/65
71.43% Branches 10/14
100% Functions 15/15
100% Lines 59/59
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153                                              765× 765×                               770× 770×   770× 770× 770× 770×   770×                                                                                   510× 508× 34× 34× 202×     202×   34×   28×   34×                
/*
 *  @license
 *  Copyright Hôpitaux Universitaires de Genève. All Rights Reserved.
 *
 *  Use of this source code is governed by an Apache-2.0 license that can be
 *  found in the LICENSE file at https://github.com/DSI-HUG/dejajs-components/blob/master/LICENSE
 */
 
import { Injectable } from '@angular/core';
import {BehaviorSubject,  from as observableFrom ,  Observable ,  Subject } from 'rxjs';
import {delay, filter, first, map} from 'rxjs/operators';
 
/**
 * Service to measure the theorical size of a text inside a container
 */
@Injectable()
export class DejaTextMetricsService {
    private canvas: HTMLCanvasElement;
    private element$: Subject<HTMLElement> = new Subject();
    private computedStyles: CSSStyleDeclaration;
    private charSize$ = new BehaviorSubject<number[]>(null);
 
    /**
     * Constructor
     * Add observable to wait for element to be set. And then take its properties to measure all ASCII char size.
     */
    constructor() {
        observableFrom(this.element$).pipe(
            delay(1),
            first())
            .subscribe((element) => {
                const charSize = [];
                for (let i = 0; i < 255; i++) {
                    const c = String.fromCharCode(i);
                    charSize[i] = this.getTextWidth(c, element);
                }
                this.charSize$.next(charSize);
            });
    }
 
    /** Setter for base element */
    public set metricsElem(elem: HTMLElement) {
        this.element$.next(elem);
    }
 
    /**
     * Calcule la longeur (en pixels) d'une chaine de caractères
     *
     * @param text Le texte à mesurer
     * @param elem Le conteneur du texte
     *
     * @return la largeur du texte donné
     */
    public getTextWidth(text: string, elem: HTMLElement): number {
        this.computedStyles = window.getComputedStyle(elem);
        const font = `${this.computedStyles.fontSize} ${this.computedStyles.fontFamily}`;
 
        const canvas = this.canvas || (this.canvas = document.createElement('canvas'));
        const context = canvas.getContext('2d');
        context.font = font;
        const metrics = context.measureText(text);
 
        return metrics.width * 1.1; // Correction for letter-spacing
    }
 
    /**
     * Retourne la largeur maximum d'un tableau de strings.
     *
     * @param texts les textes à comparer.
     * @param elem Le conteneur du texte
     *
     * @return la width du texte le plus long dans le tableau donné en param.
     */
    public getTextMaxWidth(texts: string[], elem: HTMLElement): number {
        let maxWidth = 0;
 
        texts.forEach((text: string) => {
            const width = this.getTextWidth(text, elem);
            Eif (width > maxWidth) {
                maxWidth = width;
            }
        });
 
        return maxWidth;
    }
 
    /**
     * Mesure la heuteur théorique d'un texte contenu dans un conteneur d'une taille donnée.
     *
     * @param maxWidth taille du conteneur
     * @param text texte à mesurer
     *
     * @return Hauteur théorique du conteneur.
     */
    public getTextHeight(maxWidth: number, text: string): Observable<number> {
        return this.getNumberOfLines(maxWidth, text).pipe(
            map((numberOfLines: number) => {
                const computedLineHeight = parseInt(this.computedStyles.lineHeight.replace('px', ''), 10);
                const lineHeight = (!isNaN(computedLineHeight)) ?
                    computedLineHeight :
                    Math.floor(parseInt(this.computedStyles.fontSize.replace('px', ''), 10) * 1.5);
 
                return lineHeight * +numberOfLines;
            }));
    }
 
    /**
     * Calcule le nombre de lignes qu'un texte va prendre en fonction de la largeur de son conteneur.
     *
     * @param maxWidth taille du conteneur
     * @param text texte à mesurer
     *
     * @return Nombre de lignes théoriques du conteneur.
     */
    private getNumberOfLines(maxWidth: number, text: string): Observable<number> {
        return this.charSize$.pipe(
            filter((charSize) => charSize !== null),
            map((charSize) => {
 
                let tmpSize = 0;
                let numberOfLines = 1;
                let averageCharSize = 0;
                Eif (text.length > 0) {
                    const arr = text.split(' ');
                    let spaceWidth = 0;
                    const printableCharSizeArray = charSize.filter((size) => size > 0);
                    averageCharSize = printableCharSizeArray.reduce((a, b) => a + b, 0) / printableCharSizeArray.length;
                    arr.forEach((txt: string) => {
                        let w = 0;
                        for (let j = 0; j < txt.length; j++) {
                            const charCode = txt.charCodeAt(j);
                            // Si le caractère fait partie de la table ascii qu'on a calculé dans this.getAllCharsize() on incrémente la taille du mot de sa taille.
                            // Sinon, on ajoute la moyenne des tailles calculées (qui correspond théoriquement à la taille moyenne d'un caractère)
                            w += (charSize[charCode]) ? charSize[charCode] : averageCharSize;
                        }
                        if ((tmpSize + w + spaceWidth) > maxWidth) {
                            tmpSize = w;
                            numberOfLines++;
                        } else {
                            tmpSize += w + spaceWidth;
                        }
                        if (spaceWidth === 0) {
                            spaceWidth = charSize[32];
                        }
                    });
                }
 
                return numberOfLines;
            }));
    }
}