@itwin/core-common
Version:
iTwin.js components common to frontend and backend
294 lines • 14.8 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Annotation
*/
import { ColorDef } from "../ColorDef";
import { FontType } from "../Fonts";
/** Predefined markers for list items in text annotations.
* These values control the appearance of list item markers (e.g., bullet, circle, square, dash, number) that denote the start of a list item in a list.
* @beta
*/
export var ListMarkerEnumerator;
(function (ListMarkerEnumerator) {
/** English Alphabet */
ListMarkerEnumerator["Letter"] = "A";
ListMarkerEnumerator["RomanNumeral"] = "I";
ListMarkerEnumerator["Number"] = "1";
ListMarkerEnumerator["Bullet"] = "\u2022";
ListMarkerEnumerator["Circle"] = "\u25CB";
ListMarkerEnumerator["Square"] = "\u25A0";
/** EM Dash */
ListMarkerEnumerator["Dash"] = "\u2013";
})(ListMarkerEnumerator || (ListMarkerEnumerator = {}));
/** Set of predefined shapes that can be computed and drawn around the margins of a [[TextBlock]]
* @beta
*/
export const textAnnotationFrameShapes = ["none", "line", "rectangle", "circle", "equilateralTriangle", "diamond", "square", "pentagon", "hexagon", "octagon", "capsule", "roundedRectangle"];
/** Set of predefined shapes that can be used as terminators for leaders in a [[TextAnnotation]]
* @beta
*/
export const terminatorShapes = ["openArrow", "closedArrow", "closedArrowFilled", "circle", "circleFilled", "slash", "none"];
;
;
function deepFreeze(obj) {
if (obj === null || typeof obj !== "object" || Object.isFrozen(obj))
return;
Object.getOwnPropertyNames(obj).forEach((prop) => {
const value = obj[prop];
if (value && typeof value === "object") {
deepFreeze(value);
}
});
Object.freeze(obj);
}
/** A description of the formatting to be applied to a [[TextBlockComponent]].
* Named instances of these settings can be stored as [AnnotationTextStyle]($backend)s in an iModel.
* @note This is an immutable type. Use [[clone]] to create a modified copy.
* @see [[TextStyleSettingsProps]] for documentation of each of the settings.
* @beta
*/
export class TextStyleSettings {
/** The color of the text. */
color;
/** The font stored in an iModel, used to draw the contents of a [[TextRun]].
*/
font;
/** The height of the text, in meters. Many other settings use the text height as the basis for computing their own values.
* For example, the height and offset from baseline of a subscript [[TextRun]] are computed as textHeight * [[subScriptScale]] and
* textHeight * [[subScriptOffsetFactor]], respectively.
*/
textHeight;
/** Multiplier used to compute the vertical distance between two lines of text.
* The distance is computed in meters as lineSpacingFactor * [[textHeight]] of the [[TextBlock]].
*/
lineSpacingFactor;
/** Multiplier used to compute the vertical distance between two paragraphs of text.
* The distance is computed in meters as paragraphSpacingFactor * the [[TextBlock]]'s [[textHeight]].
*/
paragraphSpacingFactor;
/** Specifies whether the content of a [[TextRun]] should be rendered **bold**. */
isBold;
/** Specifies whether the content of a [[TextRun]] should be rendered in *italics*. */
isItalic;
/** Specifies whether the content of a [[TextRun]] should be underlined. */
isUnderlined;
/** Multiplier used to compute the height of both the numerator and denominator of a [[FractionRun]].
* The height is computed in meters as stackedFractionScale * [[textHeight]].
*/
stackedFractionScale;
/** Specifies how to separate the numerator and denominator of a [[FractionRun]]. */
stackedFractionType;
/** Multiplier used to compute the vertical offset from the baseline for a subscript [[TextRun]].
* The offset is computed in meters as subScriptOffsetFactor * [[textHeight]].
*/
subScriptOffsetFactor;
/** Multiplier used to compute the height of a subscript [[TextRun]].
* The height is computed as subScriptScale * [[textHeight]].
*/
subScriptScale;
/** Multiplier used to compute the vertical offset from the baseline for a super [[TextRun]].
* The offset is computed in meters as superScriptOffsetFactor * [[textHeight]].
*/
superScriptOffsetFactor;
/** Multiplier used to compute the height of a superscript [[TextRun]].
* The height is computed as superScriptScale * [[textHeight]].
*/
superScriptScale;
/** Multiplier used to compute the width of each glyph, relative to [[textHeight]]. */
widthFactor;
/** Properties describing appearance of leaders in a [[TextAnnotation]].
* Used when producing geometry for [[TextAnnotation]].
*/
leader;
/** The size (in meters) used to calculate the tab stops in a run.
* These are equally spaced from the left edge of the TextBlock.
* [[tabInterval]] is also used in lists to compute the offset of each child or [[Paragraph]].
* The [[listMarker]] is right justified on [[indentation]] + [[tabInterval]]*(depth - 1/2).
* [[Paragraph]]s will start at [[indentation]] + [[tabInterval]]*depth.
*/
tabInterval;
/** The offset (in meters) from the left edge of the text block to the start of the line of text.
* In lists, the indentation is added to offset of list items.
* The [[listMarker]] is right justified on [[indentation]] + [[tabInterval]]*(depth - 1/2).
* [[Paragraph]]s will start at [[indentation]] + [[tabInterval]]*depth.
*/
indentation;
/** The marker used to indicate the start of a list item.
* Default: [[ListMarkerEnumerator.Number]].
*/
listMarker;
/** The frame settings of the [[TextAnnotation]]. */
frame;
/** The margins to surround the document content. */
margins;
/** The alignment of the text content. */
justification;
/** A fully-populated JSON representation of the default settings. A real `font` must be provided before use. */
static defaultProps = {
color: "subcategory",
font: { name: "", type: FontType.TrueType },
textHeight: 1,
lineSpacingFactor: 0.5,
paragraphSpacingFactor: 0.5,
isBold: false,
isItalic: false,
isUnderlined: false,
stackedFractionScale: 0.7,
stackedFractionType: "horizontal",
subScriptOffsetFactor: -0.15,
subScriptScale: 2 / 3,
superScriptOffsetFactor: 0.5,
superScriptScale: 2 / 3,
widthFactor: 1,
leader: {
color: "inherit",
wantElbow: false,
elbowLength: 1.0,
terminatorShape: terminatorShapes[0],
terminatorHeightFactor: 1.0,
terminatorWidthFactor: 1.0,
},
tabInterval: 4,
indentation: 0,
listMarker: { enumerator: "1", terminator: "period", case: "lower" },
frame: {
shape: "none",
fillColor: "none",
borderColor: ColorDef.black.toJSON(),
borderWeight: 1,
},
margins: {
left: 0,
right: 0,
top: 0,
bottom: 0
},
justification: "left",
};
/** Settings initialized to all default values. */
static defaults = new TextStyleSettings({});
constructor(props, defaults) {
if (!defaults) {
defaults = TextStyleSettings.defaultProps;
}
this.color = props.color ?? defaults.color;
const font = {
name: props.font?.name ?? defaults.font.name,
type: props.font?.type ?? defaults.font.type,
};
this.font = Object.freeze(font);
this.textHeight = props.textHeight ?? defaults.textHeight;
this.lineSpacingFactor = props.lineSpacingFactor ?? defaults.lineSpacingFactor;
this.paragraphSpacingFactor = props.paragraphSpacingFactor ?? defaults.paragraphSpacingFactor;
this.isBold = props.isBold ?? defaults.isBold;
this.isItalic = props.isItalic ?? defaults.isItalic;
this.isUnderlined = props.isUnderlined ?? defaults.isUnderlined;
this.stackedFractionScale = props.stackedFractionScale ?? defaults.stackedFractionScale;
this.stackedFractionType = props.stackedFractionType ?? defaults.stackedFractionType;
this.subScriptOffsetFactor = props.subScriptOffsetFactor ?? defaults.subScriptOffsetFactor;
this.subScriptScale = props.subScriptScale ?? defaults.subScriptScale;
this.superScriptOffsetFactor = props.superScriptOffsetFactor ?? defaults.superScriptOffsetFactor;
this.superScriptScale = props.superScriptScale ?? defaults.superScriptScale;
this.widthFactor = props.widthFactor ?? defaults.widthFactor;
const leader = {
color: props.leader?.color ?? defaults.leader.color,
wantElbow: props.leader?.wantElbow ?? defaults.leader.wantElbow,
elbowLength: props.leader?.elbowLength ?? defaults.leader.elbowLength,
terminatorShape: props.leader?.terminatorShape ?? defaults.leader.terminatorShape,
terminatorHeightFactor: props.leader?.terminatorHeightFactor ?? defaults.leader.terminatorHeightFactor,
terminatorWidthFactor: props.leader?.terminatorWidthFactor ?? defaults.leader.terminatorWidthFactor,
};
this.leader = Object.freeze(leader);
this.tabInterval = props.tabInterval ?? defaults.tabInterval;
this.indentation = props.indentation ?? defaults.indentation;
this.listMarker = props.listMarker ?? defaults.listMarker;
const frame = {
shape: props.frame?.shape ?? defaults.frame.shape,
fillColor: props.frame?.fillColor ?? defaults.frame.fillColor,
borderColor: props.frame?.borderColor ?? defaults.frame.borderColor,
borderWeight: props.frame?.borderWeight ?? defaults.frame.borderWeight,
};
// Cast to indicate to TypeScript that the frame properties are all defined
this.frame = Object.freeze(frame);
this.margins = Object.freeze({
left: props.margins?.left ?? defaults.margins.left,
right: props.margins?.right ?? defaults.margins.right,
top: props.margins?.top ?? defaults.margins.top,
bottom: props.margins?.bottom ?? defaults.margins.bottom,
});
this.justification = props.justification ?? defaults.justification;
}
/** Create a copy of these settings, modified according to the properties defined by `alteredProps`. */
clone(alteredProps) {
return alteredProps ? new TextStyleSettings(alteredProps, this) : this;
}
/** Create settings from their JSON representation. */
static fromJSON(props) {
return props ? new TextStyleSettings(props) : TextStyleSettings.defaults;
}
toJSON() {
return structuredClone(this);
}
/** Compare two [[TextLeaderStyleProps]] for equality.
* @param other The other leader style properties to compare against.
* @returns true if the two leader styles are equal, false otherwise.
*/
leaderEquals(other) {
return this.leader.color === other.color && this.leader.wantElbow === other.wantElbow
&& this.leader.elbowLength === other.elbowLength && this.leader.terminatorShape === other.terminatorShape && this.leader.terminatorHeightFactor === other.terminatorHeightFactor
&& this.leader.terminatorWidthFactor === other.terminatorWidthFactor;
}
frameEquals(other) {
return this.frame?.shape === other.shape
&& this.frame?.fillColor === other.fillColor
&& this.frame?.borderColor === other.borderColor
&& this.frame?.borderWeight === other.borderWeight;
}
marginsEqual(other) {
return Object.entries(this.margins).every(([key, value]) => value === other[key]);
}
equals(other) {
return this.color === other.color && this.font.name === other.font.name && this.font.type === other.font.type
&& this.textHeight === other.textHeight && this.widthFactor === other.widthFactor
&& this.lineSpacingFactor === other.lineSpacingFactor && this.paragraphSpacingFactor === other.paragraphSpacingFactor
&& this.isBold === other.isBold && this.isItalic === other.isItalic && this.isUnderlined === other.isUnderlined
&& this.stackedFractionType === other.stackedFractionType && this.stackedFractionScale === other.stackedFractionScale
&& this.subScriptOffsetFactor === other.subScriptOffsetFactor && this.subScriptScale === other.subScriptScale
&& this.superScriptOffsetFactor === other.superScriptOffsetFactor && this.superScriptScale === other.superScriptScale
&& this.tabInterval === other.tabInterval && this.indentation === other.indentation
&& this.listMarker.case === other.listMarker.case && this.listMarker.enumerator === other.listMarker.enumerator && this.listMarker.terminator === other.listMarker.terminator
&& this.justification === other.justification
&& this.leaderEquals(other.leader)
&& this.frameEquals(other.frame)
&& this.marginsEqual(other.margins);
}
/**
* Returns a list of validation errors for this instance.
*
* A TextStyleSettings object may contain values that are invalid in all contexts.
* If this method returns any error strings, using the settings will likely result in rendering failures or runtime exceptions.
*
* This method only checks for universally invalid values. Additional domain-specific validation may be required depending on the context in which these settings are used.
*
* @returns An array of error strings describing the invalid values, or an empty array if the settings are valid.
*/
getValidationErrors() {
const errorMessages = [];
if (this.font.name.trim() === "") {
errorMessages.push("font name must be provided");
}
if (this.textHeight <= 0) {
errorMessages.push("textHeight must be greater than 0");
}
if (this.stackedFractionScale <= 0) {
errorMessages.push("stackedFractionScale must be greater than 0");
}
return errorMessages;
}
}
deepFreeze(TextStyleSettings.defaultProps);
deepFreeze(TextStyleSettings.defaults);
//# sourceMappingURL=TextStyle.js.map