Source: api/Printer.js

Retour à la documentation
define([
        //mapped libs
        "dojo/_base/declare", "dijit/_WidgetBase", "dojo/_base/array", "dojo/_base/lang",
        "spw/api/SpwViewer", "dojo/query", "dojo/dom-style", "dojo/dom-geometry",
        "dojo/dom-construct", "dojo/dom-attr", "spw/api/MessageManager", "esri/tasks/PrintTask",
        "esri/tasks/PrintTemplate", "esri/tasks/PrintParameters", "dojo/Deferred", "esri/geometry/Point", "dojo/on", "esri/geometry/Extent",
        "dojo/_base/config", "spw/libs/html2canvas",  "spw/api/Utils",
        //unmapped libs
        "spw/libs/rgbcolor","spw/libs/StackBlur",
        "spw/libs/canvg", "dojo/NodeList-traverse", "spw/libs/es6-promise.min", "spw/libs/es6-promise.auto.min"],

    function (declare, _WidgetBase, array, lang, SpwViewer, query, domStyle, geom,
              domConstruct, domAttr, MessageManager, PrintTask, PrintTemplate, PrintParameters,
              Deferred, Point, on, Extent, dojoConfig, html2canvas, Utils) {

        var Printer = declare("spw.api.Printer", [_WidgetBase], /** @lends spw.api.Printer */{ 


            /**
             * Permet de définir si l'image doit être redimensionnée ou coupée.
             * @public
             * @instance
             * @type Boolean
             */
            crop: true,

            /**
             * Element du DOM à capturer
             * @public
             * @instance
             * @type DomElement
             */
            domToPrint: null,

            /**
             * Fonction de retour lorsque l'image a été générée
             * @private
             * @instance
             * @type Function
             */
            successHandler: null,

            /**
             * Longueur de l'image à générer en pixel
             * @private
             * @instance
             * @type Integer
             */
            imageWidth: 400,

            /**
             * Hauteur de l'image à générer en pixel
             * @private
             * @instance
             * @type Integer
             */
            imageHeight: 400,

            stylesToReset: [],

            currentReportParams: null,

            nodesToReposition: ['.esriControlsBR'],

            /**
             * @constructs
             * @extends {dijit._WidgetBase}
             * @classdesc Classe utilitaire pour gérer l'impression de la carte.
             * @param {Object} config
             * @param {Boolean} config.crop {@link spw.api.Printer#crop}
             * @param {DomElement} config.domToPrint {@link spw.api.Printer#domToPrint}
             */
            constructor: function(config){
                this.domToPrint = SpwViewer.getInstance().get('spwMap').get('esriMap').root;
            },

            /**
             * Permet de générer l'image de la carte sur base d'un PrintTask type Esri.
             * @inner
             * @param serverUrl {String} L'url du serveur "PrintTask" Esri
             * @param imageWidth {Integer} Longueur de l'image à générer
             * @param imageHeight {Integer} Hauteur de l'image à générer
             * @param successHandler {Function} Callback de retour en cas de réussite
             * @param errorHandler {Function} Callback de retour en cas d'échec
             */
            getImagePrintTask: function(serverUrl, imageWidth, imageHeight, successHandler, errorHandler){
                var printTask = new PrintTask(serverUrl);

                var template = new PrintTemplate();
                template.exportOptions = {
                    width: imageWidth,
                    height: imageHeight,
                    dpi: 96
                };
                template.format = "png32";

                var params = new PrintParameters();
                params.map = SpwViewer.getInstance().get('spwMap').get('esriMap');
                params.outSpatialReference = SpwViewer.getInstance().get('spatialReference');
                params.template = template;

                printTask.execute(params, successHandler, errorHandler);
            },
            
            beforeGetImage: function(){
                this.oldCenterMap = SpwViewer.getInstance().get('spwMap').getCurrentExtent().getCenter()
            },

            afterGetImage: function(){},

            /**
             * Permet de générer une image de l'élément passé en paramètre via la balise Canvas HTML5
             * @param {DomElement} domToPrint l'élément à capturer
             * @param {Number} imageWidth la largeur de l'image
             * @param {Number} imageHeight la hauteur de l'image
             * @param crop {Boolean} si l'image doit être rognée
             * @param successHandler le handler en cas de réussite.
             */
            getImage: function(domToPrint, imageWidth, imageHeight, crop, extent, successHandler, rosaceUrl, printComesFromIdentify) {
            	this.beforeGetImage();
                var templateItem = {imageWidth: imageWidth, imageHeight: imageHeight};
                if(rosaceUrl == null){
                    rosaceUrl = "./images/templates/rosaces.png";
                }

                this.stylesToReset.length = 0;

                // compatibilité ?
                var oldExtent = SpwViewer.getInstance().get('spwMap').getCurrentExtent();

                if (successHandler == null && typeof(extent) === 'function') {
                    successHandler = extent;
                    extent = null;
                }

                if(successHandler){
                    this.successHandler = successHandler;
                } else {
                    console.error("No print callback. Printing is cancelled.");
                    return;
                }
                if(domToPrint){
                    this.domToPrint = domToPrint;
                }
                if(imageWidth){
                    this.imageWidth = imageWidth;
                }
                if(imageHeight){
                    this.imageHeight = imageHeight;
                }
                this.crop = crop;

                if(printComesFromIdentify){
                    MessageManager.getInstance().displayModalMessage("Nous préparons votre rapport.<br>Cela peut prendre quelques secondes…");
                }else{
                    MessageManager.getInstance().displayModalMessage("Nous préparons votre carte.<br>Cela peut prendre quelques secondes…");
                }

                if(!this.crop){
                    this.renderMapCanvas(null, rosaceUrl, templateItem);
                } else {
                    var spwMap = SpwViewer.getInstance().get('spwMap');

                    this.recalculateMap().then(lang.hitch(this, function(domItemOverflowed) {
                        if (spwMap.isUpdating()) {
                            var mapUpdateEndHandler = spwMap.on(spwMap.events.MapUpdateEnd, lang.hitch(this, function(){
                                if (!spwMap.isUpdating()) {
                                    domStyle.set(SpwViewer.getInstance().get('spwMap').domNode, {
                                        top: 0,
                                        left: 0
                                    });

                                    this.renderMapCanvas(domItemOverflowed, rosaceUrl, templateItem).then(lang.hitch(this, function(canvas) {
                                        MessageManager.getInstance().hideModalMessage();
                                        successHandler(canvas);
                                    	this.afterGetImage();
                                    }));

                                    mapUpdateEndHandler.remove();
                                }
                            }));
                        }
                        else {
                            domStyle.set(SpwViewer.getInstance().get('spwMap').domNode, {
                                top: 0,
                                left: 0
                            });

                            this.renderMapCanvas(domItemOverflowed, rosaceUrl,templateItem).then(lang.hitch(this, function(canvas) {
                                MessageManager.getInstance().hideModalMessage();
                                successHandler(canvas);
                            	this.afterGetImage();
                            }));
                        }
                    }));

                }
            },

            resetWidgets: function() {
                this.ws = SpwViewer.getInstance().spwWidgetsManager.getWidgets({
                    activated: true,
                    position: 'map'
                });
                array.forEach(this.ws, lang.hitch(this, function(w) {
                    w.hideOnPrint && domStyle.set(w.domNode, 'display', '');

                    if (w.mapPosition) {
                        domStyle.set(w.domNode, w.mapPosition);
                    }
                }));
            },

            resetNodes: function() {
                array.forEach(this.nodesToReposition, lang.hitch(this, function(nodeToReposition) {
                    var node = query(nodeToReposition)[0];

                    if (node && this.nodesOriginalPosition[nodeToReposition]) {
                        domStyle.set(node, this.nodesOriginalPosition[nodeToReposition]);
                    }
                }));
            },

            repositionWidgets: function(imageWidth, imageHeight) {
                var spwMap = SpwViewer.getInstance().get('spwMap');

                var overflowW = (imageWidth - spwMap.getWidth()) / 2;
                var overflowH = (imageHeight - spwMap.getHeight()) / 2;

                this.ws = SpwViewer.getInstance().spwWidgetsManager.getWidgets({
                    activated: true,
                    position: 'map'
                });

                array.forEach(this.ws, lang.hitch(this, function(w) {
                    w.hideOnPrint && domStyle.set(w.domNode, 'display', 'none');

                    var mapPosition = w.mapPosition;

                    if (mapPosition) {
                        var top = parseInt(mapPosition.top);
                        var bottom = parseInt(mapPosition.bottom);
                        var right = parseInt(mapPosition.right);
                        var left = parseInt(mapPosition.left);

                        var style = {
                            top: isNaN(top) ? null : top + 'px',
                            left: isNaN(left) ? null : left + 'px',
                            right: isNaN(right) ? null : right + 'px',
                            bottom: isNaN(bottom) ? null : bottom + 'px'
                        };

                        if (overflowH < 0) {
                            if (!isNaN(bottom)) {
                                style.bottom = (bottom - overflowH) + 'px';
                            }
                            else if (!isNaN(top)) {
                                style.top = (top - overflowH) + 'px';
                            }
                        }

                        if (overflowW < 0) {
                            if (!isNaN(right)) {
                                style.right = (right - overflowW) + 'px';
                            }
                            else if (!isNaN(left)) {
                                style.left = (left - overflowW) + 'px';
                            }
                        }

                        domStyle.set(w.domNode, style);
                    }
                }));

                this.nodesOriginalPosition = {};

                array.forEach(this.nodesToReposition, lang.hitch(this, function(nodeToReposition) {
                    var node = query(nodeToReposition)[0];

                    if (node) {
                        var top = parseInt(domStyle.get(node, 'top'));
                        var bottom = parseInt(domStyle.get(node, 'bottom'));
                        var right = parseInt(domStyle.get(node, 'right'));
                        var left = parseInt(domStyle.get(node, 'left'));

                        var style = {
                            top: isNaN(top) ? null : top + 'px',
                            left: isNaN(left) ? null : left + 'px',
                            right: isNaN(right) ? null : right + 'px',
                            bottom: isNaN(bottom) ? null : bottom + 'px'
                        };

                        if (style.top && style.bottom) {
                            delete style.top;
                        }

                        if (style.left && style.right) {
                            delete style.left;
                        }

                        this.nodesOriginalPosition[nodeToReposition] = lang.clone(style);

                        if (overflowH < 0) {
                            if (!isNaN(bottom)) {
                                style.bottom = (bottom - overflowH) + 'px';
                            }
                            else if (!isNaN(top)) {
                                style.top = (top - overflowH) + 'px';
                            }
                        }

                        if (overflowW < 0) {
                            if (!isNaN(right)) {
                                style.right = (right - overflowW) + 'px';
                                delete style.left;
                            }
                            else if (!isNaN(left)) {
                                style.left = (left - overflowW) + 'px';
                            }
                        }

                        domStyle.set(node, style);
                    }
                }));
            },

            /**
             * Transforme les éléments HTML en Canvas.
             * @param domItemOverflowed les élements HTML à transformer
             */
            renderMapCanvas: function(domItemOverflowed, rosaceUrl, template) {
                var def = new Deferred();
                this.repositionWidgets(template.imageWidth, template.imageHeight);
                //var canvases = this._createSvgCanvases();

                //timeout of 2 sec is set to ensure graphics are redraw on client screen => should be changed by the proper event (map-redrawed, ...)
                setTimeout(function(){
                    query("img", this.domToPrint).forEach(function(i){
                        if((!i.style.opacity && i.style.opacity !== 0) && (i.parentNode.style.opacity || i.parentNode.style.opacity === 0)){
                            i.style.opacity = i.parentNode.style.opacity;
                            i.tempOpacity = true;
                        }
                    });
                    html2canvas(this.domToPrint, {
                        allowTaint: false,
                        logging: false,
                        proxy: SpwViewer.getInstance().get('proxyPageUrl'),
                        useCORS: true
                    }).then(function(canvas){

                        this.resetMap(domItemOverflowed);

                        //query('svg').forEach(function(svgTag){svgTag.style.display = svgTag['data-display'];delete svgTag['data-display'];});
                        //array.forEach(canvases, function(c){domConstruct.destroy(c);});
                        query("img", this.domToPrint).forEach(function(i){
                            if(i.tempOpacity){
                                delete i.style.opacity;
                                delete i.tempOpacity;
                            }
                        });
                        this._cropCanvas(canvas).then(lang.hitch(this, function(newCanvas) {
                            var isIe = Utils.testNavigatorIsIE();
                            if (isIe) {
                                require(["spw/libs/canvg"], function() {
                                    var xmlSerializer = new XMLSerializer();
                                    var spwMap = SpwViewer.getInstance().get('spwMap');
                                    var overflowW = (template.imageWidth - spwMap.getWidth()) / 2;
                                    var overflowH = (template.imageHeight - spwMap.getHeight()) / 2;

                                    var canvasbis = document.createElement('canvas');
                                    canvasbis.width = newCanvas.width;
                                    canvasbis.height = newCanvas.height;

                                    query('svg', this.domToPrint).forEach(function(svg) {
                                        var stringSvg = xmlSerializer.serializeToString(svg);
                                        canvg(canvasbis, stringSvg);
                                    });

                                    var destCtx = newCanvas.getContext('2d');
                                    destCtx.drawImage(canvasbis, overflowW, overflowH);

                                    this.getFinalImageCanvas(rosaceUrl, def, newCanvas);
                                }.bind(this))
                            } else {
                                this.getFinalImageCanvas(rosaceUrl, def, newCanvas);
                            }
                        }));
                    }.bind(this));
                }.bind(this), 3000);
                return def;
            },

            getFinalImageCanvas: function(rosaceUrl, def, newCanvas) {
                var _rosaceUrl = './images/templates/rosaces.png';
                if(rosaceUrl){
                    _rosaceUrl = rosaceUrl;
                } else if(rosaceUrl === ""){
                    this.resetWidgets();
                    this.resetNodes();
                    def.resolve(newCanvas);
                    return;
                }
                try{
                    this.resetWidgets();
                    this.resetNodes();
                    var img = domConstruct.create('img', {
                        'src': _rosaceUrl
                    }, this.domNode);
                    var ctx = newCanvas.getContext('2d');
                    img.onload = lang.hitch(this, function () {
                        ctx.drawImage(img, -5, newCanvas.height - 67, 60, 60);
                        def.resolve(newCanvas);
                    })
                    img.onerror = lang.hitch(this, function(){
                        def.resolve(newCanvas);
                    })
                }catch(e){
                    this.resetWidgets();
                    this.resetNodes();
                    def.resolve(newCanvas);
                }
            },

            /**
             * Transform les éléments SVG en Canvas
             * @returns {Array}
             */
            _createSvgCanvases: function() {
                var canvases = [];
                query('svg').forEach(function(svgTag){
                    var c = domConstruct.create('canvas', {style:'position: absolute;'}, svgTag, "after");
                    canvases.push(c);

                    var divSvg = domConstruct.create("div");
                    var clonedSvg = lang.clone(svgTag);
                    domAttr.set(clonedSvg, "xmlns:xlink","http://www.w3.org/1999/xlink");
                    divSvg.appendChild(clonedSvg);

                    canvg(c, divSvg.innerHTML);

                    svgTag['data-display'] = svgTag.style.display;
                    svgTag.style.display = 'none';
                });
                return canvases;
            },

            /**
             * Recalcule la taille de la carte en fonction de la taille de l'image demandée.
             * @returns {Array}
             */
            recalculateMap: function() {
                var spwMap = SpwViewer.getInstance().get('spwMap');
                var overflowW = this.imageWidth - spwMap.getWidth();
                var overflowH = this.imageHeight - spwMap.getHeight();

                var node = spwMap.domNode;

                var absoluteXY = {
                    x: 0,
                    y: 0
                };

                if (overflowW < 0) {
                    overflowW = 0;
                }

                if (overflowH < 0) {
                    overflowH = 0;
                }

                this.stylesToReset.push({
                    node: node,
                    style: {
                        position: domStyle.get(node, 'position'),
                        left: domStyle.get(node, 'left') + 'px',
                        top: domStyle.get(node, 'top') + 'px',
                        width: domStyle.get(node, 'width') + 'px',
                        height: domStyle.get(node, 'height') + 'px'
                    }
                });

                domStyle.set(node, {
                    position: 'absolute',
                    left: absoluteXY.x + 'px',
                    top: absoluteXY.y + 'px',
                    width: spwMap.getWidth() + overflowW + 'px',
                    height: spwMap.getHeight() + overflowH + 'px'
                });

                var domItemOverflowed = new Array();

                query(SpwViewer.getInstance().get('spwMap').domNode).parents().forEach(function(item){
                    var overflowProp = domStyle.get(item, "overflow");
                    if(overflowProp == "hidden"){
                        domItemOverflowed.push([item, overflowProp]);
                        domStyle.set(item, "overflow", "visible");
                    }
                });

                var def = new Deferred();

                spwMap.esriMap._decr();
                var isUpdated= false;
                on.once(spwMap.esriMap,'update-start',lang.hitch(this,function(){
                    isUpdated=true;
                    on.once(spwMap.esriMap, 'update-end', lang.hitch(this, function() {
                        this.resizeAndCenterMap(def, spwMap, overflowW, overflowH, domItemOverflowed);
                    }));
                }))
                spwMap.resize();
                spwMap.esriMap.resize(true);
                spwMap.esriMap.reposition();
                if(!isUpdated){
                    this.resizeAndCenterMap(def, spwMap, overflowW, overflowH,domItemOverflowed);
                }
                return def;
            },



            //PARAMS:
                //jasperServerUrl: jasper server url
                //jasperTemplate: jasper template in his XML form
                //Data: an array representing the data to include in the template: [{label:'', level: '', symbol: ''}]
                //Properties: properties of the template
                //options: options for the report: logoWallon: picture to show in header, header1: ?, rosaceUrl: url of rosaces pictures
            // downloadReport: function(jasperServerUrl, jasperTemplate, Data, Properties, options) {
            downloadReport: function(jasperServerUrl, jasperTemplate, Data, options) {
                var defaultOptions = {
                    logoWallon: "images/templates/logoWallon2.png",
                    header1: "Géoportail de la Wallonie",
                    rosaceUrl: './images/templates/rosaces.png'
                };
                var opt = lang.mixin(defaultOptions, options);

                var defaultProp = {
                    url: dojoConfig.geoviewerApiUrl + "/templates/JasperTemplate.jrxml", 
                    jasperServerUrl : dojoConfig.geoviewerApiUrl + "/Report",
                    reportTitle: "Géoportail de la Wallonie"};
                var prop = lang.mixin(defaultProp, options);

                this.currentReportParams = {
                    jasperServerUrl: jasperServerUrl,
                    jasperTemplate: jasperTemplate,
                    Data: Data,
                    Properties: prop
                };
                var spwMap = SpwViewer.getInstance().get('spwMap');
                var template = {
                    url: "reportA4Portrait.jrxml",
                    default: true,
                    imageWidth: 555*1.4,
                    imageHeight: 732*1.4,
                    data: {
                        logoWallon: opt.logoWallon,
                        header1: opt.header1
                    }
                };

                var min = spwMap.pointToExtent(0, 0);
                var max = spwMap.pointToExtent(0 + template.imageWidth, 0 + template.imageHeight);

                var extent = new Extent(min.x, min.y, max.x, max.y, spwMap.getSpatialReference());

                this.getImage(spwMap.domNode, template.imageWidth, template.imageHeight, true, extent, lang.hitch(this, this.sendReport), opt.rosaceUrl, true);
            },

            sendReport: function(mapImage){
                var hiddenDiv = domConstruct.create('div', {},document.body);

                var form = domConstruct.create("form");
                form.method = "post";
                form.target="_blank";
                form.enctype="multipart/form-data";
                form["accept-charset"] = "UTF-8";

                var FileName = domConstruct.create("input", {name: 'FileName', type: 'hidden'});
                FileName.value = "rapport-" + new Date().toLocaleDateString().replace(/\//gim,'-').replace(/ /gim,'-');
                form.appendChild(FileName);

                var MapImage = domConstruct.create("input", {name: 'MapImage', type: 'hidden'});
                var img = mapImage.toDataURL();
                img = img.substring(img.indexOf(",") + 1);
                MapImage.value = img;
                form.appendChild(MapImage);

                var Data = domConstruct.create("input", {name: 'Data', type: 'hidden'});
                Data.value = JSON.stringify(this.currentReportParams.Data);
                form.appendChild(Data);

                var Properties = domConstruct.create("input", {name: 'Properties', type: 'hidden'});
                Properties.value = JSON.stringify(this.currentReportParams.Properties);
                form.appendChild(Properties);


                var Template = domConstruct.create("input", {name: 'Template', type: 'hidden'});
                Template.value = encodeURIComponent(this.currentReportParams.jasperTemplate);
                form.appendChild(Template);
                hiddenDiv.appendChild(form);
                form.action = this.currentReportParams.jasperServerUrl;
                form.submit();

                document.body.removeChild(hiddenDiv);
            },


            resizeAndCenterMap: function(def, spwMap, overflowW, overflowH, domItemOverflowed) {
                var centerMap = this.oldCenterMap = spwMap.getCurrentExtent().getCenter();
                var centerScreenPt = spwMap.toScreenPoint(centerMap.x, centerMap.y);

                centerScreenPt.x -= (overflowW / 2);
                centerScreenPt.y -= (overflowH / 2);

                var newCenterMap = spwMap.toMapPoint(centerScreenPt.x, centerScreenPt.y);

                spwMap.centerAt(newCenterMap.x, newCenterMap.y);

                def.resolve(domItemOverflowed);
            },

            /**
             * Rétablit les dimensions de la carte telles qu'avant son redimensionnement.
             * @param domItemOverflowed
             */
            resetMap: function(domItemOverflowed){
                array.forEach(domItemOverflowed, function(domItemArr){
                    domStyle.set(domItemArr[0], "overflow", domItemArr[1]);
                });

                domStyle.set(SpwViewer.getInstance().get('spwMap').getParent().domNode, {
                    width:"100%",
                    height:"100%"
                });

                array.forEach(this.stylesToReset, function(styleToReset) {
                    domStyle.set(styleToReset.node, styleToReset.style);
                });

                domStyle.set(SpwViewer.getInstance().get('spwMap').domNode, {
                    width:"100%",
                    height:"100%"
                });

                this.stylesToReset.length = 0;

                SpwViewer.getInstance().spwViewerInnerBorderContainer.resize();
                SpwViewer.getInstance().get('spwMap').resize();
                SpwViewer.getInstance().get('spwMap').get('esriMap').resize();
                if (this.oldCenterMap) {
                    SpwViewer.getInstance().get('spwMap').centerAt(this.oldCenterMap.x, this.oldCenterMap.y);
                }
            },

            /**
             * Rogne l'image
             * @param canvas
             */
            _useSpaceCanvas: function(canvas){
                var newCanvas;
                var imgSrc = canvas.toDataURL();

                var img = new Image();

                img.onload = lang.hitch(this, function(){
                    newCanvas = domConstruct.create('canvas', {width:this.imageWidth, height:this.imageHeight});
                    newCanvas.getContext("2d").drawImage (img, 0, 0, img.width, img.height);
                    MessageManager.getInstance().hideModalMessage();
                    this.successHandler(newCanvas);
                });

                img.src = imgSrc;
            },

            /**
             * Dimensionne l'image en fonction de la taille demandée
             * @param canvas
             */
            _cropCanvas: function(canvas) {
                var def = new Deferred();

                var newCanvas;
                var imgSrc = canvas.toDataURL();

                var img = new Image();

                var tw = Math.round(this.imageWidth);
                var th = Math.round(this.imageHeight);

                img.onload = lang.hitch(this, function(){
                    newCanvas = domConstruct.create('canvas', {
                        width:tw,
                        height:th
                    });

                    var srcX = Math.round((img.width / 2) - (this.imageWidth / 2));
                    var srcY = Math.round((img.height / 2) - (this.imageHeight / 2));

                    newCanvas.getContext("2d").drawImage(img, srcX, srcY, tw, th, 0, 0, tw, th);

                    def.resolve(newCanvas);
                });

                img.src = imgSrc;

                return def;
            }
        });

        return Printer;
    });