001 /*---------------------------------------------------------------------------- 002 This file is part of deegree, http://deegree.org/ 003 Copyright (C) 2001-2009 by: 004 Department of Geography, University of Bonn 005 and 006 lat/lon GmbH 007 008 This library is free software; you can redistribute it and/or modify it under 009 the terms of the GNU Lesser General Public License as published by the Free 010 Software Foundation; either version 2.1 of the License, or (at your option) 011 any later version. 012 This library is distributed in the hope that it will be useful, but WITHOUT 013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 014 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 015 details. 016 You should have received a copy of the GNU Lesser General Public License 017 along with this library; if not, write to the Free Software Foundation, Inc., 018 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 019 020 Contact information: 021 022 lat/lon GmbH 023 Aennchenstr. 19, 53177 Bonn 024 Germany 025 http://lat-lon.de/ 026 027 Department of Geography, University of Bonn 028 Prof. Dr. Klaus Greve 029 Postfach 1147, 53001 Bonn 030 Germany 031 http://www.geographie.uni-bonn.de/deegree/ 032 033 e-mail: info@deegree.org 034 ----------------------------------------------------------------------------*/ 035 036 package org.deegree.framework.util; 037 038 import java.awt.Graphics2D; 039 import java.awt.RenderingHints; 040 import java.awt.geom.AffineTransform; 041 import java.awt.geom.Rectangle2D; 042 import java.awt.image.BufferedImage; 043 import java.net.MalformedURLException; 044 import java.net.URL; 045 046 import org.apache.batik.bridge.BaseScriptingEnvironment; 047 import org.apache.batik.bridge.BridgeContext; 048 import org.apache.batik.bridge.BridgeException; 049 import org.apache.batik.bridge.GVTBuilder; 050 import org.apache.batik.bridge.ViewBox; 051 import org.apache.batik.dom.svg.SVGDOMImplementation; 052 import org.apache.batik.dom.svg.SVGOMDocument; 053 import org.apache.batik.dom.util.DOMUtilities; 054 import org.apache.batik.ext.awt.image.GraphicsUtil; 055 import org.apache.batik.gvt.CanvasGraphicsNode; 056 import org.apache.batik.gvt.GraphicsNode; 057 import org.apache.batik.transcoder.SVGAbstractTranscoder; 058 import org.apache.batik.transcoder.TranscoderException; 059 import org.apache.batik.transcoder.TranscoderOutput; 060 import org.apache.batik.transcoder.TranscodingHints; 061 import org.apache.batik.transcoder.keys.BooleanKey; 062 import org.apache.batik.util.ParsedURL; 063 import org.w3c.dom.DOMImplementation; 064 import org.w3c.dom.Document; 065 import org.w3c.dom.svg.SVGSVGElement; 066 067 /** 068 * <code>AliasingSVGTranscoder</code> to create a BufferedImage from an SVG with the possibility to 069 * set preferences of aliasing 070 * 071 * @author <a href="mailto:buesching@lat-lon.de">Lyn Buesching</a> 072 * @author last edited by: $Author:$ 073 * 074 * @version $Revision:$, $Date:$ 075 * 076 */ 077 public class AliasingSVGTranscoder extends SVGAbstractTranscoder { 078 079 public static final TranscodingHints.Key KEY_ALIASING = new BooleanKey(); 080 081 private BufferedImage bufferedImage = null; 082 083 @Override 084 protected void transcode( Document document, String uri, TranscoderOutput output ) 085 throws TranscoderException { 086 087 if ( ( document != null ) && !( document.getImplementation() instanceof SVGDOMImplementation ) ) { 088 DOMImplementation impl; 089 impl = (DOMImplementation) hints.get( KEY_DOM_IMPLEMENTATION ); 090 // impl = SVGDOMImplementation.getDOMImplementation(); 091 document = DOMUtilities.deepCloneDocument( document, impl ); 092 if ( uri != null ) { 093 try { 094 URL url = new URL( uri ); 095 ( (SVGOMDocument) document ).setURLObject( url ); 096 } catch ( MalformedURLException mue ) { 097 } 098 } 099 } 100 101 ctx = createBridgeContext(); 102 SVGOMDocument svgDoc = (SVGOMDocument) document; 103 SVGSVGElement root = svgDoc.getRootElement(); 104 105 // build the GVT tree 106 builder = new GVTBuilder(); 107 // flag that indicates if the document is dynamic 108 109 boolean isDynamic = ( hints.containsKey( KEY_EXECUTE_ONLOAD ) 110 && ( (Boolean) hints.get( KEY_EXECUTE_ONLOAD ) ).booleanValue() && ctx.isDynamicDocument( svgDoc ) ); 111 112 GraphicsNode gvtRoot; 113 try { 114 if ( isDynamic ) 115 ctx.setDynamicState( BridgeContext.DYNAMIC ); 116 117 gvtRoot = builder.build( ctx, svgDoc ); 118 119 // dispatch an 'onload' event if needed 120 if ( ctx.isDynamic() ) { 121 BaseScriptingEnvironment se; 122 se = new BaseScriptingEnvironment( ctx ); 123 se.loadScripts(); 124 se.dispatchSVGLoadEvent(); 125 } 126 } catch ( BridgeException ex ) { 127 throw new TranscoderException( ex ); 128 } 129 130 // get the 'width' and 'height' attributes of the SVG document 131 float docWidth = (float) ctx.getDocumentSize().getWidth(); 132 float docHeight = (float) ctx.getDocumentSize().getHeight(); 133 134 setImageSize( docWidth, docHeight ); 135 136 // compute the preserveAspectRatio matrix 137 AffineTransform Px; 138 139 // take the AOI into account if any 140 if ( hints.containsKey( KEY_AOI ) ) { 141 Rectangle2D aoi = (Rectangle2D) hints.get( KEY_AOI ); 142 // transform the AOI into the image's coordinate system 143 Px = new AffineTransform(); 144 double sx = width / aoi.getWidth(); 145 double sy = height / aoi.getHeight(); 146 double scale = Math.min( sx, sy ); 147 Px.scale( scale, scale ); 148 double tx = -aoi.getX() + ( width / scale - aoi.getWidth() ) / 2; 149 double ty = -aoi.getY() + ( height / scale - aoi.getHeight() ) / 2; 150 ; 151 Px.translate( tx, ty ); 152 // take the AOI transformation matrix into account 153 // we apply first the preserveAspectRatio matrix 154 curAOI = aoi; 155 } else { 156 String ref = new ParsedURL( uri ).getRef(); 157 158 try { 159 Px = ViewBox.getViewTransform( ref, root, width, height ); 160 } catch ( BridgeException ex ) { 161 throw new TranscoderException( ex ); 162 } 163 164 if ( Px.isIdentity() && ( width != docWidth || height != docHeight ) ) { 165 // The document has no viewBox, we need to resize it by hand. 166 // we want to keep the document size ratio 167 float xscale, yscale; 168 xscale = width / docWidth; 169 yscale = height / docHeight; 170 float scale = Math.min( xscale, yscale ); 171 Px = AffineTransform.getScaleInstance( scale, scale ); 172 } 173 174 curAOI = new Rectangle2D.Float( 0, 0, width, height ); 175 } 176 177 CanvasGraphicsNode cgn = getCanvasGraphicsNode( gvtRoot ); 178 if ( cgn != null ) { 179 cgn.setViewingTransform( Px ); 180 curTxf = new AffineTransform(); 181 } else { 182 curTxf = Px; 183 } 184 185 gvtRoot = renderImage( output, gvtRoot, Px, (int) width, (int) height ); 186 187 this.root = gvtRoot; 188 189 } 190 191 private GraphicsNode renderImage( TranscoderOutput output, GraphicsNode gvtRoot, AffineTransform Px, int w, int h ) 192 throws TranscoderException { 193 194 Graphics2D g2d = createGraphics( w, h ); 195 // Check anti-aliasing preference 196 if ( hints.containsKey( KEY_ALIASING ) ) { 197 boolean antialias = ( (Boolean) hints.get( KEY_ALIASING ) ).booleanValue(); 198 g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, antialias ? RenderingHints.VALUE_ANTIALIAS_ON 199 : RenderingHints.VALUE_ANTIALIAS_OFF ); 200 } else { 201 g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); 202 } 203 204 g2d.clip( new java.awt.Rectangle( 0, 0, w, h ) ); 205 g2d.transform( Px ); 206 gvtRoot.paint( g2d ); 207 g2d.dispose(); 208 return null; 209 } 210 211 private Graphics2D createGraphics( int w, int h ) { 212 bufferedImage = new BufferedImage( w, h, BufferedImage.TYPE_INT_ARGB ); 213 Graphics2D g2d = GraphicsUtil.createGraphics( bufferedImage ); 214 return g2d; 215 } 216 217 /** 218 * @return BufferedImage the created BufferedImage 219 */ 220 public BufferedImage getBufferedImage() { 221 return bufferedImage; 222 } 223 224 }