KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > core > print > cHTMLPart


1 //The contents of this file are subject to the Mozilla Public License Version 1.1
2
//(the "License"); you may not use this file except in compliance with the
3
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
4
//
5
//Software distributed under the License is distributed on an "AS IS" basis,
6
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
7
//for the specific language governing rights and
8
//limitations under the License.
9
//
10
//The Original Code is "The Columba Project"
11
//
12
//The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
13
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
14
//
15
//All Rights Reserved.
16
//
17
package org.columba.core.print;
18
19 import java.awt.Graphics2D JavaDoc;
20 import java.awt.Rectangle JavaDoc;
21 import java.awt.Shape JavaDoc;
22 import java.awt.geom.Point2D JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.net.URL JavaDoc;
25 import java.util.logging.Logger JavaDoc;
26
27 import javax.swing.JTextPane JavaDoc;
28 import javax.swing.text.Document JavaDoc;
29 import javax.swing.text.View JavaDoc;
30 import javax.swing.text.html.HTMLDocument JavaDoc;
31 import javax.swing.text.html.HTMLEditorKit JavaDoc;
32
33
34 /**
35  * Class for representing a HTML print object. Objects of this
36  * type is intended for inclusion in cDocument objects for printing.
37  * Division into multiple pages represented by cPage is supported.
38  *
39  * @author Karl Peder Olesen (karlpeder), 20030601
40  *
41  */

42 public class cHTMLPart extends cPrintObject {
43
44     private static final Logger JavaDoc LOG = Logger.getLogger("org.columba.core.print");
45
46     /** IContainer holding the HTML to be printed (used to control layout etc. */
47     private JTextPane JavaDoc mPane = null;
48
49     /** Y-coordinate in mPane to start printing at */
50     private cUnit mStartY = new cCmUnit(0.0);
51
52     /** Flag indicating whether scaling of the print is allowed (default is no) */
53     private boolean mScaleAllowed;
54
55     /**
56      * Creates a new empty HTML print object.
57      * As default, scaling the print to fit is not allowed
58      */

59     public cHTMLPart() {
60         this(false);
61     }
62
63     /**
64      * Creates a new empty HTML print object and sets whether scaling is allowed
65      * @param scaleAllowed If true, the print is allowed to "scale to fit"
66      */

67     public cHTMLPart(boolean scaleAllowed) {
68         super();
69         mScaleAllowed = scaleAllowed;
70     }
71
72     /**
73      * Sets the HTML document to be printed.
74      * @param html HTML document to be printed
75      */

76     public void setHTML(HTMLDocument JavaDoc html) {
77         mPane = new JTextPane JavaDoc();
78         mPane.setDoubleBuffered(false);
79         mPane.setContentType("text/html");
80         mPane.setDocument(html); // "store" html in jTextPane container
81
mStartY = new cCmUnit(0.0); // reset starting position in y-direction
82
}
83
84     /**
85      * Sets the HTML document to be printed.
86      * Precondition: The URL given contains a HTML document
87      * @param url url to file with HTML document
88      * @throws IOException if errors occur while reading HTML document from file
89      */

90     public void setHTML(URL JavaDoc url) throws IOException JavaDoc {
91         /*
92          * By using an instance of SyncHTMLEditorKit, the html should load
93          * synchroniously - so everything is loaded before printing starts
94          */

95         mPane = new JTextPane JavaDoc();
96         mPane.setDoubleBuffered(false);
97         mPane.setEditorKit(new SyncHTMLEditorKit());
98         mPane.setContentType("text/html");
99         mPane.setPage(url);
100         mStartY = new cCmUnit(0.0); // reset starting position in y-direction
101
}
102
103     /**
104      * Sets the starting position in y-direction. A starting position != 0 is used
105      * when printing anything but the first page. This bookkeeping is usually done
106      * internally when creating pagebreaks using the method breakBlock.
107      * Therefore this method has not been made public.
108      * @param y Starting position in y-direction
109      */

110     protected void setStartY(cUnit y) {
111         mStartY = new cCmUnit(y);
112     }
113
114     /**
115      * Prints the contents of this HTML print object using the supplied
116      * Graphics2D object.
117      * @param g Used for rendering (i.e. printing) this HTML print object
118      * @see org.columba.core.print.cPrintObject#print(java.awt.Graphics2D)
119      */

120     public void print(Graphics2D JavaDoc g) {
121         /*
122              * *20030609, karlpeder* Introduced scaling
123              */

124         computePositionAndSize();
125
126         // get origin & size information (height as "total" height minus current pos.)
127
cPoint origin = getDrawingOrigin();
128         double width = getDrawingSize().getWidth().getPoints();
129         double height = getPage().getPrintableAreaSize().getHeight()
130                             .sub(getLocation().getY()).getPoints();
131
132         /*
133          * TODO (@author karlpeder): Guess that right thing to do is to get height as getDrawingSize().getHeight(),
134          * since this should take top- and bottom margin of this print
135          * object into account. But the height seems not to be set
136          * correctly in computePositionAndSize() (*20030604, karlpeder*)
137          */

138         // set size of mPane according to the available width
139
// and fetch root view
140
mPane.setSize((int) width, Integer.MAX_VALUE);
141         mPane.validate();
142
143         View JavaDoc rootView = mPane.getUI().getRootView(mPane);
144
145         // scale the graphics
146
double scale = scaleFactor(new cPointUnit(width));
147         g.scale(scale, scale);
148
149         // set clipping for the graphics object
150
Shape JavaDoc oldClip = g.getClip();
151         g.setClip((int) (origin.getX().getPoints() / scale),
152             (int) (origin.getY().getPoints() / scale), (int) (width / scale),
153             (int) (height / scale));
154
155         // translate g to line up with origin of print area (trans 1)
156
Point2D.Double JavaDoc trans = new Point2D.Double JavaDoc(g.getClipBounds().getX(),
157                 g.getClipBounds().getY());
158         g.translate(trans.getX(), trans.getY());
159
160         // set allocation (defines print area together with the clipping
161
// and translation made above), and print...
162
Rectangle JavaDoc allocation = new Rectangle JavaDoc(0, (int) -mStartY.getPoints(),
163                 (int) mPane.getMinimumSize().getWidth(),
164                 (int) mPane.getPreferredSize().getHeight());
165         printView(g, rootView, allocation, height / scale);
166
167         // translate graphics object back to original position and reset clip and scaling
168
g.translate(-trans.getX(), -trans.getY());
169         g.scale(1 / scale, 1 / scale);
170         g.setClip(oldClip);
171     }
172
173     /**
174      * Private utility to print a view (called from the print method).<br>
175      * The traversal through views and their children is the same as in
176      * calcBreakHeightForView.
177      *
178      * @param g Graphics object to print on
179      * @param view The View object to operate on
180      * @param allocation Allocation for the view (where to render)
181      * @param maxHeight Views starting after maxHeight is not printed
182      */

183     private void printView(Graphics2D JavaDoc g, View JavaDoc view, Shape JavaDoc allocation,
184         double maxHeight) {
185         if (view.getViewCount() > 0) {
186             // child views exist - operate recursively on these
187
Shape JavaDoc childAllocation;
188             View JavaDoc childView;
189
190             for (int i = 0; i < view.getViewCount(); i++) {
191                 childAllocation = view.getChildAllocation(i, allocation);
192
193                 if (childAllocation != null) {
194                     childView = view.getView(i);
195
196                     // handle child view by recursive call
197
printView(g, childView, childAllocation, maxHeight);
198                 }
199             }
200         } else {
201             // no childs - we have a leaf view (i.e. with contents)
202
double viewStartY = allocation.getBounds().getY();
203
204             if ((viewStartY >= 0) && (viewStartY < maxHeight)) {
205                 // view starts on page - print it
206
view.paint(g, allocation);
207             }
208         }
209     }
210
211     /**
212      * Returns the size of this HTML print object subject to the
213      * given width.<br>
214      * If scaling is allowed, and the contents can not be fitted inside
215      * the given width, the content is scaled to fit before the size is
216      * returned, i.e. the scaled size is returned.<br>
217      * NB: The height returned will always be from the starting point
218      * (which could be different from the top) to the end of the current
219      * content, independent on whether everything will or can be printed
220      * on onto one page.
221      *
222      * @param maxWidth Max. allowable width this print object can occupy
223      *
224      * @see org.columba.core.print.cPrintObject#getSize(org.columba.core.print.cUnit)
225      */

226     public cSize getSize(cUnit maxWidth) {
227         /*
228              * *20030609, karlpeder* Introduced scaling
229              */

230
231         // resize jTextPane component to calculate height and get it
232
double width = maxWidth.sub(leftMargin).sub(rightMargin).getPoints();
233         mPane.setSize((int) width, Integer.MAX_VALUE);
234         mPane.validate();
235
236         double height = mPane.getPreferredSize().getHeight();
237
238         // correct for starting position if printing should not start at the top
239
height = height - mStartY.getPoints();
240
241         // calculate size and return it
242
double scale = scaleFactor(new cPointUnit(width));
243         cUnit w = new cCmUnit(maxWidth); // width unchanged
244
cUnit h = new cCmUnit();
245         h.setPoints(height); // height of content
246
h.addI(topMargin); // + top margin
247
h.addI(bottomMargin); // + bottom margin
248
h.setPoints(h.getPoints() * scale); // height corrected for scaling
249

250         return new cSize(w, h);
251     }
252
253     /**
254      * Returns the scale, which should be applied to the content to make it
255      * fit inside the given width.<br>
256      * If scaling is not allowed, 1.0 will be returned.<br>
257      * If the content fits inside the given width, or is smaller, 1.0 will
258      * be returned.
259      * @author Karl Peder Olesen (karlpeder), 20030609
260     * @param maxWidth Max. allowable width this print object can occupy
261      * @return scale to be applied to make the contents fit inside the given width
262      */

263     private double scaleFactor(cUnit maxWidth) {
264         mPane.validate(); // ensure contents is layed out properly
265

266         if (!mScaleAllowed) {
267             LOG.info("Scaling not active - returning scale=1.0");
268
269             return 1.0;
270         } else {
271             // calculate scaling and return it
272
double width = maxWidth.sub(leftMargin).sub(rightMargin).getPoints();
273             double scale;
274
275             if (mPane.getMinimumSize().getWidth() > width) {
276                 scale = width / mPane.getMinimumSize().getWidth();
277             } else {
278                 scale = 1.0; // do not scale up, i.e. no scale factor above 1.0
279
}
280
281             LOG.info("Returning scale=" + scale);
282
283             return scale;
284         }
285     }
286
287     /**
288      * Divides (breaks) this HTML print object into a remainder (which fits
289      * inside the given max height) and the rest. The remainder is returned and
290      * "the rest" is stored by modifying this object.
291      *
292      * @param w Max. allowable width this print object can occupy
293      * @param maxHeight Max. allowable height before breaking
294      * @return The part of the print object, which fits inside the given max height
295      *
296      * @see org.columba.core.print.cPrintObject#breakBlock(org.columba.core.print.cUnit, org.columba.core.print.cUnit)
297      */

298     public cPrintObject breakBlock(cUnit w, cUnit maxHeight) {
299         /*
300              * *20030609, karlpeder* Introduced scaling
301              */

302
303         // get size of content (width, height is size without scaling)
304
cSize contentSize = this.getSize(w); // scaled size
305
double scale = scaleFactor(w);
306         int width = (int) (contentSize.getWidth().getPoints() / scale);
307         int height = (int) (contentSize.getHeight().getPoints() / scale);
308         int startY = (int) mStartY.getPoints();
309
310         // define allocation rectangle (startY is used to compensate for
311
// different start point if printing shall not start from the top)
312
Rectangle JavaDoc allocation = new Rectangle JavaDoc(0, -startY, width, height
313                 + startY);
314
315         // set initial value for height where this print object should be broken
316
double breakHeight = maxHeight.getPoints() / scale; // in points, without scale
317

318         /*
319          * calculate a new break height according to the contents, possibly
320          * smaller to break before some content (i.e. not to break in the
321          * middle of something
322          */

323         View JavaDoc rootView = mPane.getUI().getRootView(mPane);
324         breakHeight = calcBreakHeightFromView(rootView, allocation, breakHeight);
325
326         // create remainder
327
cHTMLPart remainder = new cHTMLPart(mScaleAllowed);
328         remainder.setHTML((HTMLDocument JavaDoc) mPane.getDocument());
329         remainder.setStartY(mStartY);
330
331         // modify "this" to start where remainder ends
332
cUnit newStartY = new cCmUnit();
333
334         if (breakHeight < height) {
335             newStartY.setPoints(mStartY.getPoints() + breakHeight);
336         } else { // this happends if there's nothing left for the next page
337
newStartY = mStartY.add(contentSize.getHeight());
338         }
339
340         this.setStartY(newStartY);
341
342         return remainder;
343     }
344
345     /**
346      * Private utility to calculate break height based on the contents
347      * of a view. If the break height calculated is not smaller than the
348      * actual break height, actBreakHeight is returned unchanged.
349      * @param view The View object to operate on
350      * @param allocation Allocation for the view (where to render)
351      * @param actBreakHeight Actual break height
352      */

353     private double calcBreakHeightFromView(View JavaDoc view, Shape JavaDoc allocation,
354         double actBreakHeight) {
355         if (view.getViewCount() > 0) {
356             // child views exist - operate recursively on these
357
double breakHeight = actBreakHeight;
358             Shape JavaDoc childAllocation;
359             View JavaDoc childView;
360
361             for (int i = 0; i < view.getViewCount(); i++) {
362                 childAllocation = view.getChildAllocation(i, allocation);
363
364                 if (childAllocation != null) {
365                     childView = view.getView(i);
366
367                     // calculate break height for child, and use updated
368
// value in the further processing
369
breakHeight = calcBreakHeightFromView(childView,
370                             childAllocation, breakHeight);
371                 }
372             }
373
374             return breakHeight; // return (possibly) updated value
375
} else {
376             // no childs - we have a leaf view (i.e. with contents)
377
double allocY = allocation.getBounds().getY();
378             double allocMaxY = allocation.getBounds().getMaxY();
379             if ((allocY >= 0) && (allocY < actBreakHeight)
380                     && (allocMaxY > actBreakHeight)) {
381                 // view starts on page and exceeds it
382

383                 /*
384                  * If the height of a view exceeds the paperheight, there should
385                  * be no break before (since it will be impossible to fit it in
386                  * anywhere => an infinite loop). We don't have access to the
387                  * pageheight here, therefore an "educated guess" is made:
388                  * No breaks are inserted before views starting within the first
389                  * 1% (chosen to avoid round-off errors) of the available space
390                  * given by actBreakHeight. If the view starts after the first 1%,
391                  * a break is inserted and the view will start at the top of the
392                  * next page (i.e. withing the first 1% this time).
393                  */

394                 if (allocY < (actBreakHeight * 0.01)) {
395                     return actBreakHeight; // unchanged, i.e. no breaks before this view
396
} else {
397                     // view can be broken
398
if (allocY < actBreakHeight) {
399                         return allocY; // break before start of view
400
} else {
401                         return actBreakHeight; // unchanged
402
}
403                 }
404             } else {
405                 return actBreakHeight; // unchanged
406
}
407         }
408     }
409 }
410
411
412 /**
413  * Utility class used for loading html synchroniously into a jTextPane
414  * @author Karl Peder Olesen (karlpeder), 20030604
415  */

416 class SyncHTMLEditorKit extends HTMLEditorKit JavaDoc {
417     /**
418      * Create an uninitialized text storage model that is appropriate for
419      * this type of editor.<br>
420      * The document returned will load synchroniously.
421      *
422      * @see javax.swing.text.EditorKit#createDefaultDocument()
423      */

424     public Document JavaDoc createDefaultDocument() {
425         Document JavaDoc doc = super.createDefaultDocument();
426         ((HTMLDocument JavaDoc) doc).setAsynchronousLoadPriority(-1);
427
428         return doc;
429     }
430 }
431
Popular Tags