@svgedit/svgcanvas 7.2.7 → 7.4.1
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/common/browser.js +104 -37
- package/common/logger.js +151 -0
- package/common/util.js +96 -155
- package/core/blur-event.js +106 -42
- package/core/clear.js +13 -3
- package/core/coords.js +214 -90
- package/core/copy-elem.js +27 -13
- package/core/dataStorage.js +84 -21
- package/core/draw.js +80 -40
- package/core/elem-get-set.js +161 -77
- package/core/event.js +143 -28
- package/core/history.js +50 -30
- package/core/historyrecording.js +4 -2
- package/core/json.js +54 -12
- package/core/layer.js +11 -17
- package/core/math.js +102 -23
- package/core/namespaces.js +5 -5
- package/core/paint.js +95 -24
- package/core/paste-elem.js +58 -19
- package/core/path-actions.js +812 -791
- package/core/path-method.js +236 -37
- package/core/path.js +45 -10
- package/core/recalculate.js +410 -15
- package/core/sanitize.js +46 -14
- package/core/select.js +44 -20
- package/core/selected-elem.js +146 -31
- package/core/selection.js +16 -6
- package/core/svg-exec.js +99 -27
- package/core/svgroot.js +1 -1
- package/core/text-actions.js +327 -306
- package/core/undo.js +20 -5
- package/core/units.js +8 -6
- package/core/utilities.js +282 -170
- package/dist/svgcanvas.js +31590 -53383
- package/dist/svgcanvas.js.map +1 -1
- package/package.json +55 -54
- package/publish.md +1 -6
- package/svgcanvas.d.ts +225 -0
- package/svgcanvas.js +9 -9
- package/vite.config.mjs +20 -0
- package/rollup.config.mjs +0 -38
package/core/svg-exec.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { jsPDF as JsPDF } from 'jspdf'
|
|
9
9
|
import 'svg2pdf.js'
|
|
10
10
|
import * as history from './history.js'
|
|
11
|
+
import { error } from '../common/logger.js'
|
|
11
12
|
import {
|
|
12
13
|
text2xml,
|
|
13
14
|
cleanupElement,
|
|
@@ -131,7 +132,7 @@ const svgToString = (elem, indent) => {
|
|
|
131
132
|
const nsMap = svgCanvas.getNsMap()
|
|
132
133
|
const out = []
|
|
133
134
|
const unit = curConfig.baseUnit
|
|
134
|
-
const unitRe = new RegExp(
|
|
135
|
+
const unitRe = new RegExp(`^-?[\\d\\.]+${unit}$`)
|
|
135
136
|
|
|
136
137
|
if (elem) {
|
|
137
138
|
cleanupElement(elem)
|
|
@@ -164,7 +165,10 @@ const svgToString = (elem, indent) => {
|
|
|
164
165
|
// }
|
|
165
166
|
if (curConfig.dynamicOutput) {
|
|
166
167
|
vb = elem.getAttribute('viewBox')
|
|
167
|
-
|
|
168
|
+
if (!vb) {
|
|
169
|
+
vb = [0, 0, res.w, res.h].join(' ')
|
|
170
|
+
}
|
|
171
|
+
out.push(` viewBox="${vb}" xmlns="${NS.SVG}"`)
|
|
168
172
|
} else {
|
|
169
173
|
if (unit !== 'px') {
|
|
170
174
|
res.w = convertUnit(res.w, unit) + unit
|
|
@@ -193,14 +197,14 @@ const svgToString = (elem, indent) => {
|
|
|
193
197
|
nsMap[uri] !== 'xml'
|
|
194
198
|
) {
|
|
195
199
|
nsuris[uri] = true
|
|
196
|
-
out.push(
|
|
200
|
+
out.push(` xmlns:${nsMap[uri]}="${uri}"`)
|
|
197
201
|
}
|
|
198
202
|
if (el.attributes.length > 0) {
|
|
199
203
|
for (const [, attr] of Object.entries(el.attributes)) {
|
|
200
204
|
const u = attr.namespaceURI
|
|
201
205
|
if (u && !nsuris[u] && nsMap[u] !== 'xmlns' && nsMap[u] !== 'xml') {
|
|
202
206
|
nsuris[u] = true
|
|
203
|
-
out.push(
|
|
207
|
+
out.push(` xmlns:${nsMap[u]}="${u}"`)
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
210
|
}
|
|
@@ -469,7 +473,7 @@ const setSvgString = (xmlString, preventUndo) => {
|
|
|
469
473
|
|
|
470
474
|
Object.entries(ids).forEach(([key, value]) => {
|
|
471
475
|
if (value > 1) {
|
|
472
|
-
const nodes = content.querySelectorAll(
|
|
476
|
+
const nodes = content.querySelectorAll(`[id="${key}"]`)
|
|
473
477
|
for (let i = 1; i < nodes.length; i++) {
|
|
474
478
|
nodes[i].setAttribute('id', svgCanvas.getNextId())
|
|
475
479
|
}
|
|
@@ -525,14 +529,20 @@ const setSvgString = (xmlString, preventUndo) => {
|
|
|
525
529
|
if (content.getAttribute('viewBox')) {
|
|
526
530
|
const viBox = content.getAttribute('viewBox')
|
|
527
531
|
const vb = viBox.split(/[ ,]+/)
|
|
528
|
-
|
|
529
|
-
|
|
532
|
+
const vbWidth = Number(vb[2])
|
|
533
|
+
const vbHeight = Number(vb[3])
|
|
534
|
+
if (Number.isFinite(vbWidth)) {
|
|
535
|
+
attrs.width = vbWidth
|
|
536
|
+
}
|
|
537
|
+
if (Number.isFinite(vbHeight)) {
|
|
538
|
+
attrs.height = vbHeight
|
|
539
|
+
}
|
|
530
540
|
// handle content that doesn't have a viewBox
|
|
531
541
|
} else {
|
|
532
542
|
;['width', 'height'].forEach(dim => {
|
|
533
543
|
// Set to 100 if not given
|
|
534
544
|
const val = content.getAttribute(dim) || '100%'
|
|
535
|
-
if (String(val).
|
|
545
|
+
if (String(val).slice(-1) === '%') {
|
|
536
546
|
// Use user units if percentage given
|
|
537
547
|
percs = true
|
|
538
548
|
} else {
|
|
@@ -558,16 +568,25 @@ const setSvgString = (xmlString, preventUndo) => {
|
|
|
558
568
|
// Percentage width/height, so let's base it on visible elements
|
|
559
569
|
if (percs) {
|
|
560
570
|
const bb = getStrokedBBoxDefaultVisible()
|
|
561
|
-
|
|
562
|
-
|
|
571
|
+
if (bb && typeof bb === 'object') {
|
|
572
|
+
attrs.width = bb.width + bb.x
|
|
573
|
+
attrs.height = bb.height + bb.y
|
|
574
|
+
} else {
|
|
575
|
+
if (attrs.width === null || attrs.width === undefined) {
|
|
576
|
+
attrs.width = 100
|
|
577
|
+
}
|
|
578
|
+
if (attrs.height === null || attrs.height === undefined) {
|
|
579
|
+
attrs.height = 100
|
|
580
|
+
}
|
|
581
|
+
}
|
|
563
582
|
}
|
|
564
583
|
|
|
565
584
|
// Just in case negative numbers are given or
|
|
566
585
|
// result from the percs calculation
|
|
567
|
-
if (attrs.width <= 0) {
|
|
586
|
+
if (!Number.isFinite(attrs.width) || attrs.width <= 0) {
|
|
568
587
|
attrs.width = 100
|
|
569
588
|
}
|
|
570
|
-
if (attrs.height <= 0) {
|
|
589
|
+
if (!Number.isFinite(attrs.height) || attrs.height <= 0) {
|
|
571
590
|
attrs.height = 100
|
|
572
591
|
}
|
|
573
592
|
|
|
@@ -596,7 +615,7 @@ const setSvgString = (xmlString, preventUndo) => {
|
|
|
596
615
|
if (!preventUndo) svgCanvas.addCommandToHistory(batchCmd)
|
|
597
616
|
svgCanvas.call('sourcechanged', [svgCanvas.getSvgContent()])
|
|
598
617
|
} catch (e) {
|
|
599
|
-
|
|
618
|
+
error('Error setting SVG string', e, 'svg-exec')
|
|
600
619
|
return false
|
|
601
620
|
}
|
|
602
621
|
|
|
@@ -666,16 +685,26 @@ const importSvgString = (xmlString, preserveDimension) => {
|
|
|
666
685
|
|
|
667
686
|
// TODO: properly handle preserveAspectRatio
|
|
668
687
|
const // canvasw = +svgContent.getAttribute('width'),
|
|
669
|
-
|
|
688
|
+
rawCanvash = Number(svgCanvas.getSvgContent().getAttribute('height'))
|
|
689
|
+
const canvash =
|
|
690
|
+
Number.isFinite(rawCanvash) && rawCanvash > 0
|
|
691
|
+
? rawCanvash
|
|
692
|
+
: (Number(svgCanvas.getCurConfig().dimensions?.[1]) || 100)
|
|
670
693
|
// imported content should be 1/3 of the canvas on its largest dimension
|
|
671
694
|
|
|
695
|
+
const vbWidth = vb[2]
|
|
696
|
+
const vbHeight = vb[3]
|
|
697
|
+
const importW = Number.isFinite(vbWidth) && vbWidth > 0 ? vbWidth : (innerw > 0 ? innerw : 100)
|
|
698
|
+
const importH = Number.isFinite(vbHeight) && vbHeight > 0 ? vbHeight : (innerh > 0 ? innerh : 100)
|
|
699
|
+
const safeImportW = Number.isFinite(importW) && importW > 0 ? importW : 100
|
|
700
|
+
const safeImportH = Number.isFinite(importH) && importH > 0 ? importH : 100
|
|
672
701
|
ts =
|
|
673
|
-
|
|
674
|
-
? 'scale(' + canvash / 3 /
|
|
675
|
-
: 'scale(' + canvash / 3 /
|
|
702
|
+
safeImportH > safeImportW
|
|
703
|
+
? 'scale(' + canvash / 3 / safeImportH + ')'
|
|
704
|
+
: 'scale(' + canvash / 3 / safeImportW + ')'
|
|
676
705
|
|
|
677
706
|
// Hack to make recalculateDimensions understand how to scale
|
|
678
|
-
ts =
|
|
707
|
+
ts = `translate(0) ${ts} translate(0)`
|
|
679
708
|
|
|
680
709
|
symbol = svgCanvas.getDOMDocument().createElementNS(NS.SVG, 'symbol')
|
|
681
710
|
const defs = findDefs()
|
|
@@ -738,7 +767,7 @@ const importSvgString = (xmlString, preserveDimension) => {
|
|
|
738
767
|
svgCanvas.addCommandToHistory(batchCmd)
|
|
739
768
|
svgCanvas.call('changed', [svgCanvas.getSvgContent()])
|
|
740
769
|
} catch (e) {
|
|
741
|
-
|
|
770
|
+
error('Error importing SVG string', e, 'svg-exec')
|
|
742
771
|
return null
|
|
743
772
|
}
|
|
744
773
|
|
|
@@ -865,8 +894,8 @@ const convertImagesToBase64 = async svgElement => {
|
|
|
865
894
|
}
|
|
866
895
|
reader.readAsDataURL(blob)
|
|
867
896
|
})
|
|
868
|
-
} catch (
|
|
869
|
-
|
|
897
|
+
} catch (err) {
|
|
898
|
+
error('Failed to fetch image', err, 'svg-exec')
|
|
870
899
|
}
|
|
871
900
|
}
|
|
872
901
|
})
|
|
@@ -905,10 +934,14 @@ const rasterExport = (
|
|
|
905
934
|
|
|
906
935
|
const canvas = document.createElement('canvas')
|
|
907
936
|
const ctx = canvas.getContext('2d')
|
|
937
|
+
if (!ctx) {
|
|
938
|
+
reject(new Error('Canvas 2D context not available'))
|
|
939
|
+
return
|
|
940
|
+
}
|
|
908
941
|
|
|
909
|
-
const
|
|
910
|
-
const
|
|
911
|
-
|
|
942
|
+
const res = svgCanvas.getResolution()
|
|
943
|
+
const width = res.w
|
|
944
|
+
const height = res.h
|
|
912
945
|
canvas.width = width
|
|
913
946
|
canvas.height = height
|
|
914
947
|
|
|
@@ -1013,7 +1046,7 @@ const exportPDF = (
|
|
|
1013
1046
|
}
|
|
1014
1047
|
|
|
1015
1048
|
img.onerror = err => {
|
|
1016
|
-
|
|
1049
|
+
error('Failed to load SVG into image element', err, 'svg-exec')
|
|
1017
1050
|
reject(err)
|
|
1018
1051
|
}
|
|
1019
1052
|
|
|
@@ -1112,7 +1145,7 @@ const uniquifyElemsMethod = g => {
|
|
|
1112
1145
|
let j = attrs.length
|
|
1113
1146
|
while (j--) {
|
|
1114
1147
|
const attr = attrs[j]
|
|
1115
|
-
attr.ownerElement.setAttribute(attr.name,
|
|
1148
|
+
attr.ownerElement.setAttribute(attr.name, `url(#${newid})`)
|
|
1116
1149
|
}
|
|
1117
1150
|
|
|
1118
1151
|
// remap all href attributes
|
|
@@ -1142,7 +1175,11 @@ const setUseDataMethod = parent => {
|
|
|
1142
1175
|
|
|
1143
1176
|
Array.prototype.forEach.call(elems, (el, _) => {
|
|
1144
1177
|
const dataStorage = svgCanvas.getDataStorage()
|
|
1145
|
-
const
|
|
1178
|
+
const href = svgCanvas.getHref(el)
|
|
1179
|
+
if (!href || !href.startsWith('#')) {
|
|
1180
|
+
return
|
|
1181
|
+
}
|
|
1182
|
+
const id = href.substr(1)
|
|
1146
1183
|
const refElem = svgCanvas.getElement(id)
|
|
1147
1184
|
if (!refElem) {
|
|
1148
1185
|
return
|
|
@@ -1301,6 +1338,41 @@ const convertGradientsMethod = elem => {
|
|
|
1301
1338
|
grad.setAttribute('x2', (gCoords.x2 - bb.x) / bb.width)
|
|
1302
1339
|
grad.setAttribute('y2', (gCoords.y2 - bb.y) / bb.height)
|
|
1303
1340
|
grad.removeAttribute('gradientUnits')
|
|
1341
|
+
} else if (grad.tagName === 'radialGradient') {
|
|
1342
|
+
const getNum = (value, fallback) => {
|
|
1343
|
+
const num = Number(value)
|
|
1344
|
+
return Number.isFinite(num) ? num : fallback
|
|
1345
|
+
}
|
|
1346
|
+
let cx = getNum(grad.getAttribute('cx'), 0.5)
|
|
1347
|
+
let cy = getNum(grad.getAttribute('cy'), 0.5)
|
|
1348
|
+
let r = getNum(grad.getAttribute('r'), 0.5)
|
|
1349
|
+
let fx = getNum(grad.getAttribute('fx'), cx)
|
|
1350
|
+
let fy = getNum(grad.getAttribute('fy'), cy)
|
|
1351
|
+
|
|
1352
|
+
// If has transform, convert
|
|
1353
|
+
const tlist = getTransformList(grad)
|
|
1354
|
+
if (tlist?.numberOfItems > 0) {
|
|
1355
|
+
const m = transformListToTransform(tlist).matrix
|
|
1356
|
+
const cpt = transformPoint(cx, cy, m)
|
|
1357
|
+
const fpt = transformPoint(fx, fy, m)
|
|
1358
|
+
const rpt = transformPoint(cx + r, cy, m)
|
|
1359
|
+
cx = cpt.x
|
|
1360
|
+
cy = cpt.y
|
|
1361
|
+
fx = fpt.x
|
|
1362
|
+
fy = fpt.y
|
|
1363
|
+
r = Math.hypot(rpt.x - cpt.x, rpt.y - cpt.y)
|
|
1364
|
+
grad.removeAttribute('gradientTransform')
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
if (!bb.width || !bb.height) {
|
|
1368
|
+
return
|
|
1369
|
+
}
|
|
1370
|
+
grad.setAttribute('cx', (cx - bb.x) / bb.width)
|
|
1371
|
+
grad.setAttribute('cy', (cy - bb.y) / bb.height)
|
|
1372
|
+
grad.setAttribute('fx', (fx - bb.x) / bb.width)
|
|
1373
|
+
grad.setAttribute('fy', (fy - bb.y) / bb.height)
|
|
1374
|
+
grad.setAttribute('r', r / Math.max(bb.width, bb.height))
|
|
1375
|
+
grad.removeAttribute('gradientUnits')
|
|
1304
1376
|
}
|
|
1305
1377
|
}
|
|
1306
1378
|
})
|
package/core/svgroot.js
CHANGED
|
@@ -14,7 +14,7 @@ import { text2xml } from './utilities.js'
|
|
|
14
14
|
* @param {ArgumentsArray} dimensions - dimensions of width and height
|
|
15
15
|
* @returns {svgRootElement}
|
|
16
16
|
*/
|
|
17
|
-
export const svgRootElement =
|
|
17
|
+
export const svgRootElement = (svgdoc, dimensions) => {
|
|
18
18
|
return svgdoc.importNode(
|
|
19
19
|
text2xml(
|
|
20
20
|
`<svg id="svgroot" xmlns="${NS.SVG}" xlinkns="${NS.XLINK}" width="${dimensions[0]}"
|