package charts;
import java.awt.Dimension;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Date;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import net.hanjava.svg.SVG2EMF;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.transcoder.SVGAbstractTranscoder;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.batik.util.XMLResourceDescriptor;
import org.apache.fop.render.ps.EPSTranscoder;
import org.apache.fop.svg.PDFTranscoder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import charts.representations.Format;
import charts.representations.Representation;
public abstract class AbstractChart implements Chart {
private Date created = new Date();
private String svgDocument = null;
@Override
public abstract ChartDescription getDescription();
public abstract Drawable getChart();
public abstract String getCSV() throws UnsupportedFormatException;
@Override
public Representation outputAs(Format format, Dimension queryDimensions)
throws UnsupportedFormatException {
switch (format) {
case CSV:
return format.createRepresentation(getCSV());
case EMF: // For embedding in Word documents
return format.createRepresentation(
renderEMF(getChart()));
case SVG:
return format.createRepresentation(
renderSVG(getChart(), queryDimensions));
case PNG:
return format.createRepresentation(
renderPNG(getChart(), queryDimensions));
case EPS:
return format.createRepresentation(
renderEPS(getChart(), queryDimensions));
case PDF:
return format.createRepresentation(
renderPDF(getChart(), queryDimensions));
}
throw new Chart.UnsupportedFormatException();
}
protected byte[] renderPDF(Drawable d, Dimension dimensions) {
try {
Document doc = toDocument(getDrawableDocument(d), dimensions);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PDFTranscoder t = new PDFTranscoder();
if (dimensions.getWidth() > 0.0) {
t.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH,
(float) dimensions.getWidth());
}
if (dimensions.getHeight() > 0.0) {
t.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT,
(float) dimensions.getHeight());
}
t.transcode(new TranscoderInput(doc), new TranscoderOutput(os));
return os.toByteArray();
} catch(IOException e) {
throw new RuntimeException(e);
} catch(TranscoderException e) {
throw new RuntimeException(e);
}
}
protected String renderEPS(Drawable d, Dimension dimensions) {
try {
Document doc = toDocument(getDrawableDocument(d), dimensions);
ByteArrayOutputStream os = new ByteArrayOutputStream();
EPSTranscoder t = new EPSTranscoder();
if (dimensions.getWidth() > 0.0) {
t.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH,
(float) dimensions.getWidth());
}
if (dimensions.getHeight() > 0.0) {
t.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT,
(float) dimensions.getHeight());
}
t.transcode(new TranscoderInput(doc), new TranscoderOutput(os));
return os.toString();
} catch(IOException e) {
throw new RuntimeException(e);
} catch(TranscoderException e) {
throw new RuntimeException(e);
}
}
protected byte[] renderEMF(Drawable d) {
final InputStream is = new ByteArrayInputStream(
getDrawableDocument(d).getBytes());
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
SVG2EMF.convert("", is, os);
} catch (IOException e) {
throw new RuntimeException(e);
}
return os.toByteArray();
}
protected byte[] renderPNG(Drawable d, Dimension dimensions) {
try {
Document doc = toDocument(getDrawableDocument(d), dimensions);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PNGTranscoder t = new PNGTranscoder();
if (dimensions.getWidth() > 0.0) {
t.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH,
(float) dimensions.getWidth());
}
if (dimensions.getHeight() > 0.0) {
t.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT,
(float) dimensions.getHeight());
}
t.transcode(new TranscoderInput(doc), new TranscoderOutput(os));
return os.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (TranscoderException e) {
throw new RuntimeException(e);
}
}
protected String renderSVG(Drawable d, Dimension dimensions) {
final Document doc;
try {
doc = new SAXSVGDocumentFactory(
XMLResourceDescriptor.getXMLParserClassName())
.createDocument("file:///test.svg",
new CharArrayReader(getDrawableDocument(d).toCharArray()));
} catch (IOException e) {
throw new RuntimeException(e);
}
scaleSvg(doc, dimensions);
final StringWriter sw = new StringWriter();
try {
TransformerFactory.newInstance().newTransformer()
.transform(new DOMSource(doc), new StreamResult(sw));
} catch (TransformerException e) {
throw new RuntimeException(e);
}
return sw.toString();
}
protected String getDrawableDocument(Drawable d) {
if (svgDocument == null) {
svgDocument = (new ChartRenderer(d)).render();
}
return svgDocument;
}
protected Float getFloat(String[] values) {
try {
return Float.parseFloat(values[0]);
} catch (Exception e) {
return null;
}
}
private Document scaleSvg(Document doc, Dimension dimensions) {
final Element root = doc.getDocumentElement();
final String h = root.getAttribute("height");
final String w = root.getAttribute("width");
root.setAttributeNS(null, "viewBox",
String.format("0 0 %s %s", w, h));
final Dimension scaledDimensions =
scaleDimensions(extractSvgDimensions(doc), dimensions);
// Set requested height / width
final long newH = Math.round(scaledDimensions.getHeight());
final long newW = Math.round(scaledDimensions.getWidth());
root.setAttribute("height", newH+"");
root.setAttribute("width", newW+"");
return doc;
}
private Dimension extractSvgDimensions(Document svg) {
final Element root = svg.getDocumentElement();
return new Dimension(
Integer.parseInt(root.getAttribute("width")),
Integer.parseInt(root.getAttribute("height")));
}
/**
* Sets missing dimensions to correct version for aspect ratio.
*
* @param dOrig Original image dimensions
* @param dNew Proposed new image dimensions
* @return Dimensions where neither height or width is 0
*/
private Dimension scaleDimensions(Dimension dOrig, Dimension dNew) {
final Dimension dScaled = new Dimension();
if (dNew.getWidth() > 0) {
if (dNew.getHeight() > 0) {
return dNew; // Complete already
}
// Get new height in proportion
dScaled.setSize(
dNew.getWidth(),
dNew.getWidth() * (dOrig.getHeight() / dOrig.getWidth()));
} else {
if (dNew.getHeight() <= 0) {
return dOrig; // Blank new => use original
}
// Get new width in proportion
dScaled.setSize(
dNew.getHeight() * (dOrig.getWidth() / dOrig.getHeight()),
dNew.getHeight());
}
return dScaled;
}
private Document toDocument(String svg, Dimension dimensions)
throws IOException {
// Turn back into DOM
String parserName = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parserName);
Document doc = f.createDocument("file:///test.svg",
new CharArrayReader(svg.toCharArray()));
scaleSvg(doc, dimensions);
return doc;
}
@Override
public Date created() {
return created;
}
}