0011960: print with 300 dpi by default
[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: true,
17   
18   debug: false,
19   
20   constructor: function(config) {
21     Ext.apply(this, config);
22     
23     if (this.debug) {
24         this.printStrategy = 'window';
25     }
26     Ext.ux.Printer.BaseRenderer.superclass.constructor.call(this, config);
27   },
28
29   /**
30    * template method to intercept when document is ready
31    *
32    * @param {Document} document
33    * @pram {Ext.Component} component
34    */
35   onBeforePrint: Ext.emptyFn,
36
37   /**
38    * Prints the component
39    * @param {Ext.Component} component The component to print
40    */
41   print: function(component) {
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         
74     Ext.fly(frame).set({
75       id: id,
76       name: id,
77       style: {
78         position: 'absolute',
79         width: '210mm',
80         height: '297mm',
81         top: '-10000px',
82         left: '-10000px'
83       }
84     });
85     
86     doc.body.appendChild(frame);
87
88     Ext.fly(frame).set({
89       src : Ext.SSL_SECURE_URL
90     });
91
92     var doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
93         
94     doc.open();
95     doc.write(this.generateHTML(component));
96     doc.close();
97
98     this.doPrintOnStylesheetLoad.defer(10, this, [frame.contentWindow, component]);
99   },
100   
101   /**
102    * check if style is loaded and do print afterwards
103    * 
104    * @param {window} win
105    */
106   doPrintOnStylesheetLoad: function(win, component) {
107     var el = win.document.getElementById('csscheck'),
108         comp = el.currentStyle || getComputedStyle(el, null);
109     if (comp.display !== "none") {
110       return this.doPrintOnStylesheetLoad.defer(10, this, [win, component]);
111     }
112
113     this.onBeforePrint(win.document, component);
114
115     this.doPrint(win);
116   },
117
118   doPrint: function(win) {
119     if (this.useHtml2Canvas) {
120       var me = this;
121
122       var canvas = win.document.createElement("canvas");
123       canvas.width = win.innerWidth;
124       canvas.height = win.innerHeight;
125
126       me.setDPI(canvas, 300);
127
128       html2canvas(win.document.body, {
129         canvas: canvas,
130         grabMouse: false,
131         onrendered: function (canvas) {
132           var screenshot = canvas.toDataURL();
133           me.useHtml2Canvas = false;
134           win.document.body.innerHTML = '<img style="display: block; width: 100%" />';
135           win.document.body.firstChild.onload = me.doPrint.createDelegate(me, [win]);
136           win.document.body.firstChild.src = screenshot;
137         }
138       });
139     } else {
140       win.print();
141       if (!this.debug) {
142         win.close();
143       }
144     }
145   },
146
147   setDPI: function (canvas, dpi) {
148     // Set up CSS size if it's not set up already
149     if (!canvas.style.width)
150       canvas.style.width = canvas.width + 'px';
151     if (!canvas.style.height)
152       canvas.style.height = canvas.height + 'px';
153
154     var scaleFactor = dpi / 96;
155     canvas.width = Math.ceil(canvas.width * scaleFactor);
156     canvas.height = Math.ceil(canvas.height * scaleFactor);
157     var ctx = canvas.getContext('2d');
158     ctx.scale(scaleFactor, scaleFactor);
159   },
160
161   /**
162    * Generates the HTML Markup which wraps whatever this.generateBody produces
163    * @param {Ext.Component} component The component to generate HTML for
164    * @return {String} An HTML fragment to be placed inside the print window
165    */
166   generateHTML: function(component) {
167     return new Ext.XTemplate(
168       '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
169       '<html>',
170         '<head>',
171           '<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />',
172           this.getAdditionalHeaders(),
173           '<link href="' + this.stylesheetPath + '?' + new Date().getTime() + '" rel="stylesheet" type="text/css" media="screen,print" />',
174           '<title>' + this.getTitle(component) + '</title>',
175         '</head>',
176         '<body>',
177           '<div id="csscheck"></div>',
178           this.generateBody(component),
179         '</body>',
180       '</html>'
181     ).apply(this.prepareData(component));
182   },
183   
184   /**
185    * Returns the HTML that will be placed into the <head> element of th print window.
186    * @param {Ext.Component} component The component to render
187    * @return {String} The HTML fragment to place inside the print window's <head> element
188    */
189   getAdditionalHeaders: function(component) {
190     return '';
191   },
192   /**
193    * Returns the HTML that will be placed into the print window. This should produce HTML to go inside the
194    * <body> element only, as <head> is generated in the print function
195    * @param {Ext.Component} component The component to render
196    * @return {String} The HTML fragment to place inside the print window's <body> element
197    */
198   generateBody: Ext.emptyFn,
199   
200   /**
201    * Prepares data suitable for use in an XTemplate from the component 
202    * @param {Ext.Component} component The component to acquire data from
203    * @return {Array} An empty array (override this to prepare your own data)
204    */
205   prepareData: function(component) {
206     return component;
207   },
208   
209   /**
210    * Returns the title to give to the print window
211    * @param {Ext.Component} component The component to be printed
212    * @return {String} The window title
213    */
214   getTitle: function(component) {
215     return typeof component.getTitle == 'function' ? component.getTitle() : (component.title || "Printing");
216   },
217   
218   /**
219    * @property stylesheetPath
220    * @type String
221    * The path at which the print stylesheet can be found (defaults to 'stylesheets/print.css')
222    */
223   stylesheetPath: 'stylesheets/print.css'
224 });