Statistics
| Revision:

root / trunk / extensions / extGraph / src / org / gvsig / graph / core / DocumentRenderer.java @ 31873

History | View | Annotate | Download (16.6 KB)

1
/*  Copyright 2002
2
    Kei G. Gauthier
3
    Suite 301
4
    77 Winsor Street
5
    Ludlow, MA  01056
6
 */
7

    
8
package org.gvsig.graph.core;
9

    
10
import java.awt.Component;
11
import java.awt.Graphics;
12
import java.awt.Graphics2D;
13
import java.awt.Rectangle;
14
import java.awt.Shape;
15
import java.awt.print.PageFormat;
16
import java.awt.print.Paper;
17
import java.awt.print.Printable;
18
import java.awt.print.PrinterException;
19
import java.awt.print.PrinterJob;
20

    
21
import javax.print.Doc;
22
import javax.print.DocFlavor;
23
import javax.print.DocPrintJob;
24
import javax.print.PrintException;
25
import javax.print.PrintService;
26
import javax.print.PrintServiceLookup;
27
import javax.print.ServiceUI;
28
import javax.print.SimpleDoc;
29
import javax.print.attribute.HashPrintRequestAttributeSet;
30
import javax.print.attribute.PrintRequestAttributeSet;
31
import javax.print.attribute.standard.MediaPrintableArea;
32
import javax.print.event.PrintJobAdapter;
33
import javax.print.event.PrintJobEvent;
34
import javax.print.event.PrintJobListener;
35
import javax.swing.JEditorPane;
36
import javax.swing.JOptionPane;
37
import javax.swing.text.Document;
38
import javax.swing.text.PlainDocument;
39
import javax.swing.text.View;
40
import javax.swing.text.html.HTMLDocument;
41

    
42
import org.apache.log4j.Logger;
43

    
44
import com.iver.andami.PluginServices;
45
import com.iver.cit.gvsig.project.documents.layout.Attributes;
46

    
47
public class DocumentRenderer implements Printable {
48
        /*
49
         * DocumentRenderer prints objects of type Document. Text attributes,
50
         * including fonts, color, and small icons, will be rendered to a printed
51
         * page. DocumentRenderer computes line breaks, paginates, and performs
52
         * other formatting.
53
         * 
54
         * An HTMLDocument is printed by sending it as an argument to the
55
         * print(HTMLDocument) method. A PlainDocument is printed the same way.
56
         * Other types of documents must be sent in a JEditorPane as an argument to
57
         * the print(JEditorPane) method. Printing Documents in this way will
58
         * automatically display a print dialog.
59
         * 
60
         * As objects which implement the Printable Interface, instances of the
61
         * DocumentRenderer class can also be used as the argument in the
62
         * setPrintable method of the PrinterJob class. Instead of using the print()
63
         * methods detailed above, a programmer may gain access to the formatting
64
         * capabilities of this class without using its print dialog by creating an
65
         * instance of DocumentRenderer and setting the document to be printed with
66
         * the setDocument() or setJEditorPane(). The Document may then be printed
67
         * by setting the instance of DocumentRenderer in any PrinterJob.
68
         */
69

    
70
        private static boolean useDefaultJavaPrinting = true;
71
        
72
        protected int currentPage = -1; // Used to keep track of when
73
        // the page to print changes.
74

    
75
        protected JEditorPane jeditorPane; // Container to hold the
76
        // Document. This object will
77
        // be used to lay out the
78
        // Document for printing.
79

    
80
        protected double pageEndY = 0; // Location of the current page
81
        // end.
82

    
83
        protected double pageStartY = 0; // Location of the current page
84
        // start.
85

    
86
        protected boolean scaleWidthToFit = true; // boolean to allow control over
87
        // whether pages too wide to fit
88
        // on a page will be scaled.
89

    
90
        private PrintService[] m_cachePrintServices = null;
91
        private PrintService m_cachePrintService = null;
92

    
93
        /*
94
         * The DocumentRenderer class uses pFormat and pJob in its methods. Note
95
         * that pFormat is not the variable name used by the print method of the
96
         * DocumentRenderer. Although it would always be expected to reference the
97
         * pFormat object, the print method gets its PageFormat as an argument.
98
         */
99
        protected PageFormat pFormat;
100
        protected PrinterJob pJob;
101

    
102
        /*
103
         * The constructor initializes the pFormat and PJob variables.
104
         */
105
        public DocumentRenderer() {
106
                pFormat = new PageFormat();
107
                pJob = PrinterJob.getPrinterJob();
108
        }
109

    
110
        /*
111
         * Method to get the current Document
112
         */
113
        public Document getDocument() {
114
                if (jeditorPane != null)
115
                        return jeditorPane.getDocument();
116
                else
117
                        return null;
118
        }
119

    
120
        /*
121
         * Method to get the current choice the width scaling option.
122
         */
123
        public boolean getScaleWidthToFit() {
124
                return scaleWidthToFit;
125
        }
126

    
127
        /*
128
         * pageDialog() displays a page setup dialog.
129
         */
130
        public void pageDialog() {
131
                pFormat = pJob.pageDialog(pFormat);
132
        }
133

    
134
        /*
135
         * The print method implements the Printable interface. Although Printables
136
         * may be called to render a page more than once, each page is painted in
137
         * order. We may, therefore, keep track of changes in the page being
138
         * rendered by setting the currentPage variable to equal the pageIndex, and
139
         * then comparing these variables on subsequent calls to this method. When
140
         * the two variables match, it means that the page is being rendered for the
141
         * second or third time. When the currentPage differs from the pageIndex, a
142
         * new page is being requested.
143
         * 
144
         * The highlights of the process used print a page are as follows:
145
         * 
146
         * I. The Graphics object is cast to a Graphics2D object to allow for
147
         * scaling. II. The JEditorPane is laid out using the width of a printable
148
         * page. This will handle line breaks. If the JEditorPane cannot be sized at
149
         * the width of the graphics clip, scaling will be allowed. III. The root
150
         * view of the JEditorPane is obtained. By examining this root view and all
151
         * of its children, printView will be able to determine the location of each
152
         * printable element of the document. IV. If the scaleWidthToFit option is
153
         * chosen, a scaling ratio is determined, and the graphics2D object is
154
         * scaled. V. The Graphics2D object is clipped to the size of the printable
155
         * page. VI. currentPage is checked to see if this is a new page to render.
156
         * If so, pageStartY and pageEndY are reset. VII. To match the coordinates
157
         * of the printable clip of graphics2D and the allocation rectangle which
158
         * will be used to lay out the views, graphics2D is translated to begin at
159
         * the printable X and Y coordinates of the graphics clip. VIII. An
160
         * allocation Rectangle is created to represent the layout of the Views.
161
         * 
162
         * The Printable Interface always prints the area indexed by reference to
163
         * the Graphics object. For instance, with a standard 8.5 x 11 inch page
164
         * with 1 inch margins the rectangle X = 72, Y = 72, Width = 468, and Height
165
         * = 648, the area 72, 72, 468, 648 will be painted regardless of which page
166
         * is actually being printed.
167
         * 
168
         * To align the allocation Rectangle with the graphics2D object two things
169
         * are done. The first step is to translate the X and Y coordinates of the
170
         * graphics2D object to begin at the X and Y coordinates of the printable
171
         * clip, see step VII. Next, when printing other than the first page, the
172
         * allocation rectangle must start laying out in coordinates represented by
173
         * negative numbers. After page one, the beginning of the allocation is
174
         * started at minus the page end of the prior page. This moves the part
175
         * which has already been rendered to before the printable clip of the
176
         * graphics2D object.
177
         * 
178
         * X. The printView method is called to paint the page. Its return value
179
         * will indicate if a page has been rendered.
180
         * 
181
         * Although public, print should not ordinarily be called by programs other
182
         * than PrinterJob.
183
         */
184
        public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) {
185
                double scale = 1.0;
186
                Graphics2D graphics2D;
187
                View rootView;
188
                // I
189
                graphics2D = (Graphics2D) graphics;
190
                // II
191
                jeditorPane.setSize((int) pageFormat.getImageableWidth(),
192
                                Integer.MAX_VALUE);
193
                jeditorPane.validate();
194
                // III
195
                rootView = jeditorPane.getUI().getRootView(jeditorPane);
196
                // IV
197
                graphics2D.translate(70, 0);
198
                if ((scaleWidthToFit)
199
                                && (jeditorPane.getMinimumSize().getWidth() > pageFormat
200
                                                .getImageableWidth())) {
201
                        scale = pageFormat.getImageableWidth()
202
                                        / jeditorPane.getMinimumSize().getWidth();
203
                        graphics2D.scale(scale, scale);
204
                }
205
                // V
206
                graphics2D.setClip((int) (pageFormat.getImageableX() / scale),
207
                                (int) (pageFormat.getImageableY() / scale), (int) (pageFormat
208
                                                .getImageableWidth() / scale), (int) (pageFormat
209
                                                .getImageableHeight() / scale));
210
                // VI
211
                if (pageIndex > currentPage) {
212
                        currentPage = pageIndex;
213
                        pageStartY += pageEndY;
214
                        pageEndY = graphics2D.getClipBounds().getHeight();
215
                }
216
                // VII
217
                graphics2D.translate(graphics2D.getClipBounds().getX(), graphics2D
218
                                .getClipBounds().getY());
219
                // VIII
220
                Rectangle allocation = new Rectangle(0, (int) -pageStartY,
221
                                (int) (jeditorPane.getMinimumSize().getWidth()),
222
                                (int) (jeditorPane.getPreferredSize().getHeight()));
223
                // X
224
                if (printView(graphics2D, allocation, rootView)) {
225
                        return Printable.PAGE_EXISTS;
226
                } else {
227
                        pageStartY = 0;
228
                        pageEndY = 0;
229
                        currentPage = -1;
230
                        return Printable.NO_SUCH_PAGE;
231
                }
232
        }
233

    
234
        /*
235
         * print(HTMLDocument) is called to set an HTMLDocument for printing.
236
         */
237
        public void print(HTMLDocument htmlDocument) {
238
                setDocument(htmlDocument);
239
                printDialog();
240
        }
241

    
242
        /*
243
         * print(JEditorPane) prints a Document contained within a JEDitorPane.
244
         */
245
        public void print(JEditorPane jedPane) {
246
                setDocument(jedPane);
247
                printDialog();
248
        }
249

    
250
        /*
251
         * print(PlainDocument) is called to set a PlainDocument for printing.
252
         */
253
        public void print(PlainDocument plainDocument) {
254
                setDocument(plainDocument);
255
                printDialog();
256
        }
257

    
258
        /*
259
         * A protected method, printDialog(), displays the print dialog and
260
         * initiates printing in response to user input.
261
         */
262
        protected void printDialog() {
263
                Attributes m_attributes = new Attributes();
264
                m_attributes.setIsLandScape(false);
265
                PageFormat pf = m_attributes.getPageFormat();
266
                pf.setOrientation(PageFormat.PORTRAIT);
267
                double w = pf.getImageableWidth();
268
                double h = pf.getImageableHeight();
269
                Paper paper = pf.getPaper();
270
                paper.setImageableArea(pf.getImageableX() + 100, pf.getImageableY(), w -100, h);
271
                pf.setPaper(paper);
272
                PrintRequestAttributeSet att = m_attributes.toPrintAttributes();
273
//                att = new HashPrintRequestAttributeSet();
274
                DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
275
                try {
276
                        if (m_cachePrintServices == null) {
277
                                m_cachePrintServices = PrintServiceLookup.lookupPrintServices(
278
                                                flavor, null);
279
                        }
280

    
281
                        PrintService defaultService = null;
282

    
283
                        if (m_cachePrintService == null) {
284
                                defaultService = PrintServiceLookup.lookupDefaultPrintService();
285
                        }
286

    
287
                        if(m_cachePrintService == null && defaultService == null && m_cachePrintServices.length > 0)
288
                    defaultService = m_cachePrintServices[0];
289
                        
290
            if ((defaultService == null) && (m_cachePrintService == null)) {
291
                JOptionPane.showMessageDialog((Component) PluginServices
292
                        .getMainFrame(),PluginServices.getText(this,"ninguna_impresora_configurada"));
293

    
294
                return;
295
            }
296
            if (useDefaultJavaPrinting) {
297
                    try {
298
                            m_cachePrintService = ServiceUI.printDialog(null, 200, 200,
299
                                            m_cachePrintServices, defaultService, flavor, att);
300
                    }
301
                    catch (RuntimeException ex) {
302
                            useDefaultJavaPrinting=false;
303
                            Logger logger = PluginServices.getLogger();
304
                            logger.error("Error showing print dialog", ex); 
305
                            
306
                            // workaround a problem with Java 1.5 with moder CUPS versions
307
                            // this try-catch block may be safely removed when we move to Java 1.6
308
                            logger.debug("Opening gvSIG's internal Java 1.7 CUPS printing dialog");
309
                            m_cachePrintService = backport1_7.javax.print.ServiceUI.printDialog(null, 200, 200,
310
                                            m_cachePrintServices, defaultService, flavor, att);
311
                    }
312
            }
313
            else {
314
                    PluginServices.getLogger().debug("Opening gvSIG's internal Java 1.7 CUPS printing dialog");
315
                    m_cachePrintService = backport1_7.javax.print.ServiceUI.printDialog(null, 200, 200,
316
                                    m_cachePrintServices, defaultService, flavor, att);
317
            }
318

    
319
                        if (m_cachePrintService != null) {
320
                                DocPrintJob jobNuevo = m_cachePrintService.createPrintJob();
321
                                PrintJobListener pjlistener = new PrintJobAdapter() {
322
                                        public void printDataTransferCompleted(PrintJobEvent e) {
323
                                                System.out.println("Fin de impresi?n");
324
                                        }
325
                                };
326

    
327
                                jobNuevo.addPrintJobListener(pjlistener);
328

    
329
                                Doc doc = new SimpleDoc(this, flavor, null);
330
                                jobNuevo.print(doc, att);
331
                        }
332
                }
333
                catch (PrintException printerException) {
334
                        pageStartY = 0;
335
                        pageEndY = 0;
336
                        currentPage = -1;
337
                        System.out.println("Error Printing Document");
338
                }
339

    
340

    
341
                // if (pJob.printDialog(att)) {
342
                // pJob.setPrintable(this,pFormat);
343
                // try {
344
                // pJob.print(att);
345
                // }
346
                // }
347
        }
348

    
349
        /*
350
         * printView is a recursive method which iterates through the tree structure
351
         * of the view sent to it. If the view sent to printView is a branch view,
352
         * that is one with children, the method calls itself on each of these
353
         * children. If the view is a leaf view, that is a view without children
354
         * which represents an actual piece of text to be painted, printView
355
         * attempts to render the view to the Graphics2D object.
356
         * 
357
         * I. When any view starts after the beginning of the current printable
358
         * page, this means that there are pages to print and the method sets
359
         * pageExists to true. II. When a leaf view is taller than the printable
360
         * area of a page, it cannot, of course, be broken down to fit a single
361
         * page. Such a View will be printed whenever it intersects with the
362
         * Graphics2D clip. III. If a leaf view intersects the printable area of the
363
         * graphics clip and fits vertically within the printable area, it will be
364
         * rendered. IV. If a leaf view does not exceed the printable area of a page
365
         * but does not fit vertically within the Graphics2D clip of the current
366
         * page, the method records that this page should end at the start of the
367
         * view. This information is stored in pageEndY.
368
         */
369
        protected boolean printView(Graphics2D graphics2D, Shape allocation,
370
                        View view) {
371
                boolean pageExists = false;
372
                Rectangle clipRectangle = graphics2D.getClipBounds();
373
                Shape childAllocation;
374
                View childView;
375

    
376
                if (view.getViewCount() > 0) {
377
                        for (int i = 0; i < view.getViewCount(); i++) {
378
                                childAllocation = view.getChildAllocation(i, allocation);
379
                                if (childAllocation != null) {
380
                                        childView = view.getView(i);
381
                                        if (printView(graphics2D, childAllocation, childView)) {
382
                                                pageExists = true;
383
                                        }
384
                                }
385
                        }
386
                } else {
387
                        // I
388
                        if (allocation.getBounds().getMaxY() >= clipRectangle.getY()) {
389
                                pageExists = true;
390
                                // II
391
                                if ((allocation.getBounds().getHeight() > clipRectangle
392
                                                .getHeight())
393
                                                && (allocation.intersects(clipRectangle))) {
394
                                        view.paint(graphics2D, allocation);
395
                                } else {
396
                                        // III
397
                                        if (allocation.getBounds().getY() >= clipRectangle.getY()) {
398
                                                if (allocation.getBounds().getMaxY() <= clipRectangle
399
                                                                .getMaxY()) {
400
                                                        view.paint(graphics2D, allocation);
401
                                                } else {
402
                                                        // IV
403
                                                        if (allocation.getBounds().getY() < pageEndY) {
404
                                                                pageEndY = allocation.getBounds().getY();
405
                                                        }
406
                                                }
407
                                        }
408
                                }
409
                        }
410
                }
411
                return pageExists;
412
        }
413

    
414
        /*
415
         * Method to set the content type the JEditorPane.
416
         */
417
        protected void setContentType(String type) {
418
                jeditorPane.setContentType(type);
419
        }
420

    
421
        /*
422
         * Method to set an HTMLDocument as the Document to print.
423
         */
424
        public void setDocument(HTMLDocument htmlDocument) {
425
                jeditorPane = new JEditorPane();
426
                setDocument("text/html", htmlDocument);
427
        }
428

    
429
        /*
430
         * Method to set the Document to print as the one contained in a
431
         * JEditorPane. This method is useful when Java does not provide direct
432
         * access to a particular Document type, such as a Rich Text Format
433
         * document. With this method such a document can be sent to the
434
         * DocumentRenderer class enclosed in a JEditorPane.
435
         */
436
        public void setDocument(JEditorPane jedPane) {
437
                jeditorPane = jedPane; // new JEditorPane();
438
                // setDocument(jedPane.getContentType(),jedPane.getDocument());
439
        }
440

    
441
        /*
442
         * Method to set a PlainDocument as the Document to print.
443
         */
444
        public void setDocument(PlainDocument plainDocument) {
445
                jeditorPane = new JEditorPane();
446
                setDocument("text/plain", plainDocument);
447
        }
448

    
449
        /*
450
         * Method to set the content type and document of the JEditorPane.
451
         */
452
        protected void setDocument(String type, Document document) {
453
                setContentType(type);
454
                jeditorPane.setDocument(document);
455
        }
456

    
457
        /*
458
         * Method to set the current choice of the width scaling option.
459
         */
460
        public void setScaleWidthToFit(boolean scaleWidth) {
461
                scaleWidthToFit = scaleWidth;
462
        }
463
}