/*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53177 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.portal.common.control; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URL; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JREmptyDataSource; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JasperPrintManager; import net.sf.jasperreports.engine.JasperRunManager; import org.deegree.enterprise.control.AbstractListener; import org.deegree.enterprise.control.FormEvent; import org.deegree.enterprise.control.RPCMember; import org.deegree.enterprise.control.RPCStruct; import org.deegree.enterprise.control.RPCWebEvent; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.util.BasicUUIDFactory; import org.deegree.framework.util.ImageUtils; import org.deegree.framework.util.KVP2Map; import org.deegree.framework.util.MapUtils; import org.deegree.framework.util.StringTools; import org.deegree.framework.xml.NamespaceContext; import org.deegree.framework.xml.XMLFragment; import org.deegree.framework.xml.XMLParsingException; import org.deegree.framework.xml.XMLTools; import org.deegree.graphics.Encoders; import org.deegree.model.crs.CRSFactory; import org.deegree.model.crs.CoordinateSystem; import org.deegree.model.crs.UnknownCRSException; import org.deegree.model.spatialschema.Point; import org.deegree.ogcbase.CommonNamespaces; import org.deegree.ogcwebservices.InconsistentRequestException; import org.deegree.ogcwebservices.wms.operation.GetMap; import org.deegree.portal.PortalException; import org.deegree.portal.PortalUtils; import org.deegree.portal.context.Layer; import org.deegree.portal.context.Style; import org.deegree.portal.context.ViewContext; import org.deegree.security.drm.model.User; import org.xml.sax.SAXException; /** * performs a print request/event by creating a PDF document from * the current map. For this JASPER is used. Well known parameters * that can be passed to a jaser report are:<br> * <ul> * <li>MAP</li> * <li>LEGEND</li> * <li>DATE</li> * <li>MAPSCALE</li> * </ul> * <br>Additionaly parameters named 'TA:XXXX' can be used. deegree * will create a k-v-p for each TA:XXXX passed as part of RPC. * * * @version $Revision: 1.11 $ * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author last edited by: $Author: poth $ * * @version 1.0. $Revision: 1.11 $, $Date: 2006/11/27 09:07:53 $ * * @since 2.0 */ public abstract class AbstractSimplePrintListener extends AbstractListener { private static ILogger LOG = LoggerFactory.getLogger( AbstractSimplePrintListener.class ); private static final double DEFAULT_PIXEL_SIZE = 0.00028; /** * @param e */ public void actionPerformed( FormEvent e ) { RPCWebEvent rpc = (RPCWebEvent) e; try { validate( rpc ); } catch ( Exception ex ) { LOG.logError( ex.getMessage(), ex ); gotoErrorPage( ex.getMessage() ); } ViewContext vc = getViewContext( rpc ); if ( vc == null ) { LOG.logError( "no valid ViewContext available; maybe your session has reached timeout limit" ); //$NON-NLS-1$ gotoErrorPage( Messages.getString( "AbstractSimplePrintListener.MISSINGCONTEXT" ) ); setNextPage( "igeoportal/error.jsp" ); return; } try { printMap( vc, rpc ); } catch ( Exception ex ) { ex.printStackTrace(); LOG.logError( ex.getMessage(), ex ); gotoErrorPage( ex.getMessage() ); setNextPage( "igeoportal/error.jsp" ); } } /** * * @param vc * @param rpc * @throws PortalException * @throws IOException * @throws SAXException * @throws XMLParsingException * @throws InconsistentRequestException * @throws UnknownCRSException */ private void printMap( ViewContext vc, RPCWebEvent rpc ) throws PortalException, IOException, InconsistentRequestException, XMLParsingException, SAXException, UnknownCRSException { List<String> getMap = createGetMapRequests( vc ); String image = performGetMapRequests( getMap ); String legend = accessLegend( createLegendURLs( vc ) ); String format = (String) rpc.getRPCMethodCall().getParameters()[0].getValue(); RPCStruct struct = (RPCStruct) rpc.getRPCMethodCall().getParameters()[1].getValue(); String printTemplate = (String) struct.getMember( "TEMPLATE" ).getValue(); ServletContext sc = ( (HttpServletRequest) getRequest() ).getSession( true ).getServletContext(); String path = sc.getRealPath( "/WEB-INF/igeoportal/print" ) + '/' + printTemplate + ".jasper"; String pathx = sc.getRealPath( "/WEB-INF/igeoportal/print" ) + '/' + printTemplate + ".jrxml"; Map parameter = new HashMap(); parameter.put( "MAP", image ); parameter.put( "LEGEND", legend ); SimpleDateFormat sdf = new SimpleDateFormat( "dd.MM.yyyy", Locale.getDefault() ); // TODO deprecated - will be remove in future versions parameter.put( "DATUM", sdf.format( new GregorianCalendar().getTime() ) ); //-------------------------------------------------------- parameter.put( "DATE", sdf.format( new GregorianCalendar().getTime() ) ); double scale = calcScale( pathx, getMap.get( 0 ) ); parameter.put( "MAPSCALE", "" + Math.round( scale ) ); LOG.logDebug( "print map scale: ", scale ); // set text area values RPCMember[] members = struct.getMembers(); for ( int i = 0; i < members.length; i++ ) { if ( members[i].getName().startsWith( "TA:" ) ) { String s = members[i].getName().substring( 3, members[i].getName().length() ); String val = (String) members[i].getValue(); if ( val != null ) { val = new String( val.getBytes(), "UTF-8" ); } LOG.logDebug( "text area name: ", s ); LOG.logDebug( "text area value: ", val ); parameter.put( s, val ); } } if ( "application/pdf".equals( format ) ) { // create the pdf Object result = null; try { JRDataSource ds = new JREmptyDataSource(); result = JasperRunManager.runReportToPdf( path, parameter, ds ); } catch ( JRException e ) { LOG.logError( e.getLocalizedMessage(), e ); throw new PortalException( Messages.getString( "AbstractSimplePrintListener.REPORTCREATION" ) ); } finally { File file = new File( image ); file.delete(); file = new File( legend ); file.delete(); } forwardPDF( result ); } else if ( "image/png".equals( format ) ) { // create the image Image result = null; try { JRDataSource ds = new JREmptyDataSource(); JasperPrint prt = JasperFillManager.fillReport( path, parameter, ds ); result = JasperPrintManager.printPageToImage( prt, 0, 1 ); } catch ( JRException e ) { LOG.logError( e.getLocalizedMessage(), e ); throw new PortalException( Messages.getString( "AbstractSimplePrintListener.REPORTCREATION" ) ); } finally { File file = new File( image ); file.delete(); file = new File( legend ); file.delete(); } forwardImage( result, format ); } } private double calcScale( String path, String getmap ) throws InconsistentRequestException, XMLParsingException, IOException, SAXException, UnknownCRSException { Map model = KVP2Map.toMap( getmap ); model.put( "ID", "22" ); GetMap gm = GetMap.create( model ); File file = new File( path ); XMLFragment xml = new XMLFragment( file.toURL() ); String xpathW = "detail/band/image/reportElement[./@key = 'image-1']/@width"; String xpathH = "detail/band/image/reportElement[./@key = 'image-1']/@height"; NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); int w = XMLTools.getRequiredNodeAsInt( xml.getRootElement(), xpathW, nsc ); int h = XMLTools.getRequiredNodeAsInt( xml.getRootElement(), xpathH, nsc ); CoordinateSystem crs = CRSFactory.create( gm.getSrs() ); return MapUtils.calcScale( w, h, gm.getBoundingBox(), crs, DEFAULT_PIXEL_SIZE ); } /** * accesses the Legende URLs passed, draws the result onto an image that * are stored in a temporary file. The name of the file will be returned. * @param name * @return */ private String accessLegend( List<String[]> legends ) throws IOException { int width = Integer.parseInt( getInitParameter( "LEGENDWIDTH" ) ); int height = Integer.parseInt( getInitParameter( "LEGENDHEIGHT" ) ); BufferedImage bi = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB ); Graphics g = bi.getGraphics(); g.setColor( Color.BLACK ); int k = 10; for ( int i = 0; i < legends.size(); i++ ) { String[] s = legends.get( i ); if ( s[1] != null ) { LOG.logDebug( "reading legend: " + s[1] ); Image img = ImageUtils.loadImage( new URL( s[1] ) ); if ( img.getWidth( null ) < 50 ) { // it is assumed that no label iss assigned g.drawImage( img, 0, k, null ); g.drawString( s[0], img.getWidth( null ) + 10, k + img.getHeight( null ) / 2 ); } else { g.drawImage( img, 0, k, null ); } k = k + img.getHeight( null ) + 10; } else { g.drawString( "- " + s[0], 0, k + 10 ); k = k + 20; } } g.dispose(); return storeImage( bi ); } /** * performs the GetMap requests passed, draws the result onto an image that * are stored in a temporary file. The name of the file will be returned. * @param list * @return * @throws PortalException * @throws IOException */ private String performGetMapRequests( List<String> list ) throws PortalException, IOException { Map map = KVP2Map.toMap( list.get( 0 ) ); map.put( "ID", "ww" ); GetMap getMap = null; try { getMap = GetMap.create( map ); } catch ( Exception e ) { LOG.logError( e.getMessage(), e ); String s = Messages.format( "AbstractSimplePrintListener.GETMAPCREATION", list.get( 0 ) ); throw new PortalException( s ); } BufferedImage bi = new BufferedImage( getMap.getWidth(), getMap.getHeight(), BufferedImage.TYPE_INT_ARGB ); Graphics g = bi.getGraphics(); for ( int i = 0; i < list.size(); i++ ) { URL url = new URL( list.get( i ) ); Image img = ImageUtils.loadImage( url ); g.drawImage( img, 0, 0, null ); } g.dispose(); return storeImage( bi ); } /** * stores the passed image in the defined temporary directory and returns * the dynamicly created filename * @param bi * @return * @throws IOException */ private String storeImage( BufferedImage bi ) throws IOException { BasicUUIDFactory fac = new BasicUUIDFactory(); String s = fac.createUUID().toANSIidentifier(); String tempDir = getInitParameter( "TEMPDIR" ); if ( !tempDir.endsWith( "/" ) ) { tempDir = tempDir + '/'; } if ( tempDir.startsWith( "/" ) ) { tempDir = tempDir.substring( 1, tempDir.length() ); } ServletContext sc = ( (HttpServletRequest) this.getRequest() ).getSession( true ).getServletContext(); String fileName = StringTools.concat( 300, sc.getRealPath( tempDir ), '/', s, ".png" ); FileOutputStream fos = new FileOutputStream( new File( fileName ) ); Encoders.encodePng( fos, bi ); fos.close(); return fileName; } private void forwardPDF( Object result ) throws PortalException { // must be a byte array String tempDir = getInitParameter( "TEMPDIR" ); if ( !tempDir.endsWith( "/" ) ) { tempDir = tempDir + '/'; } if ( tempDir.startsWith( "/" ) ) { tempDir = tempDir.substring( 1, tempDir.length() ); } BasicUUIDFactory fac = new BasicUUIDFactory(); ServletContext sc = ( (HttpServletRequest) this.getRequest() ).getSession( true ).getServletContext(); String fileName = fac.createUUID().toANSIidentifier(); String s = StringTools.concat( 200, sc.getRealPath( tempDir ), '/', fileName, ".pdf" ); try { RandomAccessFile raf = new RandomAccessFile( s, "rw" ); raf.write( (byte[]) result ); raf.close(); } catch ( Exception e ) { e.printStackTrace(); LOG.logError( "could not write temporary pdf file: " + s, e ); throw new PortalException( Messages.format( "AbstractSimplePrintListener.PDFCREATION", s ), e ); } getRequest().setAttribute( "PDF", StringTools.concat( 200, tempDir, fileName, ".pdf" ) ); } private void forwardImage( Image result, String format ) throws PortalException { format = format.substring( format.indexOf( '/' ) + 1 ); String tempDir = getInitParameter( "TEMPDIR" ); if ( !tempDir.endsWith( "/" ) ) { tempDir = tempDir + '/'; } if ( tempDir.startsWith( "/" ) ) { tempDir = tempDir.substring( 1, tempDir.length() ); } BasicUUIDFactory fac = new BasicUUIDFactory(); ServletContext sc = ( (HttpServletRequest) this.getRequest() ).getSession( true ).getServletContext(); String fileName = fac.createUUID().toANSIidentifier(); String s = StringTools.concat( 200, sc.getRealPath( tempDir ), "/", fileName, ".", format ); try { // make sure we have a BufferedImage if ( !( result instanceof BufferedImage ) ) { BufferedImage img = new BufferedImage( result.getWidth( null ), result.getHeight( null ), BufferedImage.TYPE_INT_ARGB ); Graphics g = img.getGraphics(); g.drawImage( result, 0, 0, null ); g.dispose(); result = img; } ImageUtils.saveImage( (BufferedImage) result, s, 1 ); } catch ( Exception e ) { e.printStackTrace(); LOG.logError( "could not write temporary pdf file: " + s, e ); throw new PortalException( Messages.format( "AbstractSimplePrintListener.PDFCREATION", s ), e ); } getRequest().setAttribute( "PDF", StringTools.concat( 200, tempDir, fileName, ".", format ) ); } /** * fills the passed PrintMap request template with required values * @param vc * @param rpc * @param template * @return * @throws PortalException */ private List<String> createGetMapRequests( ViewContext vc ) { User user = getUser(); // set boundingbox/envelope Point[] points = vc.getGeneral().getBoundingBox(); int width = Integer.parseInt( getInitParameter( "WIDTH" ) ); int height = Integer.parseInt( getInitParameter( "HEIGHT" ) ); StringBuffer sb = new StringBuffer( 1000 ); sb.append( "&BBOX=" ).append( points[0].getX() ).append( ',' ); sb.append( points[0].getY() ).append( ',' ).append( points[1].getX() ); sb.append( ',' ).append( points[1].getY() ).append( "&WIDTH=" ); sb.append( width ).append( "&HEIGHT=" ).append( height ); if ( user != null ) { sb.append( "&user=" ).append( user.getName() ); sb.append( "&password=" ).append( user.getPassword() ); } String[] reqs = PortalUtils.createBaseRequests( vc ); List<String> list = new ArrayList<String>( reqs.length ); for ( int i = 0; i < reqs.length; i++ ) { list.add( reqs[i] + sb.toString() ); LOG.logDebug( "GetMap request:", reqs[i] + sb.toString() ); } return list; } /** * returns <code>null</code> and should be overwirtten by an extending class * @return */ protected User getUser() { return null; } /** * reads the view context to print from the users session * @param rpc * @return */ abstract protected ViewContext getViewContext( RPCWebEvent rpc ); /** * returns legend access URLs for all visible layers of the passed * view context. If a visible layer does not define a LegendURL * @param vc * @return */ private List<String[]> createLegendURLs( ViewContext vc ) { Layer[] layers = vc.getLayerList().getLayers(); List<String[]> list = new ArrayList<String[]>(); for ( int i = 0; i < layers.length; i++ ) { if ( !layers[i].isHidden() ) { Style style = layers[i].getStyleList().getCurrentStyle(); String[] s = new String[2]; s[0] = layers[i].getTitle(); if ( style.getLegendURL() != null ) { s[1] = style.getLegendURL().getOnlineResource().toExternalForm(); } list.add( s ); } } return list; } /** * validates the incoming request/RPC if conatins all required elements * @param rpc * @throws PortalException */ protected void validate( RPCWebEvent rpc ) throws PortalException { RPCStruct struct = (RPCStruct) rpc.getRPCMethodCall().getParameters()[1].getValue(); if ( struct.getMember( "TEMPLATE" ) == null ) { throw new PortalException( Messages.getString( "portal.common.control.VALIDATIONERROR" ) ); } if ( rpc.getRPCMethodCall().getParameters()[0].getValue() == null ) { throw new PortalException( Messages.getString( "portal.common.control.VALIDATIONERROR" ) ); } } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: AbstractSimplePrintListener.java,v $ Revision 1.11 2006/11/27 09:07:53 poth JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code. Revision 1.10 2006/11/16 14:15:40 schmitz Added png download support. Revision 1.9 2006/10/17 20:31:20 poth *** empty log message *** Revision 1.8 2006/09/25 12:47:00 poth bug fixes - map scale calculation Revision 1.7 2006/08/04 09:24:35 poth map scale rounded to integer and passed as string to reports Revision 1.6 2006/07/25 06:23:16 poth support for scale added (will be passed as MAPSCALE) to the jasper report Revision 1.5 2006/07/21 09:32:02 poth *** empty log message *** Revision 1.4 2006/07/14 14:31:39 poth logging and URL decoding added Revision 1.3 2006/07/12 10:50:30 taddei substituted "mm" by "MM" for Date (by HP) Revision 1.2 2006/07/12 10:25:35 poth fix for date format / date now is available in jasper templates through parameter DATUM (marked as deprecated) and DATE Revision 1.1 2006/05/21 11:59:36 poth initial load up Revision 1.2 2006/05/20 15:55:53 poth bug fix / support for legend visualization Revision 1.1 2006/05/19 15:13:57 poth simple printlistener initial loadup ********************************************************************** */