opensheetmusicdisplay/opensheetmusicdisplay

View on GitHub
src/MusicalScore/Graphical/GraphicalLabel.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import { TextAlignmentEnum } from "../../Common/Enums/TextAlignment";
import { Label } from "../Label";
import { BoundingBox } from "./BoundingBox";
import { Clickable } from "./Clickable";
import { EngravingRules } from "./EngravingRules";
import { MusicSheetCalculator } from "./MusicSheetCalculator";

/**
 * The graphical counterpart of a Label
 */
export class GraphicalLabel extends Clickable {
    private label: Label;
    private rules: EngravingRules;
    public TextLines: {text: string, xOffset: number, width: number}[];
    /** A reference to the Node in the SVG, if SVGBackend, otherwise undefined.
     *  Allows manipulation without re-rendering, e.g. for dynamics, lyrics, etc.
     *  For the Canvas backend, this is unfortunately not possible.
     */
    public SVGNode: Node;
    /** Read-only informational variable only set once by lyrics centering algorithm. */
    public CenteringXShift: number = 0;
    public ColorXML: string;

    /**
     * Creates a new GraphicalLabel from a Label
     * @param label  label object containing text
     * @param textHeight Height of text
     * @param alignment Alignement like left, right, top, ...
     * @param parent Parent Bounding Box where the label is attached to
     */
    constructor(label: Label, textHeight: number, alignment: TextAlignmentEnum, rules: EngravingRules,
                parent: BoundingBox = undefined, ) {
        super();
        this.label = label;
        this.boundingBox = new BoundingBox(this, parent);
        this.label.fontHeight = textHeight;
        this.label.textAlignment = alignment;
        this.rules = rules;
    }

    public get Label(): Label {
        return this.label;
    }

    public toString(): string {
        return `${this.label.text} (${this.boundingBox.RelativePosition.x},${this.boundingBox.RelativePosition.y})`;
    }

    /**
     * Calculate GraphicalLabel's Borders according to its Alignment
     * Create also the text-lines and their offsets here
     */
    public setLabelPositionAndShapeBorders(): void {
        if (this.Label.text.trim() === "") {
            return;
        }
        this.TextLines = [];
        const labelMarginBorderFactor: number = this.rules?.LabelMarginBorderFactor ?? 0.1;
        const lines: string[] = this.Label.text.split(/[\n\r]+/g);
        const numOfLines: number = lines.length;
        let maxWidth: number = 0;
        for (let i: number = 0; i < numOfLines; i++) {
            const line: string = lines[i].trim();
            const widthToHeightRatio: number =
            MusicSheetCalculator.TextMeasurer.computeTextWidthToHeightRatio(
               line, this.Label.font, this.Label.fontStyle, this.label.fontFamily);
            const currWidth: number = this.Label.fontHeight * widthToHeightRatio;
            // const currWidth: number = MusicSheetCalculator.TextMeasurer.computeTextWidth(
            //     line, this.Label.font, this.Label.fontStyle, this.label.fontFamily);
            maxWidth = Math.max(maxWidth, currWidth);
            // here push only text and width of the text:
            this.TextLines.push({text: line, xOffset: 0, width: currWidth});
        }

        // maxWidth is calculated ->
        // now also set the x-offsets:
        for (const line of this.TextLines) {
            let xOffset: number = 0;
            switch (this.Label.textAlignment) {
                case TextAlignmentEnum.RightBottom:
                case TextAlignmentEnum.RightCenter:
                case TextAlignmentEnum.RightTop:
                    xOffset = maxWidth - line.width;
                    break;
                case TextAlignmentEnum.CenterBottom:
                case TextAlignmentEnum.CenterCenter:
                case TextAlignmentEnum.CenterTop:
                    xOffset = (maxWidth - line.width) / 2;
                    break;
                default:
                    break;
            }
            line.xOffset = xOffset;
        }

        let height: number = this.Label.fontHeight * numOfLines;
        if (this.rules.SpacingBetweenTextLines > 0 && this.TextLines.length > 1) {
            height += (this.rules.SpacingBetweenTextLines * numOfLines) / 10;
        }
        const bbox: BoundingBox = this.PositionAndShape;

        switch (this.Label.textAlignment) {
            case TextAlignmentEnum.CenterBottom:
                bbox.BorderTop = -height;
                bbox.BorderLeft = -maxWidth / 2;
                bbox.BorderBottom = 0;
                bbox.BorderRight = maxWidth / 2;
                break;
            case TextAlignmentEnum.CenterCenter:
                bbox.BorderTop = -height / 2;
                bbox.BorderLeft = -maxWidth / 2;
                bbox.BorderBottom = height / 2;
                bbox.BorderRight = maxWidth / 2;
                break;
            case TextAlignmentEnum.CenterTop:
                bbox.BorderTop = 0;
                bbox.BorderLeft = -maxWidth / 2;
                bbox.BorderBottom = height;
                bbox.BorderRight = maxWidth / 2;
                break;
            case TextAlignmentEnum.LeftBottom:
                bbox.BorderTop = -height;
                bbox.BorderLeft = 0;
                bbox.BorderBottom = 0;
                bbox.BorderRight = maxWidth;
                break;
            case TextAlignmentEnum.LeftCenter:
                bbox.BorderTop = -height / 2;
                bbox.BorderLeft = 0;
                bbox.BorderBottom = height / 2;
                bbox.BorderRight = maxWidth;
                break;
            case TextAlignmentEnum.LeftTop:
                bbox.BorderTop = 0;
                bbox.BorderLeft = 0;
                bbox.BorderBottom = height;
                bbox.BorderRight = maxWidth;
                break;
            case TextAlignmentEnum.RightBottom:
                bbox.BorderTop = -height;
                bbox.BorderLeft = -maxWidth;
                bbox.BorderBottom = 0;
                bbox.BorderRight = 0;
                break;
            case TextAlignmentEnum.RightCenter:
                bbox.BorderTop = -height / 2;
                bbox.BorderLeft = -maxWidth;
                bbox.BorderBottom = height / 2;
                bbox.BorderRight = 0;
                break;
            case TextAlignmentEnum.RightTop:
                bbox.BorderTop = 0;
                bbox.BorderLeft = -maxWidth;
                bbox.BorderBottom = height;
                bbox.BorderRight = 0;
                break;
            default:
        }
        bbox.BorderMarginTop = bbox.BorderTop - height * labelMarginBorderFactor;
        bbox.BorderMarginLeft = bbox.BorderLeft - height * labelMarginBorderFactor;
        bbox.BorderMarginBottom = bbox.BorderBottom + height * labelMarginBorderFactor;
        bbox.BorderMarginRight = bbox.BorderRight + height * labelMarginBorderFactor;
    }
}