{"version":3,"file":"elements_parser.min.mjs","sources":["../../../src/parser/elements_parser.ts"],"sourcesContent":["import { Gradient } from '../gradient/Gradient';\nimport { Group } from '../shapes/Group';\nimport { FabricImage } from '../shapes/Image';\nimport { classRegistry } from '../ClassRegistry';\nimport {\n  invertTransform,\n  multiplyTransformMatrices,\n  qrDecompose,\n} from '../util/misc/matrix';\nimport { removeTransformMatrixForSvgParsing } from '../util/transform_matrix_removal';\nimport type { FabricObject } from '../shapes/Object/FabricObject';\nimport { Point } from '../Point';\nimport { CENTER, FILL, STROKE } from '../constants';\nimport { getGradientDefs } from './getGradientDefs';\nimport { getCSSRules } from './getCSSRules';\nimport type { LoadImageOptions } from '../util';\nimport type { CSSRules, TSvgReviverCallback } from './typedefs';\nimport type { ParsedViewboxTransform } from './applyViewboxTransform';\nimport type { SVGOptions } from '../gradient';\nimport { getTagName } from './getTagName';\nimport { parseTransformAttribute } from './parseTransformAttribute';\n\nconst findTag = (el: Element) =>\n  classRegistry.getSVGClass(getTagName(el).toLowerCase());\n\ntype StorageType = {\n  fill: SVGGradientElement;\n  stroke: SVGGradientElement;\n  clipPath: Element[];\n};\n\ntype NotParsedFabricObject = FabricObject & {\n  fill: string;\n  stroke: string;\n  clipPath?: string;\n  clipRule?: CanvasFillRule;\n};\n\nexport class ElementsParser {\n  declare elements: Element[];\n  declare options: LoadImageOptions & ParsedViewboxTransform;\n  declare reviver?: TSvgReviverCallback;\n  declare regexUrl: RegExp;\n  declare doc: Document;\n  declare clipPaths: Record<string, Element[]>;\n  declare gradientDefs: Record<string, SVGGradientElement>;\n  declare cssRules: CSSRules;\n\n  constructor(\n    elements: Element[],\n    options: LoadImageOptions & ParsedViewboxTransform,\n    reviver: TSvgReviverCallback | undefined,\n    doc: Document,\n    clipPaths: Record<string, Element[]>,\n  ) {\n    this.elements = elements;\n    this.options = options;\n    this.reviver = reviver;\n    this.regexUrl = /^url\\(['\"]?#([^'\"]+)['\"]?\\)/g;\n    this.doc = doc;\n    this.clipPaths = clipPaths;\n    this.gradientDefs = getGradientDefs(doc);\n    this.cssRules = getCSSRules(doc);\n  }\n\n  parse(): Promise<Array<FabricObject | null>> {\n    return Promise.all(\n      this.elements.map((element) => this.createObject(element)),\n    );\n  }\n\n  async createObject(el: Element): Promise<FabricObject | null> {\n    const klass = findTag(el);\n    if (klass) {\n      const obj: NotParsedFabricObject = await klass.fromElement(\n        el,\n        this.options,\n        this.cssRules,\n      );\n      this.resolveGradient(obj, el, FILL);\n      this.resolveGradient(obj, el, STROKE);\n      if (obj instanceof FabricImage && obj._originalElement) {\n        removeTransformMatrixForSvgParsing(\n          obj,\n          obj.parsePreserveAspectRatioAttribute(),\n        );\n      } else {\n        removeTransformMatrixForSvgParsing(obj);\n      }\n      await this.resolveClipPath(obj, el);\n      this.reviver && this.reviver(el, obj);\n      return obj;\n    }\n    return null;\n  }\n\n  extractPropertyDefinition(\n    obj: NotParsedFabricObject,\n    property: 'fill' | 'stroke' | 'clipPath',\n    storage: Record<string, StorageType[typeof property]>,\n  ): StorageType[typeof property] | undefined {\n    const value = obj[property]!,\n      regex = this.regexUrl;\n    if (!regex.test(value)) {\n      return undefined;\n    }\n    // verify: can we remove the 'g' flag? and remove lastIndex changes?\n    regex.lastIndex = 0;\n    // we passed the regex test, so we know is not null;\n    const id = regex.exec(value)![1];\n    regex.lastIndex = 0;\n    // @todo fix this\n    return storage[id];\n  }\n\n  resolveGradient(\n    obj: NotParsedFabricObject,\n    el: Element,\n    property: 'fill' | 'stroke',\n  ) {\n    const gradientDef = this.extractPropertyDefinition(\n      obj,\n      property,\n      this.gradientDefs,\n    ) as SVGGradientElement;\n    if (gradientDef) {\n      const opacityAttr = el.getAttribute(property + '-opacity');\n      const gradient = Gradient.fromElement(gradientDef, obj, {\n        ...this.options,\n        opacity: opacityAttr,\n      } as SVGOptions);\n      obj.set(property, gradient);\n    }\n  }\n\n  // TODO: resolveClipPath could be run once per clippath with minor work per object.\n  // is a refactor that i m not sure is worth on this code\n  async resolveClipPath(\n    obj: NotParsedFabricObject,\n    usingElement: Element,\n    exactOwner?: Element,\n  ) {\n    const clipPathElements = this.extractPropertyDefinition(\n      obj,\n      'clipPath',\n      this.clipPaths,\n    ) as Element[];\n    if (clipPathElements) {\n      const objTransformInv = invertTransform(obj.calcTransformMatrix());\n      const clipPathTag = clipPathElements[0].parentElement!;\n      let clipPathOwner = usingElement;\n      while (\n        !exactOwner &&\n        clipPathOwner.parentElement &&\n        clipPathOwner.getAttribute('clip-path') !== obj.clipPath\n      ) {\n        clipPathOwner = clipPathOwner.parentElement;\n      }\n      // move the clipPath tag as sibling to the real element that is using it\n      clipPathOwner.parentElement!.appendChild(clipPathTag);\n\n      // this multiplication order could be opposite.\n      // but i don't have an svg to test it\n      // at the first SVG that has a transform on both places and is misplaced\n      // try to invert this multiplication order\n      const finalTransform = parseTransformAttribute(\n        `${clipPathOwner.getAttribute('transform') || ''} ${\n          clipPathTag.getAttribute('originalTransform') || ''\n        }`,\n      );\n\n      clipPathTag.setAttribute(\n        'transform',\n        `matrix(${finalTransform.join(',')})`,\n      );\n\n      const container = await Promise.all(\n        clipPathElements.map((clipPathElement) => {\n          return findTag(clipPathElement)\n            .fromElement(clipPathElement, this.options, this.cssRules)\n            .then((enlivedClippath: NotParsedFabricObject) => {\n              removeTransformMatrixForSvgParsing(enlivedClippath);\n              enlivedClippath.fillRule = enlivedClippath.clipRule!;\n              delete enlivedClippath.clipRule;\n              return enlivedClippath;\n            });\n        }),\n      );\n      const clipPath =\n        container.length === 1 ? container[0] : new Group(container);\n      const gTransform = multiplyTransformMatrices(\n        objTransformInv,\n        clipPath.calcTransformMatrix(),\n      );\n      if (clipPath.clipPath) {\n        await this.resolveClipPath(\n          clipPath,\n          clipPathOwner,\n          // this is tricky.\n          // it tries to differentiate from when clipPaths are inherited by outside groups\n          // or when are really clipPaths referencing other clipPaths\n          clipPathTag.getAttribute('clip-path') ? clipPathOwner : undefined,\n        );\n      }\n      const { scaleX, scaleY, angle, skewX, translateX, translateY } =\n        qrDecompose(gTransform);\n      clipPath.set({\n        flipX: false,\n        flipY: false,\n      });\n      clipPath.set({\n        scaleX,\n        scaleY,\n        angle,\n        skewX,\n        skewY: 0,\n      });\n      clipPath.setPositionByOrigin(\n        new Point(translateX, translateY),\n        CENTER,\n        CENTER,\n      );\n      obj.clipPath = clipPath;\n    } else {\n      // if clip-path does not resolve to any element, delete the property.\n      delete obj.clipPath;\n      return;\n    }\n  }\n}\n"],"names":["findTag","el","classRegistry","getSVGClass","getTagName","toLowerCase","ElementsParser","constructor","elements","options","reviver","doc","clipPaths","this","regexUrl","gradientDefs","getGradientDefs","cssRules","getCSSRules","parse","Promise","all","map","element","createObject","klass","obj","fromElement","resolveGradient","FILL","STROKE","FabricImage","_originalElement","removeTransformMatrixForSvgParsing","parsePreserveAspectRatioAttribute","resolveClipPath","extractPropertyDefinition","property","storage","value","regex","test","lastIndex","id","exec","gradientDef","opacityAttr","getAttribute","gradient","Gradient","opacity","set","usingElement","exactOwner","clipPathElements","objTransformInv","invertTransform","calcTransformMatrix","clipPathTag","parentElement","clipPathOwner","clipPath","appendChild","finalTransform","parseTransformAttribute","setAttribute","join","container","clipPathElement","then","enlivedClippath","fillRule","clipRule","length","Group","gTransform","multiplyTransformMatrices","undefined","scaleX","scaleY","angle","skewX","translateX","translateY","qrDecompose","flipX","flipY","skewY","setPositionByOrigin","Point","CENTER"],"mappings":"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"}