src/MusicalScore/Graphical/GraphicalLabel.ts
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;
}
}