@svgedit/svgcanvas 7.2.7 → 7.4.2

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/core/undo.js CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import * as draw from './draw.js'
8
8
  import * as hstry from './history.js'
9
+ import { BBOX_AFFECTING_ATTRS } from './history.js'
9
10
  import {
10
11
  getRotationAngle, getBBox as utilsGetBBox, setHref, getStrokedBBoxDefaultVisible
11
12
  } from './utilities.js'
@@ -46,11 +47,26 @@ export const getUndoManager = () => {
46
47
  if (eventType === EventTypes.BEFORE_UNAPPLY || eventType === EventTypes.BEFORE_APPLY) {
47
48
  svgCanvas.clearSelection()
48
49
  } else if (eventType === EventTypes.AFTER_APPLY || eventType === EventTypes.AFTER_UNAPPLY) {
50
+ const cmdType = cmd.type()
51
+ const isApply = (eventType === EventTypes.AFTER_APPLY)
52
+ if (cmdType === 'ChangeElementCommand' && cmd.elem === svgCanvas.getSvgContent()) {
53
+ const values = isApply ? cmd.newValues : cmd.oldValues
54
+ if (values.width !== null && values.width !== undefined && values.width !== '') {
55
+ const newContentW = Number(values.width)
56
+ if (Number.isFinite(newContentW) && newContentW > 0) {
57
+ svgCanvas.contentW = newContentW
58
+ }
59
+ }
60
+ if (values.height !== null && values.height !== undefined && values.height !== '') {
61
+ const newContentH = Number(values.height)
62
+ if (Number.isFinite(newContentH) && newContentH > 0) {
63
+ svgCanvas.contentH = newContentH
64
+ }
65
+ }
66
+ }
49
67
  const elems = cmd.elements()
50
68
  svgCanvas.pathActions.clear()
51
69
  svgCanvas.call('changed', elems)
52
- const cmdType = cmd.type()
53
- const isApply = (eventType === EventTypes.AFTER_APPLY)
54
70
  if (cmdType === 'MoveElementCommand') {
55
71
  const parent = isApply ? cmd.newParent : cmd.oldParent
56
72
  if (parent === svgCanvas.getSvgContent()) {
@@ -116,7 +132,7 @@ export const getUndoManager = () => {
116
132
  * @param {Element} elem - The (text) DOM element to clone
117
133
  * @returns {Element} Cloned element
118
134
  */
119
- export const ffClone = function (elem) {
135
+ export const ffClone = (elem) => {
120
136
  if (!isGecko()) { return elem }
121
137
  const clone = elem.cloneNode(true)
122
138
  elem.before(clone)
@@ -189,7 +205,10 @@ export const changeSelectedAttributeNoUndoMethod = (attr, newValue, elems) => {
189
205
  } else if (attr === '#href') {
190
206
  setHref(elem, newValue)
191
207
  } else if (newValue) {
192
- elem.setAttribute(attr, isNaN(parseFloat(newValue)) ? newValue : parseFloat(newValue))
208
+ // Number(), unlike parseFloat(), rejects a value with a trailing unit suffix
209
+ // (e.g. "10pt", "2px") instead of silently truncating it to a bare number.
210
+ const asNumber = Number(newValue)
211
+ elem.setAttribute(attr, Number.isNaN(asNumber) ? newValue : asNumber)
193
212
  } else if (typeof newValue === 'number') {
194
213
  elem.setAttribute(attr, newValue)
195
214
  } else {
@@ -213,7 +232,7 @@ export const changeSelectedAttributeNoUndoMethod = (attr, newValue, elems) => {
213
232
  elem = ffClone(elem)
214
233
  }
215
234
  // Timeout needed for Opera & Firefox
216
- // codedread: it is now possible for this function to be called with elements
235
+ // codedread: it is now possible for this to be called with elements
217
236
  // that are not in the selectedElements array, we need to only request a
218
237
  // selector if the element is in that array
219
238
  if (selectedElements.includes(elem)) {
@@ -224,26 +243,39 @@ export const changeSelectedAttributeNoUndoMethod = (attr, newValue, elems) => {
224
243
  svgCanvas.selectorManager.requestSelector(elem).resize()
225
244
  }, 0)
226
245
  }
227
- // if this element was rotated, and we changed the position of this element
228
- // we need to update the rotational transform attribute
246
+ // Only recalculate rotation center for attributes that change element geometry.
247
+ // Non-geometric attributes (stroke-width, fill, opacity, etc.) don't affect
248
+ // the bbox center, so the rotation is already correct and must not be touched.
249
+ // BBOX_AFFECTING_ATTRS is imported from history.js to keep the list in one place.
229
250
  const angle = getRotationAngle(elem)
230
- if (angle !== 0 && attr !== 'transform') {
251
+ if (angle !== 0 && attr !== 'transform' && BBOX_AFFECTING_ATTRS.has(attr)) {
231
252
  const tlist = getTransformList(elem)
232
253
  let n = tlist.numberOfItems
233
254
  while (n--) {
234
255
  const xform = tlist.getItem(n)
235
256
  if (xform.type === 4) {
236
- // remove old rotate
257
+ // Compute bbox BEFORE removing the rotation so we can bail out
258
+ // safely if getBBox returns nothing (avoids losing the rotation).
259
+ const box = utilsGetBBox(elem)
260
+ if (!box) break
261
+
237
262
  tlist.removeItem(n)
238
263
 
239
- const box = utilsGetBBox(elem)
264
+ // Transform bbox center through only the transforms that come
265
+ // AFTER the rotation in the list (not the pre-rotation transforms).
266
+ // After removeItem(n), what was at n+1 is now at n.
267
+ let centerMatrix
268
+ if (n < tlist.numberOfItems) {
269
+ centerMatrix = transformListToTransform(tlist, n, tlist.numberOfItems - 1).matrix
270
+ } else {
271
+ centerMatrix = svgCanvas.getSvgRoot().createSVGMatrix() // identity
272
+ }
240
273
  const center = transformPoint(
241
- box.x + box.width / 2, box.y + box.height / 2, transformListToTransform(tlist).matrix
274
+ box.x + box.width / 2, box.y + box.height / 2, centerMatrix
242
275
  )
243
- const cx = center.x
244
- const cy = center.y
276
+
245
277
  const newrot = svgCanvas.getSvgRoot().createSVGTransform()
246
- newrot.setRotate(angle, cx, cy)
278
+ newrot.setRotate(angle, center.x, center.y)
247
279
  tlist.insertItemBefore(newrot, n)
248
280
  break
249
281
  }
@@ -264,7 +296,7 @@ export const changeSelectedAttributeNoUndoMethod = (attr, newValue, elems) => {
264
296
  * @param {Element[]} elems - The DOM elements to apply the change to
265
297
  * @returns {void}
266
298
  */
267
- export const changeSelectedAttributeMethod = function (attr, val, elems) {
299
+ export const changeSelectedAttributeMethod = (attr, val, elems) => {
268
300
  const selectedElements = svgCanvas.getSelectedElements()
269
301
  elems = elems || selectedElements
270
302
  svgCanvas.undoMgr.beginUndoableChange(attr, elems)
package/core/units.js CHANGED
@@ -6,6 +6,8 @@
6
6
  * @copyright 2010 Alexis Deveria, 2010 Jeff Schiller
7
7
  */
8
8
 
9
+ import { error } from '../common/logger.js'
10
+
9
11
  const NSSVG = 'http://www.w3.org/2000/svg'
10
12
 
11
13
  const wAttrs = ['x', 'x1', 'cx', 'rx', 'width']
@@ -62,7 +64,7 @@ let typeMap_ = {}
62
64
  * @param {module:units.ElementContainer} elementContainer - An object implementing the ElementContainer interface.
63
65
  * @returns {void}
64
66
  */
65
- export const init = function (elementContainer) {
67
+ export const init = (elementContainer) => {
66
68
  elementContainer_ = elementContainer
67
69
 
68
70
  // Get correct em/ex values by creating a temporary SVG.
@@ -124,7 +126,7 @@ export const shortFloat = (val) => {
124
126
  return Number(Number(val).toFixed(digits))
125
127
  }
126
128
  if (Array.isArray(val)) {
127
- return shortFloat(val[0]) + ',' + shortFloat(val[1])
129
+ return `${shortFloat(val[0])},${shortFloat(val[1])}`
128
130
  }
129
131
  return Number.parseFloat(val).toFixed(digits) - 0
130
132
  }
@@ -214,8 +216,8 @@ export const convertToNum = (attr, val) => {
214
216
  }
215
217
  return num * Math.sqrt((width * width) + (height * height)) / Math.sqrt(2)
216
218
  }
217
- const unit = val.substr(-2)
218
- const num = val.substr(0, val.length - 2)
219
+ const unit = val.slice(-2)
220
+ const num = val.slice(0, -2)
219
221
  // Note that this multiplication turns the string into a number
220
222
  return num * typeMap_[unit]
221
223
  }
@@ -237,7 +239,7 @@ export const isValidUnit = (attr, val, selectedElement) => {
237
239
  // Not a number, check if it has a valid unit
238
240
  val = val.toLowerCase()
239
241
  return Object.keys(typeMap_).some((unit) => {
240
- const re = new RegExp('^-?[\\d\\.]+' + unit + '$')
242
+ const re = new RegExp(`^-?[\\d\\.]+${unit}$`)
241
243
  return re.test(val)
242
244
  })
243
245
  }
@@ -253,7 +255,7 @@ export const isValidUnit = (attr, val, selectedElement) => {
253
255
  try {
254
256
  const elem = elementContainer_.getElement(val)
255
257
  result = (!elem || elem === selectedElement)
256
- } catch (e) { console.error(e) }
258
+ } catch (e) { error('Error getting element by ID', e, 'units') }
257
259
  return result
258
260
  }
259
261
  return true