Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ComFreek/9d651a8935c467143f2d to your computer and use it in GitHub Desktop.
Save ComFreek/9d651a8935c467143f2d to your computer and use it in GitHub Desktop.
Utility classes for transforming arbitrary SVG elements with Batik. Read more: http://comfreek.blogspot.com/2014/06/batik-how-to-transform-arbitrary-svg.html
package com.github.comfreek.util;
import java.awt.geom.AffineTransform;
/**
* Utility class.<br>
* Its primary intent is to provide {@link #affineTransformToString(AffineTransform) affineTransformToString()}.
*
* @author ComFreek <comfreek@outlook.com> if not otherwise noted.
* @license MIT (for all parts whose author is ComFreek)
*/
public class AffineTransformUtils {
private AffineTransformUtils() {
}
/**
* Converts an AffineTransform to a "matrix(...)" string representation as
* used in SVG files for the "transform" attribute.
*
* @see <a
* href="http://www.w3.org/TR/SVG11/coords.html#TransformAttribute">W3C SVG
* 1.1 Specification: The 'transform' attribute</a>
* @see <a
* href="https://developer.mozilla.org/en/docs/Web/SVG/Attribute/transform">Mozilla
* Developer Network: SVG transforma attribute</a>
*
* @author Jonathan Wood in a mail to the xmlgraphics-batik-users mailing list: <a href="http://mail-archives.apache.org/mod_mbox/xmlgraphics-batik-users/201208.mbox/%3cCAKiJDQTpJE-4hEGG-QMn=cDXK2tAn5H52TWBCZJxZ30pf15DYQ@mail.gmail.com%3e">click here</a>
*
* @param at The AffineTransform object
* @return Returns a "matrix(...)" string representation. for more
* information
*/
public static String affineTransformToString(final AffineTransform at) {
double[] matrix = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
at.getMatrix(matrix);
return matrixArrayToString(matrix);
}
/**
* Converts a double array in the form of {a, b, c, d, e, f} to a string
* representation in the form of "matrix(a b c d e f)".
*
* The returned string can be used in transform attributes of elements in
* SVG files. files.
*
* @see AffineTransformUtils#affineTransformToString(AffineTransform)
* @see <a
* href="http://www.w3.org/TR/SVG11/coords.html#TransformAttribute">W3C SVG
* 1.1 Specification: The 'transform' attribute</a>
* @see <a
* href="https://developer.mozilla.org/en/docs/Web/SVG/Attribute/transform">Mozilla
* Developer Network: SVG transforma attribute</a>
*
* @author Jonathan Wood in a mail to the xmlgraphics-batik-users mailing list: <a href="http://mail-archives.apache.org/mod_mbox/xmlgraphics-batik-users/201208.mbox/%3cCAKiJDQTpJE-4hEGG-QMn=cDXK2tAn5H52TWBCZJxZ30pf15DYQ@mail.gmail.com%3e">click here</a>
*
* @param vals A double array a
* @return Returns the string representation in the form of "matrix(a b c d e f)".
*/
public static String matrixArrayToString(double[] vals) {
return new StringBuilder("matrix(").append(vals[0]).append(" ").append(vals[1]).append(" ").append(vals[2]).append(" ").append(vals[3]).append(" ").append(vals[4]).append(" ").append(vals[5]).append(") ").toString();
}
}
package com.github.comfreek.util;
import java.awt.geom.AffineTransform;
import org.apache.batik.parser.AWTTransformProducer;
import org.apache.batik.parser.TransformListParser;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
/**
* This class provides static utility methods for the use with Batik and SVG
* elements.
*
* @author ComFreek <comfreek@outlook.com> if not otherwise noted.
* @license MIT (for all parts whose author is ComFreek)
*/
public class SVGUtils {
private SVGUtils() {
}
/**
* Applies an AffineTransform to a (SVG) element.
* <br>
* While this function works with all classes equal to or subclassing
* {@link org.w3c.dom.Element Element}, adding and modifying the "transform"
* attribute is probably of no effect.
* <br>
* Example:
* <pre>
* <code> // Document svgDoc = ...;
* Element elem = svgDoc.getElementById("your-id");
* AffineTransform translateBy5Units =
* AffineTransform.getTranslateInstance(5, 0);
* transformElement(elem, translateBy5Units);
*
* AffineTransform scaleByFactor10 =
* AffineTransform.getScaleInstance(10, 10);
* transformElement(elem, scaleByFactor10);
* </code>
* </pre>
*
* @see #getElementTransform(Element)
*
* @param element The element.
* @param transform The AffineTransform you want to apply. The resulting
* transformation of the object is
* <code>transform.{@link AffineTransform#concatenate(AffineTransform) concatenated}(oldTransform)</code>,
* where <code>oldTransform</code> represents the current local (i.e.
* possible parents' transforms will not be taken into acconut) transform of
* the element.
*/
public static void transformElement(final Element element, final AffineTransform transform) {
final AffineTransform oldTransform = getElementTransform(element);
transform.concatenate(oldTransform);
element.setAttributeNS(null, SVGConstants.SVG_TRANSFORM_ATTRIBUTE,
AffineTransformUtils.affineTransformToString(transform));
}
/**
* Retrieves and parses the transformation attribute of a Element into an
* AffineTransform object.
* <br>
*
* This function can cope with all transform values supported by
* {@link org.apache.batik.parser.TransformListParser TransformListParser}.
* These include <code>translate()</code>, <code>rotate()</code>,
* <code>scale()</code>, <code>skew()</code> and <code>matrix()</code>. It
* will also correctly handle empty or non-existing attributes (i.e.
* returning an Identity matrix).
*
* @see #transformElement(Element, AffineTransform)
*
* @author fireball in a mail to the xmlgraphics-batik-users mailing list:
* <a href="http://mail-archives.apache.org/mod_mbox/xmlgraphics-batik-users/201208.mbox/%3c1344872547972-4655199.post@n4.nabble.com%3e">click here</a>
*
* @param element The SVG element.
* @return An AffineTransform which equals the (local) transformations
* specified in the SVG element.
*/
public static AffineTransform getElementTransform(final Element element) {
final String oldTransformStr = element.getAttributeNS(null, SVGConstants.SVG_TRANSFORM_ATTRIBUTE);
final TransformListParser tListParser = new TransformListParser();
final AWTTransformProducer tProducer = new AWTTransformProducer();
tListParser.setTransformListHandler(tProducer);
tListParser.parse(oldTransformStr);
return tProducer.getAffineTransform();
}
}
// Consult Batik's documentation or Google if you don't
// know how to load a document.
// Document svgDoc = ...;
Element elem = svgDoc.getElementById("your-id");
AffineTransform translateBy5Units =
AffineTransform.getTranslateInstance(5, 0);
transformElement(elem, translateBy5Units);
AffineTransform scaleByFactor10 =
AffineTransform.getScaleInstance(10, 10);
transformElement(elem, scaleByFactor10);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment