//$HeadURL$ /*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2007 by: 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.igeo.views.swing.print; import static javax.imageio.ImageIO.write; import static net.sf.jasperreports.engine.JasperFillManager.fillReport; import static net.sf.jasperreports.engine.JasperPrintManager.printPageToImage; import static org.deegree.framework.log.LoggerFactory.getLogger; import static org.deegree.graphics.MapFactory.createMapView; import static org.deegree.igeo.i18n.Messages.get; import static org.deegree.igeo.views.LayerPane.createThemes; import static org.deegree.model.spatialschema.GeometryFactory.createEnvelope; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.jasperreports.engine.JREmptyDataSource; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JasperExportManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JasperReport; import net.sf.jasperreports.engine.design.JasperDesign; import net.sf.jasperreports.engine.util.JRLoader; import org.deegree.framework.log.ILogger; import org.deegree.framework.util.FileUtils; import org.deegree.framework.util.MapUtils; import org.deegree.framework.util.Pair; import org.deegree.framework.utils.MapTools; 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.MapView; import org.deegree.graphics.RenderException; import org.deegree.graphics.Theme; import org.deegree.igeo.mapmodel.Layer; import org.deegree.igeo.mapmodel.LayerGroup; import org.deegree.igeo.mapmodel.MapModel; import org.deegree.igeo.mapmodel.MapModelVisitor; import org.deegree.igeo.views.DialogFactory; import org.deegree.igeo.views.swing.print.PrintOptionsPanel.Scale; import org.deegree.igeo.views.swing.print.SelectTemplatePanel.Template; import org.deegree.igeo.views.swing.util.GuiUtils; import org.deegree.igeo.views.swing.util.IGeoFileFilter; import org.deegree.igeo.views.swing.util.wizard.MiscActions; import org.deegree.igeo.views.swing.util.wizard.MiscActions.EmptyAction; import org.deegree.igeo.views.swing.util.wizard.Wizard.Action; import org.deegree.model.spatialschema.Envelope; import org.deegree.ogcbase.CommonNamespaces; import org.w3c.dom.Element; import org.xml.sax.SAXException; /** * <code>PrintActions</code> * * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a> * @author last edited by: $Author$ * * @version $Revision$, $Date$ */ public class PrintActions { static final ILogger LOG = getLogger( PrintActions.class ); /** * @param options * @return a new action */ public static Action optionsAction( final PrintOptionsPanel options ) { return new EmptyAction() { @Override public void addListener( ActionListener listener ) { options.scale.addActionListener( listener ); options.atLeastVisible.addActionListener( listener ); options.selectScale.addActionListener( listener ); options.complex.addActionListener( listener ); } @Override public boolean canForward() { return options.selectScale.isSelected() ? options.scale.getSelectedItem() != null && options.scale.getSelectedItem() instanceof Scale : true; } @Override public boolean forward() { if ( options.file == null && options.fileChanged ) { options.file = new File( options.fileField.getText() ); if ( options.file.exists() && !DialogFactory.openConfirmDialogYESNO( "Application", options, get( "$DI10022", options.file.toString() ), get( "$DI10019" ) ) ) { options.file = null; return false; } } if ( options.file == null ) { DialogFactory.openInformationDialog( "Application", options, get( "$MD10357" ), get( "$DI10018" ) ); return false; } return true; } }; } /** * @param reportPanel * @param mm * @param options * @param dpi * @param wmsSizeScale * @return the print action */ public static Action printAction( final JasperReportPanel reportPanel, final MapModel mm, final PrintOptionsPanel options, final int dpi, final double wmsSizeScale ) { return new EmptyAction() { @Override public boolean forward() { Map<String, Object> parameters = new HashMap<String, Object>(); try { int width = options.width.getInt(); int height = options.height.getInt(); if ( options.atLeastVisible.isSelected() || options.selectScale.isSelected() ) { Pair<Integer, Integer> dim = extractMapDimension( reportPanel ); calculatePrinterFriendlySize( dim, dpi ); width = dim.first; height = dim.second; } final List<Theme> themes = new ArrayList<Theme>(); final Envelope originalExtent = mm.getEnvelope(); final Envelope extent; if ( options.selectScale.isSelected() ) { Envelope e = MapUtils.scaleEnvelope( originalExtent, MapUtils.calcScale( mm.getTargetDevice().getPixelWidth(), mm.getTargetDevice().getPixelHeight(), originalExtent, originalExtent.getCoordinateSystem(), MapUtils.DEFAULT_PIXEL_SIZE ), ( (Scale) options.scale.getSelectedItem() ).scale ); extent = fitEnvelope( e, width, height ); } else { extent = fitEnvelope( originalExtent, width, height ); } mm.setEnvelope( extent ); mm.walkLayerTree( new MapModelVisitor() { public void visit( Layer layer ) throws Exception { double mis = layer.getMinScaleDenominator(); double mxs = layer.getMaxScaleDenominator(); if ( layer.isVisible() && mis <= mm.getScaleDenominator() && mxs >= mm.getScaleDenominator() ) { List<Theme> layerThemes = createThemes( layer.getCurrentStyle(), layer.getDataAccess(), extent.getCoordinateSystem() ); themes.addAll( layerThemes ); } } public void visit( LayerGroup layerGroup ) throws Exception { // not using grouping nodes } } ); BufferedImage img = renderMap( mm, width, height, themes, extent, wmsSizeScale ); parameters.put( "MAP", img ); mm.setEnvelope( originalExtent ); reportPanel.fillParameterMap( parameters ); img = renderLegend( mm, reportPanel, dpi ); parameters.put( "LEGEND", img ); if ( options.selectScale.isSelected() ) { String s = options.scale.getSelectedItem().toString(); parameters.put( "SCALE", "1 : " + s ); } else { parameters.put( "SCALE", "1 : " + Math.round( mm.getScaleDenominator() ) ); } if ( reportPanel.report.getRootElement().getAttribute( "language" ) == null || reportPanel.report.getRootElement().getAttribute( "language" ).trim().length() == 0 ) { // dirty hack to fix a bug in iReport 3.1.x // Newer Jasper templates are defined in name space // http://jasperreports.sourceforge.net/jasperreports // In this case the root element must contain the attribute 'language="java"' to enable // compilation of it. iReport 3.1.x does not create this attribute reportPanel.report.getRootElement().setAttribute( "language", "java" ); } JasperReport jasperReport = null; try { String loc = reportPanel.report.getSystemId().toExternalForm(); int idx = loc.lastIndexOf( '.' ); URL url = new URL( loc.substring( 0, idx ) + ".jasper" ); LOG.logInfo( "try using tempale: ", url ); jasperReport = (JasperReport) JRLoader.loadObject( url ); } catch ( Throwable e ) { String s = FileUtils.readTextFile( reportPanel.report.getSystemId() ).toString(); LOG.logInfo( "compiled tempale not available, use xml file: ", s ); JasperDesign jasperDesign = net.sf.jasperreports.engine.xml.JRXmlLoader.load( new ByteArrayInputStream( s.getBytes() ) ); jasperReport = net.sf.jasperreports.engine.JasperCompileManager.compileReport( jasperDesign ); } JasperPrint print = fillReport( jasperReport, parameters, new JREmptyDataSource() ); if ( IGeoFileFilter.PDF.accept( options.file ) ) { JasperExportManager.exportReportToPdfFile( print, options.file.toString() ); } if ( IGeoFileFilter.HTML.accept( options.file ) ) { JasperExportManager.exportReportToHtmlFile( print, options.file.toString() ); } if ( IGeoFileFilter.XML.accept( options.file ) ) { JasperExportManager.exportReportToXmlFile( print, options.file.toString(), true ); } if ( IGeoFileFilter.JPEG.accept( options.file ) || IGeoFileFilter.PNG.accept( options.file ) ) { Image result = printPageToImage( print, 0, 1 ); if ( IGeoFileFilter.JPEG.accept( options.file ) ) { write( (RenderedImage) result, "jpg", options.file ); } if ( IGeoFileFilter.PNG.accept( options.file ) ) { write( (RenderedImage) result, "png", options.file ); } } } catch ( IOException e ) { GuiUtils.unknownError( LOG, e, reportPanel ); // the template was already loaded // before... } catch ( JRException e ) { GuiUtils.showErrorMessage( reportPanel, get( "$MD10358", e.getMessage() ), e ); e.printStackTrace(); } catch ( RenderException e ) { GuiUtils.showErrorMessage( reportPanel, get( "$MD10359", e.getMessage() ), e ); e.printStackTrace(); } catch ( Exception e ) { GuiUtils.unknownError( LOG, e, reportPanel ); e.printStackTrace(); } return true; } private BufferedImage renderLegend( MapModel mm, JasperReportPanel jasper, int dpi ) throws Exception { BufferedImage legend = MapTools.getLegendAsImage( mm, true ); Pair<Integer, Integer> dim = extractLegendDimension( jasper ); calculatePrinterFriendlySize( dim, dpi ); float qx = ( (float) dim.first ) / (float) legend.getWidth(); float qy = ( (float) dim.second ) / (float) legend.getHeight(); float q = qy; if ( qx < qy ) { q = qx; } // scale a bit down for better looking q *= 0.98f; BufferedImage out = new BufferedImage( dim.first, dim.second, BufferedImage.TYPE_INT_RGB ); Graphics g = out.getGraphics(); g.setColor( Color.WHITE ); g.fillRect( 0, 0, dim.first, dim.second ); if ( q < 1 ) { // scale down if necessary g.drawImage( legend, 0, 0, Math.round( legend.getWidth() * q ), Math.round( legend.getHeight() * q ), null ); g.dispose(); } else { // scale up a little so the legend becomes readable g.drawImage( legend, 0, 0, Math.min( dim.first, legend.getWidth() * 3 ), Math.min( dim.second, legend.getHeight() * 3 ), null ); } g.dispose(); return out; } /** * * @param mm * MapModel * @param width * map width * @param height * map height * @param themes * list of tThemes to be rendered * @param extent * map extent for rendering (may differ from current map extent) * @param wmsSizeScale * @return map rendered onto a BufferedImage * @throws Exception */ private BufferedImage renderMap( final MapModel mm, int width, int height, final List<Theme> themes, final Envelope extent, final double wmsSizeScale ) throws Exception { List<Theme> tmp = new ArrayList<Theme>(); for ( int i = themes.size() - 1; i >= 0; i-- ) { tmp.add( themes.get( i ) ); } int h = mm.getTargetDevice().getPixelHeight(); int w = mm.getTargetDevice().getPixelWidth(); if ( wmsSizeScale >= 1 ) { int hh = (int) Math.round( height / wmsSizeScale ); mm.getTargetDevice().setPixelHeight( hh ); int ww = (int) Math.round( width / wmsSizeScale ); mm.getTargetDevice().setPixelWidth( ww ); } BufferedImage img = null; try { MapView mv = createMapView( "iGeoDesktop", extent, extent.getCoordinateSystem(), tmp.toArray( new Theme[tmp.size()] ), MapUtils.DEFAULT_PIXEL_SIZE ); img = new BufferedImage( width, height, BufferedImage.TYPE_INT_RGB ); Graphics2D g = img.createGraphics(); g.setBackground( new Color( 0 ) ); g.fillRect( 0, 0, width, height ); g.setClip( 0, 0, width, height ); mv.paint( g ); g.dispose(); } catch ( Exception e ) { throw e; } finally { mm.getTargetDevice().setPixelHeight( h ); mm.getTargetDevice().setPixelWidth( w ); } return img; } }; } static Pair<Integer, Integer> extractMapDimension( JasperReportPanel jasper ) throws XMLParsingException { NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); nsc.addNamespace( "jasper", URI.create( "http://jasperreports.sourceforge.net/jasperreports" ) ); Element root = jasper.report.getRootElement(); // old jasper version int width = XMLTools.getNodeAsInt( root, "//image/reportElement/@width" + "[../../imageExpression='$P{MAP}']", null, -1 ); int height = XMLTools.getNodeAsInt( root, "//image/reportElement/@height" + "[../../imageExpression='$P{MAP}']", null, -1 ); if ( width == -1 ) { // new jasper version width = XMLTools.getRequiredNodeAsInt( root, "//jasper:image/jasper:reportElement/@width" + "[../../jasper:imageExpression='$P{MAP}']", nsc ); height = XMLTools.getRequiredNodeAsInt( root, "//jasper:image/jasper:reportElement/@height" + "[../../jasper:imageExpression='$P{MAP}']", nsc ); } return new Pair<Integer, Integer>( width, height ); } static Pair<Integer, Integer> extractLegendDimension( JasperReportPanel jasper ) throws XMLParsingException { NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); nsc.addNamespace( "jasper", URI.create( "http://jasperreports.sourceforge.net/jasperreports" ) ); Element root = jasper.report.getRootElement(); // old jasper version int width = XMLTools.getNodeAsInt( root, "//image/reportElement/@width" + "[../../imageExpression='$P{LEGEND}']", null, -1 ); int height = XMLTools.getNodeAsInt( root, "//image/reportElement/@height" + "[../../imageExpression='$P{LEGEND}']", null, -1 ); if ( width == -1 ) { // new jasper version width = XMLTools.getRequiredNodeAsInt( root, "//jasper:image/jasper:reportElement/@width" + "[../../jasper:imageExpression='$P{LEGEND}']", nsc ); height = XMLTools.getRequiredNodeAsInt( root, "//jasper:image/jasper:reportElement/@height" + "[../../jasper:imageExpression='$P{LEGEND}']", nsc ); } return new Pair<Integer, Integer>( width, height ); } static void calculatePrinterFriendlySize( Pair<Integer, Integer> pixels, int dpi ) { pixels.first = (int) Math.round( pixels.first / 72d * dpi ); pixels.second = (int) Math.round( pixels.second / 72d * dpi ); } static Envelope fitEnvelope( Envelope env, int width, int height ) { double mapRatio = (double) width / (double) height; double envRatio = env.getWidth() / env.getHeight(); double minx, miny, maxx, maxy; if ( mapRatio > envRatio ) { miny = env.getMin().getY(); maxy = env.getMax().getY(); minx = env.getCentroid().getX() - env.getHeight() * mapRatio / 2d; maxx = env.getCentroid().getX() + env.getHeight() * mapRatio / 2d; } else { minx = env.getMin().getX(); maxx = env.getMax().getX(); miny = env.getCentroid().getY() - env.getWidth() / mapRatio / 2d; maxy = env.getCentroid().getY() + env.getWidth() / mapRatio / 2d; } env = createEnvelope( minx, miny, maxx, maxy, env.getCoordinateSystem() ); return createEnvelope( minx, miny, maxx, maxy, env.getCoordinateSystem() ); } /** * @param fromList * @param jasper * @param optionsPanel * @param dpi * @return a new action */ public static Action selectTemplateAction( final SelectTemplatePanel fromList, final JasperReportPanel jasper, final PrintOptionsPanel optionsPanel, final int dpi ) { return MiscActions.chainAction( MiscActions.monitorJListAction( fromList.list ), new EmptyAction() { @Override public boolean forward() { Template template = (Template) fromList.list.getSelectedValue(); try { XMLFragment doc = new XMLFragment( template.location ); jasper.init( doc ); Pair<Integer, Integer> dim = extractMapDimension( jasper ); calculatePrinterFriendlySize( dim, dpi ); optionsPanel.width.setInt( dim.first ); optionsPanel.height.setInt( dim.second ); optionsPanel.calculateAspectRatio(); return true; } catch ( IOException e ) { GuiUtils.showErrorMessage( fromList, get( "$MD10360", e.getMessage() ), e ); } catch ( SAXException e ) { GuiUtils.showErrorMessage( fromList, get( "$MD10361", e.getMessage() ), e ); } catch ( XMLParsingException e ) { GuiUtils.showErrorMessage( fromList, get( "$MD10362", e.getMessage() ), e ); } return false; } } ); } }