{"version":3,"file":"Textbox.min.mjs","sources":["../../../src/shapes/Textbox.ts"],"sourcesContent":["import type { Abortable, TClassProperties, TOptions } from '../typedefs';\nimport { IText } from './IText/IText';\nimport { classRegistry } from '../ClassRegistry';\nimport { createTextboxDefaultControls } from '../controls/commonControls';\nimport { JUSTIFY } from './Text/constants';\nimport type { TextStyleDeclaration } from './Text/StyledText';\nimport type { SerializedITextProps, ITextProps } from './IText/IText';\nimport type { ITextEvents } from './IText/ITextBehavior';\nimport type { TextLinesInfo } from './Text/Text';\nimport type { Control } from '../controls/Control';\nimport type { CSSRules } from '../parser/typedefs';\nimport { parseAttributes } from '../parser/parseAttributes';\nimport { Path } from './Path';\nimport { DEFAULT_SVG_FONT_SIZE } from '../constants';\n\n// @TODO: Many things here are configuration related and shouldn't be on the class nor prototype\n// regexes, list of properties that are not suppose to change by instances, magic consts.\n// this will be a separated effort\nexport const textboxDefaultValues: Partial<TClassProperties<Textbox>> = {\n  minWidth: 20,\n  dynamicMinWidth: 2,\n  lockScalingFlip: true,\n  noScaleCache: false,\n  _wordJoiners: /[ \\t\\r]/,\n  splitByGrapheme: false,\n};\n\nexport type GraphemeData = {\n  wordsData: {\n    word: string[];\n    width: number;\n  }[][];\n  largestWordWidth: number;\n};\n\nexport type StyleMap = Record<string, { line: number; offset: number }>;\n\n// @TODO this is not complete\ninterface UniqueTextboxProps {\n  minWidth: number;\n  splitByGrapheme: boolean;\n  dynamicMinWidth: number;\n  _wordJoiners: RegExp;\n}\n\nexport interface SerializedTextboxProps\n  extends SerializedITextProps,\n    Pick<UniqueTextboxProps, 'minWidth' | 'splitByGrapheme'> {}\n\nexport interface TextboxProps extends ITextProps, UniqueTextboxProps {}\n\n/**\n * Textbox class, based on IText, allows the user to resize the text rectangle\n * and wraps lines automatically. Textboxes have their Y scaling locked, the\n * user can only change width. Height is adjusted automatically based on the\n * wrapping of lines.\n */\nexport class Textbox<\n    Props extends TOptions<TextboxProps> = Partial<TextboxProps>,\n    SProps extends SerializedTextboxProps = SerializedTextboxProps,\n    EventSpec extends ITextEvents = ITextEvents,\n  >\n  extends IText<Props, SProps, EventSpec>\n  implements UniqueTextboxProps\n{\n  /**\n   * Minimum width of textbox, in pixels.\n   * @type Number\n   * @default\n   */\n  declare minWidth: number;\n\n  /**\n   * Minimum calculated width of a textbox, in pixels.\n   * fixed to 2 so that an empty textbox cannot go to 0\n   * and is still selectable without text.\n   * @type Number\n   * @default\n   */\n  declare dynamicMinWidth: number;\n\n  /**\n   * Use this boolean property in order to split strings that have no white space concept.\n   * this is a cheap way to help with chinese/japanese\n   * @type Boolean\n   * @since 2.6.0\n   */\n  declare splitByGrapheme: boolean;\n\n  declare _wordJoiners: RegExp;\n\n  declare _styleMap: StyleMap;\n\n  declare isWrapping: boolean;\n\n  static type = 'Textbox';\n\n  static textLayoutProperties = [...IText.textLayoutProperties, 'width'];\n\n  static ownDefaults = textboxDefaultValues;\n\n  // James modified 自动计算文字高度\n  static enableCalcTextHeight = false;\n\n  /**\n   * James modified 自动计算文字高度\n   * 增加wrap宽度，设置为page的宽度\n   * 增加textbox wrap的默认宽度\n   * @type Number\n   * @default\n   */\n  static defaultWrapWidth = 1920;\n\n  static getDefaults(): Record<string, any> {\n    return {\n      ...super.getDefaults(),\n      ...Textbox.ownDefaults,\n    };\n  }\n\n  /**\n   * Constructor\n   * @param {String} text Text string\n   * @param {Object} [options] Options object\n   */\n  constructor(text: string, options?: Props) {\n    super(text, { ...Textbox.ownDefaults, ...options } as Props);\n  }\n\n  /**\n   * Creates the default control object.\n   * If you prefer to have on instance of controls shared among all objects\n   * make this function return an empty object and add controls to the ownDefaults object\n   */\n  static createControls(): { controls: Record<string, Control> } {\n    return { controls: createTextboxDefaultControls() };\n  }\n\n  /**\n   * Unlike superclass's version of this function, Textbox does not update\n   * its width.\n   * @private\n   * @override\n   */\n  initDimensions() {\n    if (!this.initialized) {\n      return;\n    }\n    this.isEditing && this.initDelayedCursor();\n    this._clearCache();\n    // clear dynamicMinWidth as it will be different after we re-wrap line\n    this.dynamicMinWidth = 0;\n    // wrap lines\n    this._styleMap = this._generateStyleMap(this._splitText());\n    // James modified\n    // if after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap\n    if (!this.path && this.dynamicMinWidth > this.width) {\n      this._set('width', this.dynamicMinWidth);\n    }\n    if (this.textAlign.includes(JUSTIFY)) {\n      // once text is measured we need to make space fatter to make justified text.\n      this.enlargeSpaces();\n    }\n    // James modified 取消 textbox 自动计算高度\n    if (!this.path && Textbox.enableCalcTextHeight) {\n      // clear cache and re-calculate height\n      this.height = this.calcTextHeight();\n    }\n    this.preventGroupCache = this.path ? true : false;\n  }\n\n  /**\n   * Generate an object that translates the style object so that it is\n   * broken up by visual lines (new lines and automatic wrapping).\n   * The original text styles object is broken up by actual lines (new lines only),\n   * which is only sufficient for Text / IText\n   * @private\n   */\n  _generateStyleMap(textInfo: TextLinesInfo): StyleMap {\n    let realLineCount = 0,\n      realLineCharCount = 0,\n      charCount = 0;\n    const map: StyleMap = {};\n\n    for (let i = 0; i < textInfo.graphemeLines.length; i++) {\n      if (textInfo.graphemeText[charCount] === '\\n' && i > 0) {\n        realLineCharCount = 0;\n        charCount++;\n        realLineCount++;\n      } else if (\n        !this.splitByGrapheme &&\n        this._reSpaceAndTab.test(textInfo.graphemeText[charCount]) &&\n        i > 0\n      ) {\n        // this case deals with space's that are removed from end of lines when wrapping\n        realLineCharCount++;\n        charCount++;\n      }\n\n      map[i] = { line: realLineCount, offset: realLineCharCount };\n\n      charCount += textInfo.graphemeLines[i].length;\n      realLineCharCount += textInfo.graphemeLines[i].length;\n    }\n\n    return map;\n  }\n\n  /**\n   * Returns true if object has a style property or has it on a specified line\n   * @param {Number} lineIndex\n   * @return {Boolean}\n   */\n  styleHas(property: keyof TextStyleDeclaration, lineIndex: number): boolean {\n    if (this._styleMap && !this.isWrapping) {\n      const map = this._styleMap[lineIndex];\n      if (map) {\n        lineIndex = map.line;\n      }\n    }\n    return super.styleHas(property, lineIndex);\n  }\n\n  /**\n   * Returns true if object has no styling or no styling in a line\n   * @param {Number} lineIndex , lineIndex is on wrapped lines.\n   * @return {Boolean}\n   */\n  isEmptyStyles(lineIndex: number): boolean {\n    if (!this.styles) {\n      return true;\n    }\n    let offset = 0,\n      nextLineIndex = lineIndex + 1,\n      nextOffset: number,\n      shouldLimit = false;\n    const map = this._styleMap[lineIndex],\n      mapNextLine = this._styleMap[lineIndex + 1];\n    if (map) {\n      lineIndex = map.line;\n      offset = map.offset;\n    }\n    if (mapNextLine) {\n      nextLineIndex = mapNextLine.line;\n      shouldLimit = nextLineIndex === lineIndex;\n      nextOffset = mapNextLine.offset;\n    }\n    const obj =\n      typeof lineIndex === 'undefined'\n        ? this.styles\n        : { line: this.styles[lineIndex] };\n    for (const p1 in obj) {\n      for (const p2 in obj[p1]) {\n        const p2Number = parseInt(p2, 10);\n        if (p2Number >= offset && (!shouldLimit || p2Number < nextOffset!)) {\n          // eslint-disable-next-line no-unused-vars\n          for (const p3 in obj[p1][p2]) {\n            return false;\n          }\n        }\n      }\n    }\n    return true;\n  }\n\n  /**\n   * @protected\n   * @param {Number} lineIndex\n   * @param {Number} charIndex\n   * @return {TextStyleDeclaration} a style object reference to the existing one or a new empty object when undefined\n   */\n  _getStyleDeclaration(\n    lineIndex: number,\n    charIndex: number,\n  ): TextStyleDeclaration {\n    if (this._styleMap && !this.isWrapping) {\n      const map = this._styleMap[lineIndex];\n      if (!map) {\n        return {};\n      }\n      lineIndex = map.line;\n      charIndex = map.offset + charIndex;\n    }\n    return super._getStyleDeclaration(lineIndex, charIndex);\n  }\n\n  /**\n   * @param {Number} lineIndex\n   * @param {Number} charIndex\n   * @param {Object} style\n   * @private\n   */\n  protected _setStyleDeclaration(\n    lineIndex: number,\n    charIndex: number,\n    style: object,\n  ) {\n    const map = this._styleMap[lineIndex];\n    super._setStyleDeclaration(map.line, map.offset + charIndex, style);\n  }\n\n  /**\n   * @param {Number} lineIndex\n   * @param {Number} charIndex\n   * @private\n   */\n  protected _deleteStyleDeclaration(lineIndex: number, charIndex: number) {\n    const map = this._styleMap[lineIndex];\n    super._deleteStyleDeclaration(map.line, map.offset + charIndex);\n  }\n\n  /**\n   * probably broken need a fix\n   * Returns the real style line that correspond to the wrapped lineIndex line\n   * Used just to verify if the line does exist or not.\n   * @param {Number} lineIndex\n   * @returns {Boolean} if the line exists or not\n   * @private\n   */\n  protected _getLineStyle(lineIndex: number): boolean {\n    const map = this._styleMap[lineIndex];\n    return !!this.styles[map.line];\n  }\n\n  /**\n   * Set the line style to an empty object so that is initialized\n   * @param {Number} lineIndex\n   * @param {Object} style\n   * @private\n   */\n  protected _setLineStyle(lineIndex: number) {\n    const map = this._styleMap[lineIndex];\n    super._setLineStyle(map.line);\n  }\n\n  /**\n   * Wraps text using the 'width' property of Textbox. First this function\n   * splits text on newlines, so we preserve newlines entered by the user.\n   * Then it wraps each line using the width of the Textbox by calling\n   * _wrapLine().\n   * @param {Array} lines The string array of text that is split into lines\n   * @param {Number} desiredWidth width you want to wrap to\n   * @returns {Array} Array of lines\n   */\n  _wrapText(lines: string[], desiredWidth: number): string[][] {\n    this.isWrapping = true;\n    // extract all thewords and the widths to optimally wrap lines.\n    const data = this.getGraphemeDataForRender(lines);\n    const wrapped: string[][] = [];\n    for (let i = 0; i < data.wordsData.length; i++) {\n      wrapped.push(...this._wrapLine(i, desiredWidth, data));\n    }\n    this.isWrapping = false;\n    return wrapped;\n  }\n\n  /**\n   * For each line of text terminated by an hard line stop,\n   * measure each word width and extract the largest word from all.\n   * The returned words here are the one that at the end will be rendered.\n   * @param {string[]} lines the lines we need to measure\n   *\n   */\n  getGraphemeDataForRender(lines: string[]): GraphemeData {\n    const splitByGrapheme = this.splitByGrapheme,\n      infix = splitByGrapheme ? '' : ' ';\n\n    let largestWordWidth = 0;\n\n    const data = lines.map((line, lineIndex) => {\n      let offset = 0;\n      const wordsOrGraphemes = splitByGrapheme\n        ? this.graphemeSplit(line)\n        : this.wordSplit(line);\n\n      if (wordsOrGraphemes.length === 0) {\n        return [{ word: [], width: 0 }];\n      }\n\n      return wordsOrGraphemes.map((word: string) => {\n        // if using splitByGrapheme words are already in graphemes.\n        const graphemeArray = splitByGrapheme\n          ? [word]\n          : this.graphemeSplit(word);\n        const width = this._measureWord(graphemeArray, lineIndex, offset);\n        largestWordWidth = Math.max(width, largestWordWidth);\n        offset += graphemeArray.length + infix.length;\n        return { word: graphemeArray, width };\n      });\n    });\n\n    return {\n      wordsData: data,\n      largestWordWidth,\n    };\n  }\n\n  /**\n   * Helper function to measure a string of text, given its lineIndex and charIndex offset\n   * It gets called when charBounds are not available yet.\n   * Override if necessary\n   * Use with {@link Textbox#wordSplit}\n   *\n   * @param {CanvasRenderingContext2D} ctx\n   * @param {String} text\n   * @param {number} lineIndex\n   * @param {number} charOffset\n   * @returns {number}\n   */\n  _measureWord(word: string[], lineIndex: number, charOffset = 0): number {\n    let width = 0,\n      prevGrapheme;\n    const skipLeft = true;\n    for (let i = 0, len = word.length; i < len; i++) {\n      const box = this._getGraphemeBox(\n        word[i],\n        lineIndex,\n        i + charOffset,\n        prevGrapheme,\n        skipLeft,\n      );\n      width += box.kernedWidth;\n      prevGrapheme = word[i];\n    }\n    return width;\n  }\n\n  /**\n   * Override this method to customize word splitting\n   * Use with {@link Textbox#_measureWord}\n   * @param {string} value\n   * @returns {string[]} array of words\n   */\n  wordSplit(value: string): string[] {\n    return value.split(this._wordJoiners);\n  }\n\n  /**\n   * Wraps a line of text using the width of the Textbox as desiredWidth\n   * and leveraging the known width o words from GraphemeData\n   * @private\n   * @param {Number} lineIndex\n   * @param {Number} desiredWidth width you want to wrap the line to\n   * @param {GraphemeData} graphemeData an object containing all the lines' words width.\n   * @param {Number} reservedSpace space to remove from wrapping for custom functionalities\n   * @returns {Array} Array of line(s) into which the given text is wrapped\n   * to.\n   */\n  _wrapLine(\n    lineIndex: number,\n    desiredWidth: number,\n    { largestWordWidth, wordsData }: GraphemeData,\n    reservedSpace = 0,\n  ): string[][] {\n    const additionalSpace = this._getWidthOfCharSpacing(),\n      splitByGrapheme = this.splitByGrapheme,\n      graphemeLines = [],\n      infix = splitByGrapheme ? '' : ' ';\n\n    let lineWidth = 0,\n      line: string[] = [],\n      // spaces in different languages?\n      offset = 0,\n      infixWidth = 0,\n      lineJustStarted = true;\n\n    desiredWidth -= reservedSpace;\n\n    const maxWidth = Math.max(\n      desiredWidth,\n      largestWordWidth,\n      this.dynamicMinWidth,\n    );\n    // layout words\n    const data = wordsData[lineIndex];\n    offset = 0;\n    let i;\n    for (i = 0; i < data.length; i++) {\n      const { word, width: wordWidth } = data[i];\n      offset += word.length;\n\n      lineWidth += infixWidth + wordWidth - additionalSpace;\n      if (lineWidth > maxWidth && !lineJustStarted) {\n        graphemeLines.push(line);\n        line = [];\n        lineWidth = wordWidth;\n        lineJustStarted = true;\n      } else {\n        lineWidth += additionalSpace;\n      }\n\n      if (!lineJustStarted && !splitByGrapheme) {\n        line.push(infix);\n      }\n      line = line.concat(word);\n\n      infixWidth = splitByGrapheme\n        ? 0\n        : this._measureWord([infix], lineIndex, offset);\n      offset++;\n      lineJustStarted = false;\n    }\n\n    i && graphemeLines.push(line);\n\n    // TODO: this code is probably not necessary anymore.\n    // it can be moved out of this function since largestWordWidth is now\n    // known in advance\n    if (largestWordWidth + reservedSpace > this.dynamicMinWidth) {\n      this.dynamicMinWidth = largestWordWidth - additionalSpace + reservedSpace;\n    }\n    return graphemeLines;\n  }\n\n  /**\n   * Detect if the text line is ended with an hard break\n   * text and itext do not have wrapping, return false\n   * @param {Number} lineIndex text to split\n   * @return {Boolean}\n   */\n  isEndOfWrapping(lineIndex: number): boolean {\n    if (!this._styleMap[lineIndex + 1]) {\n      // is last line, return true;\n      return true;\n    }\n    if (this._styleMap[lineIndex + 1].line !== this._styleMap[lineIndex].line) {\n      // this is last line before a line break, return true;\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * Detect if a line has a linebreak and so we need to account for it when moving\n   * and counting style.\n   * This is important only for splitByGrapheme at the end of wrapping.\n   * If we are not wrapping the offset is always 1\n   * @return Number\n   */\n  missingNewlineOffset(lineIndex: number, skipWrapping?: boolean): 0 | 1 {\n    if (this.splitByGrapheme && !skipWrapping) {\n      return this.isEndOfWrapping(lineIndex) ? 1 : 0;\n    }\n    return 1;\n  }\n\n  /**\n   * Gets lines of text to render in the Textbox. This function calculates\n   * text wrapping on the fly every time it is called.\n   * @param {String} text text to split\n   * @returns {Array} Array of lines in the Textbox.\n   * @override\n   */\n  _splitTextIntoLines(text: string) {\n    // James modified 存在path时候不按照宽度换行\n    var wrapWidth = this.path ? 10000000 : this.width;\n    // 如果width 不存在， 使用page width\n    wrapWidth = wrapWidth || Textbox.defaultWrapWidth;\n    const newText = super._splitTextIntoLines(text),\n      graphemeLines = this._wrapText(newText.lines, wrapWidth),\n      lines = new Array(graphemeLines.length);\n    for (let i = 0; i < graphemeLines.length; i++) {\n      lines[i] = graphemeLines[i].join('');\n    }\n    newText.lines = lines;\n    newText.graphemeLines = graphemeLines;\n    return newText;\n  }\n\n  getMinWidth() {\n    return Math.max(this.minWidth, this.dynamicMinWidth);\n  }\n\n  _removeExtraneousStyles() {\n    const linesToKeep = new Map();\n    for (const prop in this._styleMap) {\n      const propNumber = parseInt(prop, 10);\n      if (this._textLines[propNumber]) {\n        const lineIndex = this._styleMap[prop].line;\n        linesToKeep.set(`${lineIndex}`, true);\n      }\n    }\n    for (const prop in this.styles) {\n      if (!linesToKeep.has(prop)) {\n        delete this.styles[prop];\n      }\n    }\n  }\n\n  /**\n   * Returns object representation of an instance\n   * @method toObject\n   * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n   * @return {Object} object representation of an instance\n   */\n  toObject<\n    T extends Omit<Props & TClassProperties<this>, keyof SProps>,\n    K extends keyof T = never,\n  >(propertiesToInclude: K[] = []): Pick<T, K> & SProps {\n    return super.toObject<T, K>([\n      'minWidth',\n      'splitByGrapheme',\n      ...propertiesToInclude,\n    ] as K[]) as Pick<T, K> & SProps;\n  }\n\n  /**\n   * Returns FabricText instance from an SVG element (<b>not yet implemented</b>)\n   * @static\n   * @memberOf Text\n   * @param {HTMLElement} element Element to parse\n   * @param {Object} [options] Options object\n   */\n  static async fromElement(\n    element: HTMLElement,\n    options: Abortable,\n    cssRules?: CSSRules,\n  ) {\n    if (!element) {\n      return null;\n    }\n\n    var parsedAttributes = parseAttributes(\n      element,\n      Textbox.ATTRIBUTE_NAMES,\n      cssRules,\n    );\n    const textOptions = {\n      ...(cssRules ? JSON.parse(JSON.stringify(cssRules)) : {}),\n      ...parsedAttributes,\n    };\n    // 处理style中字体样式带单引号问题\n    let reg = /^'(.*)'$/;\n    if (reg.test(textOptions.fontFamily)) {\n      textOptions.fontFamily = textOptions.fontFamily.slice(1, -1);\n    }\n\n    textOptions.top = textOptions.top || 0;\n    textOptions.left = textOptions.left || 0;\n    if (parsedAttributes.textDecoration) {\n      var textDecoration = parsedAttributes.textDecoration;\n      if (textDecoration.indexOf('underline') !== -1) {\n        textOptions.underline = true;\n      }\n      if (textDecoration.indexOf('overline') !== -1) {\n        textOptions.overline = true;\n      }\n      if (textDecoration.indexOf('line-through') !== -1) {\n        textOptions.linethrough = true;\n      }\n      delete textOptions.textDecoration;\n    }\n    if ('dx' in parsedAttributes) {\n      textOptions.left += parsedAttributes.dx;\n    }\n    if ('dy' in parsedAttributes) {\n      textOptions.top += parsedAttributes.dy;\n    }\n    if (!('fontSize' in textOptions)) {\n      textOptions.fontSize = DEFAULT_SVG_FONT_SIZE;\n    }\n\n    let text: Textbox;\n    const paths = element.getElementsByTagName('textPath');\n    if (paths.length) {\n      text = this._fromTextPath(paths[0], textOptions, parsedAttributes);\n    } else {\n      text = this._fromTextSpan(element, textOptions, parsedAttributes);\n    }\n    return text;\n  }\n\n  static _findSvgTextPath(element: any, id: string) {\n    const svg = element.closest('svg');\n    return svg.querySelector(id);\n  }\n\n  static _fromTextPath = (\n    textPath: any,\n    options: any,\n    parsedAttributes: { [key: string]: string },\n  ): Textbox => {\n    var parsedAnchor = parsedAttributes.textAnchor || 'left';\n\n    var textPathParsedAttributes = parseAttributes(textPath, [\n      'href',\n      'text-anchor',\n      'startOffset',\n    ]);\n    if (textPathParsedAttributes.textAnchor) {\n      parsedAnchor = textPathParsedAttributes.textAnchor;\n    }\n    if (parsedAnchor === 'middle') {\n      parsedAnchor = 'center';\n    } else if (parsedAnchor === 'end') {\n      parsedAnchor = 'right';\n    }\n    options.textAlign = parsedAnchor;\n\n    var textContent = textPath.textContent;\n    var text = new Textbox(textContent, options);\n\n    const href = textPathParsedAttributes.href;\n    if (href && href.startsWith('#')) {\n      const pathElement = Textbox._findSvgTextPath(textPath, href);\n      if (pathElement) {\n        var pathParsedAttributes = parseAttributes(\n          pathElement,\n          Path.ATTRIBUTE_NAMES,\n        );\n        const path = new Path(pathParsedAttributes.d, {\n          ...pathParsedAttributes,\n          ...{\n            strokeWidth: 1,\n            stroke: '#ff0000',\n            fill: null as any,\n            visible: false,\n          },\n        });\n        // 需要计算实际字体大小\n        Textbox.enableCalcTextHeight = true;\n        const textHeight =\n          new Textbox('i', {\n            fontFamily: options.fontFamily,\n            fontSize: options.fontSize,\n            fontStyle: options.fontStyle,\n            fontWeight: options.fontWeight,\n            width: undefined,\n            height: undefined,\n          }).height || 20;\n        text.set({\n          width: (path.width || 0) + textHeight * 2,\n          height: (path.height || 0) + textHeight * 2,\n          path,\n          pathType: 'custom',\n        } as any);\n        // 取消自动计算文字高度\n        Textbox.enableCalcTextHeight = false;\n      }\n    }\n    // 设置位置中心点为左上角\n    text.set({\n      left: options.left - (text.width || 0) / 2,\n      top: options.top - (text.height || 0) / 2,\n    });\n    return text;\n  };\n\n  static _fromTextSpan(\n    element: any,\n    options: any,\n    parsedAttributes: { [key: string]: string },\n  ): Textbox {\n    var textContent = '';\n    var parsedAnchor = parsedAttributes.textAnchor || 'left';\n    // 是否需要根据tspan位置计算对齐\n    let calcHorAlign = !parsedAttributes.textAnchor;\n    let calcAdjustHorAlign = '';\n\n    // The XML is not properly parsed in IE9 so a workaround to get\n    // textContent is through firstChild.data. Another workaround would be\n    // to convert XML loaded from a file to be converted using DOMParser (same way loadSVGFromString() does)\n\n    let lineCnt = 1;\n    let alignmentBaseline = 'auto';\n    let spanOffX = 0,\n      minSpanX: number | null = null,\n      spanOffY = 0;\n\n    if (element.hasAttribute('line-height')) {\n      options.lineHeight = parseFloat(element.getAttribute('line-height'));\n    } else if (options['line-height']) {\n      // 从style 转换过来\n      options.lineHeight = parseFloat(options['line-height']) / 100;\n    } else {\n      options.lineHeight = 1;\n    }\n\n    if (!('textContent' in element)) {\n      if ('firstChild' in element && element.firstChild !== null) {\n        if ('data' in element.firstChild && element.firstChild.data !== null) {\n          textContent = element.firstChild.data;\n        }\n      }\n    } else {\n      textContent = element.textContent;\n      let spans = element.getElementsByTagName('tspan');\n      if (spans.length > 0) {\n        // 多行文字\n        let lines: {\n            text: string;\n            left: number;\n            width?: number;\n            right?: number;\n          }[] = [],\n          lineText = '',\n          curLineLeft = 0, // 本行的 水平方向位置\n          preSpanTop = 0, // 前一个tspan的 垂直方向位置\n          sumDx = 0,\n          sumDy = 0;\n        for (let i = 0; i < spans.length; i++) {\n          var parsedSpanAttributes = parseAttributes(\n            spans[i],\n            Textbox.ATTRIBUTE_NAMES,\n          );\n\n          sumDx += Number(parsedSpanAttributes.dx) || 0;\n          sumDy += Number(parsedSpanAttributes.dy) || 0;\n          // 在使用tspan判断对齐时spanX取最小的值\n          if ('left' in parsedSpanAttributes) {\n            let spanX = parseFloat(parsedSpanAttributes.left);\n            minSpanX = minSpanX != null ? Math.min(minSpanX, spanX) : spanX;\n          }\n          // 现在多个tspan用多行文字, 所以对齐和位置只处理第一个 tspan\n          if (i === 0) {\n            if (spans[i].hasAttribute('text-anchor')) {\n              // 水平方向\n              let anchor = spans[i].getAttribute('text-anchor');\n              // left | center | right\n              // start | middle | end\n              if (anchor === 'middle') {\n                parsedAnchor = 'center';\n              } else if (anchor === 'end') {\n                parsedAnchor = 'right';\n              }\n              calcHorAlign = false;\n            }\n            if (spans[i].hasAttribute('alignment-baseline')) {\n              // 垂直方向\n              alignmentBaseline = spans[i].getAttribute('alignment-baseline');\n            }\n\n            // 处理tspan设置的位置和偏移\n            // top只处理第一行\n            if ('top' in parsedSpanAttributes) {\n              let spanY = parseFloat(parsedSpanAttributes.top);\n              spanY += sumDy;\n\n              spanOffY = options.top - spanY;\n            }\n          }\n\n          // 多个tspan可能在一行 根据 y position判断是否换行\n          // 另外没有对齐方式的，计算判断对齐方式，根据left的值判断对齐方式\n          (parsedSpanAttributes as any).left =\n            Number(parsedSpanAttributes.left || 0) + sumDx;\n          (parsedSpanAttributes as any).top =\n            Number(parsedSpanAttributes.top || 0) + sumDy;\n\n          if (i === 0 || preSpanTop === (parsedSpanAttributes as any).top) {\n            lineText += spans[i].textContent;\n            if (i === 0) {\n              curLineLeft = Number((parsedSpanAttributes as any).left || 0);\n            }\n          } else {\n            lines.push({ text: lineText, left: curLineLeft });\n            lineText = spans[i].textContent;\n            curLineLeft = Number((parsedSpanAttributes as any).left || 0);\n          }\n\n          // 记住上一个span的Y位置\n          preSpanTop = (parsedSpanAttributes as any).top;\n        }\n        // 加上最后一行\n        if (lineText.length) {\n          lines.push({ text: lineText, left: curLineLeft });\n        }\n        // 加上换行符号\n        textContent = lines.map((v) => v.text).join('\\n');\n        lineCnt = lines.length;\n\n        if (calcHorAlign) {\n          if (lines.some((v) => v.left !== curLineLeft)) {\n            // 取最left的最小值,和right最大值\n            let minLeft = lines[0].left,\n              maxRight = lines[0].left,\n              maxWidth = 0,\n              minCenter: number | null = null,\n              maxCenter: number | null = null,\n              maxChar = 0,\n              maxCharIdx = 0;\n\n            lines.forEach((v, idx) => {\n              minLeft = Math.min(minLeft, v.left);\n              v.width = new Textbox(v.text, options).calcTextWidth();\n              v.right = v.left + (v.width || 0);\n              maxRight = Math.max(maxRight, v.right);\n              maxWidth = Math.max(maxWidth, v.width || 0);\n              if (v.width) {\n                const center = v.left + (v.width || 0) / 2;\n                minCenter = minCenter ? Math.min(minCenter, center) : center;\n                maxCenter = maxCenter ? Math.max(maxCenter, center) : center;\n\n                if (v.text.length > maxChar) {\n                  maxChar = v.text.length;\n                  maxCharIdx = idx;\n                }\n              }\n            });\n            let leftGap = 0,\n              rightGap = 0;\n            lines.forEach((v) => {\n              leftGap = Math.max(leftGap, (v.left || 0) - minLeft);\n              rightGap = Math.max(rightGap, maxRight - (v.right || 0));\n            });\n            // 计算中心点位置\n            if (maxCenter !== null && minCenter !== null) {\n              const centerOff = maxCenter - minCenter;\n              const onCharWidth =\n                (lines[maxCharIdx].width || 0) / lines[maxCharIdx].text.length;\n              // 左边间距大于右边两倍 或者 中心点偏移超过1/2字符且右边间距小于1/3字符\n              if (\n                leftGap / (rightGap || 1) > 2 &&\n                centerOff > onCharWidth / 2 &&\n                rightGap < onCharWidth / 3\n              ) {\n                calcAdjustHorAlign = 'right';\n              } else if (centerOff < onCharWidth / 2) {\n                calcAdjustHorAlign = 'center';\n              }\n            }\n          }\n        }\n      }\n    }\n\n    // 处理文字的水平偏移\n    spanOffX = options.left - (minSpanX || 0);\n\n    // textAlign保持和parsedAnchor一致\n    options.textAlign = parsedAnchor;\n\n    // 注释掉原来删除换行等符号的代码\n    //textContent = textContent.replace(/^\\s+|\\s+$|\\n+/g, '').replace(/\\s+/g, ' ');\n    var originalStrokeWidth = options.strokeWidth;\n    options.strokeWidth = 0;\n\n    // 导入时打开自动计算文字高度，否则文字高度和 Y 轴位置错误\n    Textbox.enableCalcTextHeight = true;\n    var text = new Textbox(textContent, options),\n      textOneLineHeight = text.height / lineCnt,\n      textHeightScaleFactor = text.getScaledHeight() / text.height,\n      lineHeightDiff =\n        (textOneLineHeight + text.strokeWidth) * text.lineHeight -\n        textOneLineHeight,\n      scaledDiff = lineHeightDiff * textHeightScaleFactor,\n      textHeight = text.getScaledHeight() / lineCnt + scaledDiff,\n      offX = 0,\n      offY = 0;\n\n    // 默认 alignment-baseline=\"before-edge\" offY = 0\n    let offScale = 0;\n    if (alignmentBaseline === 'before-edge') {\n      // 这个是1.0 导出的偏移值，多行且间距设置大于1也会有变化\n      // 暂时这么处理， 以后再解决\n      offScale = 1.1;\n    } else if (alignmentBaseline === 'auto') {\n      // alignment-baseline=\"auto\" 或者 没有设置\n      offScale = 0.02913333333;\n    }\n\n    if (offScale !== 0) {\n      offY =\n        (textHeight - text.fontSize * (offScale + text._fontSizeFraction)) /\n        text.lineHeight;\n    }\n\n    // 取消自动计算文字高度\n    Textbox.enableCalcTextHeight = false;\n    /*\n      Adjust positioning:\n        x/y attributes in SVG correspond to the bottom-left corner of text bounding box\n        fabric output by default at top, left.\n    */\n    const adjustOption: Partial<ITextProps> = {};\n    // 源码中options.width总是等于svg的宽度, 之前是new Text不能编辑文字，宽度设置为svg宽度也无效\n    // 修改为 new fabric.Textbox，并且设置为计算的正确宽度\n    const originWidth = options.width;\n    let width = text.calcTextWidth();\n    // 有时候计算出来的大小是不对的， 可能是字体的原因， 导致TextBox自动换行了, 所以加i字符的宽度\n    const oneIWidth = (new Textbox('i', options) as any).calcTextWidth();\n    width += oneIWidth;\n\n    if (originWidth !== width) {\n      adjustOption.width = width;\n    }\n    if (calcAdjustHorAlign) {\n      // tspan 按字符分开计算的对齐\n      adjustOption.textAlign = calcAdjustHorAlign;\n    }\n\n    // 2021.1.29修改\n    // Vectr1.0导出的svg，需要处理水平居中或右对齐偏移\n    // Vectr2.0导出的是按照span位置判断的对齐不需要处理整个width的位置偏移， 但是需要处理 oneIWidth的位置偏移\n    const textAlign = calcAdjustHorAlign || parsedAnchor;\n    if (textAlign === 'center') {\n      offX = (parsedAnchor === 'center' ? width : oneIWidth) / 2;\n    } else if (textAlign === 'right') {\n      offX = parsedAnchor === 'right' ? width : oneIWidth;\n    }\n\n    text.set({\n      ...adjustOption,\n      left: text.left - offX - spanOffX,\n      top: text.top - offY - spanOffY,\n      strokeWidth:\n        typeof originalStrokeWidth !== 'undefined' ? originalStrokeWidth : 1,\n    });\n    return text;\n  }\n}\n\nclassRegistry.setClass(Textbox);\nclassRegistry.setSVGClass(Textbox);\n"],"names":["textboxDefaultValues","minWidth","dynamicMinWidth","lockScalingFlip","noScaleCache","_wordJoiners","splitByGrapheme","Textbox","IText","getDefaults","super","ownDefaults","constructor","text","options","createControls","controls","createTextboxDefaultControls","initDimensions","this","initialized","isEditing","initDelayedCursor","_clearCache","_styleMap","_generateStyleMap","_splitText","path","width","_set","textAlign","includes","JUSTIFY","enlargeSpaces","enableCalcTextHeight","height","calcTextHeight","preventGroupCache","textInfo","realLineCount","realLineCharCount","charCount","map","i","graphemeLines","length","graphemeText","_reSpaceAndTab","test","line","offset","styleHas","property","lineIndex","isWrapping","isEmptyStyles","styles","nextOffset","nextLineIndex","shouldLimit","mapNextLine","obj","p1","p2","p2Number","parseInt","p3","_getStyleDeclaration","charIndex","_setStyleDeclaration","style","_deleteStyleDeclaration","_getLineStyle","_setLineStyle","_wrapText","lines","desiredWidth","data","getGraphemeDataForRender","wrapped","wordsData","push","_wrapLine","infix","largestWordWidth","wordsOrGraphemes","graphemeSplit","wordSplit","word","graphemeArray","_measureWord","Math","max","prevGrapheme","charOffset","arguments","undefined","len","_getGraphemeBox","kernedWidth","value","split","_ref","reservedSpace","additionalSpace","_getWidthOfCharSpacing","lineWidth","infixWidth","lineJustStarted","maxWidth","wordWidth","concat","isEndOfWrapping","missingNewlineOffset","skipWrapping","_splitTextIntoLines","wrapWidth","defaultWrapWidth","newText","Array","join","getMinWidth","_removeExtraneousStyles","linesToKeep","Map","prop","propNumber","_textLines","set","has","toObject","propertiesToInclude","fromElement","element","cssRules","parsedAttributes","parseAttributes","ATTRIBUTE_NAMES","textOptions","JSON","parse","stringify","fontFamily","slice","top","left","textDecoration","indexOf","underline","overline","linethrough","dx","dy","fontSize","DEFAULT_SVG_FONT_SIZE","paths","getElementsByTagName","_fromTextPath","_fromTextSpan","_findSvgTextPath","id","closest","querySelector","textContent","parsedAnchor","textAnchor","calcHorAlign","calcAdjustHorAlign","lineCnt","alignmentBaseline","spanOffX","minSpanX","spanOffY","hasAttribute","lineHeight","parseFloat","getAttribute","spans","lineText","curLineLeft","preSpanTop","sumDx","sumDy","parsedSpanAttributes","Number","spanX","min","anchor","spanY","v","some","minLeft","maxRight","minCenter","maxCenter","maxChar","maxCharIdx","forEach","idx","calcTextWidth","right","center","leftGap","rightGap","centerOff","onCharWidth","firstChild","originalStrokeWidth","strokeWidth","textOneLineHeight","textHeightScaleFactor","getScaledHeight","scaledDiff","textHeight","offX","offY","offScale","_fontSizeFraction","adjustOption","originWidth","oneIWidth","_Textbox","_defineProperty","textLayoutProperties","textPath","textPathParsedAttributes","href","startsWith","pathElement","pathParsedAttributes","Path","d","stroke","fill","visible","fontStyle","fontWeight","pathType","classRegistry","setClass","setSVGClass"],"mappings":"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"}