0012126: Use canvas print only for sheet print
[tine20] / tine20 / Tinebase / js / ux / Printer / renderers / Base.js
1 /**
2  * @class Ext.ux.Printer.BaseRenderer
3  * @extends Object
4  * @author Ed Spencer
5  * Abstract base renderer class. Don't use this directly, use a subclass instead
6  */
7 Ext.ux.Printer.BaseRenderer = Ext.extend(Object, {
8   /**
9    * @cfg {String} printStrategy window or iframe
10    */
11   printStrategy: 'iframe',
12
13   /**
14    * @cfg {Boll} useHtml2Canvas
15    */
16   useHtml2Canvas: false,
17   
18   debug: false,
19   
20   constructor: function(config) {
21     Ext.apply(this, config);
22
23     Ext.ux.Printer.BaseRenderer.superclass.constructor.call(this, config);
24   },
25
26   /**
27    * template method to intercept when document is ready
28    *
29    * @param {Document} document
30    * @pram {Ext.Component} component
31    */
32   onBeforePrint: Ext.emptyFn,
33
34   /**
35    * Prints the component
36    * @param {Ext.Component} component The component to print
37    */
38   print: function(component) {
39     this.mask = new Ext.LoadMask(Ext.getBody(), {msg: i18n._("Preparing print, please wait...")});
40     this.mask.show();
41
42     return this[this.printStrategy + 'Print'](component);
43   },
44   
45   /**
46    * Prints the component using the new window strategy
47    * @param {Ext.Component} component The component to print
48    */
49   windowPrint: function(component) {
50     var name = component && component.getXType
51              ? String.format("print_{0}_{1}", String(component.getXType()).replace(/(\.|-)/g, '_'), component.id.replace(/(\.|-)/g, '_'))
52              : "print";
53
54     var win = window.open('', name);
55     
56     win.document.write(this.generateHTML(component));
57     win.document.close();
58
59     // gecko looses its document after document.close(). but fortunally waits with printing till css is loaded itself
60     return this.doPrint(win);
61     
62     this.doPrintOnStylesheetLoad.defer(10, this, [win, component]);
63   },
64   
65   /**
66    * Prints the component using the hidden iframe strategy
67    * @param {Ext.Component} component The component to print
68    */
69   iframePrint: function(component) {
70     var id = Ext.id(),
71     doc = document,
72     frame = doc.createElement('iframe'),
73     style = {
74       position: 'absolute',
75       'background-color': '#FFFFFF',
76       width: '210mm',
77       height: '297mm',
78       top: '-10000px',
79       left: '-10000px'
80     };
81
82     if (this.debug) {
83       Ext.apply(style, {
84         top: '0px',
85         left: '0px',
86         'z-index': 10000000
87       });
88     }
89
90     Ext.fly(frame).set({
91       id: id,
92       name: id,
93       style: style
94     });
95     
96     doc.body.appendChild(frame);
97
98     Ext.fly(frame).set({
99       src : Ext.SSL_SECURE_URL
100     });
101
102     var doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
103         
104     doc.open();
105     doc.write(this.generateHTML(component));
106     doc.close();
107
108     this.doPrintOnStylesheetLoad.defer(10, this, [frame.contentWindow, component]);
109   },
110   
111   /**
112    * check if style is loaded and do print afterwards
113    * 
114    * @param {window} win
115    */
116   doPrintOnStylesheetLoad: function(win, component) {
117     var el = win.document.getElementById('csscheck'),
118         comp = el.currentStyle || getComputedStyle(el, null);
119     if (comp.display !== "none") {
120       return this.doPrintOnStylesheetLoad.defer(10, this, [win, component]);
121     }
122
123     this.onBeforePrint(win.document, component);
124
125     this.doPrint(win);
126   },
127
128   doPrint: function(win) {
129     if (this.useHtml2Canvas) {
130       var me = this;
131
132       var canvas = win.document.createElement("canvas");
133       canvas.width = win.innerWidth;
134       canvas.height = win.innerHeight;
135
136       me.setDPI(canvas, 300);
137
138       html2canvas(win.document.body, {
139         canvas: canvas,
140         grabMouse: false,
141         onrendered: function (canvas) {
142           var screenshot = canvas.toDataURL();
143           me.useHtml2Canvas = false;
144           win.document.body.innerHTML = '<img style="display: block; width: 100%" />';
145           win.document.body.firstChild.onload = me.doPrint.createDelegate(me, [win]);
146           win.document.body.firstChild.src = screenshot;
147         }
148       });
149     } else {
150       win.print();
151       this.mask.hide();
152       if (!this.debug) {
153         win.close();
154       }
155     }
156   },
157
158   setDPI: function (canvas, dpi) {
159     // Set up CSS size if it's not set up already
160     if (!canvas.style.width)
161       canvas.style.width = canvas.width + 'px';
162     if (!canvas.style.height)
163       canvas.style.height = canvas.height + 'px';
164
165     var scaleFactor = dpi / 96;
166     canvas.width = Math.ceil(canvas.width * scaleFactor);
167     canvas.height = Math.ceil(canvas.height * scaleFactor);
168     var ctx = canvas.getContext('2d');
169     ctx.scale(scaleFactor, scaleFactor);
170   },
171
172   /**
173    * Generates the HTML Markup which wraps whatever this.generateBody produces
174    * @param {Ext.Component} component The component to generate HTML for
175    * @return {String} An HTML fragment to be placed inside the print window
176    */
177   generateHTML: function(component) {
178     return new Ext.XTemplate(
179       '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
180       '<html>',
181         '<head>',
182           '<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />',
183           this.getAdditionalHeaders(),
184           '<link href="' + this.stylesheetPath + '?' + new Date().getTime() + '" rel="stylesheet" type="text/css" media="screen,print" />',
185           '<title>' + this.getTitle(component) + '</title>',
186         '</head>',
187         '<body>',
188           '<div id="csscheck"></div>',
189           this.generateBody(component),
190         '</body>',
191       '</html>'
192     ).apply(this.prepareData(component));
193   },
194   
195   /**
196    * Returns the HTML that will be placed into the <head> element of th print window.
197    * @param {Ext.Component} component The component to render
198    * @return {String} The HTML fragment to place inside the print window's <head> element
199    */
200   getAdditionalHeaders: function(component) {
201     return '';
202   },
203   /**
204    * Returns the HTML that will be placed into the print window. This should produce HTML to go inside the
205    * <body> element only, as <head> is generated in the print function
206    * @param {Ext.Component} component The component to render
207    * @return {String} The HTML fragment to place inside the print window's <body> element
208    */
209   generateBody: Ext.emptyFn,
210   
211   /**
212    * Prepares data suitable for use in an XTemplate from the component 
213    * @param {Ext.Component} component The component to acquire data from
214    * @return {Array} An empty array (override this to prepare your own data)
215    */
216   prepareData: function(component) {
217     return component;
218   },
219   
220   /**
221    * Returns the title to give to the print window
222    * @param {Ext.Component} component The component to be printed
223    * @return {String} The window title
224    */
225   getTitle: function(component) {
226     return typeof component.getTitle == 'function' ? component.getTitle() : (component.title || "Printing");
227   },
228   
229   /**
230    * @property stylesheetPath
231    * @type String
232    * The path at which the print stylesheet can be found (defaults to 'stylesheets/print.css')
233    */
234   stylesheetPath: 'stylesheets/print.css'
235 });