ol-ext
Version:
A set of cool extensions for OpenLayers (ol) in node modules structure
251 lines (243 loc) • 9.03 kB
JavaScript
/*
Copyright (c) 2019 Jean-Marc VIGLINO,
released under the CeCILL-B license (French BSD license)
(http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/
import ol_control_Control from 'ol/control/Control.js'
import ol_ext_element from '../util/element.js'
/** Print control to get an image of the map
* @constructor
* @fire print
* @fire error
* @fire printing
* @extends {ol.control.Control}
* @param {Object=} options Control options.
* @param {String} options.className class of the control
* @param {String} options.title button title
* @param {string} options.imageType A string indicating the image format, default image/jpeg
* @param {number} options.quality Number between 0 and 1 indicating the image quality to use for image formats that use lossy compression such as image/jpeg and image/webp
* @param {string} options.orientation Page orientation (landscape/portrait), default guest the best one
* @param {boolean} options.immediate force print even if render is not complete, default false
*/
var ol_control_Print = class olcontrolPrint extends ol_control_Control {
constructor(options) {
options = options || {};
var element = ol_ext_element.create('DIV', {
className: (options.className || 'ol-print')
});
super({
element: element,
target: options.target
});
if (!options.target) {
element.classList.add('ol-unselectable', 'ol-control');
ol_ext_element.create('BUTTON', {
type: 'button',
title: options.title || 'Print',
click: function () { this.print(); }.bind(this),
parent: element
});
}
this.set('immediate', options.immediate);
this.set('imageType', options.imageType || 'image/jpeg');
this.set('quality', options.quality || .8);
this.set('orientation', options.orientation);
}
/** Helper function to copy result to clipboard
* @param {Event} e print event
* @return {boolean}
* @private
*/
toClipboard(e, callback) {
try {
e.canvas.toBlob(function (blob) {
try {
navigator.clipboard.write([
new window.ClipboardItem(
Object.defineProperty({}, blob.type, {
value: blob,
enumerable: true
})
)
]);
if (typeof (callback) === 'function')
callback(true);
} catch (err) {
if (typeof (callback) === 'function')
callback(false);
}
});
} catch (err) {
if (typeof (callback) === 'function')
callback(false);
}
}
/** Helper function to copy result to clipboard
* @param {any} options print options
* @param {function} callback a callback function that takes a boolean if copy
*/
copyMap(options, callback) {
this.once('print', function (e) {
this.toClipboard(e, callback);
}.bind(this));
this.print(options);
}
/** Get map canvas
* @private
*/
_getCanvas(event, imageType, canvas) {
var ctx;
// ol <= 5 : get the canvas
if (event.context) {
canvas = event.context.canvas;
} else {
// Create a canvas if none
if (!canvas) {
canvas = document.createElement('canvas');
var size = this.getMap().getSize();
canvas.width = size[0];
canvas.height = size[1];
ctx = canvas.getContext('2d');
if (/jp.*g$/.test(imageType)) {
ctx.fillStyle = this.get('bgColor') || 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
} else {
ctx = canvas.getContext('2d');
}
// ol6+ : create canvas using layer canvas
this.getMap().getViewport().querySelectorAll('.ol-layers canvas, canvas.ol-fixedoverlay').forEach(function (c) {
if (c.width) {
ctx.save();
// opacity
if (c.parentNode.style.opacity === '0')
return;
ctx.globalAlpha = parseFloat(c.parentNode.style.opacity) || 1;
// Blend mode & filter
ctx.globalCompositeOperation = ol_ext_element.getStyle(c.parentNode, 'mix-blend-mode');
ctx.filter = ol_ext_element.getStyle(c.parentNode, 'filter');
// transform
var tr = ol_ext_element.getStyle(c, 'transform') || ol_ext_element.getStyle(c, '-webkit-transform');
if (/^matrix/.test(tr)) {
tr = tr.replace(/^matrix\(|\)$/g, '').split(',');
tr.forEach(function (t, i) { tr[i] = parseFloat(t); });
ctx.transform(tr[0], tr[1], tr[2], tr[3], tr[4], tr[5]);
ctx.drawImage(c, 0, 0);
} else {
ctx.drawImage(c, 0, 0, ol_ext_element.getStyle(c, 'width'), ol_ext_element.getStyle(c, 'height'));
}
ctx.restore();
}
}.bind(this));
}
return canvas;
}
/** Fast print
* @param {*} options print options
* @param {HTMLCanvasElement|undefined} [options.canvas] if none create one, only for ol@6+
* @parama {string} options.imageType
*/
fastPrint(options, callback) {
options = options || {};
if (this._ol6) {
requestAnimationFrame(function () {
callback(this._getCanvas({}, options.imageType, options.canvas));
}.bind(this));
} else {
this.getMap().once('postcompose', function (event) {
if (!event.context)
this._ol6 = true;
callback(this._getCanvas(event, options.imageType, options.canvas));
}.bind(this));
this.getMap().render();
}
}
/** Print the map
* @param {Object} options
* @param {string} options.imageType A string indicating the image format, default the control one
* @param {number} options.quality Number between 0 and 1 indicating the image quality to use for image formats that use lossy compression such as image/jpeg and image/webp
* @param {boolean} options.immediate true to prevent delay for printing
* @param {boolean} [options.size=[210,297]]
* @param {boolean} [options.format=a4]
* @param {boolean} [options.orient] default control orientation
* @param {boolean} [options.margin=10]
* @param {*} options.any any options passed to the print event when fired
* @api
*/
print(options) {
options = options || {};
var imageType = options.imageType || this.get('imageType');
var quality = options.quality || this.get('quality');
if (this.getMap()) {
if (options.immediate !== 'silent') {
this.dispatchEvent(Object.assign({
type: 'printing',
}, options));
}
// Start printing after delay to let user show info in the DOM
if (!options.immediate) {
setTimeout(function () {
options = Object.assign({}, options);
options.immediate = 'silent';
this.print(options);
}.bind(this), 200);
return;
}
// Run printing
this.getMap().once(this.get('immediate') ? 'postcompose' : 'rendercomplete', function (event) {
var canvas = this._getCanvas(event, imageType);
// Calculate print format
var size = options.size || [210, 297];
var format = options.format || 'a4';
var w, h, position;
var orient = options.orient || this.get('orientation');
var margin = typeof (options.margin) === 'number' ? options.margin : 10;
if (canvas) {
// Calculate size
if (orient !== 'landscape' && orient !== 'portrait') {
orient = (canvas.width > canvas.height) ? 'landscape' : 'portrait';
}
if (orient === 'landscape')
size = [size[1], size[0]];
var sc = Math.min((size[0] - 2 * margin) / canvas.width, (size[1] - 2 * margin) / canvas.height);
w = sc * canvas.width;
h = sc * canvas.height;
// Image position
position = [(size[0] - w) / 2, (size[1] - h) / 2];
}
// get the canvas image
var image;
try {
image = canvas ? canvas.toDataURL(imageType, quality) : null;
} catch (e) {
// Fire error event
this.dispatchEvent({
type: 'error',
canvas: canvas
});
return;
}
// Fire print event
var e = Object.assign({
type: 'print',
print: {
format: format,
orientation: orient,
unit: 'mm',
size: size,
position: position,
imageWidth: w,
imageHeight: h
},
image: image,
imageType: imageType,
quality: quality,
canvas: canvas
}, options);
this.dispatchEvent(e);
}.bind(this));
this.getMap().render();
}
}
}
export default ol_control_Print