@svgedit/svgcanvas 7.2.4 → 7.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGES.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # svgcanvas CHANGES
2
2
 
3
+
4
+ ## 7.2.7
5
+ - Prefer href to xlink href (#1059)
6
+ - Fix group rotation (#1058)
7
+ - Fixed a bug where a rotated text or image did not translate correctly. (#1055)
8
+
9
+ ## 7.2.5
10
+ - update dependencies
11
+
3
12
  ## 7.2.4
4
13
  - bug fixes
5
14
  - update dependencies
package/core/event.js CHANGED
@@ -662,9 +662,18 @@ const mouseUpEvent = (evt) => {
662
662
  const elem = selectedElements[0]
663
663
  if (elem) {
664
664
  elem.removeAttribute('style')
665
- walkTree(elem, (el) => {
666
- el.removeAttribute('style')
667
- })
665
+
666
+ // we don't remove the style elements for contents of foreignObjects
667
+ // because that is a valid way to style them
668
+ if (elem.localName === 'foreignObject') {
669
+ walkTree(elem, (el) => {
670
+ el.style.removeProperty('pointer-events')
671
+ })
672
+ } else {
673
+ walkTree(elem, (el) => {
674
+ el.removeAttribute('style')
675
+ })
676
+ }
668
677
  }
669
678
  }
670
679
  return
package/core/history.js CHANGED
@@ -425,7 +425,7 @@ export class BatchCommand extends Command {
425
425
  */
426
426
  unapply (handler) {
427
427
  super.unapply(handler, () => {
428
- this.stack.reverse().forEach((stackItem) => {
428
+ [...this.stack].reverse().forEach((stackItem) => {
429
429
  console.assert(stackItem, 'stack item should not be null')
430
430
  stackItem && stackItem.unapply(handler)
431
431
  })
package/core/paint.js CHANGED
@@ -4,7 +4,7 @@
4
4
  export default class Paint {
5
5
  /**
6
6
  * @param {module:jGraduate.jGraduatePaintOptions} [opt]
7
- */
7
+ */
8
8
  constructor (opt) {
9
9
  const options = opt || {}
10
10
  this.alpha = isNaN(options.alpha) ? 100 : options.alpha
@@ -51,33 +51,39 @@ export default class Paint {
51
51
  this.radialGradient = options.copy.radialGradient.cloneNode(true)
52
52
  break
53
53
  }
54
- // create linear gradient paint
54
+ // create linear gradient paint
55
55
  } else if (options.linearGradient) {
56
56
  this.type = 'linearGradient'
57
57
  this.solidColor = null
58
58
  this.radialGradient = null
59
- if (options.linearGradient.hasAttribute('xlink:href')) {
60
- const xhref = document.getElementById(options.linearGradient.getAttribute('xlink:href').substr(1))
59
+ const hrefAttr =
60
+ options.linearGradient.getAttribute('href') ||
61
+ options.linearGradient.getAttribute('xlink:href')
62
+ if (hrefAttr) {
63
+ const xhref = document.getElementById(hrefAttr.replace(/^#/, ''))
61
64
  this.linearGradient = xhref.cloneNode(true)
62
65
  } else {
63
66
  this.linearGradient = options.linearGradient.cloneNode(true)
64
67
  }
65
- // create linear gradient paint
68
+ // create linear gradient paint
66
69
  } else if (options.radialGradient) {
67
70
  this.type = 'radialGradient'
68
71
  this.solidColor = null
69
72
  this.linearGradient = null
70
- if (options.radialGradient.hasAttribute('xlink:href')) {
71
- const xhref = document.getElementById(options.radialGradient.getAttribute('xlink:href').substr(1))
73
+ const hrefAttr =
74
+ options.radialGradient.getAttribute('href') ||
75
+ options.radialGradient.getAttribute('xlink:href')
76
+ if (hrefAttr) {
77
+ const xhref = document.getElementById(hrefAttr.replace(/^#/, ''))
72
78
  this.radialGradient = xhref.cloneNode(true)
73
79
  } else {
74
80
  this.radialGradient = options.radialGradient.cloneNode(true)
75
81
  }
76
- // create solid color paint
82
+ // create solid color paint
77
83
  } else if (options.solidColor) {
78
84
  this.type = 'solidColor'
79
85
  this.solidColor = options.solidColor
80
- // create empty paint
86
+ // create empty paint
81
87
  } else {
82
88
  this.type = 'none'
83
89
  this.solidColor = null
@@ -236,16 +236,35 @@ export const recalculateDimensions = selected => {
236
236
  // Handle rotation transformations
237
237
  const angle = getRotationAngle(selected)
238
238
  if (angle) {
239
- // Include x and y in the rotation center calculation
240
- oldcenter = {
241
- x: box.x + box.width / 2 + x,
242
- y: box.y + box.height / 2 + y
239
+ if (selected.localName === 'image') {
240
+ // Use the center of the image as the rotation center
241
+ const xAttr = convertToNum('x', selected.getAttribute('x') || '0')
242
+ const yAttr = convertToNum('y', selected.getAttribute('y') || '0')
243
+ const width = convertToNum('width', selected.getAttribute('width') || '0')
244
+ const height = convertToNum('height', selected.getAttribute('height') || '0')
245
+ const cx = xAttr + width / 2
246
+ const cy = yAttr + height / 2
247
+ oldcenter = { x: cx, y: cy }
248
+ const transform = transformListToTransform(tlist).matrix
249
+ newcenter = transformPoint(cx, cy, transform)
250
+ } else if (selected.localName === 'text') {
251
+ // Use the center of the bounding box as the rotation center for text
252
+ const cx = box.x + box.width / 2
253
+ const cy = box.y + box.height / 2
254
+ oldcenter = { x: cx, y: cy }
255
+ newcenter = transformPoint(cx, cy, transformListToTransform(tlist).matrix)
256
+ } else {
257
+ // Include x and y in the rotation center calculation for other elements
258
+ oldcenter = {
259
+ x: box.x + box.width / 2 + x,
260
+ y: box.y + box.height / 2 + y
261
+ }
262
+ newcenter = transformPoint(
263
+ box.x + box.width / 2 + x,
264
+ box.y + box.height / 2 + y,
265
+ transformListToTransform(tlist).matrix
266
+ )
243
267
  }
244
- newcenter = transformPoint(
245
- box.x + box.width / 2 + x,
246
- box.y + box.height / 2 + y,
247
- transformListToTransform(tlist).matrix
248
- )
249
268
 
250
269
  // Remove the rotation transform from the list
251
270
  for (let i = 0; i < tlist.numberOfItems; ++i) {
package/core/sanitize.js CHANGED
@@ -21,7 +21,7 @@ const REVERSE_NS = getReverseNS()
21
21
  const svgGenericWhiteList = ['class', 'id', 'display', 'transform', 'style']
22
22
  const svgWhiteList_ = {
23
23
  // SVG Elements
24
- a: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'xlink:href', 'xlink:title'],
24
+ a: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'href', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'xlink:href', 'xlink:title'],
25
25
  circle: ['clip-path', 'clip-rule', 'cx', 'cy', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'r', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage'],
26
26
  clipPath: ['clipPathUnits'],
27
27
  defs: [],
@@ -36,20 +36,20 @@ const svgWhiteList_ = {
36
36
  feMergeNode: ['in'],
37
37
  feMorphology: ['in', 'operator', 'radius'],
38
38
  feOffset: ['dx', 'in', 'dy', 'result'],
39
- filter: ['color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y'],
39
+ filter: ['color-interpolation-filters', 'filterRes', 'filterUnits', 'height', 'href', 'primitiveUnits', 'requiredFeatures', 'width', 'x', 'xlink:href', 'y'],
40
40
  foreignObject: ['font-size', 'height', 'opacity', 'requiredFeatures', 'width', 'x', 'y'],
41
41
  g: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'font-family', 'font-size', 'font-style', 'font-weight', 'text-anchor'],
42
- image: ['clip-path', 'clip-rule', 'filter', 'height', 'mask', 'opacity', 'requiredFeatures', 'systemLanguage', 'width', 'x', 'xlink:href', 'xlink:title', 'y'],
42
+ image: ['clip-path', 'clip-rule', 'filter', 'height', 'mask', 'opacity', 'requiredFeatures', 'systemLanguage', 'width', 'x', 'href', 'xlink:href', 'xlink:title', 'y'],
43
43
  line: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'x1', 'x2', 'y1', 'y2'],
44
- linearGradient: ['gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'xlink:href', 'y1', 'y2'],
44
+ linearGradient: ['gradientTransform', 'gradientUnits', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'x1', 'x2', 'href', 'xlink:href', 'y1', 'y2'],
45
45
  marker: ['markerHeight', 'markerUnits', 'markerWidth', 'orient', 'preserveAspectRatio', 'refX', 'refY', 'se_type', 'systemLanguage', 'viewBox'],
46
46
  mask: ['height', 'maskContentUnits', 'maskUnits', 'width', 'x', 'y'],
47
47
  metadata: [],
48
48
  path: ['clip-path', 'clip-rule', 'd', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage'],
49
- pattern: ['height', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'systemLanguage', 'viewBox', 'width', 'x', 'xlink:href', 'y'],
49
+ pattern: ['height', 'patternContentUnits', 'patternTransform', 'patternUnits', 'requiredFeatures', 'systemLanguage', 'viewBox', 'width', 'x', 'href', 'xlink:href', 'y'],
50
50
  polygon: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'sides', 'shape', 'edge', 'point', 'starRadiusMultiplier', 'r', 'radialshift', 'r2', 'orient', 'cx', 'cy'],
51
51
  polyline: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'points', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'se:connector'],
52
- radialGradient: ['cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'xlink:href'],
52
+ radialGradient: ['cx', 'cy', 'fx', 'fy', 'gradientTransform', 'gradientUnits', 'r', 'requiredFeatures', 'spreadMethod', 'systemLanguage', 'href', 'xlink:href'],
53
53
  rect: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'mask', 'opacity', 'requiredFeatures', 'rx', 'ry', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'width', 'x', 'y'],
54
54
  stop: ['offset', 'requiredFeatures', 'stop-opacity', 'systemLanguage', 'stop-color', 'gradientUnits', 'gradientTransform'],
55
55
  style: ['type'],
@@ -57,10 +57,22 @@ const svgWhiteList_ = {
57
57
  switch: ['requiredFeatures', 'systemLanguage'],
58
58
  symbol: ['fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'opacity', 'overflow', 'preserveAspectRatio', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'viewBox', 'width', 'height'],
59
59
  text: ['clip-path', 'clip-rule', 'dominant-baseline', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'mask', 'opacity', 'requiredFeatures', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'text-anchor', 'letter-spacing', 'word-spacing', 'text-decoration', 'textLength', 'lengthAdjust', 'x', 'xml:space', 'y'],
60
- textPath: ['dominant-baseline', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'systemLanguage', 'xlink:href'],
60
+ textPath: ['dominant-baseline', 'href', 'method', 'requiredFeatures', 'spacing', 'startOffset', 'systemLanguage', 'xlink:href'],
61
61
  title: [],
62
62
  tspan: ['clip-path', 'clip-rule', 'dx', 'dy', 'dominant-baseline', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'font-family', 'font-size', 'font-style', 'font-weight', 'mask', 'opacity', 'requiredFeatures', 'rotate', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage', 'text-anchor', 'textLength', 'x', 'xml:space', 'y'],
63
- use: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'width', 'x', 'xlink:href', 'y', 'overflow'],
63
+ use: ['clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'height', 'href', 'mask', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'width', 'x', 'xlink:href', 'y', 'overflow'],
64
+ // Filter Primitives
65
+ feComponentTransfer: ['in', 'result'],
66
+ feFuncR: ['type', 'tableValues', 'slope', 'intercept', 'amplitude', 'exponent', 'offset'],
67
+ feFuncG: ['type', 'tableValues', 'slope', 'intercept', 'amplitude', 'exponent', 'offset'],
68
+ feFuncB: ['type', 'tableValues', 'slope', 'intercept', 'amplitude', 'exponent', 'offset'],
69
+ feFuncA: ['type', 'tableValues', 'slope', 'intercept', 'amplitude', 'exponent', 'offset'],
70
+ feConvolveMatrix: ['in', 'order', 'kernelMatrix', 'divisor', 'bias', 'targetX', 'targetY', 'edgeMode', 'kernelUnitLength', 'preserveAlpha'],
71
+ feDiffuseLighting: ['in', 'surfaceScale', 'diffuseConstant', 'kernelUnitLength', 'lighting-color'],
72
+ feSpecularLighting: ['in', 'surfaceScale', 'specularConstant', 'specularExponent', 'kernelUnitLength', 'lighting-color'],
73
+ feDisplacementMap: ['in', 'in2', 'scale', 'xChannelSelector', 'yChannelSelector'],
74
+ feTurbulence: ['baseFrequency', 'numOctaves', 'result', 'seed', 'stitchTiles', 'type'],
75
+ feTile: ['in'],
64
76
 
65
77
  // MathML Elements
66
78
  annotation: ['encoding'],
@@ -79,7 +91,7 @@ const svgWhiteList_ = {
79
91
  mphantom: [],
80
92
  mprescripts: [],
81
93
  mroot: [],
82
- mrow: ['xlink:href', 'xlink:type', 'xmlns:xlink'],
94
+ mrow: ['href', 'xlink:href', 'xlink:type', 'xmlns:xlink'],
83
95
  mspace: ['depth', 'height', 'width'],
84
96
  msqrt: [],
85
97
  mstyle: ['displaystyle', 'mathbackground', 'mathcolor', 'mathvariant', 'scriptlevel'],
@@ -93,9 +105,25 @@ const svgWhiteList_ = {
93
105
  munder: [],
94
106
  munderover: [],
95
107
  none: [],
96
- semantics: []
108
+ semantics: [],
109
+
110
+ // HTML Elements for use in a foreignObject
111
+ div: [],
112
+ p: [],
113
+ li: [],
114
+ pre: [],
115
+ ol: [],
116
+ ul: [],
117
+ span: [],
118
+ hr: [],
119
+ br: [],
120
+ h1: [],
121
+ h2: [],
122
+ h3: [],
123
+ h4: [],
124
+ h5: [],
125
+ h6: []
97
126
  }
98
- /* eslint-enable max-len */
99
127
 
100
128
  // add generic attributes to all elements of the whitelist
101
129
  Object.keys(svgWhiteList_).forEach((element) => { svgWhiteList_[element] = [...svgWhiteList_[element], ...svgGenericWhiteList] })
@@ -162,16 +190,20 @@ export const sanitizeSvg = (node) => {
162
190
  // our whitelist or is a namespace declaration for one of our allowed namespaces
163
191
  if (attrNsURI !== allowedAttrsNS[attrLocalName] && attrNsURI !== NS.XMLNS &&
164
192
  !(attrNsURI === NS.XMLNS && REVERSE_NS[attr.value])) {
165
- // Bypassing the whitelist to allow se: and oi: prefixes
166
- // We can add specific namepaces on demand for now.
167
- // Is there a more appropriate way to do this?
168
- if (attrName.startsWith('se:') || attrName.startsWith('oi:') || attrName.startsWith('data-')) {
169
- // We should bypass the namespace aswell
170
- const seAttrNS = (attrName.startsWith('se:')) ? NS.SE : ((attrName.startsWith('oi:')) ? NS.OI : null)
171
- seAttrs.push([attrName, attr.value, seAttrNS])
172
- } else {
173
- console.warn(`sanitizeSvg: attribute ${attrName} in element ${node.nodeName} not in whitelist is removed`)
174
- node.removeAttributeNS(attrNsURI, attrLocalName)
193
+ // Special case: allow href attribute even without namespace if it's in the whitelist
194
+ const isHrefAttribute = (attrLocalName === 'href' && allowedAttrs.includes('href'))
195
+ if (!isHrefAttribute) {
196
+ // Bypassing the whitelist to allow se: and oi: prefixes
197
+ // We can add specific namepaces on demand for now.
198
+ // Is there a more appropriate way to do this?
199
+ if (attrName.startsWith('se:') || attrName.startsWith('oi:') || attrName.startsWith('data-')) {
200
+ // We should bypass the namespace aswell
201
+ const seAttrNS = (attrName.startsWith('se:')) ? NS.SE : ((attrName.startsWith('oi:')) ? NS.OI : null)
202
+ seAttrs.push([attrName, attr.value, seAttrNS])
203
+ } else {
204
+ console.warn(`sanitizeSvg: attribute ${attrName} in element ${node.nodeName} not in whitelist is removed: ${node.outerHTML}`)
205
+ node.removeAttributeNS(attrNsURI, attrLocalName)
206
+ }
175
207
  }
176
208
  }
177
209
 
@@ -196,34 +228,35 @@ export const sanitizeSvg = (node) => {
196
228
  node.setAttributeNS(ns, att, val)
197
229
  })
198
230
 
199
- // for some elements that have a xlink:href, ensure the URI refers to a local element
200
- // (but not for links)
231
+ // for some elements that have a xlink:href or href, ensure the URI refers to a local element
232
+ // (but not for links and other elements where external hrefs are allowed)
201
233
  const href = getHref(node)
202
234
  if (href &&
203
235
  ['filter', 'linearGradient', 'pattern',
204
236
  'radialGradient', 'textPath', 'use'].includes(node.nodeName) && href[0] !== '#') {
205
237
  // remove the attribute (but keep the element)
206
238
  setHref(node, '')
207
- console.warn(`sanitizeSvg: attribute href in element ${node.nodeName} pointing to a non-local reference (${href}) is removed`)
239
+ console.warn(`sanitizeSvg: attribute href in element ${node.nodeName} pointing to a non-local reference (${href}) is removed: ${node.outerHTML}`)
208
240
  node.removeAttributeNS(NS.XLINK, 'href')
241
+ node.removeAttribute('href')
209
242
  }
210
243
 
211
244
  // Safari crashes on a <use> without a xlink:href, so we just remove the node here
212
245
  if (node.nodeName === 'use' && !getHref(node)) {
213
- console.warn(`sanitizeSvg: element ${node.nodeName} without a xlink:href is removed`)
246
+ console.warn(`sanitizeSvg: element ${node.nodeName} without a xlink:href or href is removed: ${node.outerHTML}`)
214
247
  node.remove()
215
248
  return
216
249
  }
217
250
  // if the element has attributes pointing to a non-local reference,
218
251
  // need to remove the attribute
219
- Object.values(['clip-path', 'fill', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke'], (attr) => {
252
+ ['clip-path', 'fill', 'filter', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke'].forEach((attr) => {
220
253
  let val = node.getAttribute(attr)
221
254
  if (val) {
222
255
  val = getUrlFromAttr(val)
223
256
  // simply check for first character being a '#'
224
257
  if (val && val[0] !== '#') {
225
258
  node.setAttribute(attr, '')
226
- console.warn(`sanitizeSvg: attribute ${attr} in element ${node.nodeName} pointing to a non-local reference (${val}) is removed`)
259
+ console.warn(`sanitizeSvg: attribute ${attr} in element ${node.nodeName} pointing to a non-local reference (${val}) is removed: ${node.outerHTML}`)
227
260
  node.removeAttribute(attr)
228
261
  }
229
262
  }
@@ -236,7 +269,7 @@ export const sanitizeSvg = (node) => {
236
269
  } else {
237
270
  // remove all children from this node and insert them before this node
238
271
  // TODO: in the case of animation elements this will hardly ever be correct
239
- console.warn(`sanitizeSvg: element ${node.nodeName} not supported is removed`)
272
+ console.warn(`sanitizeSvg: element ${node.nodeName} not supported is removed: ${node.outerHTML}`)
240
273
  const children = []
241
274
  while (node.hasChildNodes()) {
242
275
  children.push(parent.insertBefore(node.firstChild, node))
package/core/svg-exec.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  text2xml,
13
13
  cleanupElement,
14
14
  findDefs,
15
+ setHref,
15
16
  getHref,
16
17
  preventClickDefault,
17
18
  toXml,
@@ -144,7 +145,7 @@ const svgToString = (elem, indent) => {
144
145
  out.push(' ')
145
146
  }
146
147
  out.push('<')
147
- out.push(elem.nodeName)
148
+ out.push(elem.localName)
148
149
  if (elem.id === 'svgcontent') {
149
150
  // Process root element separately
150
151
  const res = svgCanvas.getResolution()
@@ -352,7 +353,7 @@ const svgToString = (elem, indent) => {
352
353
  }
353
354
  }
354
355
  out.push('</')
355
- out.push(elem.nodeName)
356
+ out.push(elem.localName)
356
357
  out.push('>')
357
358
  } else {
358
359
  out.push('/>')
@@ -443,7 +444,8 @@ const setSvgString = (xmlString, preventUndo) => {
443
444
  // const url = decodeURIComponent(m.groups.url);
444
445
  const iimg = new Image()
445
446
  iimg.addEventListener('load', () => {
446
- image.setAttributeNS(NS.XLINK, 'xlink:href', url)
447
+ // Set the href attribute to the data URL
448
+ setHref(image, val)
447
449
  })
448
450
  iimg.src = url
449
451
  }
@@ -858,7 +860,7 @@ const convertImagesToBase64 = async svgElement => {
858
860
  const reader = new FileReader()
859
861
  return new Promise(resolve => {
860
862
  reader.onload = () => {
861
- img.setAttribute('xlink:href', reader.result)
863
+ setHref(img, reader.result)
862
864
  resolve()
863
865
  }
864
866
  reader.readAsDataURL(blob)
package/core/utilities.js CHANGED
@@ -376,21 +376,22 @@ export const getUrlFromAttr = function (attrVal) {
376
376
  /**
377
377
  * @function module:utilities.getHref
378
378
  * @param {Element} elem
379
- * @returns {string} The given element's `xlink:href` value
379
+ * @returns {string} The given element's `href` value
380
380
  */
381
381
  export let getHref = function (elem) {
382
- return elem.getAttributeNS(NS.XLINK, 'href')
382
+ // Prefer 'href', fallback to 'xlink:href'
383
+ return elem.getAttribute('href') || elem.getAttributeNS(NS.XLINK, 'href')
383
384
  }
384
385
 
385
386
  /**
386
- * Sets the given element's `xlink:href` value.
387
+ * Sets the given element's `href` value.
387
388
  * @function module:utilities.setHref
388
389
  * @param {Element} elem
389
390
  * @param {string} val
390
391
  * @returns {void}
391
392
  */
392
393
  export let setHref = function (elem, val) {
393
- elem.setAttributeNS(NS.XLINK, 'xlink:href', val)
394
+ elem.setAttribute('href', val)
394
395
  }
395
396
 
396
397
  /**
@@ -665,38 +666,38 @@ export const getPathDFromElement = function (elem) {
665
666
  const h = b.height
666
667
  num = 4 - num // Why? Because!
667
668
 
668
- d = (!rx && !ry)
669
- // Regular rect
670
- ? getPathDFromSegments([
671
- ['M', [x, y]],
672
- ['L', [x + w, y]],
673
- ['L', [x + w, y + h]],
674
- ['L', [x, y + h]],
675
- ['L', [x, y]],
676
- ['Z', []]
677
- ])
678
- : getPathDFromSegments([
679
- ['M', [x, y + ry]],
680
- ['C', [x, y + ry / num, x + rx / num, y, x + rx, y]],
681
- ['L', [x + w - rx, y]],
682
- ['C', [x + w - rx / num, y, x + w, y + ry / num, x + w, y + ry]],
683
- ['L', [x + w, y + h - ry]],
684
- [
685
- 'C',
669
+ d =
670
+ !rx && !ry // Regular rect
671
+ ? getPathDFromSegments([
672
+ ['M', [x, y]],
673
+ ['L', [x + w, y]],
674
+ ['L', [x + w, y + h]],
675
+ ['L', [x, y + h]],
676
+ ['L', [x, y]],
677
+ ['Z', []]
678
+ ])
679
+ : getPathDFromSegments([
680
+ ['M', [x, y + ry]],
681
+ ['C', [x, y + ry / num, x + rx / num, y, x + rx, y]],
682
+ ['L', [x + w - rx, y]],
683
+ ['C', [x + w - rx / num, y, x + w, y + ry / num, x + w, y + ry]],
684
+ ['L', [x + w, y + h - ry]],
686
685
  [
687
- x + w,
688
- y + h - ry / num,
689
- x + w - rx / num,
690
- y + h,
691
- x + w - rx,
692
- y + h
693
- ]
694
- ],
695
- ['L', [x + rx, y + h]],
696
- ['C', [x + rx / num, y + h, x, y + h - ry / num, x, y + h - ry]],
697
- ['L', [x, y + ry]],
698
- ['Z', []]
699
- ])
686
+ 'C',
687
+ [
688
+ x + w,
689
+ y + h - ry / num,
690
+ x + w - rx / num,
691
+ y + h,
692
+ x + w - rx,
693
+ y + h
694
+ ]
695
+ ],
696
+ ['L', [x + rx, y + h]],
697
+ ['C', [x + rx / num, y + h, x, y + h - ry / num, x, y + h - ry]],
698
+ ['L', [x, y + ry]],
699
+ ['Z', []]
700
+ ])
700
701
  break
701
702
  }
702
703
  default: