0

I am trying to recreate a batch of (360 files) SVGs because they were given a <clip path> tag that points to a <rect> tag inside of <defs> tags. It also created a <polygon> element with a fill attribute. I am trying to delete the <clipPath>, <defs> and <polygon> tags, but keep the <rect> tag and give it the polygon's fill attribute.

I tried to do this in regex, but it was too complicated and didn't seem like it could be done very well.

I tried to dabble with doing it in java with an xml parser class, but I could not figure out how to access the attributes (I tried using the .getAttributes() method, but it would not pull the attributes for the element).

Here is some of my sample code:

<defs>
    <rect id="SVGID_1_" y="0" width="1023.88" height="100.08"/>
</defs>
<clipPath id="SVGID_2_">
    <use xlink:href="#SVGID_1_"  display="none" overflow="visible"/>
</clipPath>
<polygon clip-path="url(#SVGID_2_)" fill="#E81E25" points="-0.12,0 -0.12,100.08 1023.88,100.08 1023.88,0"/>

This is what it should be:

<rect fill="#E81E25" width="1023.88" height="100.08"/>

How can I batch convert 360 files to be formatted like I want above with the <rect> tag?

S Harris
  • 81
  • 10

2 Answers2

0

XSLT would be a perfect fit. Does that what you want:

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

  <!-- Identity transform for all cases except the ones we want to delete -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <!-- We do nothing for clipPaths and polygons -->
  <xsl:template match="svg:clipPath|svg:polygon"/>

  <!-- We only copy rects from defs -->
  <xsl:template match="svg:defs">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="svg:rect">
    <xsl:variable name="clipPathId" select="//svg:use[substring(@xlink:href,2) = current()/@id]/../@id"/>
    <rect>
      <xsl:copy-of select="@*"/>
      <xsl:copy-of select="//svg:polygon[@clip-path=concat('url(#', $clipPathId, ')')]/@fill"/>
    </rect>
  </xsl:template>
</xsl:stylesheet>

This assumes you want to delete all <clipPath> and <polygon> elements as well as stripping all <defs> elements, only keeping <rect> elements that are found inside.

Use any XSLT processor you like (Saxon, Xalan, xsltproc, msxsl...).

Thomas W
  • 14,757
  • 6
  • 48
  • 67
  • That answer seems close, however, it is not copying the fill attribute from the `` tag. I need to get the fill attribute from the `` and put it in my `` – S Harris Nov 11 '13 at 14:00
  • Ah, sorry, didn't quite get that part. I updated the answer. Does this work better? – Thomas W Nov 11 '13 at 20:39
  • I don't know much about XSLTs. What does "node()|@*" mean? Do you have any sites you can recommend to learn about XSLTs? – S Harris Nov 14 '13 at 14:42
  • Thomas I owe you an apology. I didn't know what XSLT was and I was not confident in your solution. However, after two days of trying to figure it out I got it to do exactly what I wanted. Thanks for your help. – S Harris Nov 14 '13 at 18:42
  • You can [read about the identity transform (which has the `node()|@*` matching pattern) on Wikipedia](https://en.wikipedia.org/wiki/Identity_transform#Using_XSLT). As for learning XSLT: If you want to dedicate some time to it, I recommend getting a book. [Here are some suggestions](http://stackoverflow.com/questions/51271/what-is-a-good-resource-for-learning-xsl) (both in book and online form). When working with XSLT, keep in mind it works sort-of-but-not-quite like a functional programming language and the control flow is determined by template matching. – Thomas W Nov 15 '13 at 09:43
  • What does "//" in //svg:polygon mean? @Thomas – S Harris Nov 26 '13 at 13:36
  • `//` means "grab all polygon elements in the document" - if there's nothing preceding the `//`. In this case it's the same as the CSS selector `polygon`. If you'd have `/svg:svg/svg:g[1]//svg:polygon`, then this would find the first `` element under the document element, and inside this element grab all polygon elements. Possibly also have a look at [this question](http://stackoverflow.com/questions/1537771/whats-the-difference-between-node-and-descendantnode-in-xpath). – Thomas W Nov 28 '13 at 06:36
0

preamble: since your files a generated by an other programm you can assume with little risk that all tags look the same, so you can try to ignore, that it is 'xml': with little risk. If your files are fom different sources (or different versions of that programm), my suggestion wont work or at least is more complex!

What I would do is parse (each) file, split the data and reassamble it, this is only a hint and i try to dont use languge specific things:

The - lets call it - primary id you can use is: "id=SVGID_xxx_"

iterate ofer the file and regex:

<rect.*id="([^"]*).*width="([^"]*).*height="([^"]*)
           ^--R1            ^--R2          ^--R3

keep the data like (depends on your language)

rect_array[R1].width=R2     // or $rect_list[$R1]["w"]=R2; // php like
rect_array[R1].height=R3

then a 2nd iteration

<polygon.*?\(([^)]*).*?fill=.([^'"]*)
           ^--R1 (id again)   ^--color     

and add it to your hash/array/map

rect_array[R1].fill=color     // or $rect_list[$R1]["f"]=color; // php like

last step is to iterate over your list/array and reassabmle it

for_all_in(rect_array).do    // foreach($rect_array as $rd) 
puts/echo rect="<rect ...    //   echo "<rect width='rd[w]' ... 

HTH

halfbit
  • 3,773
  • 2
  • 34
  • 47