[Commits] r1802 - in core/trunk/geoext: examples lib lib/GeoExt/data tests tests/lib/GeoExt/data

commits at geoext.org commits at geoext.org
Fri Jan 22 09:08:01 CET 2010


Author: ahocevar
Date: 2010-01-22 09:08:01 +0100 (Fri, 22 Jan 2010)
New Revision: 1802

Added:
   core/trunk/geoext/examples/print-page.html
   core/trunk/geoext/examples/print-page.js
   core/trunk/geoext/lib/GeoExt/data/PrintPage.js
   core/trunk/geoext/lib/GeoExt/data/PrintProvider.js
   core/trunk/geoext/tests/lib/GeoExt/data/PrintPage.html
   core/trunk/geoext/tests/lib/GeoExt/data/PrintProvider.html
Modified:
   core/trunk/geoext/lib/GeoExt.js
   core/trunk/geoext/tests/list-tests.html
Log:
Added support for basic printing using the MapFish (GeoServer) print module. r=bartvde (closes #199)

Added: core/trunk/geoext/examples/print-page.html
===================================================================
--- core/trunk/geoext/examples/print-page.html	                        (rev 0)
+++ core/trunk/geoext/examples/print-page.html	2010-01-22 08:08:01 UTC (rev 1802)
@@ -0,0 +1,31 @@
+<html>
+    <head>
+        <title>GeoExt PrintPage Example</title>
+
+        <script type="text/javascript" src="http://extjs.cachefly.net/ext-2.2.1/adapter/ext/ext-base.js"></script>
+        <script type="text/javascript" src="http://extjs.cachefly.net/ext-2.2.1/ext-all.js"></script>
+        <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2.1/resources/css/ext-all.css" />
+        <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2.1/examples/shared/examples.css" />
+        <script src="http://openlayers.org/api/2.8/OpenLayers.js"></script>
+        <script type="text/javascript" src="../lib/GeoExt.js"></script>
+
+        <script type="text/javascript" src="print-page.js"></script>
+        
+        <!-- The script below will load the capabilities of the print service
+             and save them into the global printCapabilities variable. Instead
+             of this, the PrintProvider can be configured with a url and take
+             care of fetching the capabilities. -->
+        <script type="text/javascript" src="http://demo.opengeo.org/geoserver/pdf/info.json?var=printCapabilities"></script>
+
+    </head>
+    <body>
+        <h1>Printing Contents from a MapPanel</h1>
+        <p>This example shows the how to create a printable PDF with
+        GeoExt.data.PrintProvider and GeoExt.data.PrintPage, using the
+        <a href="http://trac.mapfish.org/trac/mapfish/wiki/PrintModuleInstallation">MapFish</a>
+        or <a href="http://geoserver.org/display/GEOS/Printing+2.0+HOWTO">GeoServer</a>
+        print module.</p>
+        <p>The js is not minified so it is readable. See <a href="print-page.js">print-page.js</a>.</p>
+        <div id="content"></div>
+    </body>
+</html>


Property changes on: core/trunk/geoext/examples/print-page.html
___________________________________________________________________
Name: svn:keywords
   + Id Author Date Revision
Name: svn:eol-style
   + native

Added: core/trunk/geoext/examples/print-page.js
===================================================================
--- core/trunk/geoext/examples/print-page.js	                        (rev 0)
+++ core/trunk/geoext/examples/print-page.js	2010-01-22 08:08:01 UTC (rev 1802)
@@ -0,0 +1,72 @@
+ /**
+ * Copyright (c) 2008-2009 The Open Source Geospatial Foundation
+ * 
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+
+/** api: example[print-page]
+ *  Printing contents from a MapPanel
+ *  ---------------------------------
+ *  Using PrintPage and PrintProvider to print the visible extent of a MapPanel.
+ */
+
+var mapPanel, printPage;
+
+Ext.onReady(function() {
+    // The printProvider that connects us to the print service
+    var printProvider = new GeoExt.data.PrintProvider({
+        method: "GET", // "POST" recommended for production use
+        capabilities: printCapabilities // from the info.json script in the html
+    });
+    // Our print page. Tells the PrintProvider about the scale and center of
+    // our page.
+    printPage = new GeoExt.data.PrintPage({
+        printProvider: printProvider,
+        customParams: {
+            mapTitle: "Printing Demo",
+            comment: "This is a simple map printed from GeoExt."
+        }
+    });
+
+    // The map we want to print
+    mapPanel = new GeoExt.MapPanel({
+        region: "center",
+        layers: [new OpenLayers.Layer.WMS("Tasmania", "http://demo.opengeo.org/geoserver/wms",
+            {layers: "topp:tasmania_state_boundaries"}, {singleTile: true})],
+        center: [146.56, -41.56],
+        zoom: 7
+    });
+    // The legend to optionally include on the printout
+    var legendPanel = new GeoExt.LegendPanel({
+        region: "west",
+        width: 150,
+        bodyStyle: "padding:5px",
+        layerStore: mapPanel.layers
+    });
+    
+    var includeLegend; // controlled by the "Include legend?" checkbox
+     
+    // The main panel
+    new Ext.Panel({
+        renderTo: "content",
+        layout: "border",
+        width: 700,
+        height: 420,
+        items: [mapPanel, legendPanel],
+        bbar: ["->", {
+            text: "Print",
+            handler: function() {
+                // convenient way to fit the print page to the visible map area
+                printPage.fit(mapPanel, true);
+                // print the page, optionally including the legend
+                printProvider.print(mapPanel, printPage, includeLegend && {legend: legendPanel});
+            }
+        }, {
+            xtype: "checkbox",
+            boxLabel: "Include legend?",
+            handler: function() {includeLegend = this.checked}
+        }]
+    });
+});


Property changes on: core/trunk/geoext/examples/print-page.js
___________________________________________________________________
Name: svn:keywords
   + Id Author Date Revision
Name: svn:eol-style
   + native

Added: core/trunk/geoext/lib/GeoExt/data/PrintPage.js
===================================================================
--- core/trunk/geoext/lib/GeoExt/data/PrintPage.js	                        (rev 0)
+++ core/trunk/geoext/lib/GeoExt/data/PrintPage.js	2010-01-22 08:08:01 UTC (rev 1802)
@@ -0,0 +1,379 @@
+/**
+ * Copyright (c) 2008-2009 The Open Source Geospatial Foundation
+ * 
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+Ext.namespace("GeoExt.data");
+
+/** api: (define)
+ *  module = GeoExt.data
+ *  class = PrintPage
+ *  base_link = `Ext.util.Observable <http://extjs.com/deploy/dev/docs/?class=Ext.util.Observable>`_
+ */
+
+/** api: constructor
+ *  .. class:: PrintPage
+ * 
+ *  Provides a representation of a print page for
+ *  :class:`GeoExt.data.PrintProvider`. The extent of the page is stored as
+ *  ``OpenLayers.Feature.Vector``. Widgets can use this to display the print
+ *  extent on the map. In addition, a handle feature is also provided, which
+ *  controls can use to modify the print extent's scale and rotation.
+ */
+GeoExt.data.PrintPage = Ext.extend(Ext.util.Observable, {
+    
+    /** api:config[printProvider]
+     * :class:`GeoExt.data.PrintProvider` The print provider to use with
+     * this page.
+     */
+    
+    /** private: property[printProvider]
+     *  :class:`GeoExt.data.PrintProvider`
+     */
+    printProvider: null,
+    
+    /** api: property[feature]
+     *  ``OpenLayers.Feature.Vector`` Feature representing the page extent.
+     *  Read-only.
+     */
+    feature: null,
+    
+    /** private: property[handles]
+     *  Array(``OpenLayers.Feature.Vector``) Features providing the four
+     *  scale/rotation handles for the page.
+     */
+    handles: null,
+    
+    /** api: property[scale]
+     *  ``Ext.data.Record`` The current scale record of the page. Read-only.
+     */
+    scale: null,
+    
+    /** api: property[rotation]
+     *  ``Float`` The current rotation of the page. Read-only.
+     */
+    rotation: 0,
+    
+    /** api:config[customParams]
+     *  ``Object`` Key-value pairs of additional parameters that the
+     *  printProvider will send to the print service for this page.
+     */
+
+    /** api: property[customParams]
+     *  ``Object`` Key-value pairs of additional parameters that the
+     *  printProvider will send to the print service for this page.
+     */
+    customParams: null,
+    
+    /** private: method[constructor]
+     *  Private constructor override.
+     */
+    constructor: function(config) {
+        this.initialConfig = config;
+        Ext.apply(this, config);
+        
+        if(!this.customParams) {
+            this.customParams = {};
+        }
+        
+        this.addEvents(
+            /** api: events[change]
+             *  Triggered when any of the page properties have changed
+             *  
+             *  Listener arguments:
+             *  * printPage - :class:`GeoExt.data.PrintPage` this printPage
+             */
+            "change"
+        );
+
+        GeoExt.data.PrintPage.superclass.constructor.apply(this, arguments);
+
+        this.feature = new OpenLayers.Feature.Vector(
+            OpenLayers.Geometry.fromWKT("POLYGON((-1 -1,1 -1,1 1,-1 1,-1 -1))"));
+        this.handles = [
+            new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.Point(-1,-1)
+            ),
+            new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.Point(1,-1)
+            ),
+            new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.Point(1,1)
+            ),
+            new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.Point(-1,1)
+            )
+        ];
+        
+        this.printProvider.on({
+            "layoutchange": this.onLayoutChange,
+            scope: this
+        });
+    },
+
+    /** api: method[getCenter]
+     *  :return: ``OpenLayers.LonLat``
+     *  
+     *  Returns the current page center.
+     */
+    getCenter: function() {
+        return this.feature.geometry.getBounds().getCenterLonLat();
+    },
+    
+    /** api: method[setScale]
+     *  :param scale: ``Ext.data.Record`` The new scale record.
+     *  :param units: ``String`` map units to use for the scale calculation.
+     *      Optional if a ``layer`` that is added to a map was configured.
+     *      If neither is provided, "dd" will be assumed.
+     * 
+     *  Updates the page geometry to match a given scale. Since this takes the
+     *  current layout of the printProvider into account, this can be used to
+     *  update the page geometry feature when the layout has changed.
+     */
+    setScale: function(scale, units) {
+        var bounds = this.calculatePageBounds(scale, units);
+        var geom = bounds.toGeometry();
+        var rotation = this.rotation;
+        if(rotation != 0) {
+            geom.rotate(-rotation, geom.getCentroid());
+        }
+        this.scale = scale;
+        this.updateFeature(geom);
+    },
+    
+    /** api: method[setCenter]
+     *  :param center: ``OpenLayers.LonLat`` The new center.
+     * 
+     *  Moves the page extent to a new center.
+     */
+    setCenter: function(center) {
+        var geom = this.feature.geometry;
+        var oldCenter = geom.getBounds().getCenterLonLat();
+        var dx = center.lon - oldCenter.lon;
+        var dy = center.lat - oldCenter.lat;
+        geom.move(dx, dy);
+        this.updateFeature(geom);
+    },
+    
+    /** api: method[setRotation]
+     *  :param rotation: ``Float`` The new rotation.
+     *  
+     *  Sets a new rotation for the page geometry.
+     */
+    setRotation: function(rotation) {
+        if(this.printProvider.layout.get("rotation") === true) {
+            var geom = this.feature.geometry;
+            geom.rotate(this.rotation - rotation, geom.getCentroid());
+            this.rotation = rotation;
+            this.updateFeature(geom);
+        }
+    },
+    
+    /** api: method[fit]
+     *  :param map: :class:`GeoExt.MapPanel`|``OpenLayers.Map`` The map to fit
+     *      the page to.
+     *  :param loose: ``Boolean`` If true, the closest matching print extent
+     *      will be chosen. If set to false, the chosen print extent will
+     *      be the closest one that entirely fits into the visible map extent.
+     *      Default is false.
+     * 
+     *  Fits the page layout to the current map extent. If the map has not
+     *  been centered yet, this will do nothing.
+     */
+    fit: function(map, loose) {
+        if(map instanceof GeoExt.MapPanel) {
+            map = map.map;
+        }
+        var center = map.getCenter();
+        if(!center) {
+            return;
+        }
+        this.suspendEvents();
+        this.setCenter(center);
+        this.resumeEvents();
+        var extent = map.getExtent();
+        var units = map.getUnits();
+        var scale, looseScale, contains;
+        this.printProvider.scales.each(function(rec) {
+            looseScale = scale || rec;
+            scale = rec;
+            contains = extent.containsBounds(
+                this.calculatePageBounds(scale, units));
+            return !contains
+        }, this);
+        this.setScale(loose && contains ? looseScale : scale, units);
+    },
+
+    /** private: method[updateByHandle]
+     *  :param handle: ``OpenLayers.Feature.Vector`` the handle to use.
+     *  :param updateHandles: ``Boolean`` If set to false, only the feature
+     *      will be updated, but the handles will not be aligned. Defaults to
+     *      false.
+     *  :param rotationIncrement: ``Float`` Optional rotation increment.
+     *      Defaults to 1.
+     *  
+     *  Updates scale and rotation based on the current handle location. This
+     *  method is useful for drag handlers on the handle geometry, when
+     *  displayed on a layer.
+     */
+    updateByHandle: function(handle, updateHandles, rotationIncrement) {
+        rotationIncrement = rotationIncrement || 1;
+        var handles = this.handles;
+        handle = handle || handles[0];
+        var handleIndex = handles.indexOf(handle);
+        if(handleIndex === -1) {
+            return;
+        }
+
+        var rotate = this.printProvider.layout.get("rotation");
+        var vertices = this.feature.geometry.components[0].components;
+        var fGeom = this.feature.geometry;
+        var hGeom = handle.geometry;
+        var origin = fGeom.getCentroid();
+
+        // resize handles
+        var resizeBy = origin.distanceTo(handle.geometry) /
+            origin.distanceTo(vertices[handleIndex]);
+        var handleRect = fGeom.clone();
+        handleRect.resize(resizeBy, origin);
+
+        // rotate handles
+        if(rotate) {
+            var dxH = hGeom.x - origin.x;
+            var dyH = hGeom.y - origin.y;
+            var dxRef = vertices[handleIndex].x - origin.x;
+            var dyRef = vertices[handleIndex].y - origin.y;
+            var handleRot = Math.round(Math.atan2(dxH, dyH) * 180 / Math.PI) -
+                Math.round(Math.atan2(dxRef, dyRef) * 180 / Math.PI);
+            handleRot && handleRect.rotate(-handleRot, origin);
+        }
+        
+        this.updateHandles(handleRect, handle);
+
+        // fit scale of the page to the handle rectangle
+        var top = new OpenLayers.Geometry.LineString([
+            handles[2].geometry, handles[3].geometry]);
+        var topCenter = top.getBounds().getCenterLonLat();
+        var dist = new OpenLayers.Geometry.Point(
+            topCenter.lon, topCenter.lat).distanceTo(origin);
+        var scaleFits = [], distHash = {};
+        this.printProvider.scales.each(function(rec){
+            var bounds = this.calculatePageBounds(rec);
+            var d = Math.abs((bounds.getHeight() / 2) - dist);
+            scaleFits.push(d);
+            distHash[d.toPrecision(8)] = rec;
+        }, this);
+        var min = scaleFits.concat().sort(function(a,b){return a<b?-1:1;})[0];
+        var scale = distHash[min.toPrecision(8)];
+        var bounds = this.calculatePageBounds(scale);
+        var geom = bounds.toGeometry();
+
+        // fit rotation of the page to the handle rectangle
+        var rotation = 0;
+        if(rotate) {
+            var dx = topCenter.lon - origin.x;
+            var dy = topCenter.lat - origin.y;
+            rotation = Math.round(
+                Math.atan2(dx, dy) * 180 / Math.PI / rotationIncrement) *
+                rotationIncrement;
+            geom.rotate(-rotation, geom.getCentroid());
+        }
+        
+        this.scale = scale;
+        this.rotation = rotation;
+        this.updateFeature(geom, updateHandles === true);
+    },
+
+    /** private: method[updateFeature]
+     *  :param geometry: ``OpenLayers.Geometry`` New geometry for the feature.
+     *      If not provided, the existing geometry will be left unchanged.
+     *  :param updateHandles: ``Boolean`` If set to false, only the feature
+     *      will be updated, but the handles will not be aligned. Defaults to
+     *      true.
+     *      
+     *  Updates the page feature with a new geometry and aligns the handle
+     *  with it.
+     */
+    updateFeature: function(geometry, updateHandles) {
+        var f = this.feature;
+        geometry.id = f.geometry.id;
+        f.geometry = geometry;
+        f.layer && f.layer.drawFeature(f);
+        
+        if(updateHandles !== false) {
+            this.updateHandles();
+        }
+        this.fireEvent("change", this);
+    },
+    
+    /** private: method[updateHandles]
+     *  :param geometry: ``OpenLayers.Geometry.Polygon`` Optional. If provided,
+     *      handle geometries will be taken from the vertices of the provided
+     *      polygon instead of this page's feature's geometry.
+     *  :param excludeHandle: ``OpenLayers.Feature``. Optional. If one of the
+     *      features of the handles array is provided, its geometry will not be
+     *      updated.
+     */
+    updateHandles: function(geometry, excludeHandle) {
+        var f = this.feature;
+        var h = this.handles;
+        var c = (geometry || f.geometry).components[0].components;
+        var layer = h[0].layer;
+        var id, handle, hGeom;
+        for(var i=0; i<4; ++i) {
+            handle = h[i];
+            if(handle !== excludeHandle) {
+                hGeom = handle.geometry;
+                id = hGeom.id;
+                hGeom = c[i].clone();
+                hGeom.id = id;
+                handle.geometry = hGeom;
+                layer && layer.drawFeature(handle);
+            }
+        }
+    },
+    
+    /** private: method[calculatePageBounds]
+     *  :param scale: ``Ext.data.Record`` Scale record to calculate the page
+     *      bounds for.
+     *  :param units: ``String`` Map units to use for the scale calculation.
+     *      Optional if ``feature`` is added to layer which is added to a
+     *      map. If not provided, "dd" will be assumed.
+     *  :return: ``OpenLayers.Bounds``
+     *  
+     *  Calculates the page bounds for a given scale.
+     */
+    calculatePageBounds: function(scale, units) {
+        var s = scale.get("value");
+        var f = this.feature;
+        var geom = this.feature.geometry;
+        var center = geom.getBounds().getCenterLonLat();
+
+        var size = this.printProvider.layout.get("size");
+        var units = units ||
+            (f.layer && f.layer.map && f.layer.map.getUnits()) ||
+            "dd";
+        var unitsRatio = OpenLayers.INCHES_PER_UNIT[units];
+        var w = size.width / 72 / unitsRatio * s / 2;
+        var h = size.height / 72 / unitsRatio * s / 2;
+        
+        return new OpenLayers.Bounds(center.lon - w, center.lat - h,
+            center.lon + w, center.lat + h);
+    },
+    
+    /** private: method[onLayoutChange]
+     *  Handler for the printProvider's layoutchange event.
+     */
+    onLayoutChange: function() {
+        this.updateByHandle(this.handles[0], true);
+    },
+    
+    /** private: method[destroy]
+     */
+    destroy: function() {
+        this.printProvider.un("layoutchange", this.onLayoutChange, this);
+    }
+
+});


Property changes on: core/trunk/geoext/lib/GeoExt/data/PrintPage.js
___________________________________________________________________
Name: svn:keywords
   + Id Author Date Revision
Name: svn:eol-style
   + native

Added: core/trunk/geoext/lib/GeoExt/data/PrintProvider.js
===================================================================
--- core/trunk/geoext/lib/GeoExt/data/PrintProvider.js	                        (rev 0)
+++ core/trunk/geoext/lib/GeoExt/data/PrintProvider.js	2010-01-22 08:08:01 UTC (rev 1802)
@@ -0,0 +1,596 @@
+/**
+ * Copyright (c) 2008-2009 The Open Source Geospatial Foundation
+ * 
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+Ext.namespace("GeoExt.data");
+
+/** api: (define)
+ *  module = GeoExt.data
+ *  class = PrintProvider
+ *  base_link = `Ext.util.Observable <http://extjs.com/deploy/dev/docs/?class=Ext.util.Observable>`_
+ */
+
+/** api: example
+ *  Minimal code to print as much of the current map extent as possible as
+ *  soon as the print service capabilities are loaded, using the first layout
+ *  reported by the print service:
+ * 
+ *  .. code-block:: javascript
+ *     
+ *      var mapPanel = new GeoExt.MapPanel({
+ *          renderTo: "mappanel",
+ *          layers: [new OpenLayers.Layer.WMS("wms", "/geoserver/wms",
+ *              {layers: "topp:tasmania_state_boundaries"})],
+ *          center: [146.56, -41.56],
+ *          zoom: 7
+ *      });
+ *      var printProvider = new GeoExt.data.PrintProvider({
+ *          url: "/geoserver/pdf",
+ *          listeners: {
+ *              "loadcapabilities": function() {
+ *                  var printPage = new GeoExt.data.PrintPage({
+ *                      printProvider: printProvider
+ *                  });
+ *                  printPage.fit(mapPanel, true);
+ *                  printProvider.print(mapPanel, printPage);
+ *              }
+ *          }
+ *      });
+ */
+
+/** api: constructor
+ *  .. class:: PrintProvider
+ * 
+ *  Provides an interface to a Mapfish or GeoServer print module. For printing,
+ *  one or more instances of :class:`GeoExt.data.PrintPage` are also required
+ *  to tell the PrintProvider about the scale and extent (and optionally
+ *  rotation) of the page(s) we want to print. 
+ */
+GeoExt.data.PrintProvider = Ext.extend(Ext.util.Observable, {
+    
+    /** api: config[url]
+     *  ``String`` Base url of the print service. Only required if
+     *  :ref:`GeoExt.data.PrintProvider.capabilities` is not provided. This
+     *  is usually something like ``http://path/to/mapfish/print`` for Mapfish,
+     *  and ``http://path/to/geoserver/pdf`` for GeoServer with the printing
+     *  extension installed. This property requires that the print service is
+     *  at the same origin as the application (or accessible via proxy).
+     */
+    
+    /** private:  property[url]
+     *  ``String`` Base url of the print service. Will always have a trailing
+     *  "/".
+     */
+    url: null,
+    
+    /** api: config[capabilities]
+     *  ``Object`` Capabilities of the print service. Only required if
+     *  :ref:`GeoExt.data.PrintProvider.url` is not provided. This is the
+     *  object returned by the ``info.json`` endpoint of the print service,
+     *  and is usually obtained by including a script tag pointing to
+     *  ``http://path/to/printservice/info.json?var=myvar`` in the head of the
+     *  html document, making the capabilities accessible as ``window.myvar``.
+     *  This property should be used when no local print service or proxy is
+     *  available, or when you do not listen for the ``loadcapabilities``
+     *  events before creating components that require the PrintProvider's
+     *  capabilities to be available.
+     */
+    
+    /** private: property[capabilities]
+     *  ``Object`` Capabilities as returned from the print service.
+     */
+    capabilities: null,
+    
+    /** api: config[method]
+     *  ``String`` Either ``POST`` or ``GET`` (case-sensitive). Method to use
+     *  when sending print requests to the servlet. If the print service is at
+     *  the same origin as the application (or accessible via proxy), then
+     *  ``POST`` is recommended. Use ``GET`` when accessing a remote print
+     *  service with no proxy available, but expect issues with character
+     *  encoding and URLs exceeding the maximum length. Default is ``POST``.
+     */
+    
+    /** private: property[method]
+     *  ``String`` Either ``POST`` or ``GET`` (case-sensitive). Method to use
+     *  when sending print requests to the servlet.
+     */
+    method: "POST",
+
+    /** api: config[customParams]
+     *  ``Object`` Key-value pairs of custom data to be sent to the print
+     *  service. Optional. This is e.g. useful for complex layout definitions
+     *  on the server side that require additional parameters.
+     */
+    
+    /** api: property[customParams]
+     *  ``Object`` Key-value pairs of custom data to be sent to the print
+     *  service. Optional. This is e.g. useful for complex layout definitions
+     *  on the server side that require additional parameters.
+     */
+    customParams: null,
+    
+    /** api: property[scales]
+     *  ``Ext.data.JsonStore`` read-only. A store representing the scales
+     *  available.
+     *  
+     *  Record fields:
+     *  * name - ``String`` the name of the scale
+     *  * value - ``Float`` the scale denominator
+     */
+    scales: null,
+    
+    /** api: property[dpis]
+     *  ``Ext.data.JsonStore`` read-only. A store representing the dpis
+     *  available.
+     *  
+     *  Record fields:
+     *  * name - ``String`` the name of the dpi
+     *  * value - ``Float`` the dots per inch
+     */
+    dpis: null,
+        
+    /** api: property[layouts]
+     *  ``Ext.data.JsonStore`` read-only. A store representing the layouts
+     *  available.
+     *  
+     *  Records fields:
+     *  * name - ``String`` the name of the layout
+     *  * size - ``Object`` width and height of the map in points
+     *  * rotation - ``Boolean`` indicates if rotation is supported
+     */
+    layouts: null,
+    
+    /** api: property[dpi]
+     *  ``Ext.data.Record`` the record for the currently used resolution.
+     *  Read-only, use :ref:`GeoExt.data.PrintProvider.setDpi` to set the
+     *  value.
+     */
+    dpi: null,
+
+    /** api: property[layout]
+     *  ``Ext.data.Record`` the record of the currently used layout. Read-only,
+     *  use :ref:`GeoExt.data.PrintProvider.setLayout` to set the value.
+     */
+    layout: null,
+
+    /** private:  method[constructor]
+     *  Private constructor override.
+     */
+    constructor: function(config) {
+        this.initialConfig = config;
+        Ext.apply(this, config);
+        
+        if(!this.customParams) {
+            this.customParams = {};
+        }
+
+        this.addEvents(
+            /** api: events[loadcapabilities]
+             *  Triggered when the capabilities have finished loading. This
+             *  event will only fire when
+             *  :ref:`GeoExt.data.PrintProvider.capabilities` is not
+             *  configured.
+             *  
+             *  Listener arguments:
+             *  * printProvider - :class:`GeoExt.data.PrintProvider` this
+             *    PrintProvider
+             *  * capabilities - ``Object`` the capabilities
+             */
+            "loadcapabilities",
+            
+            /** api: events[layoutchange]
+             *  Triggered when the layout is changed.
+             *  
+             *  Listener arguments:
+             *  * printProvider - :class:`GeoExt.data.PrintProvider` this
+             *    PrintProvider
+             *  * layout - ``Ext.data.Record`` the new layout
+             */
+            "layoutchange",
+
+            /** api: events[dpichange]
+             *  Triggered when the dpi value is changed.
+             *  
+             *  Listener arguments:
+             *  * printProvider - :class:`GeoExt.data.PrintProvider` this
+             *    PrintProvider
+             *  * dpi - ``Ext.data.Record`` the new dpi record
+             */
+            "dpichange",
+            
+            /** api: events[beforeprint]
+             *  Triggered when the print method is called.
+             *  
+             *  Listener arguments:
+             *  * printProvider - :class:`GeoExt.data.PrintProvider` this
+             *    PrintProvider
+             *  * map - ``OpenLayers.Map`` the map being printed
+             *  * pages - Array of :class:`GeoExt.data.PrintPage` the print
+             *    pages being printed
+             *  * options - ``Object`` the options to the print command
+             */
+            "beforeprint",
+            
+            /** api: events[print]
+             *  Triggered when the print document is opened.
+             *  
+             *  Listener arguments:
+             *  * printProvider - :class:`GeoExt.data.PrintProvider` this
+             *    PrintProvider
+             *  * url - ``String`` the url of the print document
+             */
+            "print"
+        );
+        
+        GeoExt.data.PrintProvider.superclass.constructor.apply(this, arguments);
+
+        this.scales = new Ext.data.JsonStore({
+            root: "scales",
+            sortInfo: {field: "value", direction: "DESC"},
+            fields: ["name", {name: "value", type: "float"}]
+        });
+        
+        this.dpis = new Ext.data.JsonStore({
+            root: "dpis",
+            fields: ["name", {name: "value", type: "float"}]
+        });
+        
+        this.layouts = new Ext.data.JsonStore({
+            root: "layouts",
+            fields: [
+                "name",
+                {name: "size", mapping: "map"},
+                {name: "rotation", type: "boolean"}
+            ]
+        });
+        
+        if(config.capabilities) {
+            this.loadStores();
+        } else {
+            if(this.url.split("/").pop()) {
+                this.url += "/";            
+            }
+            this.loadCapabilities();
+        }
+    },
+    
+    /** api: method[setLayout]
+     *  :param layout: ``Ext.data.Record`` the record of the layout.
+     *  
+     *  Sets the layout for this printProvider.
+     */
+    setLayout: function(layout) {
+        this.layout = layout;
+        this.fireEvent("layoutchange", this, layout);
+    },
+    
+    /** api: method[setDpi]
+     *  :param dpi: ``Ext.data.Record`` the dpi record.
+     *  
+     *  Sets the dpi for this printProvider.
+     */
+    setDpi: function(dpi) {
+        this.dpi = dpi;
+        this.fireEvent("dpichange", this, dpi);
+    },
+
+    /** api: method[print]
+     *  :param map: ``GeoExt.MapPanel``|``OpenLayers.Map`` The map to print.
+     *  :param pages: ``Array``(:class:`GeoExt.data.PrintPage`) or
+     *      :class:`GeoExt.data.PrintPage` page(s) to print.
+     *  :param options: ``Object`` of additional options:
+     *      * ``legend``: :class:`GeoExt.LegendPanel` If provided, the legend
+     *        will be added to the print document. For the printed result to
+     *        look like the LegendPanel, the following settings in the
+     *        ``!legends`` block of the print module are recommended:
+     *        
+     *        .. code-block:: none
+     *        
+     *          maxIconWidth: 0
+     *          maxIconHeight: 0
+     *          classIndentation: 0
+     *
+     *  Sends the print command to the print service and opens a new window
+     *  with the resulting PDF.
+     */
+    print: function(map, pages, options) {
+        if(map instanceof GeoExt.MapPanel) {
+            map = map.map;
+        }
+        pages = pages instanceof Array ? pages : [pages];
+        options = options || {};
+        if(this.fireEvent("beforeprint", this, map, pages, options) === false) {
+            return;
+        }
+
+        var jsonData = Ext.apply({
+            units: map.getUnits(),
+            srs: map.baseLayer.projection.getCode(),
+            layout: this.layout.get("name"),
+            dpi: this.dpi.get("value")
+        }, this.customParams);
+
+        var pagesLayer = pages[0].feature.layer;
+        var encodedLayers = [];
+        Ext.each(map.layers, function(layer){
+            if(layer !== pagesLayer && layer.getVisibility() === true) {
+                var enc = this.encodeLayer(layer);
+                enc && encodedLayers.push(enc);
+            }
+        }, this);
+        jsonData.layers = encodedLayers;
+        
+        var encodedPages = [];
+        Ext.each(pages, function(page) {
+            var center = page.getCenter();
+            encodedPages.push(Ext.apply({
+                center: [center.lon, center.lat],
+                scale: page.scale.get("value"),
+                rotation: page.rotation
+            }, page.customParams));
+        }, this);
+        jsonData.pages = encodedPages;
+        
+        if(options.legend) {
+            var encodedLegends = [];
+            options.legend.items.each(function(cmp) {
+                if(cmp.isVisible()) {
+                    var encFn = this.encoders.legends[cmp.getXType()];
+                    encodedLegends = encodedLegends.concat(
+                        encFn.call(this, cmp));
+                }
+            }, this);
+            jsonData.legends = encodedLegends;
+        }
+
+        if(this.method === "GET") {
+            var url = this.capabilities.printURL + "?spec=" +
+                escape(Ext.encode(jsonData));
+            window.open(url);
+            this.fireEvent("print", this, url);
+        } else {
+            Ext.Ajax.request({
+                url: this.capabilities.createURL,
+                jsonData: jsonData,
+                success: function(response) {
+                    // In IE, using a Content-disposition: attachment header
+                    // may make it hard or impossible to download the pdf due
+                    // to security settings. So we'll display the pdf inline.
+                    var url = Ext.decode(response.responseText).getURL +
+                        (Ext.isIE ? "?inline=true" : "");
+                    if(Ext.isOpera || Ext.isIE) {
+                        // Make sure that Opera and IE don't replace the
+                        // content tab with the pdf
+                        window.open(url);
+                    } else {
+                        // This avoids popup blockers for all other browers
+                        window.location.href = url;                        
+                    } 
+                    this.fireEvent("print", this, url);
+                },
+                scope: this
+            });
+        }
+    },
+    
+    /** private: method[loadCapabilities]
+     */
+    loadCapabilities: function() {
+        var url = this.url + "info.json";
+        Ext.Ajax.request({
+            url: url,
+            disableCaching: false,
+            success: function(response) {
+                this.capabilities = Ext.decode(response.responseText);
+                this.loadStores();
+            },
+            scope: this
+        });
+    },
+    
+    /** private: method[loadStores]
+     */
+    loadStores: function() {
+        this.scales.loadData(this.capabilities);
+        this.dpis.loadData(this.capabilities);
+        this.layouts.loadData(this.capabilities);
+        
+        this.setLayout(this.layouts.getAt(0));
+        this.setDpi(this.dpis.getAt(0));
+        this.fireEvent("loadcapabilities", this, this.capabilities);
+    },
+        
+    /** private: method[encodeLayer]
+     *  :param layer: ``OpenLayers.Layer``
+     *  :return: ``Object``
+     * 
+     *  Encodes a layer for the print service.
+     */
+    encodeLayer: function(layer) {
+        var encLayer;
+        for(var c in this.encoders.layers) {
+            if(OpenLayers.Layer[c] && layer instanceof OpenLayers.Layer[c]) {
+                encLayer = this.encoders.layers[c].call(this, layer);
+                break;
+            }
+        }
+        // only return the encLayer object when we have a type. Prevents a
+        // fallback on base encoders like HTTPRequest.
+        return (encLayer && encLayer.type) ? encLayer : null;
+    },
+
+    /** private: method[getAbsoluteUrl]
+     *  :param url: ``String``
+     *  :return: ``String``
+     *  
+     *  Converts the provided url to an absolute url.
+     */
+    getAbsoluteUrl: function(url) {
+        var a;
+        if(Ext.isIE) {
+            a = document.createElement("<a href='" + url + "'/>");
+            a.style.display = "none";
+            document.body.appendChild(a);
+            a.href = a.href;
+            document.body.removeChild(a);
+        } else {
+            a = document.createElement("a");
+            a.href = url;
+        }
+        return a.href;
+    },
+    
+    /** private: property[encoders]
+     *  ``Object`` Encoders for all print content
+     */
+    encoders: {
+        "layers": {
+            "WMS": function(layer) {
+                var enc = this.encoders.layers.HTTPRequest.call(this, layer);
+                Ext.apply(enc, {
+                    type: 'WMS',
+                    layers: [layer.params.LAYERS].join(",").split(","),
+                    format: layer.params.FORMAT,
+                    styles: [layer.params.STYLES].join(",").split(",")
+                });
+                var param;
+                for(var p in layer.params) {
+                    param = p.toLowerCase();
+                    if(!layer.DEFAULT_PARAMS[param] &&
+                    "layers,styles,width,height,srs".indexOf(param) == -1) {
+                        if(!enc.customParams) {
+                            enc.customParams = {};
+                        }
+                        enc.customParams[p] = layer.params[p];
+                    }
+                }
+                return enc;
+            },
+            "OSM": function(layer) {
+                var enc = this.encoders.layers.TileCache.call(this, layer);
+                return Ext.apply(enc, {
+                    type: 'OSM',
+                    baseURL: enc.baseURL.substr(0, enc.baseURL.indexOf("$")),
+                    extension: "png"
+                });
+            },
+            "TileCache": function(layer) {
+                var enc = this.encoders.layers.HTTPRequest.call(this, layer);
+                return Ext.apply(enc, {
+                    type: 'TileCache',
+                    layer: layer.layername,
+                    maxExtent: layer.maxExtent.toArray(),
+                    tileSize: [layer.tileSize.w, layer.tileSize.h],
+                    extension: layer.extension,
+                    resolutions: layer.serverResolutions || layer.resolutions
+                });
+            },
+            "HTTPRequest": function(layer) {
+                return {
+                    baseURL: this.getAbsoluteUrl(layer.url instanceof Array ?
+                        layer.url[0] : layer.url),
+                    opacity: (layer.opacity != null) ? layer.opacity : 1.0,
+                    singleTile: layer.singleTile
+                };
+            },
+            "Image": function(layer) {
+                return {
+                    type: 'Image',
+                    baseURL: this.getAbsoluteUrl(layer.getURL(layer.extent)),
+                    opacity: (layer.opacity != null) ? layer.opacity : 1.0,
+                    extent: layer.extent.toArray(),
+                    pixelSize: [layer.size.w, layer.size.h],
+                    name: layer.name
+                };
+            },
+            "Vector": function(layer) {
+                if(!layer.features.length) {
+                    return;
+                }
+                
+                var encFeatures = [];
+                var encStyles = {};
+                var features = layer.features;
+                var featureFormat = new OpenLayers.Format.GeoJSON();
+                var styleFormat = new OpenLayers.Format.JSON();
+                var nextId = 1;
+                var styleDict = {};
+                var feature, style, dictKey, dictItem;
+                for(var i=0, len=features.length; i<len; ++i) {
+                    feature = features[i];
+                    style = feature.style || layer.style ||
+                    layer.styleMap.createSymbolizer(feature,
+                        feature.renderIntent);
+                    dictKey = styleFormat.write(style);
+                    dictItem = styleDict[dictKey];
+                    if(dictItem) {
+                        //this style is already known
+                        styleName = dictItem;
+                    } else {
+                        //new style
+                        styleDict[dictKey] = styleName = nextId++;
+                        if(style.externalGraphic) {
+                            encStyles[styleName] = Ext.applyIf({
+                                externalGraphic: this.getAbsoluteUrl(
+                                    style.externalGraphic)}, style);
+                        } else {
+                            encStyles[styleName] = style;
+                        }
+                    }
+                    var featureGeoJson = featureFormat.extract.feature.call(
+                        featureFormat, feature);
+                    
+                    featureGeoJson.properties = OpenLayers.Util.extend({
+                        _gx_style: styleName
+                    }, featureGeoJson.properties);
+                    
+                    encFeatures.push(featureGeoJson);
+                }
+                
+                return {
+                    type: 'Vector',
+                    styles: encStyles,
+                    styleProperty: '_gx_style',
+                    geoJson: {
+                        type: "FeatureCollection",
+                        features: encFeatures
+                    },
+                    name: layer.name,
+                    opacity: (layer.opacity != null) ? layer.opacity : 1.0
+                };
+            }
+        },
+        "legends": {
+            "gx_wmslegend": function(legend) {
+                var enc = this.encoders.legends.base.call(this, legend);
+                var icons = [];
+                for(var i=1, len=legend.items.getCount(); i<len; ++i) {
+                    icons.push(this.getAbsoluteUrl(legend.items.get(i).url));
+                };
+                enc[0].classes[0] = {
+                    name: "",
+                    icons: icons
+                };
+                return enc;
+            },
+            "gx_urllegend": function(legend) {
+                var enc = this.encoders.legends.base.call(this, legend);
+                enc[0].classes.push({
+                    name: "",
+                    icon: this.getAbsoluteUrl(legend.items.get(1).url)
+                });
+                return enc;
+            },
+            "base": function(legend){
+                return [{
+                    name: legend.items.get(0).text,
+                    classes: []
+                }];
+            }
+        }
+    }
+    
+});


Property changes on: core/trunk/geoext/lib/GeoExt/data/PrintProvider.js
___________________________________________________________________
Name: svn:keywords
   + Id Author Date Revision
Name: svn:eol-style
   + native

Modified: core/trunk/geoext/lib/GeoExt.js
===================================================================
--- core/trunk/geoext/lib/GeoExt.js	2010-01-21 20:10:57 UTC (rev 1801)
+++ core/trunk/geoext/lib/GeoExt.js	2010-01-22 08:08:01 UTC (rev 1802)
@@ -107,7 +107,9 @@
             "GeoExt/widgets/WMSLegend.js",
             "GeoExt/widgets/LegendPanel.js",
             "GeoExt/widgets/ZoomSlider.js",
-            "GeoExt/widgets/grid/FeatureSelectionModel.js"
+            "GeoExt/widgets/grid/FeatureSelectionModel.js",
+            "GeoExt/data/PrintPage.js",
+            "GeoExt/data/PrintProvider.js"
         );
 
         var agent = navigator.userAgent;

Added: core/trunk/geoext/tests/lib/GeoExt/data/PrintPage.html
===================================================================
--- core/trunk/geoext/tests/lib/GeoExt/data/PrintPage.html	                        (rev 0)
+++ core/trunk/geoext/tests/lib/GeoExt/data/PrintPage.html	2010-01-22 08:08:01 UTC (rev 1802)
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html debug="true">
+  <head>
+    <script type="text/javascript" src="../../../../../openlayers/lib/OpenLayers.js"></script>
+    <script type="text/javascript" src="../../../../../ext/adapter/ext/ext-base.js"></script>
+    <script type="text/javascript" src="../../../../../ext/ext-all-debug.js"></script>
+    <script type="text/javascript" src="../../../../lib/GeoExt.js"></script>
+
+    <script type="text/javascript">
+        var printCapabilities = {"scales":[{"name":"1:25,000","value":"25000"},{"name":"1:50,000","value":"50000"},{"name":"1:100,000","value":"100000"},{"name":"1:200,000","value":"200000"},{"name":"1:500,000","value":"500000"},{"name":"1:1,000,000","value":"1000000"},{"name":"1:2,000,000","value":"2000000"},{"name":"1:4,000,000","value":"4000000"}],"dpis":[{"name":"75","value":"75"},{"name":"150","value":"150"},{"name":"300","value":"300"}],"layouts":[{"name":"A4 portrait","map":{"width":440,"height":483},"rotation":true},{"name":"Legal","map":{"width":440,"height":483},"rotation":false}],"printURL":"http://demo.opengeo.org/geoserver/pdf/print.pdf","createURL":"http://demo.opengeo.org/geoserver/pdf/create.json"};
+        
+        function test_constructor(t) {
+            t.plan(4);
+            var log = {};
+            
+            var printProvider = new GeoExt.data.PrintProvider({
+                capabilities: printCapabilities
+            });
+            var printPage = new GeoExt.data.PrintPage({
+                printProvider: printProvider,
+                onLayoutChange: function() {log.layoutchange = true;}
+            });
+            
+            t.ok(printPage.feature, "feature initialized properly.");
+            t.eq(printPage.handles.length, 4, "handles initialized properly.");
+            t.eq(printPage.customParams, {}, "customParam initialized properly.");
+            
+            printProvider.setLayout(printProvider.layouts.getAt(1));
+            t.eq(log.layoutchange, true, "onLayoutChange called when printProvider's layout changes.");
+            
+            printPage.destroy();
+        }
+        
+        function test_setCenter(t) {
+            t.plan(2);
+            
+            var printPage = new GeoExt.data.PrintPage({
+                printProvider: new GeoExt.data.PrintProvider({
+                    capabilities: printCapabilities
+                })
+            });
+            
+            var center = new OpenLayers.LonLat(10, 11);
+            printPage.setCenter(center);
+            t.eq(printPage.getCenter().toString(), center.toString(), "center set correctly.");
+            t.geom_eq(printPage.handles[0].geometry, printPage.feature.geometry.components[0].components[0], "handle updated correctly.");
+            
+            printPage.destroy();
+        }
+        
+        function test_setScale(t) {
+            t.plan(3);
+            
+            var printProvider = new GeoExt.data.PrintProvider({
+                capabilities: printCapabilities
+            });
+            var printPage = new GeoExt.data.PrintPage({
+                printProvider: printProvider
+            });
+            
+            var scale = printProvider.scales.getAt(1);
+            printPage.setScale(scale, "m");
+            t.eq(printPage.scale.get("value"), scale.get("value"), "scale property of the print page set correctly.");
+            
+            var printSize = printProvider.layout.get("size");
+            var expectedArea = printSize.x * printSize.y;
+            t.eq(parseInt(printPage.handles[0].geometry.distanceTo(printPage.handles[1].geometry) * 72 * OpenLayers.INCHES_PER_UNIT["m"] / scale.get("value")), printSize.width, "width of the print feature is correct.");
+            t.eq(parseInt(printPage.handles[1].geometry.distanceTo(printPage.handles[2].geometry) * 72 * OpenLayers.INCHES_PER_UNIT["m"] / scale.get("value")), printSize.height, "height of the print feature is correct.");
+            
+            printPage.destroy();
+        }
+        
+        function test_setRotation(t) {
+            t.plan(3);
+
+            var printPage = new GeoExt.data.PrintPage({
+                printProvider: new GeoExt.data.PrintProvider({
+                    capabilities: printCapabilities
+                })
+            });
+            
+            printPage.setRotation(90);
+            t.eq(printPage.rotation, 90, "rotation set correctly.");
+            t.eq(printPage.handles[0].geometry.x.toPrecision(8), printPage.handles[1].geometry.x.toPrecision(8), "x-coords of handle geometries show that the extent is rotated by 90 degrees.");
+            t.eq(printPage.handles[1].geometry.y.toPrecision(8), printPage.handles[2].geometry.y.toPrecision(8), "y-coords of handle geometries show that the extent is rotated by 90 degrees.");
+            
+            printPage.destroy();
+        }
+        
+        function test_fit(t) {
+            t.plan(3);
+            
+            var center = new OpenLayers.LonLat(146.56, -41.56);
+            var mapPanel = new GeoExt.MapPanel({
+                renderTo: "map",
+                width: 400,
+                height: 300,
+                layers: [new OpenLayers.Layer.WMS("wms", "http://demo.opengeo.org/geoserver/wms", {layers: "topp:tasmania_water_bodies"})],
+                center: center,
+                zoom: 7
+            });
+
+            var printPage = new GeoExt.data.PrintPage({
+                printProvider: new GeoExt.data.PrintProvider({
+                    capabilities: printCapabilities
+                })
+            });
+
+            printPage.fit(mapPanel);
+            t.eq(printPage.getCenter().toString(), center.toString(), "Print page centered correctly.");
+            t.eq(printPage.scale.get("value"), 2000000, "Print scale set correctly.");
+
+            printPage.fit(mapPanel, true);
+            t.eq(printPage.scale.get("value"), 4000000, "Print scale for 'loose' option set correctly.");
+            
+            printPage.destroy();
+            mapPanel.destroy();
+        }
+        
+        function test_updateByHandle(t) {
+            t.plan(4);
+            var printPage = new GeoExt.data.PrintPage({
+                printProvider: new GeoExt.data.PrintProvider({
+                    capabilities: printCapabilities
+                })
+            });
+
+            // position a handle so it should rotate the extent by 45 degrees and scale it
+            // to twice its size
+            printPage.handles[0].geometry.y = 0;
+            printPage.handles[0].geometry.x = -4.5;
+            expectedOppositeHandleGeom = new OpenLayers.Geometry.Point(4.5, 0);
+            
+            printPage.updateByHandle(printPage.handles[0]);
+            t.eq(printPage.rotation, 45, "Correctly rotated 45 degrees by handle.");
+            t.eq(printPage.scale.get("value"), 4000000, "Correctly scaled by handle to 1:4000000");
+            t.geom_eq(printPage.handles[2].geometry, expectedOppositeHandleGeom, "Opposite handle not repositioned if updateHandles argument is not set.");
+
+            printPage.updateByHandle(printPage.handles[0], true);
+            t.ok(!printPage.handles[2].geometry.equals(expectedOppositeHandleGeom), "Opposite handle repositioned when updateHandles argument is set to true.");
+        }
+        
+    </script>
+  </head>  
+  <body>
+    <div id="map"></div>
+  </body>
+</html>


Property changes on: core/trunk/geoext/tests/lib/GeoExt/data/PrintPage.html
___________________________________________________________________
Name: svn:keywords
   + Id Author Date Revision
Name: svn:eol-style
   + native

Added: core/trunk/geoext/tests/lib/GeoExt/data/PrintProvider.html
===================================================================
--- core/trunk/geoext/tests/lib/GeoExt/data/PrintProvider.html	                        (rev 0)
+++ core/trunk/geoext/tests/lib/GeoExt/data/PrintProvider.html	2010-01-22 08:08:01 UTC (rev 1802)
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<html debug="true">
+  <head>
+    <script type="text/javascript" src="../../../../../openlayers/lib/OpenLayers.js"></script>
+    <script type="text/javascript" src="../../../../../ext/adapter/ext/ext-base.js"></script>
+    <script type="text/javascript" src="../../../../../ext/ext-all-debug.js"></script>
+    <script type="text/javascript" src="../../../../lib/GeoExt.js"></script>
+
+    <script type="text/javascript">
+        var printCapabilities = {"scales":[{"name":"1:25,000","value":"25000"},{"name":"1:50,000","value":"50000"},{"name":"1:100,000","value":"100000"},{"name":"1:200,000","value":"200000"},{"name":"1:500,000","value":"500000"},{"name":"1:1,000,000","value":"1000000"},{"name":"1:2,000,000","value":"2000000"},{"name":"1:4,000,000","value":"4000000"}],"dpis":[{"name":"75","value":"75"},{"name":"150","value":"150"},{"name":"300","value":"300"}],"layouts":[{"name":"A4 portrait","map":{"width":440,"height":483},"rotation":true},{"name":"Legal","map":{"width":440,"height":483},"rotation":false}],"printURL":"http://demo.opengeo.org/geoserver/pdf/print.pdf","createURL":"http://demo.opengeo.org/geoserver/pdf/create.json"};
+        
+        function test_constructor(t) {
+            t.plan(7);
+            var log = {};
+            
+            var origRequest = Ext.Ajax.request;
+            Ext.Ajax.request = function(req) {
+                req.success.call(req.scope, {responseText: Ext.encode(printCapabilities)});
+            }
+            var printProvider = new GeoExt.data.PrintProvider({
+                url: "PrintProvider",
+                listeners: {
+                    "loadcapabilities": function() {
+                        log.loadcapabilities = arguments;
+                    }
+                }
+            });
+            Ext.Ajax.request = origRequest;
+            
+            t.eq(printProvider.customParams, {}, "customParams initialized properly.");
+            t.ok(log.loadcapabilities[0] == printProvider, "printProvider passed as 1st argument of the loadcapabilities listener.");
+            t.ok(log.loadcapabilities[1] == printProvider.capabilities, "capabilities passed as 2nd argument of the loadcapabilities listener.");
+            t.eq(printProvider.capabilities.createURL, "http://demo.opengeo.org/geoserver/pdf/create.json", "capabilities available and createURL correct after loadcapabilities event");
+            t.ok(printProvider.layout == printProvider.layouts.getAt(0), "layout set to first available layout record.");
+            t.ok(printProvider.dpi == printProvider.dpis.getAt(0), "dpi set to first available dpi record.");
+            t.eq(printProvider.scales.getCount(), 8, "8 scales read in properly.");
+        }
+        
+        function test_setLayout(t) {
+            t.plan(3);
+            var log = {};
+            var printProvider = new GeoExt.data.PrintProvider({
+                capabilities: printCapabilities,
+                listeners: {
+                    "layoutchange": function() {
+                        log.layoutchange = arguments;
+                    }
+                }
+            });
+            var layout = printProvider.layouts.getAt(1);
+            printProvider.setLayout(layout);
+            t.ok(printProvider.layout == layout, "layout set correctly.");
+            t.ok(log.layoutchange[0] == printProvider, "printProvider passed as 1st argment of the layoutchange listener.");
+            t.ok(log.layoutchange[1] == layout, "layout passed as 2nd argment of the layoutchange listener.");
+        }
+
+        function test_setDpi(t) {
+            t.plan(3);
+            var log = {};
+            var printProvider = new GeoExt.data.PrintProvider({
+                capabilities: printCapabilities,
+                listeners: {
+                    "dpichange": function() {
+                        log.dpichange = arguments;
+                    }
+                }
+            });
+            var dpi = printProvider.dpis.getAt(1);
+            printProvider.setDpi(dpi);
+            t.ok(printProvider.dpi == dpi, "dpi set correctly.");
+            t.ok(log.dpichange[0] == printProvider, "printProvider passed as 1st argment of the dpichange listener.");
+            t.ok(log.dpichange[1] == dpi, "dpi passed as 2nd argment of the dpichange listener.");
+        }
+        
+        function test_getAbsoluteUrl(t) {
+            t.plan(2);
+            
+            var getAbsoluteUrl = GeoExt.data.PrintProvider.prototype.getAbsoluteUrl;
+            var baseUrl = parent.location.href.substr(0, parent.location.href.indexOf("/tests/run-tests.html"));
+            t.eq(getAbsoluteUrl("/foo/bar.html"), location.protocol + "//" + location.host + (location.port && ":" + location.port) + "/foo/bar.html", "Relative url converted to absolute url correctly.");
+            t.eq(getAbsoluteUrl("../../../../bar.html"), baseUrl + "/bar.html", "Relative url with relative path converted to absolute url correctly.");
+        }
+        
+        function test_print(t) {
+            t.plan(1);
+            var log = {};
+            var printProvider = new GeoExt.data.PrintProvider({
+                capabilities: printCapabilities,
+                customParams: {customParam: "foo"},
+                listeners: {
+                    "dpichange": function() {
+                        log.dpichange = arguments;
+                    }
+                }
+            });
+            var printPage = new GeoExt.data.PrintPage({
+                printProvider: printProvider,
+                customParams: {mapTitle: "foo", comment: "bar"}
+            });
+            var map = new OpenLayers.Map("map");
+            
+            // layers to test all supported layer encoders
+            var layers = [
+                new OpenLayers.Layer.WMS("wms", "http://demo.opengeo.org/geoserver/wms", {
+                    layers: ["topp:tasmania_state_boundaries","topp:tasmania_water_bodies"], format: "image/gif", vendorFoo: "bar"
+                }),
+                new OpenLayers.Layer.OSM("osm", {isBaseLayer: false}),
+                new OpenLayers.Layer.TileCache("tilecache",
+                    ["http://c0.labs.metacarta.com/wms-c/cache/",
+                     "http://c1.labs.metacarta.com/wms-c/cache/",
+                     "http://c2.labs.metacarta.com/wms-c/cache/",
+                     "http://c3.labs.metacarta.com/wms-c/cache/",
+                     "http://c4.labs.metacarta.com/wms-c/cache/"],
+                    "basic",
+                    {
+                        serverResolutions: [0.703125, 0.3515625, 0.17578125, 0.087890625, 
+                                            0.0439453125, 0.02197265625, 0.010986328125, 
+                                            0.0054931640625, 0.00274658203125, 0.001373291015625, 
+                                            0.0006866455078125, 0.00034332275390625, 0.000171661376953125, 
+                                            0.0000858306884765625, 0.00004291534423828125, 0.000021457672119140625],
+                        isBaseLayer: false
+                    }
+                ),
+                new OpenLayers.Layer.Image(
+                    "image",
+                    "http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif",
+                    new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
+                    new OpenLayers.Size(580, 288),
+                    {numZoomLevels: 3, isBaseLayer: false}
+                ),
+                new OpenLayers.Layer.Vector("vector")
+            ];
+            map.addLayers(layers);
+            
+            // give the vector layer a feature
+            map.getLayersBy("name", "vector")[0].addFeatures([new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2))]);
+            
+            map.setCenter(new OpenLayers.LonLat(1, 2), 3);
+            printPage.fit(map);
+            
+            // create a layer store for the legend
+            var layerStore = new GeoExt.data.LayerStore({
+                layers: [layers[0], layers[1]]
+            });
+            layerStore.getAt(1).set("legendURL", "http://trac.geoext.org/chrome/site/img/GeoExt.png");
+            
+            // a legend panel to test all legend encoders
+            var legend = new GeoExt.LegendPanel({
+                renderTo: "legend",
+                layerStore: layerStore
+            });
+            
+            var origRequest = Ext.Ajax.request;
+            Ext.Ajax.request = function(req) {
+                log.request = req;
+                var _open = window.open;
+                window.open = Ext.emptyFn;
+                req.success.call(req.scope, {
+                    responseText: '{"getURL":"foo"}'
+                });
+                window.open = _open;
+            }
+            printProvider.print(map, [printPage], {legend: legend});
+            Ext.Ajax.request = origRequest;
+            
+            var expect = {"units":"degrees","srs":"EPSG:4326","layout":"A4 portrait","dpi":75,"layers":[{"baseURL":"http://demo.opengeo.org/geoserver/wms","opacity":1,"singleTile":false,"type":"WMS","layers":["topp:tasmania_state_boundaries","topp:tasmania_water_bodies"],"format":"image/gif","styles":[""],"customParams":{"VENDORFOO":"bar"}},{"baseURL":"http://c0.labs.metacarta.com/wms-c/cache/","opacity":1,"singleTile":false,"type":"TileCache","layer":"basic","maxExtent":[-180,-90,180,90],"tileSize":[256,256],"extension":"png","resolutions":[0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125,0.000021457672119140625]},{"type":"Image","baseURL":"http://earthtrends.wri.org/images/maps/4_m_citylights_lg.gif","opacity":1,"extent":[-180,-88.759,180,88.759],"pixelSize":[580,288],"name":"image"},{"type":"Vector","styles":{"1":{"fillColor":"#ee9900","fillOpacity":0.4,"hoverFillColor":"white","hoverFillOpacity":0.8,"strokeColor":"#ee9900","strokeOpacity":1,"strokeWidth":1,"strokeLinecap":"round","strokeDashstyle":"solid","hoverStrokeColor":"red","hoverStrokeOpacity":1,"hoverStrokeWidth":0.2,"pointRadius":6,"hoverPointRadius":1,"hoverPointUnit":"%","pointerEvents":"visiblePainted","cursor":"inherit"}},"styleProperty":"_gx_style","geoJson":{"type":"FeatureCollection","features":[{"type":"Feature","id":"OpenLayers.Feature.Vector_72","properties":{"_gx_style":1},"geometry":{"type":"Point","coordinates":[1,2]}}]},"name":"vector","opacity":1}],"pages":[{"mapTitle":"foo","comment":"bar","center":[1,2],"scale":4000000,"rotation":0}],"customParam":"foo","legends":[{"name":"osm","classes":[{"name":"","icon":"http://trac.geoext.org/chrome/site/img/GeoExt.png"}]},{"name":"wms","classes":[{"name":"","icons":["http://demo.opengeo.org/geoserver/wms?FORMAT=image%2Fgif&VENDORFOO=bar&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&EXCEPTIONS=application%2Fvnd.ogc.se_xml&LAYER=topp%3Atasmania_state_boundaries","http://demo.opengeo.org/geoserver/wms?FORMAT=image%2Fgif&VENDORFOO=bar&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&EXCEPTIONS=application%2Fvnd.ogc.se_xml&LAYER=topp%3Atasmania_water_bodies"]}]}]};
+
+            t.eq(log.request.jsonData, expect, "Request with encoded layers and legend is correct.");
+            
+            layerStore.destroy();
+            printPage.destroy();
+            map.destroy();
+        }
+    </script>
+  </head>  
+  <body>
+    <div id="map" style="width:400px; height:300px"></div>
+    <div id="legend" style="width:200px; height:300px"></div>
+  </body>
+</html>


Property changes on: core/trunk/geoext/tests/lib/GeoExt/data/PrintProvider.html
___________________________________________________________________
Name: svn:keywords
   + Id Author Date Revision
Name: svn:eol-style
   + native

Modified: core/trunk/geoext/tests/list-tests.html
===================================================================
--- core/trunk/geoext/tests/list-tests.html	2010-01-21 20:10:57 UTC (rev 1801)
+++ core/trunk/geoext/tests/list-tests.html	2010-01-22 08:08:01 UTC (rev 1802)
@@ -7,6 +7,8 @@
   <li>lib/GeoExt/data/LayerRecord.html</li>
   <li>lib/GeoExt/data/LayerReader.html</li>
   <li>lib/GeoExt/data/LayerStore.html</li>
+  <li>lib/GeoExt/data/PrintPage.html</li>
+  <li>lib/GeoExt/data/PrintProvider.html</li>
   <li>lib/GeoExt/data/ScaleStore.html</li>
   <li>lib/GeoExt/data/ProtocolProxy.html</li>
   <li>lib/GeoExt/data/WFSCapabilitiesReader.html</li>



More information about the Commits mailing list