Fork me on GitHub
#off-topic
<
2021-04-21
>
tvaughan12:04:21

Does anyone know of a tool like https://github.com/svg/svgo but in Java or Clojure? I want to be able to "normalize" svg drawings into some sort of common denominator, e.g. apply transformations to paths. There's no dom or gui involved, so just xml in and xml out

tvaughan12:04:42

This https://stackoverflow.com/a/15113751 is an example of some of the calculations that need to be done. From what I remember, I don't think xslt can do this, right? It's been years since I've worked with xslt

flowthing14:04:48

There's quite a lot behind that SO link so I'm not sure what exactly you mean. Do you mean transforming matrix(...)? You could do it with XPath 2.0, at least. (Shameless plug: https://github.com/eerohele/sigel)

tvaughan14:04:43

I mean I need to be able to do something like:

# cat example.svg
<g transform="matrix(0.443,0.896,-0.896,0.443,589.739,-373.223)">
    <path d="M486,313s27-9,43-29l26,4,1,23-22,5s-25-6-48-3z" />
</g>
# svgo example.svg -o -
<g><path d="M524.589 200.892s20.025 20.205 45.033 25.681l7.934 25.068-20.165 11.085-14.226-17.497s-5.699-25.058-18.576-44.337z"/></g>
according to the calculation rules in the stackoverflow link

tvaughan14:04:47

Thanks for the pointer to sigel. This looks like it could be useful for working with the normalized svg

flowthing14:04:44

Right, I won't delve into the specifics of the calculation rules, but here's an example stylesheet that parses the matrix(...) values as floats and multiplies them by four:

<xsl:stylesheet version="3.0" xmlns:xsl="" xmlns:xs="">
    <xsl:template match="g">
      <g>
        <xsl:variable name="m"
          select="
            for-each(tokenize(substring-before(substring-after(@transform, '('), ')'), ','), xs:float#1)
          " as="xs:float*"/>

        <xsl:for-each select="$m">
          <m><xsl:value-of select=". * 4"/></m>
        </xsl:for-each>
      </g>
    </xsl:template>
</xsl:stylesheet>

flowthing14:04:03

Given that input, it produces:

λ saxon -xsl:stylesheet.xsl -s:input.xml | xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<g xmlns:xs="">
  <m>1.772</m>
  <m>3.584</m>
  <m>-3.584</m>
  <m>1.772</m>
  <m>2358.956</m>
  <m>-1492.892</m>
</g>

flowthing14:04:43

That's just to illustrate that I think you can do that with XSLT & XPath. Whether you want to is another matter, of course. 🙂

tvaughan14:04:44

That's very interesting! Thanks for the example

flowthing14:04:41

A full-fledged parser for possible values of the transform attribute is another matter, of course. If that's mainly what you need to do, XSLT probably isn't very useful. Perhaps you could use XPath to reach into the values, hand them to a Clojure function that parses and transforms them, then sticks them into an output XML tree. Or something along those lines.

tvaughan15:04:59

Right, I see how xslt and xpath could be very helpful if I choose to roll my own. I hadn't thought to use them this way. Thanks for the suggestion!

flowthing15:04:44

Sure. An additional thing to consider is using a combination of XSLT and Java/Clojure using integrated extension functions: https://cljdoc.org/d/me.flowthing/sigel/1.0.1/api/sigel.extension Essentially, you can define a Clojure function you call from your XSLT stylesheet.

flowthing15:04:23

That is, you could write the attribute value parser in Clojure (maybe with Instaparse?) and use XSLT for transforming the XML tree.

3