KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > faceless > pdf > LayoutBox


1 // $Id: LayoutBox.java,v 1.6 2003/11/04 17:16:01 mike Exp $
2

3 package org.faceless.pdf;
4
5 import java.util.*;
6 import java.text.*;
7
8 /**
9  * <p>
10  * A <code>LayoutBox</code> is a box for laying out text, which allows a great
11  * deal more control over positioning than the standard
12  * {@link PDFPage#drawText drawText} method.
13  * </p><p>
14  * A LayoutBox has a fixed width but no predefined height. Text and "Boxes" can
15  * be added to the box, and when the box is complete it can be drawn onto a
16  * page using the {@link PDFPage#drawLayoutBox PDFPage.drawLayoutBox} method. At its simplest, the
17  * following will create a single line of Text on the page.
18  * </p>
19  * <pre>
20  * PDFStyle style = new PDFStyle();
21  * style.setFont(new StandardFont(StandardFont.HELVETICA), 12);
22  * style.setFillColor(Color.black);
23  *
24  * <b>LayoutBox box = new LayoutBox(page.getWidth()-100);</b>
25  * <b>box.addText("Hello, World", style, Locale.getDefault());</b>
26  * <b>page.drawLayoutBox(box, 50, page.getHeight()-50);</b>
27  * </pre>
28  * <p>
29  * The LayoutBox class also allows "boxes" to be inserted into the flow, which
30  * can later be used to position images or similar items in the text. For
31  * example, the following code will produce an image in the top-left
32  * hand corner with text wrapping around it:
33  * </p>
34  * <pre>
35  * PDFStyle style = new PDFStyle();
36  * style.setFont(new StandardFont(StandardFont.HELVETICA), 12);
37  * style.setFillColor(Color.black);
38  *
39  * LayoutBox box = new LayoutBox(page.getWidth()-100);
40  * <b>LayoutBox.Box imagebox = box.addBoxLeft(100,100, PDFStyle.TEXTALIGN_BASELINE);</b>
41  * imagebox.setImage(myimage);
42  * box.addText("Hello, World", style, Locale.getDefault());
43  *
44  * page.drawLayoutBox(box, 50, page.getHeight()-50);
45  * </pre>
46  * <p>
47  * Images can also be drawn inline with the {@link #addBoxInline addBoxInline} method, or to
48  * the right with the {@link #addBoxRight addBoxRight} method.
49  * </p>
50  *
51  * @since 1.2
52  * @version $Id: LayoutBox.java,v 1.6 2003/11/04 17:16:01 mike Exp $
53  */

54 public class LayoutBox extends PeeredObject
55 {
56     /**
57      * A flag indicating that the Box created by {@link #addBoxLeft} or {@link #addBoxRight}
58      * does not require either a left or right margin to line up against.
59      */

60      public static final int CLEAR_NONE=0;
61
62     /**
63      * A flag indicating that the Box created by {@link #addBoxLeft} or {@link #addBoxRight}
64      * should always be flat against the left margin - it should have no content to
65      * its left.
66      */

67     public static final int CLEAR_LEFT=1;
68
69     /**
70      * A flag indicating that the Box created by {@link #addBoxLeft} or {@link #addBoxRight}
71      * should always be flat against the right margin - it should have no content to
72      * its right.
73      */

74     public static final int CLEAR_RIGHT=2;
75
76     final org.faceless.pdf2.LayoutBox box;
77
78     Object JavaDoc getPeer()
79     {
80         return box;
81     }
82
83     LayoutBox(org.faceless.pdf2.LayoutBox box)
84     {
85         this.box=box;
86     }
87
88     /**
89      * Create a new LayoutBox of the specified width. Note that if
90      * you're working with bidirectional text, you should use the
91      * other constructor and pass in a Locale.
92      * @param width the width of the LayoutBox, in points
93      */

94     public LayoutBox(float width)
95     {
96     box = new org.faceless.pdf2.LayoutBox(width);
97     }
98
99     /**
100      * Create a new LayoutBox of the specified width, and with the specified
101      * Locale as the parent locale of the LayoutBox. This is necessary in
102      * order to correctly order items on a line during bidirectional text
103      * processing. The Locale specified here is the overall locale of the
104      * LayoutBox, it may be overridden on a phrase-by-phrase basis by
105      * specifying the locale in the <code>addText</code> methods.
106      *
107      * @param width the width of the LayoutBox, in points
108      * @param locale the overall locale of the LayoutBox
109      * @since 1.2.10
110      */

111     public LayoutBox(float width, Locale locale)
112     {
113     box = new org.faceless.pdf2.LayoutBox(width, locale);
114     }
115
116     /**
117      * Set the default style of the box. This is used to determine
118      * the alignment of the text in the box - left, centered, right
119      * or justified. It should be set immediately after the box is
120      * created, or at least before the first line is positioned. If
121      * it is not called, the default style of the box is the style
122      * used in the first call to {@link #addText addText},
123      * {@link #addTextNoBreak addTextNoBreak} or {@link #addLineBreak addLineBreak}
124      *
125      * @param style the default style of the LayoutBox
126      * @see #getStyle
127      */

128     public void setStyle(PDFStyle style)
129     {
130     box.setStyle(style.style);
131     }
132
133     /**
134      * Add a line of text to the LayoutBox. The text may be broken into
135      * smaller units to fit the line, in which case the {@link LayoutBox.Text#getNextTwin} method
136      * can be used to traverse through them.
137      * @param string the text to display
138      * @param style the style in which to display the text
139      * @param locale the locale of the text. With locales where this is unlikely
140      * to make a difference, i.e. western european languages, this may be <code>null</code>
141      * @return a <code>Text</code> object representing this string.
142      */

143     public Text addText(String JavaDoc string, PDFStyle style, Locale locale)
144     {
145     return (Text)PeeredObject.getPeer(box.addText(string, style.style, locale));
146     }
147
148     /**
149      * Add a line of text to the LayoutBox. As for {@link #addText(String,PDFStyle,Locale)},
150      * but the text is not ligaturized first, for speed. If it needs to be, run it
151      * through the {@link PDFFont#ligaturize(char[],int,int,Locale)} method first.
152      *
153      * @param buf the buffer containing the text to add
154      * @param off the offset of the start of the meaningful content of the buffer
155      * @param len the length of the meaningful content of the buffer
156      * @param style the style in which to display the text
157      * @param locale the locale of the text. With locales where this is unlikely
158      * to make a difference, i.e. western european languages, this may be <code>null</code>
159      * @since 1.2.1
160      */

161     public Text addText(char[] buf, int off, int len, PDFStyle style, Locale locale)
162     {
163     return (Text)PeeredObject.getPeer(box.addText(buf, off, len, style.style, locale));
164     }
165
166     /**
167      * Add a line of text to the LayoutBox. The text will not be broken into
168      * smaller units to fit the line, but will appear as one line. It is the
169      * callers responsibilty to ensure the text will actually fit on the line.
170      * @param string the text to display
171      * @param style the style in which to display the text
172      * @param locale the locale of the text. With locales where this is unlikely
173      * to make a difference, i.e. western european languages, this may be <code>null</code>
174      * @return a <code>Text</code> object representing this string.
175      */

176     public Text addTextNoBreak(String JavaDoc string, PDFStyle style, Locale locale)
177     {
178     return (Text)PeeredObject.getPeer(box.addTextNoBreak(string, style.style, locale));
179     }
180
181     /**
182      * Add a line of text to the LayoutBox. As for {@link #addTextNoBreak(String,PDFStyle,Locale)},
183      * but the text is not ligaturized first, for speed. If it needs to be, run it
184      * through the {@link PDFFont#ligaturize(char[],int,int,Locale)} method first.
185      *
186      * @param buf the buffer containing the text to add
187      * @param off the offset of the start of the meaningful content of the buffer
188      * @param len the length of the meaningful content of the buffer
189      * @param style the style in which to display the text
190      * @param locale the locale of the text. With locales where this is unlikely
191      * to make a difference, i.e. western european languages, this may be <code>null</code>
192      * @return a <code>Text</code> object representing this string.
193      * @since 1.2.1
194      */

195     public Text addTextNoBreak(char[] buf, int off, int len, PDFStyle style, Locale locale)
196     {
197     return (Text)PeeredObject.getPeer(box.addTextNoBreak(buf, off, len, style.style, locale));
198     }
199
200     /**
201      * Add a line-break in the specified style. Line Breaks reset the cursor
202      * to the left (or right, for RTL text) side of the LayoutBox, and move the
203      * cursor down the page by <tt>style.getFontLeading()</tt> points
204      * @param style the style defining the font in which to add the linebreak
205      */

206     public void addLineBreak(PDFStyle style)
207     {
208     box.addLineBreak(style.style);
209     }
210
211     /**
212      * Add a new Box which will be appear "inline" - i.e. positioned in the same way
213      * as the text.
214      * @param width the width of the rectangle
215      * @param height the height of the rectangle
216      * @param align how to align the text - one of {@link PDFStyle#TEXTALIGN_TOP},
217      * {@link PDFStyle#TEXTALIGN_MIDDLE} or {@link PDFStyle#TEXTALIGN_BOTTOM}
218      * @return a {@link LayoutBox.Box} representing this object
219      */

220     public Box addBoxInline(float width, float height, int align)
221     {
222     return (Box)PeeredObject.getPeer(box.addBoxInline(width,height,align));
223     }
224
225     /**
226      * Add a new box that takes the full width of the LayoutBox, less the width of
227      * any left or right floating boxes. Use this method for reserving a block in
228      * the middle of the paragraph for other content.
229      * @param height the height of the box.
230      * @since 1.2.1
231      */

232     public Box addBoxFullWidth(float height)
233     {
234     return (Box)PeeredObject.getPeer(box.addBoxFullWidth(height));
235     }
236
237     /**
238      * Add a new Box which will float at the left of the LayoutBox. Content
239      * which follows this rectangle will appear either to the right or below
240      * it, depending on the value of <tt>clearflags</tt>
241      * @param width the width of the rectangle
242      * @param height the height of the rectangle
243      * @param clearflags logical-or of zero or more of {@link #CLEAR_LEFT} or {@link #CLEAR_RIGHT}
244      * @return a {@link LayoutBox.Box} representing this object
245      */

246     public Box addBoxLeft(float width, float height, int clearflags)
247     {
248     return (Box)PeeredObject.getPeer(box.addBoxLeft(width,height,clearflags));
249     }
250
251     /**
252      * Add a new Box which will float at the right of the LayoutBox. Content
253      * which follows this rectangle will appear either to the left or below
254      * it, depending on the value of <tt>clearflags</tt>
255      * @param width the width of the rectangle
256      * @param height the height of the rectangle
257      * @param clearflags logical-or of zero or more of {@link #CLEAR_LEFT} or {@link #CLEAR_RIGHT}
258      * @return a {@link LayoutBox.Box} representing this object
259      */

260     public Box addBoxRight(float width, float height, int clearflags)
261     {
262     return (Box)PeeredObject.getPeer(box.addBoxRight(width,height,clearflags));
263     }
264
265     /**
266      * <p>
267      * Add a horizontal tab to the LayoutBox. Tabs are specified as an array
268      * of floats which represent the position (in points) of the tab stop
269      * from the left of the box. For instance, <code>{ 80, 100, 150 }</code>
270      * would cause the first cursor to move right to 80, 100 or 150 points
271      * from the left of the LayoutBox, depending on how far in it already was.
272      * </p><p>
273      * If the cursor is past the last entry in the array, then tab stops
274      * are assumed to continue over the same width to the right of the box.
275      * The width of these stops is the same as the last specified width - so
276      * in the example above, tab stops 200, 250, 300, 350 and so on are implied.
277      * This means that for the simplest case - a tab stop every 50 points -
278      * all you need to do is specify an array with a single float of <code>{ 50 }</code>.
279      * </p><p>
280      * If there are no further tab stops available on the current line, the
281      * cursor is moved to the start of the next line.
282      * </p><p>
283      * <b>Note</b> Tab stops only work with left-aligned text. Consequently, if the
284      * alignment for the LayoutBox is anything other than <code>TEXTALIGN_LEFT</code>,
285      * an exception is thrown.
286      * </p>
287      * @param stops an array of one or more floats defining the position of tab
288      * stops in points from the left edge of the LayoutBox
289      * @throws IllegalStateException if the LayoutBox is not left-aligned.
290      * @return the cursor position in points from the left edge of the LayoutBox
291      * @since 1.2.10
292      */

293     public float addTab(float[] stops)
294     {
295     return box.addTab(stops);
296     }
297
298     /**
299      * Return true if the LayoutBox is empty, false if it's not
300      */

301     public boolean isEmpty()
302     {
303     return box.isEmpty();
304     }
305
306     /**
307      * Return the height of the positioned items in the LayoutBox, in points
308      * @return the height of the LayoutBox
309      */

310     public float getHeight()
311     {
312     return box.getHeight();
313     }
314
315     /**
316      * Return true if the LayoutBox has been flushed - ie. if there are any items
317      * of text that are still to be positioned. A LayoutBox should be flushed before
318      * the {@link #getBoxes} or the {@link PDFPage#drawLayoutBox PDFPage.drawLayoutBox} methods are called.
319      * @see #flush
320      */

321     public boolean isFlushed()
322     {
323     return box.isFlushed();
324     }
325
326     /**
327      * Flush the flowbox. Flushing causes any items which have not yet been positioned
328      * (specifically any text on the last incomplete line of the LayoutBox) to be positioned.
329      * @see #isFlushed
330      */

331     public void flush()
332     {
333     box.flush();
334     }
335
336     /**
337      * Return the list of boxes which make up the LayoutBox. Unless the LayoutBox has
338      * been {@link #flush flushed}, some of these items may not have a position
339      * specified.
340      * @return a list of {@link LayoutBox.Box} objects which make up the visible contents
341      * of the LayoutBox.
342      * @see #isFlushed
343      * @see #flush
344      */

345     public Box[] getBoxes()
346     {
347     org.faceless.pdf2.LayoutBox.Box[] newboxes = box.getBoxes();
348     Box[] oldboxes = new Box[newboxes.length];
349     for (int i=0;i<newboxes.length;i++) {
350         oldboxes[i]=(Box)PeeredObject.getPeer(newboxes[i]);
351     }
352     return oldboxes;
353     }
354
355     /**
356      * Return the style of the LayoutBox, as set by {@link #setStyle}
357      */

358     public PDFStyle getStyle()
359     {
360         return (PDFStyle)PeeredObject.getPeer(box.getStyle());
361     }
362
363
364
365     //-------------------------------------------------------------------------
366

367
368
369     /**
370      * Split a LayoutBox into two boxes at the specified height. The current box
371      * is shortened and a new box returned containing all the content below the
372      * requested row.
373      * @return a new LayoutBox containing all the data below the requested row.
374      * @since 1.2.1
375      */

376     public LayoutBox splitAt(float splitpos)
377     {
378     return (LayoutBox)PeeredObject.getPeer(box.splitAt(splitpos));
379     }
380
381     /**
382      * Return the number of lines in the LayoutBox. Blank lines are not
383      * included in the total. The LayoutBox should be flushed before
384      * calling this method to ensure the correct result.
385      * @since 1.2.1
386      */

387     public int getNumberOfLines()
388     {
389     return box.getNumberOfLines();
390     }
391
392
393     /**
394      * <p>
395      * A class representing a Box, several of which make up the visible
396      * content of a LayoutBox. Boxs on their own represent nothing but a
397      * space in the LayoutBox layout - if the rectangle is to be filled with
398      * some content (an image, for example), that has to be done separately.
399      * </p><p>
400      * The {@link LayoutBox.Text} class is a subclass of Box which is used to
401      * specifically display a phrase of text.
402      * </p>
403      * @see #getBoxes
404      * @since 1.2
405      */

406     public static class Box extends PeeredObject
407     {
408     final org.faceless.pdf2.LayoutBox.Box box;
409     
410     Box(org.faceless.pdf2.LayoutBox.Box box)
411     {
412         this.box=box;
413     }
414
415     Object JavaDoc getPeer()
416     {
417         return box;
418     }
419
420     /**
421      * Return which line number this text belongs to. The first line of text in the
422      * LayoutBox has line number 0, the next 1 and so on. Blank lines (created by
423      * one or more calls to {@link #addLineBreak} do not count as lines.
424      * Left or right floating boxes will have the same line number as the text next
425      * to the top of the box. If the LayoutBox has not been flushed then this method
426      * may return -1, indicating the line number hasn't been set yet.
427      * @since 1.2.1
428      */

429     public final int getLineNumber()
430     {
431         return box.getLineNumber();
432     }
433
434     /**
435      * Return the left edge of this Box relative to the parent LayoutBox, in points
436      */

437     public final float getLeft()
438     {
439         return box.getLeft();
440     }
441
442     /**
443      * Return the right edge of this Box relative to the parent LayoutBox, in points
444      */

445     public final float getRight()
446     {
447         return box.getRight();
448     }
449
450     /**
451      * Return the top edge of this Box relative to the parent LayoutBox, in points.
452      * Note that in standard PDF geometry, (0,0) is at the bottom left of the page, so
453      * this value will usually be negative, indicating the top is below the top of the
454      * LayoutBox on the page.
455      * @see #getBottom
456      * @see #getLineTop
457      */

458     public final float getTop()
459     {
460         return box.getTop();
461     }
462
463     /**
464      * Return the bottom edge of this Box relative to the parent LayoutBox, in points.
465      * @see #getTop
466      * @see #getLineBottom
467      */

468     public final float getBottom()
469     {
470         return box.getBottom();
471     }
472
473     /**
474      * Return the top edge of the line this Box sits on, relative to the parent
475      * LayoutBox, in points. Note that in standard PDF geometry, (0,0) is at the bottom
476      * left of the page, so this value will usually be negative, indicating the top is
477      * below the top of the LayoutBox on the page. The LayoutBox should be flushed
478      * before this method is called to ensure the correct result
479      * @since 1.2.1
480      * @see #getLineBottom
481      * @see #getTop
482      */

483     public final float getLineTop()
484     {
485         return box.getLineTop();
486     }
487
488     /**
489      * Return the bottom edge of the line this Box sits on, relative to the parent
490      * LayoutBox, in points. Note that in standard PDF geometry, (0,0) is at the bottom
491      * left of the page, so this value will usually be negative, indicating the top is
492      * below the top of the LayoutBox on the page. The LayoutBox should be flushed
493      * before this method is called to ensure the correct result
494      * @since 1.2.1
495      * @see #getLineTop
496      * @see #getTop
497      */

498     public final float getLineBottom()
499     {
500         return box.getLineBottom();
501     }
502
503     public String JavaDoc toString()
504     {
505         return "{l="+getLeft()+" r="+getRight()+" t="+getTop()+" b="+getBottom()+" lt="+getLineTop()+" lb="+getLineBottom()+" #"+getLineNumber()+"}";
506     }
507
508     /**
509      * Set an optional image which goes with this Box. The image will be automatically
510      * drawn when the LayoutBox is drawn to a page.
511      */

512     public void setImage(PDFImage image)
513     {
514         box.setImage(image.image);
515     }
516
517     /**
518      * Return the optional image set by the {@link #setImage setImage} method,
519      * or <code>null</code> if no image was defined.
520      */

521     public PDFImage getImage()
522     {
523         return (PDFImage)PeeredObject.getPeer(box.getImage());
524     }
525     }
526
527     /**
528      * The <tt>Text</tt> class is a subclass of {@link LayoutBox.Box} which
529      * is specifically for displaying Text.
530      * @since 1.2
531      */

532     public static class Text extends Box
533     {
534         Text(org.faceless.pdf2.LayoutBox.Text text)
535     {
536         super(text);
537     }
538
539     /**
540      * Return the style of this Text item
541      */

542     public PDFStyle getStyle()
543     {
544         return (PDFStyle)PeeredObject.getPeer(((org.faceless.pdf2.LayoutBox.Text)this.box).getStyle());
545     }
546
547     /**
548      * Return the content of this Text item as a String.
549      */

550     public String JavaDoc getText()
551     {
552         return ((org.faceless.pdf2.LayoutBox.Text)this.box).getText();
553     }
554
555     /**
556      * Return the next "twin" of this Text item, or <code>null</code> if none
557      * exist. A "twin" is a second (or third, fourth etc.) <code>Text</code> object
558      * which was created when a phrase of text was split at the end of the line.
559      */

560     public Text getNextTwin()
561     {
562         return (Text)PeeredObject.getPeer(((org.faceless.pdf2.LayoutBox.Text)this.box).getNextTwin());
563     }
564
565     /**
566      * Replace the contents of this Text box with a different line of text.
567      * This method does <b>not</b> reflow the text, so you need to ensure
568      * that the contents of the box are the same length by sticking to digits
569      * or using a fixed-width font. This method is not recommended for normal
570      * use.
571      * @since 1.2.1
572      */

573     public void setText(String JavaDoc s)
574     {
575         ((org.faceless.pdf2.LayoutBox.Text)this.box).setText(s);
576     }
577     }
578 }
579
Popular Tags