KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > layout > LineArea


1 /*
2  * $Id: LineArea.java,v 1.53.2.19 2003/06/06 07:27:59 jeremias Exp $
3  * ============================================================================
4  * The Apache Software License, Version 1.1
5  * ============================================================================
6  *
7  * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without modifica-
10  * tion, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if any, must
20  * include the following acknowledgment: "This product includes software
21  * developed by the Apache Software Foundation (http://www.apache.org/)."
22  * Alternately, this acknowledgment may appear in the software itself, if
23  * and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. The names "FOP" and "Apache Software Foundation" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * apache@apache.org.
29  *
30  * 5. Products derived from this software may not be called "Apache", nor may
31  * "Apache" appear in their name, without prior written permission of the
32  * Apache Software Foundation.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  * ============================================================================
45  *
46  * This software consists of voluntary contributions made by many individuals
47  * on behalf of the Apache Software Foundation and was originally created by
48  * James Tauber <jtauber@jtauber.com>. For more information on the Apache
49  * Software Foundation, please see <http://www.apache.org/>.
50  */

51 package org.apache.fop.layout;
52
53 // Java
54
import java.util.ArrayList JavaDoc;
55 import java.util.StringTokenizer JavaDoc;
56 import java.awt.Rectangle JavaDoc;
57
58 // FOP
59
import org.apache.fop.datatypes.IDReferences;
60 import org.apache.fop.fo.properties.Hyphenate;
61 import org.apache.fop.fo.properties.LeaderAlignment;
62 import org.apache.fop.fo.properties.LeaderPattern;
63 import org.apache.fop.fo.properties.TextAlign;
64 import org.apache.fop.fo.properties.VerticalAlign;
65 import org.apache.fop.fo.properties.WhiteSpaceCollapse;
66 import org.apache.fop.fo.properties.WrapOption;
67 import org.apache.fop.layout.hyphenation.Hyphenation;
68 import org.apache.fop.layout.hyphenation.Hyphenator;
69 import org.apache.fop.layout.inline.*;
70 import org.apache.fop.messaging.MessageHandler;
71 import org.apache.fop.render.Renderer;
72
73 public class LineArea extends Area {
74
75     protected int lineHeight;
76     protected int halfLeading;
77     protected int nominalFontSize;
78     protected int nominalGlyphHeight;
79
80     protected int allocationHeight;
81     protected int startIndent;
82     protected int endIndent;
83
84     private int placementOffset;
85     private int textAlign;
86
87     private FontState currentFontState; // not the nominal, which is
88
// in this.fontState
89
private float red, green, blue;
90     private int wrapOption;
91     private int whiteSpaceCollapse;
92     private int vAlign;
93
94     /* hyphenation */
95     private HyphenationProps hyphProps;
96
97     /*
98      * the width of text that has definitely made it into the line
99      * area
100      */

101     private int finalWidth = 0;
102
103     /* the position to shift a link rectangle in order to compensate
104      * for links embedded within a word
105      */

106     protected int embeddedLinkStart = 0;
107
108     /* the width of the current word so far */
109     // protected int wordWidth = 0;
110

111     /* values that prev (below) may take */
112     protected static final int NOTHING = 0;
113     protected static final int WHITESPACE = 1;
114     protected static final int TEXT = 2;
115     protected static final int MULTIBYTECHAR = 3;
116
117     /* the character type of the previous character */
118     private int prev = NOTHING;
119
120     /* width of spaces before current word */
121     private int spaceWidth = 0;
122
123     /*
124      * the inline areas that have not yet been added to the line
125      * because subsequent characters to come (in a different addText)
126      * may be part of the same word
127      */

128     private ArrayList JavaDoc pendingAreas = new ArrayList JavaDoc();
129
130     /* the width of the pendingAreas */
131     private int pendingWidth = 0;
132
133     /* text-decoration of the previous text */
134     protected boolean prevUlState = false;
135     protected boolean prevOlState = false;
136     protected boolean prevLTState = false;
137
138     // Whether the line has already be aligned text and expanded
139
// leaders.
140
private boolean aligned = false;
141     private boolean hasPageNumbers = false;
142
143     public class Leader {
144         int leaderPattern;
145         int leaderLengthMinimum;
146         int leaderLengthOptimum;
147         int leaderLengthMaximum;
148         int ruleStyle;
149         int ruleThickness;
150         int leaderPatternWidth;
151         int leaderAlignment;
152         FontState fontState;
153         float red;
154         float green;
155         float blue;
156         int placementOffset;
157         int position;
158
159         Leader(int leaderPattern, int leaderLengthMinimum,
160                int leaderLengthOptimum, int leaderLengthMaximum,
161                int ruleStyle, int ruleThickness,
162                int leaderPatternWidth, int leaderAlignment,
163                FontState fontState,
164                float red, float green, float blue,
165                int placementOffset,
166                int position) {
167             this.leaderPattern=leaderPattern;
168             this.leaderLengthMinimum=leaderLengthMinimum;
169             this.leaderLengthOptimum=leaderLengthOptimum;
170             this.leaderLengthMaximum=leaderLengthMaximum;
171             this.ruleStyle=ruleStyle;
172             this.ruleThickness=ruleThickness;
173             this.leaderPatternWidth=leaderPatternWidth;
174             this.leaderAlignment=leaderAlignment;
175             this.fontState=fontState;
176             this.red=red;
177             this.green=green;
178             this.blue=blue;
179             this.placementOffset=placementOffset;
180             this.position = position;
181         }
182         void expand() {
183             char dot = '.';
184             int dotWidth = fontState.getCharWidth(dot);
185             char space = ' ';
186             int spaceWidth = fontState.getCharWidth(space);
187             int idx=children.indexOf(this);
188             children.remove(this);
189             switch (leaderPattern) {
190             case LeaderPattern.SPACE:
191                 InlineSpace spaceArea = new InlineSpace(leaderLengthOptimum
192                                                         , false);
193                 children.add(idx,spaceArea);
194                 break;
195             case LeaderPattern.RULE:
196                 LeaderArea leaderArea = new LeaderArea(fontState, red, green,
197                                                        blue, "",
198                                                        leaderLengthOptimum,
199                                                        leaderPattern,
200                                                        ruleThickness,
201                                                        ruleStyle);
202                 leaderArea.setYOffset(placementOffset);
203                 children.add(idx,leaderArea);
204                 break;
205             case LeaderPattern.DOTS:
206                 // if the width of a dot is larger than leader-pattern-width
207
// ignore this property
208
if (this.leaderPatternWidth < dotWidth) {
209                     this.leaderPatternWidth = 0;
210                 }
211                 // if value of leader-pattern-width is 'use-font-metrics' (0)
212
if (this.leaderPatternWidth == 0) {
213                     if (dotWidth == 0) {
214                         MessageHandler.errorln("char " + dot
215                                                + " has width 0. Using width 100 instead.");
216                         dotWidth = 100;
217                     }
218                     // if leader-alignment is used, calculate space to
219
// insert before leader so that all dots will be
220
// parallel.
221
if (leaderAlignment == LeaderAlignment.REFERENCE_AREA) {
222                         int nextRepeatedLeaderPatternCycle
223                             = (int)Math.ceil((double)position/(double)dotWidth);
224                         int spaceBeforeLeader =
225                             dotWidth * nextRepeatedLeaderPatternCycle
226                             - position;
227                         // appending indent space leader-alignment setting
228
// InlineSpace to false, so it is not used in line
229
// justification
230
if (spaceBeforeLeader > 0) {
231                             children.add(idx, new InlineSpace(spaceBeforeLeader,
232                                                               false));
233                             idx++;
234                             // shorten leaderLength, otherwise - in
235
// case of leaderLength=remaining length -
236
// it will cut off the end of leaderlength
237
leaderLengthOptimum -= spaceBeforeLeader;
238                         }
239                     }
240                     int factor = (int)Math.floor(leaderLengthOptimum / dotWidth);
241                     char[] leaderChars = new char[factor];
242                     for (int i = 0; i < factor; i++) {
243                         leaderChars[i] = dot;
244                     }
245                     String JavaDoc leaderWord = new String JavaDoc(leaderChars);
246                     int leaderWordWidth = fontState.getWordWidth(leaderWord);
247                     WordArea leaderPatternArea =
248                         new WordArea(fontState, red, green, blue,
249                                      leaderWord,leaderWordWidth);
250                     leaderPatternArea.setYOffset(placementOffset);
251                     children.add(idx, leaderPatternArea);
252                     int spaceAfterLeader = leaderLengthOptimum
253                         - leaderWordWidth;
254                     if (spaceAfterLeader!=0) {
255                         children.add(idx+1, new InlineSpace(spaceAfterLeader,
256                                                             false));
257                     }
258                 } else {
259                     // if leader-alignment is used, calculate space to
260
// insert before leader so that all dots will be
261
// parallel.
262
if (leaderAlignment == LeaderAlignment.REFERENCE_AREA) {
263                         int nextRepeatedLeaderPatternCycle
264                             = (int)Math.ceil((double)position/(double)leaderPatternWidth);
265                         int spaceBeforeLeader =
266                             leaderPatternWidth * nextRepeatedLeaderPatternCycle
267                             - position;
268                         // appending indent space leader-alignment setting
269
// InlineSpace to false, so it is not used in line
270
// justification
271
if (spaceBeforeLeader > 0) {
272                             children.add(idx, new InlineSpace(spaceBeforeLeader,
273                                                               false));
274                             idx++;
275                             // shorten leaderLength, otherwise - in
276
// case of leaderLength=remaining length -
277
// it will cut off the end of leaderlength
278
leaderLengthOptimum -= spaceBeforeLeader;
279                         }
280                     }
281                     // calculate the space to insert between the dots
282
// and create a inline area with this width
283
int dotsFactor =
284                         (int)Math.floor(((double)leaderLengthOptimum)
285                                         / ((double)leaderPatternWidth));
286                     // add combination of dot + space to fill leader
287
// is there a way to do this in a more effective way?
288
for (int i = 0; i < dotsFactor; i++) {
289                         InlineSpace spaceBetweenDots =
290                             new InlineSpace(leaderPatternWidth - dotWidth,
291                                             false);
292                         WordArea leaderPatternArea =
293                             new WordArea(this.fontState,
294                                          this.red, this.green, this.blue,
295                                          new String JavaDoc("."), dotWidth);
296                         leaderPatternArea.setYOffset(placementOffset);
297                         children.add(idx,leaderPatternArea);
298                         idx++;
299                         children.add(idx,spaceBetweenDots);
300                         idx++;
301                     }
302                     // append at the end some space to fill up to leader length
303
children.add(idx,new InlineSpace(leaderLengthOptimum
304                                                      - dotsFactor
305                                                      * leaderPatternWidth));
306                     idx++;
307                 }
308                 break;
309                 // leader pattern use-content not implemented.
310
case LeaderPattern.USECONTENT:
311                 MessageHandler.errorln("leader-pattern=\"use-content\" not "
312                                        + "supported by this version of Fop");
313                 return;
314             }
315         }
316     }
317  
318     public LineArea(FontState fontState, int lineHeight, int halfLeading,
319                     int allocationWidth, int startIndent, int endIndent,
320                     LineArea prevLineArea) {
321         super(fontState);
322
323         this.currentFontState = fontState;
324         this.lineHeight = lineHeight;
325         this.nominalFontSize = fontState.getFontSize();
326         this.nominalGlyphHeight = fontState.getAscender()
327             - fontState.getDescender();
328
329         this.placementOffset = fontState.getAscender();
330         this.contentRectangleWidth = allocationWidth - startIndent
331             - endIndent;
332         this.fontState = fontState;
333
334         this.allocationHeight = this.nominalGlyphHeight;
335         this.halfLeading = this.lineHeight - this.allocationHeight;
336
337         this.startIndent = startIndent;
338         this.endIndent = endIndent;
339
340         if (prevLineArea != null) {
341             // There might be InlineSpaces at the beginning
342
// that should not be there - eat them
343
boolean eatMoreSpace = true;
344             pendingWidth = prevLineArea.pendingWidth;
345
346             for (int i = 0; i < prevLineArea.pendingAreas.size(); i++) {
347                 Object JavaDoc b = prevLineArea.pendingAreas.get(i);
348                 if (eatMoreSpace) {
349                     if (b instanceof InlineSpace) {
350                         InlineSpace is = (InlineSpace)b;
351                         if (is.isEatable()) {
352                             pendingWidth -= is.getSize();
353                         } else {
354                             eatMoreSpace = false;
355                             pendingAreas.add(b);
356                         }
357                     } else {
358                         eatMoreSpace = false;
359                         pendingAreas.add(b);
360                     }
361                 } else {
362                     pendingAreas.add(b);
363                 }
364             }
365             prevLineArea.pendingWidth=0;
366             prevLineArea.pendingAreas=null;
367         }
368     }
369
370     public void render(Renderer renderer) {
371         if (pendingWidth > 0) {
372             MessageHandler.error("Areas pending, text probably lost in line"
373                                  + getLineText());
374         }
375         if (hasPageNumbers) {
376             IDReferences idReferences = renderer.getIDReferences();
377             for (int i = 0; i < children.size(); i++) {
378                 Object JavaDoc o = children.get(i);
379                 if ( o instanceof PageNumberInlineArea) {
380                     PageNumberInlineArea pia = (PageNumberInlineArea)o;
381                     FontState piaFontState = pia.getFontState();
382                     finalWidth-=piaFontState.getWordWidth(pia.getText());
383                     pia.resolve(idReferences);
384                     finalWidth+=piaFontState.getWordWidth(pia.getText());
385                 }
386             }
387         }
388         if (!aligned) {
389             int padding = 0;
390             switch (textAlign) {
391             case TextAlign.START: // left
392
padding = this.getContentWidth() - finalWidth;
393                 endIndent += padding;
394                 for (int i = 0; i < children.size(); i++) {
395                     Object JavaDoc o = children.get(i);
396                     if (o instanceof LineArea.Leader) {
397                         LineArea.Leader leader = (LineArea.Leader)o;
398                         leader.expand();
399                     }
400                 }
401                 break;
402             case TextAlign.END: // right
403
padding = this.getContentWidth() - finalWidth;
404                 startIndent += padding;
405                 for (int i = 0; i < children.size(); i++) {
406                     Object JavaDoc o = children.get(i);
407                     if (o instanceof LineArea.Leader) {
408                         LineArea.Leader leader = (LineArea.Leader)o;
409                         leader.expand();
410                     }
411                 }
412                 break;
413             case TextAlign.CENTER: // center
414
padding = (this.getContentWidth() - finalWidth) / 2;
415                 startIndent += padding;
416                 endIndent += padding;
417                 for (int i = 0; i < children.size(); i++) {
418                     Object JavaDoc o = children.get(i);
419                     if (o instanceof LineArea.Leader) {
420                         LineArea.Leader leader = (LineArea.Leader)o;
421                         leader.expand();
422                     }
423                 }
424                 break;
425             case TextAlign.JUSTIFY: // justify
426
// first pass - count the spaces
427
int leaderCount = 0;
428                 int spaceCount = 0;
429                 for (int i = 0; i < children.size(); i++ ) {
430                     Object JavaDoc o = children.get(i);
431                     if (o instanceof InlineSpace) {
432                         InlineSpace space = (InlineSpace)o;
433                         if (space.getResizeable()) {
434                             spaceCount++;
435                         }
436                     } else if(o instanceof LineArea.Leader) {
437                         leaderCount++;
438                     }
439                 }
440                 padding = (this.getContentWidth() - finalWidth);
441                 if (padding!=0) {
442                     if (leaderCount>0) {
443                         int offset=0;
444                         for (int i = 0; i < children.size(); i++) {
445                             Object JavaDoc o = children.get(i);
446                             if (o instanceof LineArea.Leader) {
447                                 LineArea.Leader leader = (LineArea.Leader)o;
448                                 int leaderExpansionMaximum=
449                                     leader.leaderLengthMaximum - leader.leaderLengthOptimum;
450                                 int leaderExpansionMinimum=
451                                     leader.leaderLengthMinimum - leader.leaderLengthOptimum;
452                                 if (leaderExpansionMaximum < padding) {
453                                     leader.leaderLengthOptimum =
454                                         leader.leaderLengthMaximum;
455                                     leader.expand();
456                                     padding-=leaderExpansionMaximum;
457                                     offset+=leaderExpansionMaximum;
458                                 } else if (padding < leaderExpansionMinimum) {
459                                     leader.leaderLengthOptimum =
460                                         leader.leaderLengthMinimum;
461                                     leader.expand();
462                                     padding-=leaderExpansionMinimum;
463                                     offset+=leaderExpansionMinimum;
464                                 } else {
465                                     leader.leaderLengthOptimum += padding;
466                                     leader.expand();
467                                     padding=0;
468                                     offset+=padding;
469                                 }
470                             } else if (o instanceof InlineArea) {
471                                 ((InlineArea)o).setXOffset(((InlineArea)o).getXOffset() + offset);
472                             }
473                         }
474                     }
475                     if (padding != 0) {
476                         if (spaceCount > 0) {
477                             if (padding > 0) {
478                                 // The line is actually short of
479
// padding mod spaceCount
480
// millipoints. Should implement
481
// Bresenham-like algorithm for
482
// compensating, but it's not worth
483
// yet.
484
// If there are ref-area aligned leaders,
485
// they will be no longer aligned.
486
padding = padding/spaceCount;
487                                 spaceCount = 0;
488                                 // second pass - add additional space
489
for (int i = 0; i < children.size(); i++) {
490                                     Object JavaDoc o = children.get(i);
491                                     if (o instanceof InlineSpace) {
492                                         InlineSpace space = (InlineSpace)o;
493                                         if (space.getResizeable()) {
494                                             space.setSize(space.getSize() + padding);
495                                             spaceCount++;
496                                         }
497                                     } else if (o instanceof InlineArea) {
498                                         ((InlineArea)o).setXOffset(((InlineArea)o).getXOffset() + i * padding);
499                                     }
500                                 }
501                             } else {
502                                 MessageHandler.log("Area overflow in line "
503                                                    + getLineText());
504                             }
505                         } else {
506                             // no spaces
507
MessageHandler.log("No spaces to justify text in line "
508                                                + getLineText());
509                         }
510                     }
511                 }
512                 break;
513             default:
514                 MessageHandler.errorln("bad align: "+textAlign);
515             break;
516             }
517             aligned = true;
518         }
519         renderer.renderLineArea(this);
520     }
521
522     public int addPageNumberCitation(String JavaDoc refid, LinkSet ls) {
523
524         /*
525          * We should add code here to handle the case where the page
526          * number doesn't fit on the current line
527          */

528
529         // Space must be allocated for the page number, so currently we
530
// give it 3 spaces
531

532         int width = 3*currentFontState.getCharWidth(' ');
533
534
535         PageNumberInlineArea pia
536             = new PageNumberInlineArea(currentFontState,
537                                        this.red, this.green, this.blue,
538                                        refid, width);
539         
540         pia.setYOffset(placementOffset);
541         pendingAreas.add(pia);
542         pendingWidth += width;
543         prev = TEXT;
544         hasPageNumbers = true;
545
546         return -1;
547     }
548
549
550     /**
551      * adds text to line area
552      *
553      * @return int character position
554      */

555     public int addText(char data[], int start, int end, LinkSet ls,
556                        TextState textState) {
557         // this prevents an array index out of bounds
558
// which occurs when some text is laid out again.
559
if (start == -1)
560             return -1;
561         boolean overrun = false;
562
563         int wordStart = start;
564         int wordLength = 0;
565         int wordWidth = 0;
566         // With CID fonts, space isn't neccesary currentFontState.width(32)
567
int whitespaceWidth = currentFontState.getCharWidth(' ');
568
569         boolean isText = false;
570         boolean isMultiByteChar = false;
571
572         /* iterate over each character */
573         for (int i = start; i < end; i++) {
574             int charWidth;
575             /* get the character */
576             char c = data[i];
577             if (!(isSpace(c) || (c == '\n') || (c == '\r') || (c == '\t')
578                   || (c == '\u2028'))) {
579                 charWidth = currentFontState.getCharWidth(c);
580                 isText = true;
581                 isMultiByteChar = (c > 127);
582                 // Add support for zero-width spaces
583
if (charWidth <= 0 && c != '\u200B' && c != '\uFEFF')
584                     charWidth = whitespaceWidth;
585             } else {
586                 if ((c == '\n') || (c == '\r') || (c == '\t'))
587                     charWidth = whitespaceWidth;
588                 else
589                     charWidth = currentFontState.getCharWidth(c);
590
591                 isText = false;
592                 isMultiByteChar = false;
593
594                 if (prev == WHITESPACE) {
595
596                     // if current & previous are WHITESPACE
597

598                     if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
599                         if (isSpace(c)) {
600                             spaceWidth += currentFontState.getCharWidth(c);
601                         } else if (c == '\n' || c == '\u2028') {
602                             // force line break
603
if (spaceWidth > 0) {
604                                 InlineSpace is = new InlineSpace(spaceWidth);
605                                 is.setUnderlined(textState.getUnderlined());
606                                 is.setOverlined(textState.getOverlined());
607                                 is.setLineThrough(textState.getLineThrough());
608                                 addChild(is);
609                                 finalWidth += spaceWidth;
610                                 spaceWidth = 0;
611                             }
612                             return i + 1;
613                         } else if (c == '\t') {
614                             spaceWidth += 8 * whitespaceWidth;
615                         }
616                     } else if (c == '\u2028') {
617                         // Line separator
618
// Breaks line even if WhiteSpaceCollapse = True
619
if (spaceWidth > 0) {
620                             InlineSpace is = new InlineSpace(spaceWidth);
621                             is.setUnderlined(textState.getUnderlined());
622                             is.setOverlined(textState.getOverlined());
623                             is.setLineThrough(textState.getLineThrough());
624                             addChild(is);
625                             finalWidth += spaceWidth;
626                             spaceWidth = 0;
627                         }
628                         return i + 1;
629                     }
630
631                 } else if (prev == TEXT || prev == MULTIBYTECHAR ) {
632
633                     // if current is WHITESPACE and previous TEXT
634
// the current word made it, so
635
// add the space before the current word (if there
636
// was some)
637

638                     if (spaceWidth > 0) {
639                         InlineSpace is = new InlineSpace(spaceWidth);
640                         if (prevUlState) {
641                             is.setUnderlined(textState.getUnderlined());
642                         }
643                         if (prevOlState) {
644                             is.setOverlined(textState.getOverlined());
645                         }
646                         if (prevLTState) {
647                             is.setLineThrough(textState.getLineThrough());
648                         }
649                         addChild(is);
650                         finalWidth += spaceWidth;
651                         spaceWidth = 0;
652                     }
653
654                     // add any pending areas
655

656                     for (int j = 0; j < pendingAreas.size(); j++ ) {
657                         Box box = (Box)pendingAreas.get(j);
658                         if (box instanceof InlineArea) {
659                             if (ls != null) {
660                                 Rectangle JavaDoc lr =
661                                     new Rectangle JavaDoc(finalWidth, 0,
662                                                   ((InlineArea)box).getContentWidth(),
663                                                   fontState.getFontSize());
664                                 ls.addRect(lr, this, (InlineArea)box);
665                             }
666                         }
667                         addChild(box);
668                     }
669
670                     finalWidth += pendingWidth;
671
672                     // reset pending areas array
673
pendingWidth = 0;
674                     pendingAreas = new ArrayList JavaDoc();
675
676                     // add the current word
677

678                     if (wordLength > 0) {
679                         // The word might contain nonbreaking
680
// spaces. Split the word and add InlineSpace
681
// as necessary. All spaces inside the word
682
// Have a fixed width.
683
addSpacedWord(new String JavaDoc(data, wordStart, wordLength),
684                                       ls, finalWidth, 0, textState, false);
685                         finalWidth += wordWidth;
686
687                         // reset word width
688
wordWidth = 0;
689                     }
690
691                     // deal with this new whitespace following the
692
// word we just added
693
prev = WHITESPACE;
694
695                     embeddedLinkStart =
696                         0; // reset embeddedLinkStart since a space was encountered
697

698                     spaceWidth = currentFontState.getCharWidth(c);
699
700                     /*
701                      * here is the place for white-space-treatment value 'ignore':
702                      * if (this.spaceTreatment ==
703                      * SpaceTreatment.IGNORE) {
704                      * // do nothing
705                      * } else {
706                      * spaceWidth = currentFontState.width(32);
707                      * }
708                      */

709
710
711                     if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
712                         if (c == '\n' || c == '\u2028') {
713                             // force a line break
714
return i + 1;
715                         } else if (c == '\t') {
716                             spaceWidth = whitespaceWidth;
717                         }
718                     } else if (c == '\u2028') {
719                         return i + 1;
720                     }
721                 } else {
722
723                     // if current is WHITESPACE and no previous
724

725                     if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
726                         if (isSpace(c)) {
727                             prev = WHITESPACE;
728                             spaceWidth = currentFontState.getCharWidth(c);
729                         } else if (c == '\n') {
730                             // force line break
731
// textdecoration not used because spaceWidth is 0
732
InlineSpace is = new InlineSpace(spaceWidth);
733                             addChild(is);
734                             return i + 1;
735                         } else if (c == '\t') {
736                             prev = WHITESPACE;
737                             spaceWidth = 8 * whitespaceWidth;
738                         }
739
740                     } else {
741                         // skip over it
742
wordStart++;
743                     }
744                 }
745
746             }
747
748             if (isText) { // current is TEXT
749

750                 int curr = isMultiByteChar ? MULTIBYTECHAR : TEXT;
751                 if (prev == WHITESPACE) {
752
753                     // if current is TEXT and previous WHITESPACE
754

755                     wordWidth = charWidth;
756                     if ((finalWidth + spaceWidth + wordWidth)
757                         > this.getContentWidth()) {
758                         if (overrun)
759                             MessageHandler.log("area contents overflows area "
760                                                + "in line "
761                                                + getLineText());
762                         if (this.wrapOption == WrapOption.WRAP) {
763                             return i;
764                         }
765                     }
766                     prev = curr;
767                     wordStart = i;
768                     wordLength = 1;
769                 } else if (prev == TEXT || prev == MULTIBYTECHAR ) {
770                     if ( prev == TEXT && curr == TEXT || ! canBreakMidWord()) {
771                         wordLength++;
772                         wordWidth += charWidth;
773                     } else {
774
775 // if (spaceWidth > 0) { // for text-align="justify"
776
InlineSpace is = new InlineSpace(spaceWidth);
777                         if (prevUlState) {
778                             is.setUnderlined(textState.getUnderlined());
779                         }
780                         if (prevOlState) {
781                             is.setOverlined(textState.getOverlined());
782                         }
783                         if (prevLTState) {
784                             is.setLineThrough(textState.getLineThrough());
785                         }
786                         addChild(is);
787                         finalWidth += spaceWidth;
788                         spaceWidth = 0;
789 // }
790

791                         // add any pending areas
792

793                         for (int j = 0; j < pendingAreas.size(); j++ ) {
794                             Box box = (Box)pendingAreas.get(j);
795                             if (box instanceof InlineArea) {
796                                 if (ls != null) {
797                                     Rectangle JavaDoc lr =
798                                         new Rectangle JavaDoc(finalWidth, 0,
799                                                       ((InlineArea)box).getContentWidth(),
800                                                       fontState.getFontSize());
801                                     ls.addRect(lr, this, (InlineArea)box);
802                                 }
803                             }
804                             addChild(box);
805                         }
806
807                         finalWidth += pendingWidth;
808
809                         // reset pending areas array
810
pendingWidth = 0;
811                         pendingAreas = new ArrayList JavaDoc();
812
813                         // add the current word
814

815                         if (wordLength > 0) {
816                             // The word might contain nonbreaking
817
// spaces. Split the word and add InlineSpace
818
// as necessary. All spaces inside the word
819
// have a fixed width.
820
addSpacedWord(new String JavaDoc(data, wordStart, wordLength),
821                                           ls, finalWidth, 0, textState, false);
822                             finalWidth += wordWidth;
823                         }
824                         spaceWidth = 0;
825                         wordStart = i;
826                         wordLength = 1;
827                         wordWidth = charWidth;
828                     }
829                     prev = curr;
830                 } else { // nothing previous
831

832                     prev = curr;
833                     wordStart = i;
834                     wordLength = 1;
835                     wordWidth = charWidth;
836                 }
837
838                 if ((finalWidth + spaceWidth + pendingWidth + wordWidth)
839                     > this.getContentWidth()) {
840
841                     // BREAK MID WORD
842
/* if (canBreakMidWord()) {
843                       addSpacedWord(new String(data, wordStart, wordLength - 1),
844                       ls,
845                       finalWidth + spaceWidth
846                       + embeddedLinkStart, spaceWidth,
847                       textState, false);
848                       finalWidth += wordWidth;
849                       wordWidth = 0;
850                       return i;
851                       }
852 */

853                     if (this.wrapOption == WrapOption.WRAP) {
854
855                         if (hyphProps.hyphenate == Hyphenate.TRUE) {
856                             int ret = wordStart;
857                             ret = this.doHyphenation(data, i, wordStart,
858                                                      this.getContentWidth()
859                                                      - (finalWidth
860                                                         + spaceWidth
861                                                         + pendingWidth),
862                                                      finalWidth + spaceWidth
863                                                      + embeddedLinkStart,
864                                                      ls, textState);
865
866                             // current word couldn't be hypenated
867
// couldn't fit first word
868
// I am at the beginning of my line
869
if ((ret == wordStart) &&
870                                 (wordStart == start) &&
871                                 (finalWidth == 0)) {
872
873                                 MessageHandler.log("area contents overflows"
874                                                    + " area in line "
875                                                    + getLineText());
876                                 addSpacedWord(new String JavaDoc(data, wordStart, wordLength - 1),
877                                               ls,
878                                               embeddedLinkStart,
879                                               spaceWidth, textState, false);
880
881                                 finalWidth += wordWidth;
882                                 wordWidth = 0;
883                                 ret = i;
884                             }
885                             return ret;
886                         } else if (wordStart == start) {
887                             // first word
888
overrun = true;
889                             // if not at start of line, return word start
890
// to try again on a new line
891
if (finalWidth > 0) {
892                                 return wordStart;
893                             }
894                         } else {
895                             return wordStart;
896                         }
897
898                     }
899                 }
900             }
901         } // end of iteration over text
902

903         if (prev == TEXT || prev == MULTIBYTECHAR) {
904
905             if (spaceWidth > 0) {
906                 InlineSpace pis = new InlineSpace(spaceWidth);
907                 // Make sure that this space doesn't occur as
908
// first thing in the next line
909
pis.setEatable(true);
910                 if (prevUlState) {
911                     pis.setUnderlined(textState.getUnderlined());
912                 }
913                 if (prevOlState) {
914                     pis.setOverlined(textState.getOverlined());
915                 }
916                 if (prevLTState) {
917                     pis.setLineThrough(textState.getLineThrough());
918                 }
919                 pendingAreas.add(pis);
920                 pendingWidth += spaceWidth;
921                 spaceWidth = 0;
922             }
923
924             addSpacedWord(new String JavaDoc(data, wordStart, wordLength), ls,
925                           finalWidth + pendingWidth,
926                           spaceWidth, textState, true);
927
928             embeddedLinkStart += wordWidth;
929             wordWidth = 0;
930         }
931
932         if (overrun)
933             MessageHandler.log("area contents overflows area in line "
934                                + getLineText());
935         return -1;
936     }
937
938     /**
939      * adds a Leader; actually the method receives the leader properties
940      * and creates a leader area or an inline area which is appended to
941      * the children of the containing line area. <br>
942      * leader pattern use-content is not implemented.
943      */

944     public void addLeader(int leaderPattern, int leaderLengthMinimum,
945                           int leaderLengthOptimum, int leaderLengthMaximum,
946                           int ruleStyle, int ruleThickness,
947                           int leaderPatternWidth, int leaderAlignment) {
948         if (leaderLengthMinimum>leaderLengthOptimum
949             || leaderLengthOptimum>leaderLengthMaximum) {
950             MessageHandler.errorln("leader sizes wrong");
951             return;
952         }
953         if (leaderLengthOptimum>getRemainingWidth()) {
954             MessageHandler.errorln("leader width assertion failed");
955             return;
956         }
957         addPending();
958         children.add(new LineArea.Leader(leaderPattern, leaderLengthMinimum,
959                                          leaderLengthOptimum, leaderLengthMaximum,
960                                          ruleStyle, ruleThickness,
961                                          leaderPatternWidth, leaderAlignment,
962                                          fontState, red, green, blue,
963                                          placementOffset, getCurrentXPosition()));
964         finalWidth += leaderLengthOptimum;
965     }
966
967     /**
968      * adds pending inline areas to the line area
969      * normally done, when the line area is filled and
970      * added as child to the parent block area
971      */

972     public void addPending() {
973         if (spaceWidth > 0) {
974             addChild(new InlineSpace(spaceWidth));
975             finalWidth += spaceWidth;
976             spaceWidth = 0;
977         }
978
979         for (int i = 0; i < pendingAreas.size(); i++ ) {
980             Box box = (Box)pendingAreas.get(i);
981             addChild(box);
982         }
983
984         finalWidth += pendingWidth;
985
986         // reset pending areas array
987
pendingWidth = 0;
988         pendingAreas = new ArrayList JavaDoc();
989     }
990
991     /**
992      * Store text alignment.
993      * The line is aligned immediately before rendering, after
994      * page numbers have been resolved.
995      */

996     public void align(int type) {
997         textAlign = type;
998     }
999
1000    /**
1001     * Balance (vertically) the inline areas within this line.
1002     */

1003    public void verticalAlign() {
1004        int superHeight = -this.placementOffset;
1005        int maxHeight = this.allocationHeight;
1006        for (int i = 0; i < children.size(); i++ ) {
1007            Object JavaDoc o = children.get(i);
1008            if (o instanceof InlineArea) {
1009                InlineArea ia = (InlineArea)o;
1010                if (ia instanceof WordArea) {
1011                    ia.setYOffset(placementOffset);
1012                }
1013                if (ia.getHeight() > maxHeight) {
1014                    maxHeight = ia.getHeight();
1015                }
1016                int vert = ia.getVerticalAlign();
1017                if (vert == VerticalAlign.SUPER) {
1018                    int fh = fontState.getAscender();
1019                    ia.setYOffset((int)(placementOffset - (2 * fh / 3.0)));
1020                } else if (vert == VerticalAlign.SUB) {
1021                    int fh = fontState.getAscender();
1022                    ia.setYOffset((int)(placementOffset + (2 * fh / 3.0)));
1023                }
1024            }
1025        }
1026        // adjust the height of this line to the
1027
// resulting alignment height.
1028
this.allocationHeight = maxHeight;
1029    }
1030
1031    public void changeColor(float red, float green, float blue) {
1032        this.red = red;
1033        this.green = green;
1034        this.blue = blue;
1035    }
1036
1037    public void changeFont(FontState fontState) {
1038        this.currentFontState = fontState;
1039    }
1040
1041    public void changeWhiteSpaceCollapse(int whiteSpaceCollapse) {
1042        this.whiteSpaceCollapse = whiteSpaceCollapse;
1043    }
1044
1045    public void changeWrapOption(int wrapOption) {
1046        this.wrapOption = wrapOption;
1047    }
1048
1049    public void changeVerticalAlign(int vAlign) {
1050        this.vAlign = vAlign;
1051    }
1052
1053    public int getEndIndent() {
1054        return endIndent;
1055    }
1056
1057    public int getHeight() {
1058        return this.allocationHeight;
1059    }
1060
1061    public int getPlacementOffset() {
1062        return this.placementOffset;
1063    }
1064
1065    public int getStartIndent() {
1066        return startIndent;
1067    }
1068
1069    public boolean isEmpty() {
1070        return !(pendingAreas.size() > 0 || children.size() > 0);
1071        // return (prev == NOTHING);
1072
}
1073
1074    /**
1075     * sets hyphenation related traits: language, country, hyphenate, hyphenation-character
1076     * and minimum number of character to remain one the previous line and to be on the
1077     * next line.
1078     */

1079    public void changeHyphenation(HyphenationProps hyphProps) {
1080        this.hyphProps = hyphProps;
1081    }
1082
1083
1084    /**
1085     * creates a leader as String out of the given char and the leader length
1086     * and wraps it in an InlineArea which is returned
1087     */

1088    private InlineArea buildSimpleLeader(char c, int leaderLength) {
1089        int width = currentFontState.getCharWidth(c);
1090        if (width == 0) {
1091            MessageHandler.errorln("char " + c
1092                                   + " has width 0. Using width 100 instead.");
1093            width = 100;
1094        }
1095        int factor = (int)Math.floor(leaderLength / width);
1096        char[] leaderChars = new char[factor];
1097        for (int i = 0; i < factor; i++) {
1098            leaderChars[i] = c;
1099        }
1100        WordArea leaderPatternArea = new WordArea(currentFontState, this.red,
1101                                                  this.green, this.blue,
1102                                                  new String JavaDoc(leaderChars),
1103                                                  leaderLength);
1104        leaderPatternArea.setYOffset(placementOffset);
1105        return leaderPatternArea;
1106    }
1107
1108    /**
1109     * calculates the used space in this line area
1110     */

1111    private int getCurrentXPosition() {
1112        return finalWidth + spaceWidth + startIndent + pendingWidth;
1113    }
1114
1115    /**
1116     * extracts a complete word from the character data
1117     */

1118    private String JavaDoc getHyphenationWord(char[] characters, int wordStart) {
1119        boolean wordendFound = false;
1120        int counter = 0;
1121        char[] newWord = new char[characters.length]; // create a buffer
1122
while ((!wordendFound)
1123               && ((wordStart + counter) < characters.length)) {
1124            char tk = characters[wordStart + counter];
1125            if (Character.isLetter(tk)) {
1126                newWord[counter] = tk;
1127                counter++;
1128            } else {
1129                wordendFound = true;
1130            }
1131        }
1132        return new String JavaDoc(newWord, 0, counter);
1133    }
1134
1135
1136    /**
1137     * extracts word for hyphenation and calls hyphenation package,
1138     * handles cases of inword punctuation and quotation marks at the beginning
1139     * of words, but not in a internationalized way
1140     */

1141    public int doHyphenation(char[] characters, int position, int wordStart,
1142                             int remainingWidth, int startw, LinkSet ls,
1143                             TextState textState) {
1144        // check whether the language property has been set
1145
if (this.hyphProps.language.equalsIgnoreCase("none")) {
1146            MessageHandler.errorln("if property 'hyphenate' is used, a "
1147                                   + "language must be specified in line "
1148                                   + getLineText());
1149            return wordStart;
1150        }
1151
1152        /**
1153         * remaining part string of hyphenation
1154         */

1155        StringBuffer JavaDoc remainingString = new StringBuffer JavaDoc();
1156
1157        /**
1158         * for words with some inword punctuation like / or -
1159         */

1160        StringBuffer JavaDoc preString = null;
1161
1162        /**
1163         * in word punctuation character
1164         */

1165        char inwordPunctuation;
1166
1167        /**
1168         * the complete word handed to the hyphenator
1169         */

1170        String JavaDoc wordToHyphenate;
1171
1172        // width of hyphenation character
1173
int hyphCharWidth = currentFontState
1174            .getCharWidth(this.hyphProps.hyphenationChar);
1175        remainingWidth -= hyphCharWidth;
1176
1177        // handles ' or " at the beginning of the word
1178
if (characters[wordStart] == '"' || characters[wordStart] == '\'') {
1179            remainingString.append(characters[wordStart]);
1180            // extracts whole word from string
1181
wordToHyphenate = getHyphenationWord(characters, wordStart + 1);
1182        } else {
1183            wordToHyphenate = getHyphenationWord(characters, wordStart);
1184        }
1185
1186        // if the extracted word is smaller than the remaining width
1187
// we have a non letter character inside the word. at the moment
1188
// we will only handle hard hyphens and slashes
1189
if (currentFontState.getWordWidth(wordToHyphenate) < remainingWidth) {
1190            inwordPunctuation =
1191                characters[wordStart + remainingString.length()
1192                           + wordToHyphenate.length()];
1193            if (inwordPunctuation == '-' || inwordPunctuation == '/') {
1194                preString = new StringBuffer JavaDoc(wordToHyphenate);
1195                preString = preString.append(inwordPunctuation);
1196                wordToHyphenate =
1197                    getHyphenationWord(characters,
1198                                       wordStart + remainingString.length()
1199                                       + wordToHyphenate.length() + 1);
1200                remainingWidth -=
1201                    (currentFontState.getWordWidth(wordToHyphenate)
1202                     + currentFontState.getCharWidth(inwordPunctuation));
1203            }
1204        }
1205
1206        // are there any hyphenation points
1207
Hyphenation hyph =
1208            Hyphenator.hyphenate(hyphProps.language, hyphProps.country,
1209                                 wordToHyphenate,
1210                                 hyphProps.hyphenationRemainCharacterCount,
1211                                 hyphProps.hyphenationPushCharacterCount);
1212        if (hyph == null && preString == null) {
1213            // no hyphenation points and no inword non letter character
1214
return wordStart;
1215        } else if (hyph == null && preString != null) {
1216            // no hyphenation points, but a inword non-letter character
1217
remainingString.append(preString.toString());
1218            this.addWord(remainingString, startw, ls, textState);
1219            return wordStart + remainingString.length();
1220        } else if (hyph != null && preString == null) {
1221            // hyphenation points and no inword non-letter character
1222
int index = getFinalHyphenationPoint(hyph, remainingWidth);
1223            if (index != -1) {
1224                remainingString.append(hyph.getPreHyphenText(index));
1225                remainingString.append(this.hyphProps.hyphenationChar);
1226                this.addWord(remainingString, startw, ls, textState);
1227                return wordStart + remainingString.length() - 1;
1228            }
1229        } else if (hyph != null && preString != null) {
1230            // hyphenation points and a inword non letter character
1231
int index = getFinalHyphenationPoint(hyph, remainingWidth);
1232            if (index != -1) {
1233                remainingString.append(preString.append(hyph.getPreHyphenText(index)).toString());
1234                remainingString.append(this.hyphProps.hyphenationChar);
1235                this.addWord(remainingString, startw, ls, textState);
1236                return wordStart + remainingString.length() - 1;
1237            } else {
1238                remainingString.append(preString.toString());
1239                this.addWord(remainingString, startw, ls, textState);
1240                return wordStart + remainingString.length();
1241            }
1242        }
1243        return wordStart;
1244    }
1245
1246    public int getRemainingWidth() {
1247        return this.getContentWidth() + startIndent - this.getCurrentXPosition();
1248    }
1249
1250    public void setLinkSet(LinkSet ls) {}
1251
1252    public void addInlineArea(InlineArea box, LinkSet ls) {
1253        addPending();
1254        addChild(box);
1255        if (ls != null) {
1256            Rectangle JavaDoc lr = new Rectangle JavaDoc(finalWidth, 0,box.getContentWidth(),
1257                    box.getContentHeight());
1258            ls.addRect(lr, this, box);
1259        }
1260        prev = TEXT;
1261        finalWidth += box.getContentWidth();
1262    }
1263
1264    public void addInlineSpace(InlineSpace is, int spaceWidth) {
1265        addChild(is);
1266        finalWidth += spaceWidth;
1267        // spaceWidth = 0;
1268
}
1269
1270    /**
1271     * adds a single character to the line area tree
1272     */

1273    public int addCharacter(char data, LinkSet ls, boolean ul) {
1274        WordArea ia = null;
1275        int remainingWidth = this.getRemainingWidth();
1276        int width = currentFontState.getCharWidth(data);
1277        // if it doesn't fit, return
1278
if (width > remainingWidth) {
1279            return org.apache.fop.fo.flow.Character.DOESNOT_FIT;
1280        } else {
1281            // if whitespace-collapse == true, discard character
1282
if (Character.isSpaceChar(data)
1283                && whiteSpaceCollapse == WhiteSpaceCollapse.TRUE) {
1284                return org.apache.fop.fo.flow.Character.OK;
1285            }
1286            // create new InlineArea
1287
ia = new WordArea(currentFontState, this.red, this.green,
1288                              this.blue, new Character JavaDoc(data).toString(),
1289                              width);
1290            ia.setYOffset(placementOffset);
1291            ia.setUnderlined(ul);
1292            pendingAreas.add(ia);
1293            if (Character.isSpaceChar(data)) {
1294                this.spaceWidth = +width;
1295                prev = LineArea.WHITESPACE;
1296            } else {
1297                pendingWidth += width;
1298                prev = LineArea.TEXT;
1299            }
1300            return org.apache.fop.fo.flow.Character.OK;
1301        }
1302    }
1303
1304
1305
1306    /**
1307     * adds a InlineArea containing the String wordBuf to
1308     * the line area children.
1309     */

1310    private void addWord(StringBuffer JavaDoc wordBuf, int startw,
1311                         LinkSet ls, TextState textState) {
1312        if (spaceWidth > 0) {
1313            InlineSpace is = new InlineSpace(spaceWidth);
1314            if (prevUlState) {
1315                is.setUnderlined(textState.getUnderlined());
1316            }
1317            if (prevOlState) {
1318                is.setOverlined(textState.getOverlined());
1319            }
1320            if (prevLTState) {
1321                is.setLineThrough(textState.getLineThrough());
1322            }
1323            addChild(is);
1324            finalWidth += spaceWidth;
1325            spaceWidth = 0;
1326        }
1327
1328        // add any pending areas
1329

1330        for (int i = 0; i < pendingAreas.size(); i++ ) {
1331            Box box = (Box)pendingAreas.get(i);
1332            if (box instanceof InlineArea) {
1333                if (ls != null) {
1334                    Rectangle JavaDoc lr =
1335                        new Rectangle JavaDoc(finalWidth, 0,
1336                                      ((InlineArea)box).getContentWidth(),
1337                                      fontState.getFontSize());
1338                    ls.addRect(lr, this, (InlineArea)box);
1339                }
1340            }
1341            addChild(box);
1342        }
1343
1344        finalWidth += pendingWidth;
1345
1346        // reset pending areas array
1347
pendingWidth = 0;
1348        pendingAreas = new ArrayList JavaDoc();
1349        String JavaDoc word = (wordBuf != null) ? wordBuf.toString() : "";
1350        int wordWidth = currentFontState.getWordWidth(word);
1351        WordArea hia = new WordArea(currentFontState,
1352                                    this.red, this.green, this.blue,
1353                                    word, wordWidth);
1354        hia.setYOffset(placementOffset);
1355        hia.setUnderlined(textState.getUnderlined());
1356        prevUlState = textState.getUnderlined();
1357        hia.setOverlined(textState.getOverlined());
1358        prevOlState = textState.getOverlined();
1359        hia.setLineThrough(textState.getLineThrough());
1360        prevLTState = textState.getLineThrough();
1361        hia.setVerticalAlign(vAlign);
1362        this.addChild(hia);
1363
1364        if (ls != null) {
1365            Rectangle JavaDoc lr = new Rectangle JavaDoc(startw, 0,
1366                                         hia.getContentWidth(),
1367                                         fontState.getFontSize());
1368            ls.addRect(lr, this, hia);
1369        }
1370        // calculate the space needed
1371
finalWidth += wordWidth;
1372    }
1373
1374
1375    /**
1376     * extracts from a hyphenated word the best (most greedy) fit
1377     */

1378    private int getFinalHyphenationPoint(Hyphenation hyph,
1379                                         int remainingWidth) {
1380        int[] hyphenationPoints = hyph.getHyphenationPoints();
1381        int numberOfHyphenationPoints = hyphenationPoints.length;
1382
1383        int index = -1;
1384        String JavaDoc wordBegin = "";
1385        int wordBeginWidth = 0;
1386
1387        for (int i = 0; i < numberOfHyphenationPoints; i++) {
1388            wordBegin = hyph.getPreHyphenText(i);
1389            if (currentFontState.getWordWidth(wordBegin) > remainingWidth) {
1390                break;
1391            }
1392            index = i;
1393        }
1394        return index;
1395    }
1396
1397    /**
1398     * Checks if it's legal to break a word in the middle
1399     * based on the current language property.
1400     * @return true if legal to break word in the middle
1401     */

1402    private boolean canBreakMidWord() {
1403        boolean ret = false;
1404        if (hyphProps != null && hyphProps.language != null
1405            &&!hyphProps.language.equals("NONE")) {
1406            String JavaDoc lang = hyphProps.language.toLowerCase();
1407            if ("zh".equals(lang) || "ja".equals(lang) || "ko".equals(lang)
1408                || "vi".equals(lang))
1409                ret = true;
1410        }
1411        return ret;
1412    }
1413
1414
1415
1416    /**
1417     * Helper method to determine if the character is a
1418     * space with normal behaviour. Normal behaviour means that
1419     * it's not non-breaking
1420     */

1421    private boolean isSpace(char c) {
1422        if (c == ' ' || c == '\u2000' || // en quad
1423
c == '\u2001' || // em quad
1424
c == '\u2002' || // en space
1425
c == '\u2003' || // em space
1426
c == '\u2004' || // three-per-em space
1427
c == '\u2005' || // four--per-em space
1428
c == '\u2006' || // six-per-em space
1429
c == '\u2007' || // figure space
1430
c == '\u2008' || // punctuation space
1431
c == '\u2009' || // thin space
1432
c == '\u200A' || // hair space
1433
c == '\u200B') // zero width space
1434
return true;
1435        else
1436            return false;
1437    }
1438
1439
1440    /**
1441     * Method to determine if the character is a nonbreaking
1442     * space.
1443     */

1444    private boolean isNBSP(char c) {
1445        if (c == '\u00A0' || c == '\u202F' || // narrow no-break space
1446
c == '\u3000' || // ideographic space
1447
c == '\uFEFF') { // zero width no-break space
1448
return true;
1449        } else
1450            return false;
1451    }
1452
1453    /**
1454     * @return true if the character represents any kind of space
1455     */

1456    private boolean isAnySpace(char c) {
1457        boolean ret = (isSpace(c) || isNBSP(c));
1458        return ret;
1459    }
1460
1461    /**
1462     * Add a word that might contain non-breaking spaces.
1463     * Split the word into WordArea and InlineSpace and add it.
1464     * If addToPending is true, add to pending areas.
1465     */

1466    private void addSpacedWord(String JavaDoc word, LinkSet ls, int startw,
1467                               int spacew, TextState textState,
1468                               boolean addToPending) {
1469        StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(word, "\u00A0\u202F\u3000\uFEFF", true);
1470        while (st.hasMoreTokens()) {
1471            String JavaDoc currentWord = st.nextToken();
1472
1473            if (currentWord.length() == 1
1474                && (isNBSP(currentWord.charAt(0)))) {
1475                // Add an InlineSpace
1476
int spaceWidth = currentFontState
1477                    .getCharWidth(currentWord.charAt(0));
1478                if (spaceWidth > 0) {
1479                    InlineSpace is = new InlineSpace(spaceWidth);
1480                    startw += spaceWidth;
1481                    if (prevUlState) {
1482                        is.setUnderlined(textState.getUnderlined());
1483                    }
1484                    if (prevOlState) {
1485                        is.setOverlined(textState.getOverlined());
1486                    }
1487                    if (prevLTState) {
1488                        is.setLineThrough(textState.getLineThrough());
1489                    }
1490
1491                    if (addToPending) {
1492                        pendingAreas.add(is);
1493                        pendingWidth += spaceWidth;
1494                    } else {
1495                        addChild(is);
1496                    }
1497                }
1498            } else {
1499                int wordWidth = currentFontState.getWordWidth(currentWord);
1500                WordArea ia = new WordArea(currentFontState, this.red,
1501                                           this.green, this.blue,
1502                                           currentWord,
1503                                           wordWidth);
1504                ia.setYOffset(placementOffset);
1505                ia.setUnderlined(textState.getUnderlined());
1506                prevUlState = textState.getUnderlined();
1507                ia.setOverlined(textState.getOverlined());
1508                prevOlState = textState.getOverlined();
1509                ia.setLineThrough(textState.getLineThrough());
1510                prevLTState = textState.getLineThrough();
1511                ia.setVerticalAlign(vAlign);
1512
1513                if (addToPending) {
1514                    pendingAreas.add(ia);
1515                    pendingWidth += wordWidth;
1516                } else {
1517                    addChild(ia);
1518                }
1519                if (ls != null) {
1520                    Rectangle JavaDoc lr = new Rectangle JavaDoc(startw, spacew,
1521                                                 ia.getContentWidth(),
1522                                                 fontState.getFontSize());
1523                    ls.addRect(lr, this, ia);
1524                }
1525                startw += wordWidth;
1526            }
1527        }
1528    }
1529
1530    public String JavaDoc getLineText() {
1531        StringBuffer JavaDoc b = new StringBuffer JavaDoc(120);
1532        for (int i=0;i<children.size();i++) {
1533            Object JavaDoc o = children.get(i);
1534            if (o instanceof WordArea) {
1535                b.append(((WordArea)o).getText());
1536            } else if (o instanceof InlineSpace) {
1537                b.append(' ');
1538            } else if (o instanceof org.apache.fop.image.ImageArea) {
1539                b.append("<img>");
1540            } else {
1541                b.append('<');
1542                b.append(o.getClass().getName());
1543                b.append('>');
1544            }
1545        }
1546        return b.toString();
1547    }
1548}
1549
1550
Popular Tags