Convert SVG polygon to path
Asked Answered
C

5

35

I have a fairly large SVG file of administrative subdivisions that I need to work with in Raphael.JS (it has 600 polygons and weights 1.2 Mb).

Now, I need to convert these polygons to paths so that it works in Raphael. The great poly2path tool does that, but it doesn't support any batch command, so that each polygon's position relative to the others is lost.

Do you know of any tool to convert SVG polygons to paths? (I also have the AI file which was used to export the SVG).

Many thanks

Concavoconcave answered 23/5, 2012 at 9:32 Comment(1)
If you visit again @nkb, please accept your preferred answer.Burnight
F
103
  1. Open your SVG in a web browser.

  2. Run this code:

     var polys = document.querySelectorAll('polygon,polyline');
     [].forEach.call(polys,convertPolyToPath);
    
     function convertPolyToPath(poly){
       var svgNS = poly.ownerSVGElement.namespaceURI;
       var path = document.createElementNS(svgNS,'path');
       var pathdata = 'M '+poly.getAttribute('points');
       if (poly.tagName=='polygon') pathdata+='z';
       path.setAttribute('d',pathdata);
       poly.getAttributeNames().forEach(function(name){
         if(name !== 'points')
           path.setAttribute(name, poly.getAttribute(name))
       })
       poly.parentNode.replaceChild(path,poly);
     }
    
  3. Using the Developer Tools (or Firebug) of the browser, use "Copy as HTML" (or Copy SVG) on the element to get the modified source onto the clipboard.

  4. Paste into a new file and enjoy.

I have a demo of the above method (slightly modified) on my website:
http://phrogz.net/svg/convert_polys_to_paths.svg

There are two methods in use on that page; one (like the above) uses string-based techniques to get and set the points; the other uses the SVG DOM for accessing points and setting path commands.


As noted by @Interactive in the comments, you can do this via text-only transformations by:

  1. Convert all <polyline and <polygon to <path

  2. Change all points=" to d="M

  3. For any elements that were <polygon>, you need to add z as the last character of the d attribute to connect the last point to the first. For example:

     <polygon points="1,2 3,-4 5,6"/> 
    

    becomes

     <path d="M1,2 3,-4 5,6z"/> 
    

This 'hack' works because the specifications declare that a moveto command (M or m) followed by multiple coordinates is legal, with all coordinates after the first interpreted as lineto commands.

Feaster answered 24/5, 2012 at 2:9 Comment(5)
This works great with Raphael in all browsers (even IE7-8) - but not in IE9. Raphael just don't draw paths based on this function. Do you have any idea why?Hessney
@oyatek Ask this as a question with SVG tag and repro case.Feaster
@Interactive put it in a <script> block at the bottom of your SVG file, or open the developer tools and paste it into the console.Feaster
BIG ASS FYI I found. It worked for me but: Replace all instances of <polyline with <path and points=" with d="M. Found here: gist.github.com/andytlr/9283541Andyane
@Andyane This is a great hack, and it's valid. The specs for the moveto command explicitly state that additional coordinates beyond the first pair are treated as implicit lineto commands. I'll edit my answer to add.Feaster
A
6

A clicky-bunty answer:

  1. open the svg in inkscape vector graphics editor
  2. select all objects (ctrl-a)
  3. at the drop down menu point "path" select first entry "object to path" (shift-ctrl-c)
  4. save the svg and check out the path properties

Might be not an appropriate answer (because with large files the program needs some time).

Acreinch answered 3/2, 2013 at 3:9 Comment(0)
M
4

Little fix for polygon id, fill and stroke attributes save

var polys = document.querySelectorAll('polygon,polyline');
[].forEach.call(polys,convertPolyToPath);

function convertPolyToPath(poly){
  var svgNS = poly.ownerSVGElement.namespaceURI;
  var path = document.createElementNS(svgNS,'path');
  var points = poly.getAttribute('points').split(/\s+|,/);
  var x0=points.shift(), y0=points.shift();
  var pathdata = 'M'+x0+','+y0+'L'+points.join(' ');
  if (poly.tagName=='polygon') pathdata+='z';
  path.setAttribute('id',poly.getAttribute('id'));
  path.setAttribute('fill',poly.getAttribute('fill'));
  path.setAttribute('stroke',poly.getAttribute('stroke'));
  path.setAttribute('d',pathdata);

  poly.parentNode.replaceChild(path,poly);
}
Manipulate answered 9/12, 2012 at 13:32 Comment(1)
There are more attributes that might be of interest, like class, style, transform, clip-path, color, onclick, display, fill-rule, opacity and moreEspinoza
E
4

Copying everything from the developer tools seems pretty inconvenient. You could use an XSLT to transform polygons and polylines to paths:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" exclude-result-prefixes="svg"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:svg="http://www.w3.org/2000/svg">

  <!-- Identity transform: Copy everything 
       (except for polygon/polyline, handled below) -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- Turn polygons/polylines into paths, 
       copy all attributes and content 
       (except for @points: Will be matched
       by template below) -->
  <xsl:template match="svg:polygon|svg:polyline">
    <path>
      <xsl:apply-templates select="@*|node()"/>
    </path>
  </xsl:template>

  <!-- Turn the points attribute into a d attribute -->
  <xsl:template match="@points">
    <xsl:attribute name="d">
      <xsl:value-of select="concat('M',.)"/>
      <!-- If we have a polygon, we need to make 
           this a closed path by appending "z" -->
      <xsl:if test="parent::svg:polygon">
        <xsl:value-of select="'z'"/>
      </xsl:if>
    </xsl:attribute>
  </xsl:template>
</xsl:stylesheet>

Any attributes of the polygon/polyline elements will be carried over to the path element. This is also suitable for batch processing. You can run this using any XSLT processor (Saxon, Xalan, xsltproc, Altova...) or even in-browser, using the XSLTProcessor object, like:

var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(stylesheet);
var transformedSVG = xsltProcessor.transformToFragment(svgDocument).firstChild

(Similar question: Examples of polygons drawn by path vs polygon in SVG)

Espinoza answered 9/12, 2012 at 14:21 Comment(0)
P
0

Building on @halfer's solution. If you have internal CSS and want to retain the class names, you can slightly modify this to:

var polys = document.querySelectorAll('polygon,polyline');
[].forEach.call(polys,convertPolyToPath);

function convertPolyToPath(poly){
  var svgNS = poly.ownerSVGElement.namespaceURI;
  var path = document.createElementNS(svgNS,'path');
  var pathClass = poly.getAttribute('class');
  var points = poly.getAttribute('points').split(/\s+|,/);
  var x0=points.shift(), y0=points.shift();
  var pathdata = 'M'+x0+','+y0+'L'+points.join(' ');
  if (poly.tagName=='polygon') pathdata+='z';
  path.setAttribute('id',poly.getAttribute('id'));
  path.setAttribute('class', pathClass);
  path.setAttribute('d',pathdata);

  poly.parentNode.replaceChild(path,poly);
}
Poore answered 2/6, 2022 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.