/*
* EuroCarbDB, a framework for carbohydrate bioinformatics
*
* Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
* A copy of this license accompanies this distribution in the file LICENSE.txt.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* Last commit: $Rev: 1870 $ by $Author: david@nixbioinf.org $ on $Date:: 2010-02-23 #$
*/
package org.eurocarbdb.application.glycanbuilder;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import org.w3c.dom.*;
import org.apache.batik.ext.awt.g2d.GraphicContext;
/**
Utility class containing functions to export glycan structures and
other renderable objects into graphic formats.
@author Alessio Ceroni (a.ceroni@imperial.ac.uk)
*/
public class SVGUtils {
/**
Interface that must be implemented by an objects to be rendered
as an image and exported into a graphic format.
*/
public interface Renderable {
/**
Function called before the rendering process is started.
*/
public void beforeRendering();
/**
Function called to render the object into a graphic context
*/
public void paintRenderable(Graphics2D g);
/**
Return the dimension of the rendered object
*/
public Dimension getRenderableSize();
/**
Function called after the rendering process is completed
*/
public void afterRendering();
}
private SVGUtils() {}
/**
Return a map containing all the supported graphical formats.
The map is composed by pairs containing the identifier and the
description of the format.
*/
public static Map<String,String> getExportFormats() {
TreeMap<String,String> map = new TreeMap<String,String>();
map.put("svg","SVG");
map.put("pdf","PDF");
map.put("ps","PS");
map.put("eps","EPS");
map.put("bmp","BMP");
map.put("png","PNG");
//map.put("gif","GIF");
map.put("jpg","JPG");
return map;
}
/**
Return <code>true</code> if the identifier represent a
supported graphical format.
*/
static public boolean isGraphicFormat(String format) {
return getExportFormats().containsKey(format);
}
/**
Return a representation of a set of glycan structure as a
string in SVG format.
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
*/
static public String getVectorGraphics(GlycanRenderer gr, Collection<Glycan> structures) {
return getVectorGraphics(gr, structures, false, false);
}
/**
Return a representation of a set of glycan structure as a
string in SVG format.
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
*/
static public String getVectorGraphics(GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend) {
if( structures == null )
structures = new Vector<Glycan>();
try {
// Create an instance of the SVG Generator
DOMImplementation domImpl = org.apache.batik.dom.GenericDOMImplementation.getDOMImplementation();
Document document = domImpl.createDocument(null, "svg", null);
GroupingSVGGraphics2D g2d = new GroupingSVGGraphics2D(document,true);
// Render into the SVG Graphics2D
SVGGlycanRenderer sgr = new SVGGlycanRenderer(gr);
PositionManager posManager = new PositionManager();
BBoxManager bboxManager = new BBoxManager();
Rectangle all_bbox = sgr.computeBoundingBoxes(structures,show_masses,show_redend,posManager,bboxManager);
Dimension d = sgr.computeSize(all_bbox);
// clear background
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
g2d.setBackground(Color.white);
g2d.clearRect(0, 0, d.width, d.height);
// paint
for( Glycan s : structures )
sgr.paint(g2d,s,null,null,show_masses,show_redend,posManager,bboxManager);
// Stream out SVG to a string
StringWriter out = new StringWriter();
g2d.stream(out, true);
return out.toString();
}
catch(Exception e) {
LogUtils.report(e);
return null;
}
}
/**
Return a representation of a set of glycan structure as an
array of bytes in PDF format
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
*/
static public byte[] getPDFGraphics(GlycanRenderer gr, Collection<Glycan> structures) {
return getTranscodedSVG(gr,structures,false,false, new org.apache.fop.svg.PDFTranscoder());
}
/**
Return a representation of a set of glycan structure as an
array of bytes in PDF format
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
*/
static public byte[] getPDFGraphics(GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend) {
return getTranscodedSVG(gr,structures,show_masses,show_redend, new org.apache.fop.svg.PDFTranscoder());
}
/**
Return a representation of a set of glycan structure as an
array of bytes in PS format
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
*/
static public byte[] getPSGraphics(GlycanRenderer gr, Collection<Glycan> structures) {
return getTranscodedSVG(gr,structures,false,false, new org.apache.fop.render.ps.PSTranscoder());
}
/**
Return a representation of a set of glycan structure as an
array of bytes in PS format
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
*/
static public byte[] getPSGraphics(GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend) {
return getTranscodedSVG(gr,structures,show_masses,show_redend, new org.apache.fop.render.ps.PSTranscoder());
}
/**
Return a representation of a set of glycan structure as an
array of bytes in EPS format
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
*/
static public byte[] getEPSGraphics(GlycanRenderer gr, Collection<Glycan> structures) {
return getTranscodedSVG(gr,structures,false,false, new org.apache.fop.render.ps.EPSTranscoder());
}
/**
Return a representation of a set of glycan structure as an
array of bytes in EPS format
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
*/
static public byte[] getEPSGraphics(GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend) {
return getTranscodedSVG(gr,structures,show_masses,show_redend, new org.apache.fop.render.ps.EPSTranscoder());
}
static private org.apache.batik.svggen.SVGGraphics2D prepareGraphics(Dimension all_dim) {
// Create an instance of the SVG Generator
DOMImplementation domImpl = org.apache.batik.dom.GenericDOMImplementation.getDOMImplementation();
Document document = domImpl.createDocument(null, "svg", null);
org.apache.batik.svggen.SVGGraphics2D g2d = new org.apache.batik.svggen.SVGGraphics2D(document);
// compute scale factor to fit 400x400 (otherwise it does not display)
/*double sf = Math.min(400./all_dim.width,400./all_dim.height);
if( sf<1. )
g2d.scale(sf,sf);
else
sf = 1.;
all_dim.width = (int)(all_dim.width*sf);
all_dim.height = (int)(all_dim.height*sf);
*/
g2d.setBackground(Color.white);
g2d.setSVGCanvasSize(all_dim);
return g2d;
}
static private byte[] transcode(org.apache.batik.svggen.SVGGraphics2D g2d, Dimension all_dim,org.apache.batik.transcoder.Transcoder transcoder) throws Exception {
// Stream out SVG to a string
StringWriter out = new StringWriter();
g2d.stream(out, true);
String svg = out.toString();
//
if( transcoder==null )
return svg.getBytes();
// set transcoder dimensions
transcoder.addTranscodingHint(org.apache.batik.transcoder.image.ImageTranscoder.KEY_BACKGROUND_COLOR, Color.white);
transcoder.addTranscodingHint(org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, new Float(0.3528f));
transcoder.addTranscodingHint(org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_MAX_WIDTH,new Float(all_dim.width));
transcoder.addTranscodingHint(org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_MAX_HEIGHT,new Float(all_dim.height));
transcoder.addTranscodingHint(org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_WIDTH,new Float(all_dim.width));
transcoder.addTranscodingHint(org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_HEIGHT,new Float(all_dim.height));
transcoder.addTranscodingHint(org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_AOI,new Rectangle(0,0,all_dim.width,all_dim.height));
// transcode
StringReader in = new StringReader(svg);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(baos);
org.apache.batik.transcoder.TranscoderInput input = new org.apache.batik.transcoder.TranscoderInput(in);
org.apache.batik.transcoder.TranscoderOutput output = new org.apache.batik.transcoder.TranscoderOutput(bos);
transcoder.transcode(input, output);
//fos.close();
return baos.toByteArray();
}
static private byte[] getTranscodedSVG(GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend, org.apache.batik.transcoder.Transcoder transcoder) {
if( structures == null )
structures = new Vector<Glycan>();
try {
// compute size
PositionManager posManager = new PositionManager();
BBoxManager bboxManager = new BBoxManager();
Rectangle all_bbox = gr.computeBoundingBoxes(structures,show_masses,show_redend,posManager,bboxManager);
Dimension all_dim = gr.computeSize(all_bbox);
// prepare g2d
org.apache.batik.svggen.SVGGraphics2D g2d = prepareGraphics(all_dim);
// fix EPS bug (flip vertically)
if( transcoder!=null && transcoder instanceof org.apache.fop.render.ps.EPSTranscoder ) {
g2d.scale(1,-1);
g2d.translate(0,-all_dim.height);
}
// paint
for( Glycan s : structures )
gr.paint(g2d,s,null,null,show_masses,show_redend,posManager,bboxManager);
// transcode
return transcode(g2d,all_dim,transcoder);
}
catch(Exception e) {
LogUtils.report(e);
return null;
}
}
static private byte[] getTranscodedSVG(Renderable renderable, org.apache.batik.transcoder.Transcoder transcoder) {
try {
renderable.beforeRendering();
// prepare g2d
Dimension all_dim = renderable.getRenderableSize();
org.apache.batik.svggen.SVGGraphics2D g2d = prepareGraphics(all_dim);
// fix EPS bug (flip vertically)
if( transcoder!=null && (transcoder instanceof org.apache.fop.render.ps.EPSTranscoder) ) {
g2d.scale(1,-1);
g2d.translate(0,-all_dim.height);
}
// paint
renderable.paintRenderable(g2d);
renderable.afterRendering();
// transcode
return transcode(g2d,all_dim,transcoder);
}
catch(Exception e) {
LogUtils.report(e);
return null;
}
}
/**
Return an image on which a Renderable object has been painted
*/
static public BufferedImage getImage(Renderable renderable) {
return getImage(renderable,true);
}
/**
Return an image on which a Renderable object has been painted
@param opaque <code>false</code> if the background of the image
must be transparent
*/
static public BufferedImage getImage(Renderable renderable, boolean opaque) {
renderable.beforeRendering();
// Create an image that supports transparent pixels
Dimension d = renderable.getRenderableSize();
BufferedImage img = GraphicUtils.createCompatibleImage(d.width,d.height,opaque);
// prepare graphics context
Graphics2D g2d = img.createGraphics();
if(!opaque) {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
g2d.setBackground(new Color(255,255,255,0));
}
// paint
renderable.paintRenderable(g2d);
renderable.afterRendering();
return img;
}
/*public byte[] getWMFGraphics(Collection<Glycan> structures) {
try {
// compute sizes
PositionManager posManager = new PositionManager();
BBoxManager bboxManager = new BBoxManager();
Rectangle all_bbox = computeBoundingBoxes(structures,false,posManager,bboxManager);
Dimension d = computeSize(all_bbox);
// Create an instance of the WMF writer
WMF wmf = new WMF();
WMFGraphics2D g2d = new WMFGraphics2D(wmf, d.width, d.height);
// Render into the WMF Graphics2D
HashSet<Residue> selected = new HashSet<Residue>();
paintStructures(g2d,structures,selected,false,posManager,bboxManager);
// Stream out WMF
int dpi = 100;
ByteArrayOutputStream out = new ByteArrayOutputStream();
//wmf.writePlaceableWMF(out, 0, 0, d.width, d.height, dpi);
wmf.writeWMF(out);
//FileOutputStream fos = new FileOutputStream("prova.wmf");
//wmf.writePlaceableWMF(fos, 0, 0, d.width, d.height, dpi);
//wmf.writeWMF(fos);
//fos.close();
g2d.dispose();
return out.toByteArray();
}
catch(Exception e) {
LogUtils.report(e);
return null;
}
}
*/
/**
Export a representation of a set of glycan structure to a file
in a certain graphical format.
@param gr the GlycanRenderer used to render the structures
@param filename the path to the destination file
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
@param format the graphical format to be used
@return <code>true</code> if the file was successfully created
*/
static public boolean export(GlycanRenderer gr, String filename, Collection<Glycan> structures, boolean show_masses, boolean show_redend, String format) {
return export(gr,filename,structures,show_masses,show_redend,1.,format);
}
/**
Export a representation of a set of glycan structure to a file
in a certain graphical format.
@param gr the GlycanRenderer used to render the structures
@param filename the path to the destination file
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
@param scale the scaling factor to be applied to the image
@param format the graphical format to be used
@return <code>true</code> if the file was successfully created
*/
static public boolean export(GlycanRenderer gr, String filename, Collection<Glycan> structures, boolean show_masses, boolean show_redend, double scale, String format) {
try {
OutputStream os = new FileOutputStream(filename);
export(os,gr,structures,show_masses,show_redend,scale,format);
os.close();
return true;
}
catch(Exception e) {
LogUtils.report(e);
return false;
}
}
/**
Create a representation of a set of glycan structure as an
array of bytes in a certain graphical format.
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
@param format the graphical format to be used
*/
static public byte[] export(GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend, String format) {
return export(gr,structures,show_masses,show_redend,1.,format);
}
/**
Create a representation of a set of glycan structure as an
array of bytes in a certain graphical format.
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
@param scale the scaling factor to be applied to the image
@param format the graphical format to be used
*/
static public byte[] export(GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend, double scale, String format) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
export(bos,gr,structures,show_masses,show_redend,scale,format);
return bos.toByteArray();
}
catch(Exception e) {
LogUtils.report(e);
return null;
}
}
/**
Export a representation of a set of glycan structure to a stream
in a certain graphical format.
@param os the destination output stream
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
@param format the graphical format to be used
@throws Exception if the format is not supported
*/
static public void export(OutputStream os, GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend, String format) throws Exception {
export(os,gr,structures,show_masses,show_redend,1.,format);
}
/**
Export a representation of a set of glycan structure to a stream
in a certain graphical format.
@param os the destination output stream
@param gr the GlycanRenderer used to render the structures
@param structures the structures to be rendered
@param show_masses <code>true</code> if the mass information
should be included in the graphical representation
@param show_redend <code>true</code> if the reducing end marker
should be included in the graphical representation
@param scale the scaling factor to be applied to the image
@param format the graphical format to be used
@throws Exception if the format is not supported
*/
static public void export(OutputStream os, GlycanRenderer gr, Collection<Glycan> structures, boolean show_masses, boolean show_redend, double scale, String format) throws Exception {
if( format.equals("svg") )
os.write(getVectorGraphics(gr,structures,show_masses,show_redend).getBytes());
else if( format.equals("pdf") )
os.write(getPDFGraphics(gr,structures,show_masses,show_redend));
else if( format.equals("ps") )
os.write(getPSGraphics(gr,structures,show_masses,show_redend));
else if( format.equals("eps") )
os.write(getEPSGraphics(gr,structures,show_masses,show_redend));
else if( format.equals("bmp") || format.equals("png") || format.equals("jpg") )
javax.imageio.ImageIO.write(gr.getImage(structures,true,show_masses,show_redend,scale),format,os);
else
throw new Exception("Unrecognized graphic format: " + format);
}
/**
Export a representation of a Renderable object to a filename in
a certain graphical format.
@param filename the path to the destination file
@param renderable the renderable object to be exported
@param format the graphical format to be used
@throws Exception if the format is not supported
*/
static public void export(String filename, Renderable renderable, String format) throws Exception {
OutputStream os = new FileOutputStream(filename);
export(os,renderable,format);
os.close();
}
/**
Export a representation of a Renderable object to a stream in
a certain graphical format.
@param os the destination output stream
@param renderable the renderable object to be exported
@param format the graphical format to be used
@throws Exception if the format is not supported
*/
static public void export(OutputStream os, Renderable renderable, String format) throws Exception {
if( format.equals("svg") )
os.write(getTranscodedSVG(renderable,null));
else if( format.equals("pdf") )
os.write(getTranscodedSVG(renderable, new org.apache.fop.svg.PDFTranscoder()));
else if( format.equals("ps") )
os.write(getTranscodedSVG(renderable, new org.apache.fop.render.ps.PSTranscoder()));
else if( format.equals("eps") )
os.write(getTranscodedSVG(renderable, new org.apache.fop.render.ps.EPSTranscoder()));
else if( format.equals("bmp") || format.equals("png") || format.equals("jpg") )
javax.imageio.ImageIO.write(getImage(renderable),format,os);
else
throw new Exception("Unrecognized graphic format: " + format);
}
}