blob: d7a79a57583b6b4f4bde866a4861db23229d23d1 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.svg;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.batik.bridge.BaseScriptingEnvironment;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.BridgeException;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.ViewBox;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.dom.svg.SVGOMDocument;
import org.apache.batik.dom.util.DOMUtilities;
import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.gvt.CanvasGraphicsNode;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.transcoder.keys.BooleanKey;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGSVGElement;
/**
* @author sshaw
*
* Class extension for handling turning on/off anti-aliasing, replacing black and white
* colors with choice colors and turning on/off maintain aspect ratio capability.
*/
public class ImageTranscoderEx extends ImageTranscoder {
/**
* @author sshaw
*
* Utility class for returning float width and height values from a method.
*/
public class DimensionFloat extends Dimension2D {
private float w;
private float h;
/**
*
*/
public DimensionFloat(float width, float height) {
super();
this.w = width;
this.h = height;
}
/* (non-Javadoc)
* @see java.awt.geom.Dimension2D#getWidth()
*/
public double getWidth() {
return w;
}
/* (non-Javadoc)
* @see java.awt.geom.Dimension2D#getHeight()
*/
public double getHeight() {
return h;
}
/* (non-Javadoc)
* @see java.awt.geom.Dimension2D#setSize(double, double)
*/
public void setSize(double width, double height) {
this.w = (float)width;
this.h = (float)height;
}
}
/**
* Constructor to create an instance of SWTImageTranscoder.
*/
public ImageTranscoderEx() {
// empty constructor
}
public static final TranscodingHints.Key KEY_MAINTAIN_ASPECT_RATIO
= new BooleanKey();
public static final TranscodingHints.Key KEY_FILL_COLOR
= new ColorKey();
public static final TranscodingHints.Key KEY_OUTLINE_COLOR
= new ColorKey();
public static final TranscodingHints.Key KEY_ANTI_ALIASING
= new BooleanKey();
/**
* initSVGDocument
* This is an initialization method for setting alternate stylesheet if
* set as a hint and also initializes the SVG document with the appropriate
* context.
*
* @param svgDoc SVGODocument to initialize.
*/
protected void initSVGDocument(SVGOMDocument svgDoc) {
Color fillColor = null;
Color outlineColor = null;
if (hints.containsKey(KEY_FILL_COLOR)) {
fillColor = (Color)hints.get(KEY_FILL_COLOR);
}
if (hints.containsKey(KEY_OUTLINE_COLOR)) {
outlineColor = (Color)hints.get(KEY_OUTLINE_COLOR);
}
if (fillColor == null && outlineColor == null)
return;
SVGColorConverter.getInstance().replaceDocumentColors(svgDoc, fillColor, outlineColor);
}
/**
* buildGVTTree
* This method builds the GVT tree that is used to render the SVG data.
*
* @param svgDoc SVGOMDocument representing the physical SVG document.
* @param context BridgeContext containing information of the render to occur.
* @return GraphicsNode object.
* @throws TranscoderException thrown if a BridgeException occurs when building the root object.
*/
protected GraphicsNode buildGVTTree(SVGOMDocument svgDoc, BridgeContext context)
throws TranscoderException {
GVTBuilder gvtBuilder = new GVTBuilder();
GraphicsNode gvtRoot = null;
try {
gvtRoot = gvtBuilder.build(context, svgDoc);
} catch (BridgeException ex) {
throw new TranscoderException(ex);
}
return gvtRoot;
}
/**
* calculateSizeTransform
* Calculates the transformation matrix that is applied during the render operation. Specifically,
* the transformation for changing the size of the rendered data.
*
* @param svgRoot SVGSVGElement root element of the SVG tree.
* @param gvtRoot GraphicsNode graphic node root
* @param uri String of the SVG document URI
* @param docWidth float width values of the original vector.
* @param docHeight float height values of the original vector.
* @param newWidth float width values of the rendered image.
* @param newHeight float height values of the rendered image.
* @return AffineTransform object that represents the size transformation to take place.
* @throws TranscoderException thrown if a BridgeException occurs when building the root object.
*/
protected AffineTransform calculateSizeTransform(SVGSVGElement svgRoot, GraphicsNode gvtRoot, String uri,
float docWidth, float docHeight,
float newWidth, float newHeight)
throws TranscoderException {
AffineTransform Px;
String ref = null;
try {
ref = new URL(uri == null ? "": uri).getRef(); //$NON-NLS-1$
} catch (MalformedURLException ex) {
// nothing to do, catched previously
}
boolean maintainAspectRatio = true;
if (hints.containsKey(KEY_MAINTAIN_ASPECT_RATIO)) {
maintainAspectRatio = ((Boolean)hints.get(KEY_MAINTAIN_ASPECT_RATIO)).booleanValue();
}
if (maintainAspectRatio) {
try {
Px = ViewBox.getViewTransform(ref, svgRoot, newWidth, newHeight);
} catch (BridgeException ex) {
throw new TranscoderException(ex);
}
if (Px.isIdentity() && (newWidth != docWidth || newHeight != docHeight)) {
// The document has no viewBox, we need to resize it by hand.
// we want to keep the document size ratio
float xscale = newWidth / docWidth;
float yscale = newHeight / docHeight;
if (docHeight / docWidth > newHeight / newWidth) {
xscale = yscale;
} else {
yscale = xscale;
}
Px = AffineTransform.getScaleInstance(xscale, yscale);
}
}
else {
float xscale = newWidth / docWidth;
float yscale = newHeight / docHeight;
Px = AffineTransform.getScaleInstance(xscale, yscale);
}
// take the AOI into account if any
if (hints.containsKey(KEY_AOI)) {
Rectangle2D aoi = (Rectangle2D)hints.get(KEY_AOI);
// transform the AOI into the image's coordinate system
aoi = Px.createTransformedShape(aoi).getBounds2D();
AffineTransform Mx = new AffineTransform();
double sx = newWidth / aoi.getWidth();
double sy = newHeight / aoi.getHeight();
Mx.scale(sx, sy);
double tx = -aoi.getX();
double ty = -aoi.getY();
Mx.translate(tx, ty);
// take the AOI transformation matrix into account
// we apply first the preserveAspectRatio matrix
Px.preConcatenate(Mx);
}
CanvasGraphicsNode cgn = getCanvasGraphicsNode(gvtRoot);
if (cgn != null) {
cgn.setViewingTransform(Px);
curTxf = new AffineTransform();
} else {
curTxf = Px;
}
return curTxf;
}
private boolean shouldCopyDocument(Document document) {
if (!(document.getImplementation() instanceof SVGDOMImplementation))
return true;
if (hints.containsKey(KEY_FILL_COLOR) ||
hints.containsKey(KEY_OUTLINE_COLOR)) {
return true;
}
return false;
}
/**
* Transcodes the specified Document as an image in the specified output.
*
* @param document the document to transcode
* @param uri the uri of the document or null if any
* @param output the ouput where to transcode
* @exception TranscoderException if an error occured while transcoding
*/
protected void transcode(Document document,
String uri,
TranscoderOutput output)
throws TranscoderException {
if (shouldCopyDocument(document)) {
DOMImplementation impl;
impl = SVGDOMImplementation.getDOMImplementation();
document = DOMUtilities.deepCloneDocument(document, impl);
if (uri != null) {
try {
URL url = new URL(uri);
((SVGOMDocument)document).setURLObject(url);
} catch (MalformedURLException mue) {
//TODO: Implement error handling
}
}
}
ctx = new BridgeContext(userAgent);
SVGOMDocument svgDoc = (SVGOMDocument)document;
SVGSVGElement svgRoot = svgDoc.getRootElement();
// build the GVT tree
builder = new GVTBuilder();
// flag that indicates if the document is dynamic
boolean isDynamic =
(hints.containsKey(KEY_EXECUTE_ONLOAD) &&
((Boolean)hints.get(KEY_EXECUTE_ONLOAD)).booleanValue() &&
BaseScriptingEnvironment.isDynamicDocument(ctx, svgDoc));
if (isDynamic)
ctx.setDynamicState(BridgeContext.DYNAMIC);
initSVGDocument(svgDoc);
GraphicsNode gvtRoot;
try {
gvtRoot = builder.build(ctx, svgDoc);
} catch (BridgeException ex) {
throw new TranscoderException(ex);
}
// get the 'width' and 'height' attributes of the SVG document
float docWidth = (float)ctx.getDocumentSize().getWidth();
float docHeight = (float)ctx.getDocumentSize().getHeight();
setImageSize(docWidth, docHeight);
//compute the transformation matrix
AffineTransform Px = calculateSizeTransform(svgRoot, gvtRoot, uri, docWidth, docHeight, width, height);
gvtRoot = renderImage(output, gvtRoot, Px, (int)width, (int)height);
this.root = gvtRoot;
}
/**
* @param output
* @param gvtRoot
* @param Px
* @param w
* @param h
* @return
* @throws TranscoderException
*/
private GraphicsNode renderImage(TranscoderOutput output, GraphicsNode gvtRoot, AffineTransform Px, int w, int h)
throws TranscoderException {
Graphics2D g2d = createGraphics(w, h);
// Check anti-aliasing preference
if (hints.containsKey(KEY_ANTI_ALIASING)) {
boolean antialias = ((Boolean)hints.get(KEY_ANTI_ALIASING)).booleanValue();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
antialias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
} else {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
g2d.clip(new java.awt.Rectangle(0, 0, w, h));
g2d.transform(Px);
gvtRoot.paint(g2d);
postRenderImage(g2d);
return null;
}
/**
* @param w
* @param h
* @return
*/
protected Graphics2D createGraphics(int w, int h) {
bufferedImage = createImage(w, h);
Graphics2D g2d = GraphicsUtil.createGraphics(bufferedImage);
return g2d;
}
protected void postRenderImage(Graphics2D g2d) {
g2d.dispose();
}
private BufferedImage bufferedImage = null;
/**
* getBufferedImage
* Accessor to return the buffered image used for rendering.
*
* @return BufferedImage
*/
public BufferedImage getBufferedImage() {
return bufferedImage;
}
/**
* Override to create a BufferedImage type that support an alpha channel for
* transparency.
*
* @see org.apache.batik.transcoder.image.ImageTranscoder#createImage(int, int)
*/
public BufferedImage createImage(int w, int h) {
return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
/**
* Override to support the translation of the BufferedImage type into the SWT Image
* format.
*
* @see org.apache.batik.transcoder.image.ImageTranscoder#writeImage(java.awt.image.BufferedImage, org.apache.batik.transcoder.TranscoderOutput)
*/
public void writeImage(BufferedImage img, TranscoderOutput arg1)
throws TranscoderException {
bufferedImage = img;
}
}