Hello. I have a set of SVG files. I know nothing about their contents other than the fact that somewhere inside each file is an element with an id called "button". I don't know if that element will be a group, a rectangle, etc. It could be anything.
I want to load a file, and render only the given element with the "button" id to a PNG file. The bounds of the rendered image should be such that they contain the rendered object tightly with no margins. This is the closest that I've been able to manage: --8<-- import org.apache.batik.anim.dom.SAXSVGDocumentFactory; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.DocumentLoader; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgent; import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.gvt.CompositeGraphicsNode; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.gvt.renderer.DynamicRenderer; import org.apache.batik.transcoder.TranscoderException; import org.apache.batik.util.XMLResourceDescriptor; import org.w3c.dom.Document; import org.w3c.dom.Element; import javax.imageio.ImageIO; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Objects; public final class Main { private Main() { } public static void main(final String[] args) throws IOException, TranscoderException { final String parser = XMLResourceDescriptor.getXMLParserClassName(); final SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser); final String uri = "/tmp/button.svg"; final Document doc = f.createDocument(uri); final UserAgent agent = new UserAgentAdapter(); final DocumentLoader loader = new DocumentLoader(agent); final BridgeContext ctx = new BridgeContext(agent, loader); ctx.setDynamicState(BridgeContext.DYNAMIC); final GVTBuilder builder = new GVTBuilder(); builder.build(ctx, doc); final Element elem_node = doc.getElementById("button"); final GraphicsNode elem_gnode = ctx.getGraphicsNode(elem_node); final Rectangle2D elem_bounds = elem_gnode.getBounds(); System.out.println("Bounds: " + elem_bounds); { GraphicsNode node = elem_gnode; while (true) { System.out.println("Transform (g): " + node.getGlobalTransform()); System.out.println("Transform: " + node.getTransform()); final CompositeGraphicsNode parent = elem_gnode.getParent(); if (Objects.equals(parent, node)) { break; } node = parent; } } final double elem_x = elem_bounds.getX(); final double elem_y = elem_bounds.getY(); final double elem_width = elem_bounds.getWidth(); final double elem_height = elem_bounds.getHeight(); final int elem_width_i = (int) Math.ceil(elem_width); final int elem_height_i = (int) Math.ceil(elem_height); final DynamicRenderer renderer = new DynamicRenderer(); renderer.setTransform( AffineTransform.getTranslateInstance(-elem_x, -elem_y)); renderer.setTree(elem_gnode); renderer.updateOffScreen(elem_width_i, elem_height_i); final Rectangle r = new Rectangle( 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE); renderer.repaint(r); final BufferedImage image = renderer.getOffScreen(); ImageIO.write(image, "PNG", new File("/tmp/button.png")); } } -->8-- The example file used here is: http://waste.io7m.com/2016/06/18/button.svg The problem: This feels fragile. It has the feel of code that will break as soon as it's presented with any SVG file that contains some unusual combinations of transforms. I had to do a lot of experimenting with transforms. Setting transforms for any of the GVT nodes simply resulted in the transforms apparently being silently ignored. I eventually stumbled across setting an affine transform for the renderer such that it positions the rendered object at the corner of the rendered image. This feels like abuse of the API, but it's the only thing I could get to actually have any effect on the output image at all. Secondly, I have to pass Integer.MAX_VALUE as the width and height of the rectangle to be updated. Originally, I tried passing elem_width_i and elem_height_i, but this simply truncated the rendered image in ways I don't fully understand. What exactly am I supposed to pass there? What is the correct, reliable way to achieve what I'm trying to achieve? M --------------------------------------------------------------------- To unsubscribe, e-mail: batik-users-unsubscr...@xmlgraphics.apache.org For additional commands, e-mail: batik-users-h...@xmlgraphics.apache.org