importSvgString = (xmlString, preserveDimension) => {
  const dataStorage = svgCanvas.getDataStorage()
  let j
  let ts
  let useEl
  try {
    // Get unique ID
    const uid = hashCode(xmlString)

    let useExisting = false
    // Look for symbol and make sure symbol exists in image
    if (svgCanvas.getImportIds(uid) && svgCanvas.getImportIds(uid).symbol) {
      const parents = getParents(svgCanvas.getImportIds(uid).symbol, '#svgroot')
      if (parents?.length) {
        useExisting = true
      }
    }

    const batchCmd = new BatchCommand('Import Image')
    let symbol
    if (useExisting) {
      symbol = svgCanvas.getImportIds(uid).symbol
      ts = svgCanvas.getImportIds(uid).xform
    } else {
      // convert string into XML document
      const newDoc = text2xml(xmlString)

      svgCanvas.prepareSvg(newDoc)

      // import new svg document into our document
      // If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode()
      const svg = svgCanvas.getDOMDocument().adoptNode
        ? svgCanvas.getDOMDocument().adoptNode(newDoc.documentElement)
        : svgCanvas.getDOMDocument().importNode(newDoc.documentElement, true)

      svgCanvas.uniquifyElems(svg)

      const innerw = convertToNum('width', svg.getAttribute('width'))
      const innerh = convertToNum('height', svg.getAttribute('height'))
      const innervb = svg.getAttribute('viewBox')
      // if no explicit viewbox, create one out of the width and height
      const vb = innervb ? innervb.split(/[ ,]+/) : [0, 0, innerw, innerh]
      for (j = 0; j < 4; ++j) {
        vb[j] = Number(vb[j])
      }

      // TODO: properly handle preserveAspectRatio
      const // canvasw = +svgContent.getAttribute('width'),
        canvash = Number(svgCanvas.getSvgContent().getAttribute('height'))
      // imported content should be 1/3 of the canvas on its largest dimension

      ts =
        innerh > innerw
          ? 'scale(' + canvash / 3 / vb[3] + ')'
          : 'scale(' + canvash / 3 / vb[2] + ')'

      // Hack to make recalculateDimensions understand how to scale
      ts = 'translate(0) ' + ts + ' translate(0)'

      symbol = svgCanvas.getDOMDocument().createElementNS(NS.SVG, 'symbol')
      const defs = findDefs()

      if (isGecko()) {
        // Move all gradients into root for Firefox, workaround for this bug:
        // https://bugzilla.mozilla.org/show_bug.cgi?id=353575
        // TODO: Make this properly undo-able.
        const elements = svg.querySelectorAll(
          'linearGradient, radialGradient, pattern'
        )
        Array.prototype.forEach.call(elements, el => {
          defs.appendChild(el)
        })
      }

      while (svg.firstChild) {
        const first = svg.firstChild
        symbol.append(first)
      }
      const attrs = svg.attributes
      for (const attr of attrs) {
        // Ok for `NamedNodeMap`
        symbol.setAttribute(attr.nodeName, attr.value)
      }
      symbol.id = svgCanvas.getNextId()

      // Store data
      svgCanvas.setImportIds(uid, {
        symbol,
        xform: ts
      })

      findDefs().append(symbol)
      batchCmd.addSubCommand(new InsertElementCommand(symbol))
    }

    useEl = svgCanvas.getDOMDocument().createElementNS(NS.SVG, 'use')
    useEl.id = svgCanvas.getNextId()
    svgCanvas.setHref(useEl, '#' + symbol.id)
    ;(
      svgCanvas.getCurrentGroup() ||
      svgCanvas.getCurrentDrawing().getCurrentLayer()
    ).append(useEl)
    batchCmd.addSubCommand(new InsertElementCommand(useEl))
    svgCanvas.clearSelection()

    if (!preserveDimension) {
      useEl.setAttribute('transform', ts)
      recalculateDimensions(useEl)
    }
    dataStorage.put(useEl, 'symbol', symbol)
    dataStorage.put(useEl, 'ref', symbol)
    svgCanvas.addToSelection([useEl])

    // TODO: Find way to add this in a recalculateDimensions-parsable way
    // if (vb[0] !== 0 || vb[1] !== 0) {
    //   ts = 'translate(' + (-vb[0]) + ',' + (-vb[1]) + ') ' + ts;
    // }
    svgCanvas.addCommandToHistory(batchCmd)
    svgCanvas.call('changed', [svgCanvas.getSvgContent()])
  } catch (e) {
    console.error(e)
    return null
  }

  // we want to return the element so we can automatically select it
  return useEl
}
This function imports the input SVG XML as a `<symbol>` in the `<defs>`, then adds a
`<use>` to the current layer.
@function module:svgcanvas.SvgCanvas#importSvgString
@param {string} xmlString - The SVG as XML text.
@param {boolean} preserveDimension - A boolean to force to preserve initial dimension of the imported svg (force svgEdit don't apply a transformation on the imported svg)
@fires module:svgcanvas.SvgCanvas#event:changed
@returns {null|Element} This function returns null if the import was unsuccessful, or the element otherwise.
@todo
- properly handle if namespace is introduced by imported content (must add to svgcontent
and update all prefixes in the imported node)
- properly handle recalculating dimensions, `recalculateDimensions()` doesn't handle
arbitrary transform lists, but makes some assumptions about how the transform list
was obtained