Header

Sleepy-robots.org

Where robots dream of electric sheep...


Monday, July 10, 2017

How to run Processing headlessly and export your Processing visualization as an .svg using Apache Batik

For an evaluation at my company, I recently looked into different Java Drawing Library that make it easier to draw beautiful data visualizations than using plain Java awt. So I looked into Processing, a framework similar to d3.js but entirely written in Java. Its simple syntax and its powerful graphics convinced me quickly. However, another requirement of the evaluation was to be able to export the graphics into .svg format to make them scale very easSo my main question was if it is possible in any way to export .svgs from a Processing drawing. I had seen that maybe there exists a bridge to Apache Batik via the basic awt.Graphics2D, since both the SVGConverter and the PGraphicsJava2D used this as its base class. After a bit of coding trial and error, it seems that I could make it work, although a bit hacky. I just replaced the g2 JavaGraphics2D canvas from within the PGraphicsJava2D with an Apache Batik SVGgenerator (which inherits from JavaGraphics2D and is meant to be dropped into any awt compatible drawing method).  Important is that the replacing must take place after the beginDrawing() is called, as the beginDrawing() replaces the g2. The following example demonstrates that:


import java.awt.Dimension;
import java.io.StringWriter;
import java.io.Writer;

import org.apache.batik.dom.GenericDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;
import org.apache.batik.svggen.SVGGraphics2DIOException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;

import processing.awt.PGraphicsJava2D;
import processing.core.PApplet;


public abstract class ProcessingDrawer {
 
 PGraphicsJava2D canvas;         // Processing graphics canvas 
 
 SVGGraphics2D svgGenerator;         // Apache Batik svg generator
 
 int width, height;         // width and height of the canvas
 
 String svg;
 
 public ProcessingDrawer(){
  
 }
 
 public ProcessingDrawer(int width, int height) {
  this.width = width;
  this.height = height;
 }
 
 public void run(){
  PApplet.main(new String[] {this.getClass().getCanonicalName()});
 }

 public void settings() {
 }
 
 public void setup() {
  
  // Get a DOMImplementation.
  DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();

           // Create an instance of org.w3c.dom.Document.
  String svgNS = "http://www.w3.org/2000/svg";
  Document document = domImpl.createDocument(svgNS, "svg", null);

  svgGenerator = new SVGGraphics2D(document);     // Create an instance of the SVG Generator.

  svgGenerator.setSVGCanvasSize(new Dimension(width, height));
  canvas = new PGraphicsJava2D();
  canvas.setSize(width, height);
 }
 
 
 /**
  * Implement this to draw your picture.
  */
 public abstract void paintCanvas();
 
 public void draw() {
  getCanvas().beginDraw();
  getCanvas().g2 = svgGenerator;
  
  paintCanvas();
  
  getCanvas().endDraw();
  svg = exportSVG();
 }
 
 private String exportSVG(){
  return exportSVG(true);
 }
 
 private String exportSVG(boolean useCSS){
  String svgContent = "";
     try {
      Writer out = new StringWriter();
   svgGenerator.stream(out, useCSS);
   svgContent = out.toString();
  } catch (SVGGraphics2DIOException e) {
   e.printStackTrace();
  }
     
     return svgContent;
 }

 public PGraphicsJava2D getCanvas() {
  return canvas;
 }

 public String getSVG() {
  return svg;
 }

}


Now you can extend the ProcessingDrawer as follows:

import java.util.Random;

public class SimpleSketch extends ProcessingDrawer{
 
 Random r = new Random();
 
 public SimpleSketch(){
  super();
 }

 public SimpleSketch(int width, int height) {
  super(width, height);
 }

 @Override
 public void paintCanvas() {

  getCanvas().background(51);
  getCanvas().noFill();

  for(int i = 0; i < 30; i++){
   getCanvas().stroke(r.nextInt(255), r.nextInt(255), r.nextInt(255));
   getCanvas().ellipse(r.nextInt(width), r.nextInt(width), 60, 60);
  }
 }
 
 public static void main(String[] args){
  SimpleSketch ss = new SimpleSketch(400, 400);
  ss.setup();
  ss.draw();
  System.out.print(ss.getSVG());
 }
}

This code outputs the following on your commandline:





which looks like this:


I publish this up here since I could not find any beautiful solutions on the web and so maybe anybody else is happy to use this approach. If you have any suggestions, just post below.

No comments:

Post a Comment