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    }