| /****************************************************************************** |
| * 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; |
| } |
| } |