UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

294 lines • 14.8 kB
/*--------------------------------------------------------------------------------------------- * 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