@jscad/svg-deserializer 2.2.4 → 2.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/CHANGELOG.md +43 -0
- package/package.json +5 -5
- package/{constants.js → src/constants.js} +0 -0
- package/{helpers.js → src/helpers.js} +2 -2
- package/{index.js → src/index.js} +39 -30
- package/{shapesMapGeometry.js → src/shapesMapGeometry.js} +140 -103
- package/{shapesMapJscad.js → src/shapesMapJscad.js} +1 -2
- package/{svgElementHelpers.js → src/svgElementHelpers.js} +84 -81
- package/tests/inkscape.test.js +2 -3
- package/tests/instantiate.test.js +15 -15
- package/tests/issue.885.test.js +23 -0
- package/tests/translate.test.js +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,49 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [2.4.2](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/svg-deserializer@2.4.1...@jscad/svg-deserializer@2.4.2) (2021-10-17)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @jscad/svg-deserializer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [2.4.1](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/svg-deserializer@2.4.0...@jscad/svg-deserializer@2.4.1) (2021-10-04)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @jscad/svg-deserializer
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# [2.4.0](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/svg-deserializer@2.3.0...@jscad/svg-deserializer@2.4.0) (2021-09-27)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* **modeling:** corrected concat to ignore duplicate points ([#913](https://github.com/jscad/OpenJSCAD.org/issues/913))corrected appendArc to maintain last point ([3eea3ef](https://github.com/jscad/OpenJSCAD.org/commit/3eea3efed3b3d4bacb49c1ee4691bfc159b08261))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
|
|
32
|
+
* **svg-deserializer:** reworked to use saxes library, and standard module layout ([56f5725](https://github.com/jscad/OpenJSCAD.org/commit/56f572518c85d767ac680aa2da1af46c4847e63c))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# [2.3.0](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/svg-deserializer@2.2.4...@jscad/svg-deserializer@2.3.0) (2021-09-09)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Features
|
|
42
|
+
|
|
43
|
+
* **svg-deserializer:** added pathSelfClosed option to control construction of overlapping paths ([043f59d](https://github.com/jscad/OpenJSCAD.org/commit/043f59d928fcf397b0bb23e744e35d7eda5be626))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
6
49
|
## [2.2.4](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/svg-deserializer@2.2.3...@jscad/svg-deserializer@2.2.4) (2021-06-20)
|
|
7
50
|
|
|
8
51
|
**Note:** Version bump only for package @jscad/svg-deserializer
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscad/svg-deserializer",
|
|
3
|
-
"version": "2.2
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "SVG Deserializer for JSCAD",
|
|
5
5
|
"repository": "https://github.com/jscad/OpenJSCAD.org",
|
|
6
|
-
"main": "index.js",
|
|
6
|
+
"main": "src/index.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"coverage": "nyc --all --reporter=html --reporter=text npm test",
|
|
9
9
|
"test": "ava --verbose --timeout 2m './tests/*.test.js'"
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@jscad/array-utils": "2.1.0",
|
|
35
|
-
"@jscad/modeling": "2.
|
|
36
|
-
"
|
|
35
|
+
"@jscad/modeling": "2.6.0",
|
|
36
|
+
"saxes": "5.0.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"ava": "3.15.0",
|
|
40
40
|
"nyc": "15.1.0"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "3d1d02f2863c65fd95406c7098c09297b3be1c10"
|
|
43
43
|
}
|
|
File without changes
|
|
@@ -124,8 +124,8 @@ const cagColor = (value) => {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
const cssStyle = (element, name) => {
|
|
127
|
-
if ('
|
|
128
|
-
const list = element.
|
|
127
|
+
if ('style' in element) {
|
|
128
|
+
const list = element.style + ';'
|
|
129
129
|
const pat = name + '\\s*:\\s*(\\S+);'
|
|
130
130
|
const exp = new RegExp(pat, 'i')
|
|
131
131
|
let v = exp.exec(list)
|
|
@@ -10,12 +10,12 @@ have been very kindly sponsored by [Copenhagen Fabrication / Stykka](https://www
|
|
|
10
10
|
All code released under MIT license
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const saxes = require('saxes')
|
|
14
14
|
|
|
15
15
|
const { colors, transforms } = require('@jscad/modeling')
|
|
16
16
|
const { toArray } = require('@jscad/array-utils')
|
|
17
17
|
|
|
18
|
-
const version = require('
|
|
18
|
+
const version = require('../package.json').version
|
|
19
19
|
|
|
20
20
|
const { cagLengthX, cagLengthY, svgColorForTarget } = require('./helpers')
|
|
21
21
|
const { svgSvg, svgRect, svgCircle, svgGroup, svgLine, svgPath, svgEllipse, svgPolygon, svgPolyline, svgUse } = require('./svgElementHelpers')
|
|
@@ -23,24 +23,26 @@ const shapesMapGeometry = require('./shapesMapGeometry')
|
|
|
23
23
|
const shapesMapJscad = require('./shapesMapJscad')
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Deserializer of
|
|
27
|
-
* @module io/
|
|
26
|
+
* Deserializer of SVG source data to JSCAD geometries.
|
|
27
|
+
* @module io/svg-deserializer
|
|
28
28
|
* @example
|
|
29
|
-
* const { deserializer, extension } = require('@jscad/
|
|
29
|
+
* const { deserializer, extension } = require('@jscad/svg-deserializer')
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
33
|
+
* Deserialize the given SVG source into either a script or an array of geometries
|
|
34
|
+
* @see {@link https://www.w3.org/TR/SVG/intro.html|SVG Specification}
|
|
34
35
|
* @param {Object} options - options used during deserializing, REQUIRED
|
|
35
36
|
* @param {boolean} [options.addMetadata=true] - toggle injection of metadata at the start of the script
|
|
36
37
|
* @param {string} [options.filename='svg'] - filename of original SVG source
|
|
37
38
|
* @param {string} [options.output='script'] - either 'script' or 'geometry' to set desired output
|
|
38
39
|
* @param {float} [options.pxPmm] - custom pixels per mm unit
|
|
39
40
|
* @param {integer} [options.segments] - number of segments for rounded shapes
|
|
40
|
-
* @param {string} [options.target] - target 2D geometry; geom2 or path2
|
|
41
|
+
* @param {string} [options.target] - target 2D geometry; 'geom2' or 'path2'
|
|
41
42
|
* @param {string} [options.version='0.0.0'] - version number to add to the metadata
|
|
42
|
-
* @param {string}
|
|
43
|
-
* @
|
|
43
|
+
* @param {string} [options.pathSelfClosed='error'] - [error||trim||split] if path self-closes with one of commands without stop command right after
|
|
44
|
+
* @param {string} input - SVG source data
|
|
45
|
+
* @returns {(Array|String)} either an array of objects (geometry) or a string (script)
|
|
44
46
|
* @alias module:io/svg-deserializer.deserialize
|
|
45
47
|
*/
|
|
46
48
|
const deserialize = (options, input) => {
|
|
@@ -51,16 +53,17 @@ const deserialize = (options, input) => {
|
|
|
51
53
|
pxPmm: require('./constants').pxPmm,
|
|
52
54
|
segments: 32,
|
|
53
55
|
target: 'path', // target - 'geom2' or 'path'
|
|
56
|
+
pathSelfClosed: 'error',
|
|
54
57
|
version
|
|
55
58
|
}
|
|
56
59
|
options = Object.assign({}, defaults, options)
|
|
57
60
|
return options.output === 'script' ? translate(input, options) : instantiate(input, options)
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
/*
|
|
61
64
|
* Parse the given SVG source and return a set of geometries.
|
|
62
|
-
* @param {string} src svg data as text
|
|
63
|
-
* @param {object} options options (optional) anonymous object with:
|
|
65
|
+
* @param {string} src - svg data as text
|
|
66
|
+
* @param {object} options - options (optional) anonymous object with:
|
|
64
67
|
* pxPmm {number} pixels per milimeter for calcuations
|
|
65
68
|
* version: {string} version number to add to the metadata
|
|
66
69
|
* addMetadata: {boolean} flag to enable/disable injection of metadata (producer, date, source)
|
|
@@ -75,7 +78,7 @@ const instantiate = (src, options) => {
|
|
|
75
78
|
// parse the SVG source
|
|
76
79
|
createSvgParser(src, pxPmm)
|
|
77
80
|
if (!svgObj) {
|
|
78
|
-
throw new Error('SVG parsing failed, no valid
|
|
81
|
+
throw new Error('SVG parsing failed, no valid SVG data retrieved')
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
options && options.statusCallback && options.statusCallback({ progress: 50 })
|
|
@@ -86,7 +89,7 @@ const instantiate = (src, options) => {
|
|
|
86
89
|
return result
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
|
|
92
|
+
/*
|
|
90
93
|
* Parse the given SVG source and return a JSCAD script
|
|
91
94
|
* @param {string} src svg data as text
|
|
92
95
|
* @param {object} options options (optional) anonymous object with:
|
|
@@ -104,7 +107,7 @@ const translate = (src, options) => {
|
|
|
104
107
|
// parse the SVG source
|
|
105
108
|
createSvgParser(src, pxPmm)
|
|
106
109
|
if (!svgObj) {
|
|
107
|
-
throw new Error('SVG parsing failed, no valid
|
|
110
|
+
throw new Error('SVG parsing failed, no valid SVG data retrieved')
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
// convert the internal objects to JSCAD code
|
|
@@ -144,7 +147,7 @@ let svgUnitsPmm = [1, 1]
|
|
|
144
147
|
* Convert the given group (of objects) into geometries
|
|
145
148
|
*/
|
|
146
149
|
const objectify = (options, group) => {
|
|
147
|
-
const { target, segments } = options
|
|
150
|
+
const { target, segments, pathSelfClosed } = options
|
|
148
151
|
const level = svgGroups.length
|
|
149
152
|
// add this group to the heiarchy
|
|
150
153
|
svgGroups.push(group)
|
|
@@ -164,7 +167,8 @@ const objectify = (options, group) => {
|
|
|
164
167
|
level,
|
|
165
168
|
target,
|
|
166
169
|
svgGroups,
|
|
167
|
-
segments
|
|
170
|
+
segments,
|
|
171
|
+
pathSelfClosed
|
|
168
172
|
}
|
|
169
173
|
// apply base level attributes to all shapes
|
|
170
174
|
for (i = 0; i < group.objects.length; i++) {
|
|
@@ -322,14 +326,17 @@ const codify = (options, group) => {
|
|
|
322
326
|
|
|
323
327
|
const createSvgParser = (src, pxPmm) => {
|
|
324
328
|
// create a parser for the XML
|
|
325
|
-
const parser =
|
|
329
|
+
const parser = new saxes.SaxesParser()
|
|
326
330
|
if (pxPmm !== undefined && pxPmm > parser.pxPmm) {
|
|
327
331
|
parser.pxPmm = pxPmm
|
|
328
332
|
}
|
|
329
333
|
// extend the parser with functions
|
|
330
|
-
parser.
|
|
334
|
+
parser.on('error', (e) => {
|
|
335
|
+
console.log(`ERROR: SVG file, line ${parser.line}, column ${parser.column}`)
|
|
336
|
+
console.log(e)
|
|
337
|
+
})
|
|
331
338
|
|
|
332
|
-
parser.
|
|
339
|
+
parser.on('opentag', (node) => {
|
|
333
340
|
const objMap = {
|
|
334
341
|
SVG: svgSvg,
|
|
335
342
|
G: svgGroup,
|
|
@@ -345,10 +352,12 @@ const createSvgParser = (src, pxPmm) => {
|
|
|
345
352
|
DESC: () => undefined, // ignored by design
|
|
346
353
|
TITLE: () => undefined, // ignored by design
|
|
347
354
|
STYLE: () => undefined, // ignored by design
|
|
348
|
-
undefined: () => console.log('
|
|
355
|
+
undefined: () => console.log('WARNING: unsupported SVG element: ' + node.name)
|
|
349
356
|
}
|
|
350
357
|
node.attributes.position = [parser.line + 1, parser.column + 1]
|
|
351
|
-
|
|
358
|
+
|
|
359
|
+
const elementName = node.name.toUpperCase()
|
|
360
|
+
const obj = objMap[elementName] ? objMap[elementName](node.attributes, { svgObjects, customPxPmm: pxPmm }) : undefined
|
|
352
361
|
|
|
353
362
|
// case 'SYMBOL':
|
|
354
363
|
// this is just like an embedded SVG but does NOT render directly, only named
|
|
@@ -396,9 +405,9 @@ const createSvgParser = (src, pxPmm) => {
|
|
|
396
405
|
}
|
|
397
406
|
}
|
|
398
407
|
}
|
|
399
|
-
}
|
|
408
|
+
})
|
|
400
409
|
|
|
401
|
-
parser.
|
|
410
|
+
parser.on('closetag', (node) => {
|
|
402
411
|
const popGroup = () => {
|
|
403
412
|
if (svgInDefs === true) {
|
|
404
413
|
return svgDefs.pop()
|
|
@@ -414,19 +423,19 @@ const createSvgParser = (src, pxPmm) => {
|
|
|
414
423
|
G: popGroup,
|
|
415
424
|
undefined: () => {}
|
|
416
425
|
}
|
|
417
|
-
const
|
|
426
|
+
const elementName = node.name.toUpperCase()
|
|
427
|
+
const obj = objMap[elementName] ? objMap[elementName]() : undefined
|
|
418
428
|
|
|
419
429
|
// check for completeness
|
|
420
430
|
if (svgGroups.length === 0) {
|
|
421
431
|
svgObj = obj
|
|
422
432
|
}
|
|
423
|
-
}
|
|
433
|
+
})
|
|
424
434
|
|
|
425
|
-
|
|
426
|
-
|
|
435
|
+
parser.on('end', () => {
|
|
436
|
+
// console.log('SVG parsing completed')
|
|
437
|
+
})
|
|
427
438
|
|
|
428
|
-
parser.onend = function () {
|
|
429
|
-
}
|
|
430
439
|
// start the parser
|
|
431
440
|
parser.write(src).close()
|
|
432
441
|
return parser
|
|
@@ -4,7 +4,7 @@ const { svg2cagX, svg2cagY, cagLengthX, cagLengthY, cagLengthP, reflect } = requ
|
|
|
4
4
|
// const { cssPxUnit } = require('./constants')
|
|
5
5
|
|
|
6
6
|
const shapesMapGeometry = (obj, objectify, params) => {
|
|
7
|
-
const { svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, target, segments } = params
|
|
7
|
+
const { svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, target, segments, pathSelfClosed } = params
|
|
8
8
|
|
|
9
9
|
const types = {
|
|
10
10
|
group: (obj) => objectify({ target, segments }, obj),
|
|
@@ -131,7 +131,7 @@ const shapesMapGeometry = (obj, objectify, params) => {
|
|
|
131
131
|
},
|
|
132
132
|
|
|
133
133
|
path: (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, segments) => {
|
|
134
|
-
const listofpaths = expandPath(obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, segments)
|
|
134
|
+
const listofpaths = expandPath(obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, segments, pathSelfClosed)
|
|
135
135
|
// order is important
|
|
136
136
|
const listofentries = Object.entries(listofpaths).sort((a, b) => a[0].localeCompare(b[0]))
|
|
137
137
|
const shapes = listofentries.map((entry) => {
|
|
@@ -152,7 +152,12 @@ const shapesMapGeometry = (obj, objectify, params) => {
|
|
|
152
152
|
|
|
153
153
|
module.exports = shapesMapGeometry
|
|
154
154
|
|
|
155
|
-
const
|
|
155
|
+
const appendPoints = (points, geometry) => {
|
|
156
|
+
if (geometry) return geometries.path2.appendPoints(points, geometry)
|
|
157
|
+
return geometries.path2.fromPoints({ }, points)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, segments, pathSelfClosed) => {
|
|
156
161
|
const paths = {}
|
|
157
162
|
const on = 'path'
|
|
158
163
|
|
|
@@ -178,10 +183,18 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
178
183
|
let qx = 0 // 2nd control point from previous Q command
|
|
179
184
|
let qy = 0 // 2nd control point from previous Q command
|
|
180
185
|
|
|
186
|
+
const newPath = () => {
|
|
187
|
+
pi++
|
|
188
|
+
pathName = on + pi
|
|
189
|
+
pc = false
|
|
190
|
+
}
|
|
191
|
+
const ensurePath = () => {
|
|
192
|
+
if (!paths[pathName]) paths[pathName] = geometries.path2.fromPoints({}, [])
|
|
193
|
+
}
|
|
181
194
|
for (let j = 0; j < obj.commands.length; j++) {
|
|
182
195
|
const co = obj.commands[j]
|
|
183
196
|
const pts = co.p
|
|
184
|
-
|
|
197
|
+
let i = 0
|
|
185
198
|
switch (co.c) {
|
|
186
199
|
case 'm': // relative move to X,Y
|
|
187
200
|
// special case, if at beginning of path then treat like absolute M
|
|
@@ -193,20 +206,18 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
193
206
|
// FIXME paths[pathName] = paths[pathName]
|
|
194
207
|
}
|
|
195
208
|
// open a new path
|
|
196
|
-
if (pts.length >= 2) {
|
|
197
|
-
cx = cx + parseFloat(pts
|
|
198
|
-
cy = cy + parseFloat(pts
|
|
199
|
-
|
|
200
|
-
pathName =
|
|
201
|
-
pc = false
|
|
202
|
-
paths[pathName] = geometries.path2.fromPoints({ }, [[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]])
|
|
209
|
+
if (pts.length >= i + 2) {
|
|
210
|
+
cx = cx + parseFloat(pts[i++])
|
|
211
|
+
cy = cy + parseFloat(pts[i++])
|
|
212
|
+
newPath()
|
|
213
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]])
|
|
203
214
|
sx = cx; sy = cy
|
|
204
215
|
}
|
|
205
216
|
// optional implicit relative lineTo (cf SVG spec 8.3.2)
|
|
206
|
-
while (pts.length >= 2) {
|
|
207
|
-
cx = cx + parseFloat(pts
|
|
208
|
-
cy = cy + parseFloat(pts
|
|
209
|
-
paths[pathName] =
|
|
217
|
+
while (pts.length >= i + 2) {
|
|
218
|
+
cx = cx + parseFloat(pts[i++])
|
|
219
|
+
cy = cy + parseFloat(pts[i++])
|
|
220
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]], paths[pathName])
|
|
210
221
|
}
|
|
211
222
|
break
|
|
212
223
|
case 'M': // absolute move to X,Y
|
|
@@ -215,54 +226,55 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
215
226
|
// FIXME paths[pathName] = paths[pathName]
|
|
216
227
|
}
|
|
217
228
|
// open a new path
|
|
218
|
-
if (pts.length >= 2) {
|
|
219
|
-
cx = parseFloat(pts
|
|
220
|
-
cy = parseFloat(pts
|
|
221
|
-
|
|
222
|
-
pathName =
|
|
223
|
-
pc = false
|
|
224
|
-
paths[pathName] = geometries.path2.fromPoints({ }, [[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]])
|
|
229
|
+
if (pts.length >= i + 2) {
|
|
230
|
+
cx = parseFloat(pts[i++])
|
|
231
|
+
cy = parseFloat(pts[i++])
|
|
232
|
+
newPath()
|
|
233
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]])
|
|
225
234
|
sx = cx; sy = cy
|
|
226
235
|
}
|
|
227
236
|
// optional implicit absolute lineTo (cf SVG spec 8.3.2)
|
|
228
|
-
while (pts.length >= 2) {
|
|
229
|
-
cx = parseFloat(pts
|
|
230
|
-
cy = parseFloat(pts
|
|
231
|
-
paths[pathName] =
|
|
237
|
+
while (pts.length >= i + 2) {
|
|
238
|
+
cx = parseFloat(pts[i++])
|
|
239
|
+
cy = parseFloat(pts[i++])
|
|
240
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]], paths[pathName])
|
|
232
241
|
}
|
|
233
242
|
break
|
|
234
243
|
case 'a': // relative elliptical arc
|
|
235
|
-
while (pts.length >= 7) {
|
|
236
|
-
const rx = parseFloat(pts
|
|
237
|
-
const ry = parseFloat(pts
|
|
238
|
-
const ro = 0 - parseFloat(pts
|
|
239
|
-
const lf = (pts
|
|
240
|
-
const sf = (pts
|
|
241
|
-
cx = cx + parseFloat(pts
|
|
242
|
-
cy = cy + parseFloat(pts
|
|
244
|
+
while (pts.length >= i + 7) {
|
|
245
|
+
const rx = parseFloat(pts[i++])
|
|
246
|
+
const ry = parseFloat(pts[i++])
|
|
247
|
+
const ro = 0 - parseFloat(pts[i++]) * 0.017453292519943295 // radians
|
|
248
|
+
const lf = (pts[i++] === '1')
|
|
249
|
+
const sf = (pts[i++] === '1')
|
|
250
|
+
cx = cx + parseFloat(pts[i++])
|
|
251
|
+
cy = cy + parseFloat(pts[i++])
|
|
252
|
+
ensurePath()
|
|
243
253
|
paths[pathName] = geometries.path2.appendArc({ segments, endpoint: [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)], radius: [svg2cagX(rx, svgUnitsPmm), svg2cagY(ry, svgUnitsPmm)], xaxisrotation: ro, clockwise: sf, large: lf }, paths[pathName])
|
|
244
254
|
}
|
|
245
255
|
break
|
|
246
256
|
case 'A': // absolute elliptical arc
|
|
247
|
-
while (pts.length >= 7) {
|
|
248
|
-
const rx = parseFloat(pts
|
|
249
|
-
const ry = parseFloat(pts
|
|
250
|
-
const ro = 0 - parseFloat(pts
|
|
251
|
-
const lf = (pts
|
|
252
|
-
const sf = (pts
|
|
253
|
-
cx = parseFloat(pts
|
|
254
|
-
cy = parseFloat(pts
|
|
257
|
+
while (pts.length >= i + 7) {
|
|
258
|
+
const rx = parseFloat(pts[i++])
|
|
259
|
+
const ry = parseFloat(pts[i++])
|
|
260
|
+
const ro = 0 - parseFloat(pts[i++]) * 0.017453292519943295 // radians
|
|
261
|
+
const lf = (pts[i++] === '1')
|
|
262
|
+
const sf = (pts[i++] === '1')
|
|
263
|
+
cx = parseFloat(pts[i++])
|
|
264
|
+
cy = parseFloat(pts[i++])
|
|
265
|
+
ensurePath()
|
|
255
266
|
paths[pathName] = geometries.path2.appendArc({ segments, endpoint: [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)], radius: [svg2cagX(rx, svgUnitsPmm), svg2cagY(ry, svgUnitsPmm)], xaxisrotation: ro, clockwise: sf, large: lf }, paths[pathName])
|
|
256
267
|
}
|
|
257
268
|
break
|
|
258
269
|
case 'c': // relative cubic Bézier
|
|
259
|
-
while (pts.length >= 6) {
|
|
260
|
-
const x1 = cx + parseFloat(pts
|
|
261
|
-
const y1 = cy + parseFloat(pts
|
|
262
|
-
bx = cx + parseFloat(pts
|
|
263
|
-
by = cy + parseFloat(pts
|
|
264
|
-
cx = cx + parseFloat(pts
|
|
265
|
-
cy = cy + parseFloat(pts
|
|
270
|
+
while (pts.length >= i + 6) {
|
|
271
|
+
const x1 = cx + parseFloat(pts[i++])
|
|
272
|
+
const y1 = cy + parseFloat(pts[i++])
|
|
273
|
+
bx = cx + parseFloat(pts[i++])
|
|
274
|
+
by = cy + parseFloat(pts[i++])
|
|
275
|
+
cx = cx + parseFloat(pts[i++])
|
|
276
|
+
cy = cy + parseFloat(pts[i++])
|
|
277
|
+
ensurePath()
|
|
266
278
|
paths[pathName] = geometries.path2.appendBezier({ segments, controlPoints: [[svg2cagX(x1, svgUnitsPmm), svg2cagY(y1, svgUnitsPmm)], [svg2cagX(bx, svgUnitsPmm), svg2cagY(by, svgUnitsPmm)], [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]] }, paths[pathName])
|
|
267
279
|
const rf = reflect(bx, by, cx, cy)
|
|
268
280
|
bx = rf[0]
|
|
@@ -270,13 +282,14 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
270
282
|
}
|
|
271
283
|
break
|
|
272
284
|
case 'C': // absolute cubic Bézier
|
|
273
|
-
while (pts.length >= 6) {
|
|
274
|
-
const x1 = parseFloat(pts
|
|
275
|
-
const y1 = parseFloat(pts
|
|
276
|
-
bx = parseFloat(pts
|
|
277
|
-
by = parseFloat(pts
|
|
278
|
-
cx = parseFloat(pts
|
|
279
|
-
cy = parseFloat(pts
|
|
285
|
+
while (pts.length >= i + 6) {
|
|
286
|
+
const x1 = parseFloat(pts[i++])
|
|
287
|
+
const y1 = parseFloat(pts[i++])
|
|
288
|
+
bx = parseFloat(pts[i++])
|
|
289
|
+
by = parseFloat(pts[i++])
|
|
290
|
+
cx = parseFloat(pts[i++])
|
|
291
|
+
cy = parseFloat(pts[i++])
|
|
292
|
+
ensurePath()
|
|
280
293
|
paths[pathName] = geometries.path2.appendBezier({ segments, controlPoints: [[svg2cagX(x1, svgUnitsPmm), svg2cagY(y1, svgUnitsPmm)], [svg2cagX(bx, svgUnitsPmm), svg2cagY(by, svgUnitsPmm)], [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]] }, paths[pathName])
|
|
281
294
|
const rf = reflect(bx, by, cx, cy)
|
|
282
295
|
bx = rf[0]
|
|
@@ -284,11 +297,12 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
284
297
|
}
|
|
285
298
|
break
|
|
286
299
|
case 'q': // relative quadratic Bézier
|
|
287
|
-
while (pts.length >= 4) {
|
|
288
|
-
qx = cx + parseFloat(pts
|
|
289
|
-
qy = cy + parseFloat(pts
|
|
290
|
-
cx = cx + parseFloat(pts
|
|
291
|
-
cy = cy + parseFloat(pts
|
|
300
|
+
while (pts.length >= i + 4) {
|
|
301
|
+
qx = cx + parseFloat(pts[i++])
|
|
302
|
+
qy = cy + parseFloat(pts[i++])
|
|
303
|
+
cx = cx + parseFloat(pts[i++])
|
|
304
|
+
cy = cy + parseFloat(pts[i++])
|
|
305
|
+
ensurePath()
|
|
292
306
|
paths[pathName] = geometries.path2.appendBezier({ segments, controlPoints: [[svg2cagX(qx, svgUnitsPmm), svg2cagY(qy, svgUnitsPmm)], [svg2cagX(qx, svgUnitsPmm), svg2cagY(qy, svgUnitsPmm)], [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]] }, paths[pathName])
|
|
293
307
|
const rf = reflect(qx, qy, cx, cy)
|
|
294
308
|
qx = rf[0]
|
|
@@ -296,11 +310,12 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
296
310
|
}
|
|
297
311
|
break
|
|
298
312
|
case 'Q': // absolute quadratic Bézier
|
|
299
|
-
while (pts.length >= 4) {
|
|
300
|
-
qx = parseFloat(pts
|
|
301
|
-
qy = parseFloat(pts
|
|
302
|
-
cx = parseFloat(pts
|
|
303
|
-
cy = parseFloat(pts
|
|
313
|
+
while (pts.length >= i + 4) {
|
|
314
|
+
qx = parseFloat(pts[i++])
|
|
315
|
+
qy = parseFloat(pts[i++])
|
|
316
|
+
cx = parseFloat(pts[i++])
|
|
317
|
+
cy = parseFloat(pts[i++])
|
|
318
|
+
ensurePath()
|
|
304
319
|
paths[pathName] = geometries.path2.appendBezier({ segments, controlPoints: [[svg2cagX(qx, svgUnitsPmm), svg2cagY(qy, svgUnitsPmm)], [svg2cagX(qx, svgUnitsPmm), svg2cagY(qy, svgUnitsPmm)], [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]] }, paths[pathName])
|
|
305
320
|
const rf = reflect(qx, qy, cx, cy)
|
|
306
321
|
qx = rf[0]
|
|
@@ -308,9 +323,10 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
308
323
|
}
|
|
309
324
|
break
|
|
310
325
|
case 't': // relative quadratic Bézier shorthand
|
|
311
|
-
while (pts.length >= 2) {
|
|
312
|
-
cx = cx + parseFloat(pts
|
|
313
|
-
cy = cy + parseFloat(pts
|
|
326
|
+
while (pts.length >= i + 2) {
|
|
327
|
+
cx = cx + parseFloat(pts[i++])
|
|
328
|
+
cy = cy + parseFloat(pts[i++])
|
|
329
|
+
ensurePath()
|
|
314
330
|
paths[pathName] = geometries.path2.appendBezier({ segments, controlPoints: [[svg2cagX(qx, svgUnitsPmm), svg2cagY(qy, svgUnitsPmm)], [svg2cagX(qx, svgUnitsPmm), svg2cagY(qy, svgUnitsPmm)], [cx, cy]] }, paths[pathName])
|
|
315
331
|
const rf = reflect(qx, qy, cx, cy)
|
|
316
332
|
qx = rf[0]
|
|
@@ -318,9 +334,10 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
318
334
|
}
|
|
319
335
|
break
|
|
320
336
|
case 'T': // absolute quadratic Bézier shorthand
|
|
321
|
-
while (pts.length >= 2) {
|
|
322
|
-
cx = parseFloat(pts
|
|
323
|
-
cy = parseFloat(pts
|
|
337
|
+
while (pts.length >= i + 2) {
|
|
338
|
+
cx = parseFloat(pts[i++])
|
|
339
|
+
cy = parseFloat(pts[i++])
|
|
340
|
+
ensurePath()
|
|
324
341
|
paths[pathName] = geometries.path2.appendBezier({ segments, controlPoints: [[svg2cagX(qx, svgUnitsPmm), svg2cagY(qy, svgUnitsPmm)], [svg2cagX(qx, svgUnitsPmm), svg2cagY(qy, svgUnitsPmm)], [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]] }, paths[pathName])
|
|
325
342
|
const rf = reflect(qx, qy, cx, cy)
|
|
326
343
|
qx = rf[0]
|
|
@@ -328,13 +345,14 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
328
345
|
}
|
|
329
346
|
break
|
|
330
347
|
case 's': // relative cubic Bézier shorthand
|
|
331
|
-
while (pts.length >= 4) {
|
|
348
|
+
while (pts.length >= i + 4) {
|
|
332
349
|
const x1 = bx // reflection of 2nd control point from previous C
|
|
333
350
|
const y1 = by // reflection of 2nd control point from previous C
|
|
334
|
-
bx = cx + parseFloat(pts
|
|
335
|
-
by = cy + parseFloat(pts
|
|
336
|
-
cx = cx + parseFloat(pts
|
|
337
|
-
cy = cy + parseFloat(pts
|
|
351
|
+
bx = cx + parseFloat(pts[i++])
|
|
352
|
+
by = cy + parseFloat(pts[i++])
|
|
353
|
+
cx = cx + parseFloat(pts[i++])
|
|
354
|
+
cy = cy + parseFloat(pts[i++])
|
|
355
|
+
ensurePath()
|
|
338
356
|
paths[pathName] = geometries.path2.appendBezier({ segments, controlPoints: [[svg2cagX(x1, svgUnitsPmm), svg2cagY(y1, svgUnitsPmm)], [svg2cagX(bx, svgUnitsPmm), svg2cagY(by, svgUnitsPmm)], [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]] }, paths[pathName])
|
|
339
357
|
const rf = reflect(bx, by, cx, cy)
|
|
340
358
|
bx = rf[0]
|
|
@@ -342,13 +360,14 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
342
360
|
}
|
|
343
361
|
break
|
|
344
362
|
case 'S': // absolute cubic Bézier shorthand
|
|
345
|
-
while (pts.length >= 4) {
|
|
363
|
+
while (pts.length >= i + 4) {
|
|
346
364
|
const x1 = bx // reflection of 2nd control point from previous C
|
|
347
365
|
const y1 = by // reflection of 2nd control point from previous C
|
|
348
|
-
bx = parseFloat(pts
|
|
349
|
-
by = parseFloat(pts
|
|
350
|
-
cx = parseFloat(pts
|
|
351
|
-
cy = parseFloat(pts
|
|
366
|
+
bx = parseFloat(pts[i++])
|
|
367
|
+
by = parseFloat(pts[i++])
|
|
368
|
+
cx = parseFloat(pts[i++])
|
|
369
|
+
cy = parseFloat(pts[i++])
|
|
370
|
+
ensurePath()
|
|
352
371
|
paths[pathName] = geometries.path2.appendBezier({ segments, controlPoints: [[svg2cagX(x1, svgUnitsPmm), svg2cagY(y1, svgUnitsPmm)], [svg2cagX(bx, svgUnitsPmm), svg2cagY(by, svgUnitsPmm)], [svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]] }, paths[pathName])
|
|
353
372
|
const rf = reflect(bx, by, cx, cy)
|
|
354
373
|
bx = rf[0]
|
|
@@ -356,41 +375,41 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
356
375
|
}
|
|
357
376
|
break
|
|
358
377
|
case 'h': // relative Horzontal line to
|
|
359
|
-
while (pts.length >= 1) {
|
|
360
|
-
cx = cx + parseFloat(pts
|
|
361
|
-
paths[pathName] =
|
|
378
|
+
while (pts.length >= i + 1) {
|
|
379
|
+
cx = cx + parseFloat(pts[i++])
|
|
380
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]], paths[pathName])
|
|
362
381
|
}
|
|
363
382
|
break
|
|
364
383
|
case 'H': // absolute Horzontal line to
|
|
365
|
-
while (pts.length >= 1) {
|
|
366
|
-
cx = parseFloat(pts
|
|
367
|
-
paths[pathName] =
|
|
384
|
+
while (pts.length >= i + 1) {
|
|
385
|
+
cx = parseFloat(pts[i++])
|
|
386
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]], paths[pathName])
|
|
368
387
|
}
|
|
369
388
|
break
|
|
370
389
|
case 'l': // relative line to
|
|
371
|
-
while (pts.length >= 2) {
|
|
372
|
-
cx = cx + parseFloat(pts
|
|
373
|
-
cy = cy + parseFloat(pts
|
|
374
|
-
paths[pathName] =
|
|
390
|
+
while (pts.length >= i + 2) {
|
|
391
|
+
cx = cx + parseFloat(pts[i++])
|
|
392
|
+
cy = cy + parseFloat(pts[i++])
|
|
393
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]], paths[pathName])
|
|
375
394
|
}
|
|
376
395
|
break
|
|
377
396
|
case 'L': // absolute line to
|
|
378
|
-
while (pts.length >= 2) {
|
|
379
|
-
cx = parseFloat(pts
|
|
380
|
-
cy = parseFloat(pts
|
|
381
|
-
paths[pathName] =
|
|
397
|
+
while (pts.length >= i + 2) {
|
|
398
|
+
cx = parseFloat(pts[i++])
|
|
399
|
+
cy = parseFloat(pts[i++])
|
|
400
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]], paths[pathName])
|
|
382
401
|
}
|
|
383
402
|
break
|
|
384
403
|
case 'v': // relative Vertical line to
|
|
385
|
-
while (pts.length >= 1) {
|
|
386
|
-
cy = cy + parseFloat(pts
|
|
387
|
-
paths[pathName] =
|
|
404
|
+
while (pts.length >= i + 1) {
|
|
405
|
+
cy = cy + parseFloat(pts[i++])
|
|
406
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]], paths[pathName])
|
|
388
407
|
}
|
|
389
408
|
break
|
|
390
409
|
case 'V': // absolute Vertical line to
|
|
391
|
-
while (pts.length >= 1) {
|
|
392
|
-
cy = parseFloat(pts
|
|
393
|
-
paths[pathName] =
|
|
410
|
+
while (pts.length >= i + 1) {
|
|
411
|
+
cy = parseFloat(pts[i++])
|
|
412
|
+
paths[pathName] = appendPoints([[svg2cagX(cx, svgUnitsPmm), svg2cagY(cy, svgUnitsPmm)]], paths[pathName])
|
|
394
413
|
}
|
|
395
414
|
break
|
|
396
415
|
case 'z': // close current line
|
|
@@ -404,7 +423,25 @@ const expandPath = (obj, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups
|
|
|
404
423
|
console.log('Warning: Unknow PATH command [' + co.c + ']')
|
|
405
424
|
break
|
|
406
425
|
}
|
|
407
|
-
|
|
426
|
+
|
|
427
|
+
const isCloseCmd = (cmd) => cmd === 'z' || cmd === 'Z'
|
|
428
|
+
|
|
429
|
+
if (pc !== true && paths[pathName] && paths[pathName].isClosed) {
|
|
430
|
+
let coNext = obj.commands[j + 1]
|
|
431
|
+
|
|
432
|
+
if (!coNext || !isCloseCmd(coNext.c)) {
|
|
433
|
+
if (pathSelfClosed === 'trim') {
|
|
434
|
+
while (coNext && !isCloseCmd(coNext.c)) {
|
|
435
|
+
j++
|
|
436
|
+
coNext = obj.commands[j + 1]
|
|
437
|
+
}
|
|
438
|
+
} else if (pathSelfClosed === 'split') {
|
|
439
|
+
newPath()
|
|
440
|
+
} else {
|
|
441
|
+
throw new Error(`Malformed svg path at ${obj.position[0]}:${co.pos}. Path closed itself with command #${j} ${co.c}${pts.join(' ')}`)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
408
445
|
}
|
|
409
446
|
return paths
|
|
410
447
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const { svg2cagX, svg2cagY, cagLengthX, cagLengthY, cagLengthP, reflect } = require('./helpers')
|
|
2
|
-
// const { cssPxUnit } = require('./constants')
|
|
3
2
|
|
|
4
|
-
const shapesMap =
|
|
3
|
+
const shapesMap = (obj, codify, params) => {
|
|
5
4
|
const { level, indent, on, svgUnitsPmm, svgUnitsX, svgUnitsY, svgUnitsV, svgGroups, target, segments } = params
|
|
6
5
|
|
|
7
6
|
const types = {
|
|
@@ -1,60 +1,61 @@
|
|
|
1
1
|
const { cagColor, cssStyle, css2cag } = require('./helpers')
|
|
2
2
|
const { pxPmm } = require('./constants')
|
|
3
3
|
|
|
4
|
-
const svgCore =
|
|
5
|
-
if ('
|
|
4
|
+
const svgCore = (obj, element) => {
|
|
5
|
+
if ('id' in element) { obj.id = element.id }
|
|
6
6
|
if ('position' in element) { obj.position = element.position }
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const svgPresentation =
|
|
9
|
+
const svgPresentation = (obj, element) => {
|
|
10
10
|
// presentation attributes for all
|
|
11
|
-
if ('
|
|
11
|
+
if ('display' in element) { obj.visible = element.display }
|
|
12
12
|
// presentation attributes for solids
|
|
13
|
-
if ('
|
|
14
|
-
if ('
|
|
15
|
-
if ('
|
|
16
|
-
obj.fill = cagColor(element.
|
|
13
|
+
if ('color' in element) { obj.fill = cagColor(element.color); obj.stroke = obj.fill }
|
|
14
|
+
if ('opacity' in element) { obj.opacity = element.opacity }
|
|
15
|
+
if ('fill' in element) {
|
|
16
|
+
obj.fill = cagColor(element.fill)
|
|
17
17
|
} else {
|
|
18
18
|
const s = cssStyle(element, 'fill')
|
|
19
19
|
if (s) {
|
|
20
20
|
obj.fill = cagColor(s)
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
if ('
|
|
23
|
+
if ('fill-opacity' in element) { obj.opacity = element['fill-opacity'] }
|
|
24
24
|
// presentation attributes for lines
|
|
25
|
-
if ('
|
|
26
|
-
obj.strokeWidth = element['
|
|
25
|
+
if ('stroke-width' in element) {
|
|
26
|
+
obj.strokeWidth = element['stroke-width']
|
|
27
27
|
} else {
|
|
28
28
|
const sw = cssStyle(element, 'stroke-width')
|
|
29
29
|
if (sw) {
|
|
30
30
|
obj.strokeWidth = sw
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
if ('
|
|
34
|
-
obj.stroke = cagColor(element.
|
|
33
|
+
if ('stroke' in element) {
|
|
34
|
+
obj.stroke = cagColor(element.stroke)
|
|
35
35
|
} else {
|
|
36
36
|
const s = cssStyle(element, 'stroke')
|
|
37
37
|
if (s) {
|
|
38
38
|
obj.stroke = cagColor(s)
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
if ('
|
|
41
|
+
if ('stroke-opacity' in element) { obj.strokeOpacity = element['stroke-opacity'] }
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const svgTransformsRegExp = /\w+\(.+\)/i
|
|
45
|
+
|
|
46
|
+
const svgTransforms = (cag, element) => {
|
|
45
47
|
let list = null
|
|
46
|
-
if ('
|
|
47
|
-
list = element.
|
|
48
|
+
if ('transform' in element) {
|
|
49
|
+
list = element.transform
|
|
48
50
|
} else {
|
|
49
51
|
const s = cssStyle(element, 'transform')
|
|
50
52
|
if (s) { list = s }
|
|
51
53
|
}
|
|
52
54
|
if (list !== null) {
|
|
53
55
|
cag.transforms = []
|
|
54
|
-
|
|
55
|
-
let v = exp.exec(list)
|
|
56
|
+
let v = svgTransformsRegExp.exec(list)
|
|
56
57
|
while (v !== null) {
|
|
57
|
-
const s =
|
|
58
|
+
const s = svgTransformsRegExp.lastIndex
|
|
58
59
|
const e = list.indexOf(')') + 1
|
|
59
60
|
let t = list.slice(s, e) // the transform
|
|
60
61
|
t = t.trim()
|
|
@@ -87,29 +88,30 @@ const svgTransforms = function (cag, element) {
|
|
|
87
88
|
}
|
|
88
89
|
// shorten the list and continue
|
|
89
90
|
list = list.slice(e, list.length)
|
|
90
|
-
v =
|
|
91
|
+
v = svgTransformsRegExp.exec(list)
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
const
|
|
96
|
+
const viewBoxRegExp = /([\d.-]+)[\s,]+([\d.-]+)[\s,]+([\d.-]+)[\s,]+([\d.-]+)/i
|
|
97
|
+
|
|
98
|
+
const svgSvg = (element, { customPxPmm }) => {
|
|
96
99
|
// default SVG with no viewport
|
|
97
100
|
const obj = { type: 'svg', x: 0, y: 0, width: '100%', height: '100%', strokeWidth: '1' }
|
|
98
101
|
|
|
99
102
|
// default units per mm
|
|
100
103
|
obj.unitsPmm = [pxPmm, pxPmm]
|
|
101
104
|
|
|
102
|
-
if ('
|
|
105
|
+
if ('pxpmm' in element) {
|
|
103
106
|
// WOW! a supplied value for pixels per milimeter!!!
|
|
104
|
-
obj.pxPmm = element.
|
|
107
|
+
obj.pxPmm = element.pxpmm
|
|
105
108
|
obj.unitsPmm = [obj.pxPmm, obj.pxPmm]
|
|
106
109
|
}
|
|
107
|
-
if ('
|
|
108
|
-
if ('
|
|
109
|
-
if ('
|
|
110
|
-
const list = element.
|
|
111
|
-
const
|
|
112
|
-
const v = exp.exec(list)
|
|
110
|
+
if ('width' in element) { obj.width = element.width }
|
|
111
|
+
if ('height' in element) { obj.height = element.height }
|
|
112
|
+
if ('viewBox' in element) {
|
|
113
|
+
const list = element.viewBox.trim()
|
|
114
|
+
const v = viewBoxRegExp.exec(list)
|
|
113
115
|
if (v !== null) {
|
|
114
116
|
obj.viewX = parseFloat(v[1])
|
|
115
117
|
obj.viewY = parseFloat(v[2])
|
|
@@ -158,12 +160,12 @@ const svgSvg = function (element, { customPxPmm }) {
|
|
|
158
160
|
return obj
|
|
159
161
|
}
|
|
160
162
|
|
|
161
|
-
const svgEllipse =
|
|
163
|
+
const svgEllipse = (element) => {
|
|
162
164
|
const obj = { type: 'ellipse', cx: '0', cy: '0', rx: '0', ry: '0' }
|
|
163
|
-
if ('
|
|
164
|
-
if ('
|
|
165
|
-
if ('
|
|
166
|
-
if ('
|
|
165
|
+
if ('cx' in element) { obj.cx = element.cx }
|
|
166
|
+
if ('cy' in element) { obj.cy = element.cy }
|
|
167
|
+
if ('rx' in element) { obj.rx = element.rx }
|
|
168
|
+
if ('ry' in element) { obj.ry = element.ry }
|
|
167
169
|
// transforms
|
|
168
170
|
svgTransforms(obj, element)
|
|
169
171
|
// core attributes
|
|
@@ -173,12 +175,12 @@ const svgEllipse = function (element) {
|
|
|
173
175
|
return obj
|
|
174
176
|
}
|
|
175
177
|
|
|
176
|
-
const svgLine =
|
|
178
|
+
const svgLine = (element) => {
|
|
177
179
|
const obj = { type: 'line', x1: '0', y1: '0', x2: '0', y2: '0' }
|
|
178
|
-
if ('
|
|
179
|
-
if ('
|
|
180
|
-
if ('
|
|
181
|
-
if ('
|
|
180
|
+
if ('x1' in element) { obj.x1 = element.x1 }
|
|
181
|
+
if ('y1' in element) { obj.y1 = element.y1 }
|
|
182
|
+
if ('x2' in element) { obj.x2 = element.x2 }
|
|
183
|
+
if ('y2' in element) { obj.y2 = element.y2 }
|
|
182
184
|
// transforms
|
|
183
185
|
svgTransforms(obj, element)
|
|
184
186
|
// core attributes
|
|
@@ -188,9 +190,9 @@ const svgLine = function (element) {
|
|
|
188
190
|
return obj
|
|
189
191
|
}
|
|
190
192
|
|
|
191
|
-
const svgListOfPoints =
|
|
193
|
+
const svgListOfPoints = (list) => {
|
|
192
194
|
const points = []
|
|
193
|
-
const exp =
|
|
195
|
+
const exp = /([\d\-+.]+)[\s,]+([\d\-+.]+)[\s,]*/i
|
|
194
196
|
list = list.trim()
|
|
195
197
|
let v = exp.exec(list)
|
|
196
198
|
while (v !== null) {
|
|
@@ -204,7 +206,7 @@ const svgListOfPoints = function (list) {
|
|
|
204
206
|
return points
|
|
205
207
|
}
|
|
206
208
|
|
|
207
|
-
const svgPolyline =
|
|
209
|
+
const svgPolyline = (element) => {
|
|
208
210
|
const obj = { type: 'polyline' }
|
|
209
211
|
// transforms
|
|
210
212
|
svgTransforms(obj, element)
|
|
@@ -213,13 +215,13 @@ const svgPolyline = function (element) {
|
|
|
213
215
|
// presentation attributes
|
|
214
216
|
svgPresentation(obj, element)
|
|
215
217
|
|
|
216
|
-
if ('
|
|
217
|
-
obj.points = svgListOfPoints(element.
|
|
218
|
+
if ('points' in element) {
|
|
219
|
+
obj.points = svgListOfPoints(element.points)
|
|
218
220
|
}
|
|
219
221
|
return obj
|
|
220
222
|
}
|
|
221
223
|
|
|
222
|
-
const svgPolygon =
|
|
224
|
+
const svgPolygon = (element) => {
|
|
223
225
|
const obj = { type: 'polygon' }
|
|
224
226
|
// transforms
|
|
225
227
|
svgTransforms(obj, element)
|
|
@@ -228,30 +230,30 @@ const svgPolygon = function (element) {
|
|
|
228
230
|
// presentation attributes
|
|
229
231
|
svgPresentation(obj, element)
|
|
230
232
|
|
|
231
|
-
if ('
|
|
232
|
-
obj.points = svgListOfPoints(element.
|
|
233
|
+
if ('points' in element) {
|
|
234
|
+
obj.points = svgListOfPoints(element.points)
|
|
233
235
|
}
|
|
234
236
|
return obj
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
const svgRect =
|
|
239
|
+
const svgRect = (element) => {
|
|
238
240
|
const obj = { type: 'rect', x: '0', y: '0', rx: '0', ry: '0', width: '0', height: '0' }
|
|
239
241
|
|
|
240
|
-
if ('
|
|
241
|
-
if ('
|
|
242
|
-
if ('
|
|
243
|
-
obj.rx = element.
|
|
244
|
-
if (!('
|
|
242
|
+
if ('x' in element) { obj.x = element.x }
|
|
243
|
+
if ('y' in element) { obj.y = element.y }
|
|
244
|
+
if ('rx' in element) {
|
|
245
|
+
obj.rx = element.rx
|
|
246
|
+
if (!('ry' in element)) { obj.ry = obj.rx } // by SVG specification
|
|
245
247
|
}
|
|
246
|
-
if ('
|
|
247
|
-
obj.ry = element.
|
|
248
|
-
if (!('
|
|
248
|
+
if ('ry' in element) {
|
|
249
|
+
obj.ry = element.ry
|
|
250
|
+
if (!('rx' in element)) { obj.rx = obj.ry } // by SVG specification
|
|
249
251
|
}
|
|
250
252
|
if (obj.rx !== obj.ry) {
|
|
251
|
-
console.log('Warning: Unsupported RECT with
|
|
253
|
+
console.log('Warning: Unsupported RECT with rx and ry radius')
|
|
252
254
|
}
|
|
253
|
-
if ('
|
|
254
|
-
if ('
|
|
255
|
+
if ('width' in element) { obj.width = element.width }
|
|
256
|
+
if ('height' in element) { obj.height = element.height }
|
|
255
257
|
// transforms
|
|
256
258
|
svgTransforms(obj, element)
|
|
257
259
|
// core attributes
|
|
@@ -261,12 +263,12 @@ const svgRect = function (element) {
|
|
|
261
263
|
return obj
|
|
262
264
|
}
|
|
263
265
|
|
|
264
|
-
const svgCircle =
|
|
266
|
+
const svgCircle = (element) => {
|
|
265
267
|
const obj = { type: 'circle', x: '0', y: '0', radius: '0' }
|
|
266
268
|
|
|
267
|
-
if ('
|
|
268
|
-
if ('
|
|
269
|
-
if ('
|
|
269
|
+
if ('cx' in element) { obj.x = element.cx }
|
|
270
|
+
if ('cy' in element) { obj.y = element.cy }
|
|
271
|
+
if ('r' in element) { obj.radius = element.r }
|
|
270
272
|
// transforms
|
|
271
273
|
svgTransforms(obj, element)
|
|
272
274
|
// core attributes
|
|
@@ -276,7 +278,7 @@ const svgCircle = function (element) {
|
|
|
276
278
|
return obj
|
|
277
279
|
}
|
|
278
280
|
|
|
279
|
-
const svgGroup =
|
|
281
|
+
const svgGroup = (element) => {
|
|
280
282
|
const obj = { type: 'group' }
|
|
281
283
|
// transforms
|
|
282
284
|
svgTransforms(obj, element)
|
|
@@ -285,11 +287,11 @@ const svgGroup = function (element) {
|
|
|
285
287
|
// presentation attributes
|
|
286
288
|
svgPresentation(obj, element)
|
|
287
289
|
|
|
288
|
-
if ('
|
|
290
|
+
if ('x' in element || 'y' in element) {
|
|
289
291
|
let x = '0'
|
|
290
292
|
let y = '0'
|
|
291
|
-
if ('
|
|
292
|
-
if ('
|
|
293
|
+
if ('x' in element) x = element.x
|
|
294
|
+
if ('y' in element) y = element.y
|
|
293
295
|
if (!('transforms' in obj)) obj.transforms = []
|
|
294
296
|
const o = { translate: [x, y] }
|
|
295
297
|
obj.transforms.push(o)
|
|
@@ -302,7 +304,7 @@ const svgGroup = function (element) {
|
|
|
302
304
|
//
|
|
303
305
|
// Convert the PATH element into object representation
|
|
304
306
|
//
|
|
305
|
-
const svgPath =
|
|
307
|
+
const svgPath = (element) => {
|
|
306
308
|
const obj = { type: 'path' }
|
|
307
309
|
// transforms
|
|
308
310
|
svgTransforms(obj, element)
|
|
@@ -312,14 +314,15 @@ const svgPath = function (element) {
|
|
|
312
314
|
svgPresentation(obj, element)
|
|
313
315
|
|
|
314
316
|
obj.commands = []
|
|
315
|
-
if ('
|
|
317
|
+
if ('d' in element) {
|
|
316
318
|
let co = null // current command
|
|
317
319
|
let bf = ''
|
|
318
320
|
|
|
319
321
|
let i = 0
|
|
320
|
-
const l = element.
|
|
322
|
+
const l = element.d.length
|
|
323
|
+
const offset = element.position[1] - l - 2
|
|
321
324
|
while (i < l) {
|
|
322
|
-
const c = element.
|
|
325
|
+
const c = element.d[i]
|
|
323
326
|
switch (c) {
|
|
324
327
|
// numbers
|
|
325
328
|
// FIXME support E notation numbers
|
|
@@ -379,7 +382,7 @@ const svgPath = function (element) {
|
|
|
379
382
|
}
|
|
380
383
|
obj.commands.push(co)
|
|
381
384
|
}
|
|
382
|
-
co = { c: c, p: [] }
|
|
385
|
+
co = { c: c, p: [], pos: i + offset }
|
|
383
386
|
break
|
|
384
387
|
// white space
|
|
385
388
|
case ',':
|
|
@@ -408,11 +411,11 @@ const svgPath = function (element) {
|
|
|
408
411
|
}
|
|
409
412
|
|
|
410
413
|
// generate GROUP with attributes from USE element
|
|
411
|
-
// - expect
|
|
412
|
-
// - append translate(x,y) if
|
|
414
|
+
// - expect x,y,height,width,XLINK:HREF
|
|
415
|
+
// - append translate(x,y) if x,y available
|
|
413
416
|
// deep clone the referenced OBJECT and add to group
|
|
414
417
|
// - clone using JSON.parse(JSON.stringify(obj))
|
|
415
|
-
const svgUse =
|
|
418
|
+
const svgUse = (element, { svgObjects }) => {
|
|
416
419
|
const obj = { type: 'group' }
|
|
417
420
|
// transforms
|
|
418
421
|
svgTransforms(obj, element)
|
|
@@ -421,20 +424,20 @@ const svgUse = function (element, { svgObjects }) {
|
|
|
421
424
|
// presentation attributes
|
|
422
425
|
svgPresentation(obj, element)
|
|
423
426
|
|
|
424
|
-
if ('
|
|
427
|
+
if ('x' in element || 'y' in element) {
|
|
425
428
|
let x = '0'
|
|
426
429
|
let y = '0'
|
|
427
|
-
if ('
|
|
428
|
-
if ('
|
|
430
|
+
if ('x' in element) x = element.x
|
|
431
|
+
if ('y' in element) y = element.y
|
|
429
432
|
if (!('transforms' in obj)) obj.transforms = []
|
|
430
433
|
const o = { translate: [x, y] }
|
|
431
434
|
obj.transforms.push(o)
|
|
432
435
|
}
|
|
433
436
|
|
|
434
437
|
obj.objects = []
|
|
435
|
-
if ('
|
|
438
|
+
if ('xlink:href' in element) {
|
|
436
439
|
// lookup the named object
|
|
437
|
-
let ref = element['
|
|
440
|
+
let ref = element['xlink:href']
|
|
438
441
|
if (ref[0] === '#') { ref = ref.slice(1, ref.length) }
|
|
439
442
|
if (svgObjects[ref] !== undefined) {
|
|
440
443
|
ref = svgObjects[ref]
|
package/tests/inkscape.test.js
CHANGED
|
@@ -2,13 +2,12 @@ const test = require('ava')
|
|
|
2
2
|
|
|
3
3
|
const countOf = require('../../test/helpers/countOf')
|
|
4
4
|
|
|
5
|
-
const deserializer = require('../index.js')
|
|
5
|
+
const deserializer = require('../src/index.js')
|
|
6
6
|
|
|
7
7
|
// deserializer
|
|
8
8
|
|
|
9
9
|
test('deserialize : translate svg produced by inkscape to script', (t) => {
|
|
10
|
-
const sourceSvg =
|
|
11
|
-
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
10
|
+
const sourceSvg = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
12
11
|
<svg
|
|
13
12
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
14
13
|
xmlns:cc="http://creativecommons.org/ns#"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
2
|
|
|
3
|
-
const deserializer = require('../index.js')
|
|
3
|
+
const deserializer = require('../src/index.js')
|
|
4
|
+
|
|
4
5
|
const { measurements } = require('@jscad/modeling')
|
|
5
6
|
|
|
6
7
|
// deserializer
|
|
7
8
|
|
|
8
9
|
test('deserialize : instantiate svg (rect) to objects', (t) => {
|
|
9
|
-
const sourceSvg = `<svg
|
|
10
|
+
const sourceSvg = `<svg pxpmm="10" width="500" height="500">
|
|
10
11
|
<rect x="80" y="60" width="250" height="250" color="red"/>
|
|
11
12
|
<rect x="140" y="120" width="250" height="250" rx="40" color="rgb(0,255,0)"/>
|
|
12
13
|
<rect x="140" y="120" width="250" height="250" ry="40" color="blue"/>
|
|
@@ -149,7 +150,7 @@ test('deserialize : instantiate svg (line) to objects', (t) => {
|
|
|
149
150
|
t.is(shape.points.length, 2)
|
|
150
151
|
|
|
151
152
|
// test getting stroke width from group
|
|
152
|
-
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120"
|
|
153
|
+
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120">
|
|
153
154
|
<g stroke-width="2">
|
|
154
155
|
<line x1="20" y1="100" x2="100" y2="20" stroke="black"/>
|
|
155
156
|
</g>
|
|
@@ -180,7 +181,7 @@ test('deserialize : instantiate svg (path: simple) to objects', (t) => {
|
|
|
180
181
|
t.is(shape.points.length, 3)
|
|
181
182
|
|
|
182
183
|
// test getting stroke width from group
|
|
183
|
-
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120"
|
|
184
|
+
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120">
|
|
184
185
|
<g stroke-width="2">
|
|
185
186
|
<path d="M150 0 L75 200 L225 200 Z" />
|
|
186
187
|
</g>
|
|
@@ -210,7 +211,7 @@ test('deserialize : instantiate svg (path: simple) to objects', (t) => {
|
|
|
210
211
|
shape = observed[0]
|
|
211
212
|
t.is(shape.points.length, 3)
|
|
212
213
|
|
|
213
|
-
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120"
|
|
214
|
+
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120">
|
|
214
215
|
<path d="M 240.00000 56.00000 H 270.00000 V 86.00000 H 300.00000 V 116.00000 H 330.00000 V 146.00000 H 240.00000 V 56.00000 Z"/>
|
|
215
216
|
</svg>`
|
|
216
217
|
|
|
@@ -247,10 +248,10 @@ test('deserialize : instantiate svg (path: arc) to objects', (t) => {
|
|
|
247
248
|
t.is(observed.length, 2)
|
|
248
249
|
shape = observed[0]
|
|
249
250
|
t.is(shape.points.length, 15) // segments double on a 3/4 circle
|
|
250
|
-
t.deepEqual(measurements.measureBoundingBox(shape), [[64.91110599999999, -77.
|
|
251
|
+
t.deepEqual(measurements.measureBoundingBox(shape), [[64.91110599999999, -77.611105, 0], [90.21850570104527, -52.30370029895471, 0]])
|
|
251
252
|
shape = observed[1]
|
|
252
253
|
t.is(shape.points.length, 15) // segments double on a 3/4 circle
|
|
253
|
-
t.deepEqual(measurements.measureBoundingBox(shape), [[50.
|
|
254
|
+
t.deepEqual(measurements.measureBoundingBox(shape), [[50.799996, -136.03302387090216, 0], [72.27222493929787, -110.6793647936299, 0]])
|
|
254
255
|
})
|
|
255
256
|
|
|
256
257
|
// ################################
|
|
@@ -360,8 +361,7 @@ test('deserialize : instantiate svg (path: with bezier) to objects', (t) => {
|
|
|
360
361
|
// ################################
|
|
361
362
|
|
|
362
363
|
test('deserialize : instantiate svg produced by inkscape to objects', (t) => {
|
|
363
|
-
const sourceSvg =
|
|
364
|
-
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
364
|
+
const sourceSvg = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
365
365
|
<svg
|
|
366
366
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
367
367
|
xmlns:cc="http://creativecommons.org/ns#"
|
|
@@ -434,16 +434,16 @@ test('deserialize : instantiate shape with a hole to objects', (t) => {
|
|
|
434
434
|
let observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
|
|
435
435
|
t.is(observed.length, 2)
|
|
436
436
|
let shape = observed[0]
|
|
437
|
-
t.is(shape.sides.length,
|
|
437
|
+
t.is(shape.sides.length, 38)
|
|
438
438
|
shape = observed[1]
|
|
439
|
-
t.is(shape.sides.length,
|
|
439
|
+
t.is(shape.sides.length, 38)
|
|
440
440
|
|
|
441
441
|
observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
|
|
442
442
|
t.is(observed.length, 2)
|
|
443
443
|
shape = observed[0]
|
|
444
|
-
t.is(shape.points.length,
|
|
444
|
+
t.is(shape.points.length, 38)
|
|
445
445
|
shape = observed[1]
|
|
446
|
-
t.is(shape.points.length,
|
|
446
|
+
t.is(shape.points.length, 38)
|
|
447
447
|
})
|
|
448
448
|
|
|
449
449
|
// ################################
|
|
@@ -458,12 +458,12 @@ test('deserialize : instantiate shape with a nested hole to objects', (t) => {
|
|
|
458
458
|
let observed = deserializer.deserialize({ output: 'geometry', target: 'geom2', addMetaData: false }, sourceSvg)
|
|
459
459
|
t.is(observed.length, 4)
|
|
460
460
|
let shape = observed[0]
|
|
461
|
-
t.is(shape.sides.length,
|
|
461
|
+
t.is(shape.sides.length, 38)
|
|
462
462
|
|
|
463
463
|
observed = deserializer.deserialize({ output: 'geometry', target: 'path', addMetaData: false }, sourceSvg)
|
|
464
464
|
t.is(observed.length, 4)
|
|
465
465
|
shape = observed[0]
|
|
466
|
-
t.is(shape.points.length,
|
|
466
|
+
t.is(shape.points.length, 38)
|
|
467
467
|
})
|
|
468
468
|
|
|
469
469
|
// ################################
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const test = require('ava')
|
|
2
|
+
|
|
3
|
+
const deserializer = require('../src/index.js')
|
|
4
|
+
|
|
5
|
+
// deserializer
|
|
6
|
+
|
|
7
|
+
// ################################
|
|
8
|
+
|
|
9
|
+
test('deserialize issue 885', (t) => {
|
|
10
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="5.791015625" height="11.71875">
|
|
11
|
+
<path d="M1.84-3.17L0.53-3.48L0.97-7.11L5.12-7.11L5.12-5.83L2.30-5.83L2.14-4.42Q2.32-4.52 2.60-4.60Q2.89-4.68 3.16-4.68L3.16-4.68Q4.22-4.68 4.79-4.05Q5.36-3.42 5.36-2.29L5.36-2.29Q5.36-1.61 5.06-1.05Q4.75-0.50 4.20-0.20Q3.65 0.10 2.90 0.10L2.90 0.10Q2.23 0.10 1.64-0.18Q1.05-0.45 0.72-0.94Q0.39-1.42 0.40-2.02L0.40-2.02L2.05-2.02Q2.07-1.63 2.29-1.40Q2.52-1.17 2.89-1.17L2.89-1.17Q3.72-1.17 3.72-2.40L3.72-2.40Q3.72-3.54 2.70-3.54L2.70-3.54Q2.12-3.54 1.84-3.17L1.84-3.17Z"/>
|
|
12
|
+
</svg>`
|
|
13
|
+
|
|
14
|
+
t.throws(() => {
|
|
15
|
+
deserializer.deserialize({ output: 'geometry' }, svg)
|
|
16
|
+
}, { instanceOf: Error }, 'Malformed svg path at 2:445. Path closed itself with command #29: "Q2.12 -3.54 1.84 -3.17". to avoid this error use pathSelfClosed:\'split\' or pathSelfClosed:\'trim\' option')
|
|
17
|
+
|
|
18
|
+
let shapes = deserializer.deserialize({ output: 'geometry', pathSelfClosed: 'split' }, svg)
|
|
19
|
+
t.is(shapes.length, 2)
|
|
20
|
+
|
|
21
|
+
shapes = deserializer.deserialize({ output: 'geometry', pathSelfClosed: 'trim' }, svg)
|
|
22
|
+
t.is(shapes.length, 1)
|
|
23
|
+
})
|
package/tests/translate.test.js
CHANGED
|
@@ -2,14 +2,14 @@ const test = require('ava')
|
|
|
2
2
|
|
|
3
3
|
const countOf = require('../../test/helpers/countOf')
|
|
4
4
|
|
|
5
|
-
const deserializer = require('../index.js')
|
|
5
|
+
const deserializer = require('../src/index.js')
|
|
6
6
|
|
|
7
7
|
// deserializer
|
|
8
8
|
|
|
9
9
|
// ################################
|
|
10
10
|
|
|
11
11
|
test('deserialize : translate svg (rect) to script', (t) => {
|
|
12
|
-
const sourceSvg = `<svg
|
|
12
|
+
const sourceSvg = `<svg pxpmm="10" width="500" height="500">
|
|
13
13
|
<rect x="80" y="60" width="250" height="250" color="red"/>
|
|
14
14
|
<rect x="140" y="120" width="250" height="250" rx="40" color="rgb(0,255,0)"/>
|
|
15
15
|
<rect x="140" y="120" width="250" height="250" ry="40" color="blue"/>
|
|
@@ -88,7 +88,7 @@ test('deserialize : translate svg (line) to script', (t) => {
|
|
|
88
88
|
// TODO
|
|
89
89
|
|
|
90
90
|
// test getting stroke width from group
|
|
91
|
-
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120"
|
|
91
|
+
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120">
|
|
92
92
|
<g stroke-width="2">
|
|
93
93
|
<line x1="20" y1="100" x2="100" y2="20" stroke="black"/>
|
|
94
94
|
</g>
|
|
@@ -133,7 +133,7 @@ test('deserialize : translate svg (polyline) to script', (t) => {
|
|
|
133
133
|
// FIXME t.is(countOf('path2.fromPoints', obs), 1)
|
|
134
134
|
|
|
135
135
|
// test getting stroke width from group
|
|
136
|
-
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120"
|
|
136
|
+
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120">
|
|
137
137
|
<g stroke-width="2">
|
|
138
138
|
<polyline fill="none" stroke="black" points="20,100 40,60 70,80 100,20"/>
|
|
139
139
|
</g>
|
|
@@ -168,7 +168,7 @@ test('deserialize : translate svg (path: simple) to script', (t) => {
|
|
|
168
168
|
t.is(countOf('geom2.fromPoints', obs), 1)
|
|
169
169
|
|
|
170
170
|
// test getting stroke width from group
|
|
171
|
-
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120"
|
|
171
|
+
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120">
|
|
172
172
|
<g stroke-width="2">
|
|
173
173
|
<path d="M150 0 L75 200 L225 200 Z" />
|
|
174
174
|
</g>
|
|
@@ -190,7 +190,7 @@ test('deserialize : translate svg (path: simple) to script', (t) => {
|
|
|
190
190
|
t.is(countOf('path2.close', obs), 1)
|
|
191
191
|
t.is(countOf('geom2.fromPoints', obs), 0)
|
|
192
192
|
|
|
193
|
-
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120"
|
|
193
|
+
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120">
|
|
194
194
|
<path fill="#ff8000" d="m 240.00000 190.00000 h 30.00000 v 30.00000 h 30.00000 v 30.00000 h 30.00000 v 30.00000 h -90.00000 v -90.00000 z"/>
|
|
195
195
|
</svg>`
|
|
196
196
|
|
|
@@ -202,7 +202,7 @@ test('deserialize : translate svg (path: simple) to script', (t) => {
|
|
|
202
202
|
t.is(countOf('geom2.fromPoints', obs), 1)
|
|
203
203
|
t.is(countOf('colors.colorize', obs), 1) // fill
|
|
204
204
|
|
|
205
|
-
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120"
|
|
205
|
+
sourceSvg = `<svg width="120" height="120" viewBox="0 0 120 120">
|
|
206
206
|
<path d="M 240.00000 56.00000 H 270.00000 V 86.00000 H 300.00000 V 116.00000 H 330.00000 V 146.00000 H 240.00000 V 56.00000 Z"/>
|
|
207
207
|
</svg>`
|
|
208
208
|
|