/*! * SvgParser * A library to convert an SVG string to parse-able segments for CAD/CAM use * Licensed under the MIT license */ (function(root){ 'use strict'; function SvgParser(){ // the SVG document this.svg; // the top level SVG element of the SVG document this.svgRoot; this.allowedElements = ['svg','circle','ellipse','path','polygon','polyline','rect', 'line']; this.conf = { tolerance: 2, // max bound for bezier->line segment conversion, in native SVG units toleranceSvg: 0.005 // fudge factor for browser inaccuracy in SVG unit handling }; } SvgParser.prototype.config = function(config){ this.conf.tolerance = config.tolerance; } SvgParser.prototype.load = function(svgString){ if(!svgString || typeof svgString !== 'string'){ throw Error('invalid SVG string'); } var parser = new DOMParser(); var svg = parser.parseFromString(svgString, "image/svg+xml"); this.svgRoot = false; if(svg){ this.svg = svg; for(var i=0; i 0){ var transform = this.transformParse(transformString); } if(!transform){ transform = new Matrix(); } var tarray = transform.toArray(); // decompose affine matrix to rotate, scale components (translate is just the 3rd column) var rotate = Math.atan2(tarray[1], tarray[3])*180/Math.PI; var scale = Math.sqrt(tarray[0]*tarray[0]+tarray[2]*tarray[2]); if(element.tagName == 'g' || element.tagName == 'svg' || element.tagName == 'defs' || element.tagName == 'clipPath'){ element.removeAttribute('transform'); var children = Array.prototype.slice.call(element.childNodes); for(var i=0; i 0){ element.parentElement.appendChild(element.childNodes[0]); } } } // remove all elements with tag name not in the whitelist // use this to remove , etc that don't represent shapes SvgParser.prototype.filter = function(whitelist, element){ if(!whitelist || whitelist.length == 0){ throw Error('invalid whitelist'); } element = element || this.svgRoot; for(var i=0; i=0; i--){ if(i > 0 && seglist[i].pathSegTypeAsLetter == 'M' || seglist[i].pathSegTypeAsLetter == 'm'){ lastM = i; break; } } if(lastM == 0){ return false; // only 1 M command, no need to split } for( i=0; i 1){ path.parentElement.insertBefore(paths[i], path); addedPaths.push(paths[i]); } } path.remove(); return addedPaths; } // recursively run the given function on the given element SvgParser.prototype.recurse = function(element, func){ // only operate on original DOM tree, ignore any children that are added. Avoid infinite loops var children = Array.prototype.slice.call(element.childNodes); for(var i=0; i 0 && /[QqTt]/.test(seglist.getItem(i-1).pathSegTypeAsLetter)){ x1 = prevx + (prevx-prevx1); y1 = prevy + (prevy-prevy1); } else{ x1 = prevx; y1 = prevy; } case 'q': case 'Q': var pointlist = GeometryUtil.QuadraticBezier.linearize({x: prevx, y: prevy}, {x: x, y: y}, {x: x1, y: y1}, this.conf.tolerance); pointlist.shift(); // firstpoint would already be in the poly for(var j=0; j 0 && /[CcSs]/.test(seglist.getItem(i-1).pathSegTypeAsLetter)){ x1 = prevx + (prevx-prevx2); y1 = prevy + (prevy-prevy2); } else{ x1 = prevx; y1 = prevy; } case 'c': case 'C': var pointlist = GeometryUtil.CubicBezier.linearize({x: prevx, y: prevy}, {x: x, y: y}, {x: x1, y: y1}, {x: x2, y: y2}, this.conf.tolerance); pointlist.shift(); // firstpoint would already be in the poly for(var j=0; j 0 && GeometryUtil.almostEqual(poly[0].x,poly[poly.length-1].x, this.conf.toleranceSvg) && GeometryUtil.almostEqual(poly[0].y,poly[poly.length-1].y, this.conf.toleranceSvg)){ poly.pop(); } return poly; }; // expose public methods var parser = new SvgParser(); root.SvgParser = { config: parser.config.bind(parser), load: parser.load.bind(parser), getStyle: parser.getStyle.bind(parser), clean: parser.cleanInput.bind(parser), polygonify: parser.polygonify.bind(parser) }; }(window));