KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > layoutmgr > inline > TextLayoutManager


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 /* $Id: TextLayoutManager.java 453310 2006-10-05 18:44:15Z spepping $ */
19
20 package org.apache.fop.layoutmgr.inline;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.LinkedList JavaDoc;
25 import java.util.ListIterator JavaDoc;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.fop.area.Trait;
30 import org.apache.fop.area.inline.TextArea;
31 import org.apache.fop.fo.Constants;
32 import org.apache.fop.fo.FOText;
33 import org.apache.fop.fo.flow.Inline;
34 import org.apache.fop.fonts.Font;
35 import org.apache.fop.layoutmgr.InlineKnuthSequence;
36 import org.apache.fop.layoutmgr.KnuthBox;
37 import org.apache.fop.layoutmgr.KnuthElement;
38 import org.apache.fop.layoutmgr.KnuthGlue;
39 import org.apache.fop.layoutmgr.KnuthPenalty;
40 import org.apache.fop.layoutmgr.KnuthSequence;
41 import org.apache.fop.layoutmgr.LayoutContext;
42 import org.apache.fop.layoutmgr.LeafPosition;
43 import org.apache.fop.layoutmgr.Position;
44 import org.apache.fop.layoutmgr.PositionIterator;
45 import org.apache.fop.layoutmgr.TraitSetter;
46 import org.apache.fop.traits.MinOptMax;
47 import org.apache.fop.traits.SpaceVal;
48 import org.apache.fop.util.CharUtilities;
49
50 /**
51  * LayoutManager for text (a sequence of characters) which generates one
52  * or more inline areas.
53  */

54 public class TextLayoutManager extends LeafNodeLayoutManager {
55
56     /**
57      * Store information about each potential text area.
58      * Index of character which ends the area, IPD of area, including
59      * any word-space and letter-space.
60      * Number of word-spaces?
61      */

62     private class AreaInfo {
63         private short iStartIndex;
64         private short iBreakIndex;
65         private short iWScount;
66         private short iLScount;
67         private MinOptMax ipdArea;
68         private boolean bHyphenated;
69         private boolean isSpace;
70         public AreaInfo(short iSIndex, short iBIndex, short iWS, short iLS,
71                         MinOptMax ipd, boolean bHyph, boolean isSpace) {
72             iStartIndex = iSIndex;
73             iBreakIndex = iBIndex;
74             iWScount = iWS;
75             iLScount = iLS;
76             ipdArea = ipd;
77             bHyphenated = bHyph;
78             this.isSpace = isSpace;
79         }
80         
81         public String JavaDoc toString() {
82             return "[ lscnt=" + iLScount
83                 + ", wscnt=" + iWScount
84                 + ", ipd=" + ipdArea.toString()
85                 + ", sidx=" + iStartIndex
86                 + ", bidx=" + iBreakIndex
87                 + ", hyph=" + bHyphenated
88                 + ", space=" + isSpace
89                 + "]";
90         }
91
92     }
93
94     // this class stores information about changes in vecAreaInfo
95
// which are not yet applied
96
private class PendingChange {
97         public AreaInfo ai;
98         public int index;
99
100         public PendingChange(AreaInfo ai, int index) {
101             this.ai = ai;
102             this.index = index;
103         }
104     }
105
106     /**
107      * logging instance
108      */

109     private static Log log = LogFactory.getLog(TextLayoutManager.class);
110
111     // Hold all possible breaks for the text in this LM's FO.
112
private ArrayList JavaDoc vecAreaInfo;
113
114     /** Non-space characters on which we can end a line. */
115     private static final String JavaDoc BREAK_CHARS = "-/";
116
117     /** Used to reduce instantiation of MinOptMax with zero length. Do not modify! */
118     private static final MinOptMax ZERO_MINOPTMAX = new MinOptMax(0);
119     
120     private FOText foText;
121     private char[] textArray;
122     /**
123      * Contains an array of widths to adjust for kerning. The first entry can
124      * be used to influence the start position of the first letter. The entry i+1 defines the
125      * cursor advancement after the character i. A null entry means no special advancement.
126      */

127     private MinOptMax[] letterAdjustArray; //size = textArray.length + 1
128

129     private static final char NEWLINE = '\n';
130
131     private Font font = null;
132     /** Start index of first character in this parent Area */
133     private short iAreaStart = 0;
134     /** Start index of next TextArea */
135     private short iNextStart = 0;
136     /** Size since last makeArea call, except for last break */
137     private MinOptMax ipdTotal;
138     /** Size including last break possibility returned */
139     // private MinOptMax nextIPD = new MinOptMax(0);
140
/** size of a space character (U+0020) glyph in current font */
141     private int spaceCharIPD;
142     private MinOptMax wordSpaceIPD;
143     private MinOptMax letterSpaceIPD;
144     /** size of the hyphen character glyph in current font */
145     private int hyphIPD;
146     /** 1/1 of word-spacing value */
147     private SpaceVal ws;
148     /** 1/2 of word-spacing value */
149     private SpaceVal halfWS;
150     /** 1/2 of letter-spacing value */
151     private SpaceVal halfLS;
152     /** Number of space characters after previous possible break position. */
153     private int iNbSpacesPending;
154
155     private boolean bChanged = false;
156     private int iReturnedIndex = 0;
157     private short iThisStart = 0;
158     private short iTempStart = 0;
159     private LinkedList JavaDoc changeList = null;
160
161     private AlignmentContext alignmentContext = null;
162
163     private int lineStartBAP = 0;
164     private int lineEndBAP = 0;
165
166     /**
167      * Create a Text layout manager.
168      *
169      * @param node The FOText object to be rendered
170      */

171     public TextLayoutManager(FOText node) {
172         super();
173         foText = node;
174         
175         textArray = new char[node.endIndex - node.startIndex];
176         System.arraycopy(node.ca, node.startIndex, textArray, 0,
177             node.endIndex - node.startIndex);
178         letterAdjustArray = new MinOptMax[textArray.length + 1];
179
180         vecAreaInfo = new java.util.ArrayList JavaDoc();
181     }
182     
183     /** @see org.apache.fop.layoutmgr.LayoutManager#initialize */
184     public void initialize() {
185         font = foText.getCommonFont().getFontState(foText.getFOEventHandler().getFontInfo(), this);
186         
187         // With CID fonts, space isn't neccesary currentFontState.width(32)
188
spaceCharIPD = font.getCharWidth(' ');
189         // Use hyphenationChar property
190
hyphIPD = font.getCharWidth(foText.getCommonHyphenation().hyphenationCharacter);
191         
192         SpaceVal ls = SpaceVal.makeLetterSpacing(foText.getLetterSpacing());
193         halfLS = new SpaceVal(MinOptMax.multiply(ls.getSpace(), 0.5),
194                 ls.isConditional(), ls.isForcing(), ls.getPrecedence());
195         
196         ws = SpaceVal.makeWordSpacing(foText.getWordSpacing(), ls, font);
197         // Make half-space: <space> on either side of a word-space)
198
halfWS = new SpaceVal(MinOptMax.multiply(ws.getSpace(), 0.5),
199                 ws.isConditional(), ws.isForcing(), ws.getPrecedence());
200
201         // letter space applies only to consecutive non-space characters,
202
// while word space applies to space characters;
203
// i.e. the spaces in the string "A SIMPLE TEST" are:
204
// A<<ws>>S<ls>I<ls>M<ls>P<ls>L<ls>E<<ws>>T<ls>E<ls>S<ls>T
205
// there is no letter space after the last character of a word,
206
// nor after a space character
207
// NOTE: The above is not quite correct. Read on in XSL 1.0, 7.16.2, letter-spacing
208

209         // set letter space and word space dimension;
210
// the default value "normal" was converted into a MinOptMax value
211
// in the SpaceVal.makeWordSpacing() method
212
letterSpaceIPD = ls.getSpace();
213         wordSpaceIPD = MinOptMax.add(new MinOptMax(spaceCharIPD), ws.getSpace());
214
215         // if the text node is son of an inline, set vertical align
216
if (foText.getParent() instanceof Inline) {
217             Inline fobj = (Inline)foText.getParent();
218         }
219     }
220
221     /**
222      * Reset position for returning next BreakPossibility.
223      *
224      * @param prevPos the position to reset to
225      */

226     public void resetPosition(Position prevPos) {
227         if (prevPos != null) {
228             // ASSERT (prevPos.getLM() == this)
229
if (prevPos.getLM() != this) {
230                 log.error("TextLayoutManager.resetPosition: "
231                           + "LM mismatch!!!");
232             }
233             LeafPosition tbp = (LeafPosition) prevPos;
234             AreaInfo ai = (AreaInfo) vecAreaInfo.get(tbp.getLeafPos());
235             if (ai.iBreakIndex != iNextStart) {
236                 iNextStart = ai.iBreakIndex;
237                 vecAreaInfo.ensureCapacity(tbp.getLeafPos() + 1);
238                 // TODO: reset or recalculate total IPD = sum of all word IPD
239
// up to the break position
240
ipdTotal = ai.ipdArea;
241                 setFinished(false);
242             }
243         } else {
244             // Reset to beginning!
245
vecAreaInfo.clear();
246             iNextStart = 0;
247             setFinished(false);
248         }
249     }
250
251     // TODO: see if we can use normal getNextBreakPoss for this with
252
// extra hyphenation information in LayoutContext
253
private boolean getHyphenIPD(HyphContext hc, MinOptMax hyphIPD) {
254         // Skip leading word-space before calculating count?
255
boolean bCanHyphenate = true;
256         int iStopIndex = iNextStart + hc.getNextHyphPoint();
257
258         if (textArray.length < iStopIndex) {
259             iStopIndex = textArray.length;
260             bCanHyphenate = false;
261         }
262         hc.updateOffset(iStopIndex - iNextStart);
263
264         for (; iNextStart < iStopIndex; iNextStart++) {
265             char c = textArray[iNextStart];
266             hyphIPD.opt += font.getCharWidth(c);
267             // letter-space?
268
}
269         // Need to include hyphen size too, but don't count it in the
270
// stored running total, since it would be double counted
271
// with later hyphenation points
272
return bCanHyphenate;
273     }
274
275     /**
276      * Generate and add areas to parent area.
277      * This can either generate an area for each TextArea and each space, or
278      * an area containing all text with a parameter controlling the size of
279      * the word space. The latter is most efficient for PDF generation.
280      * Set size of each area.
281      * @param posIter Iterator over Position information returned
282      * by this LayoutManager.
283      * @param context LayoutContext for adjustments
284      */

285     public void addAreas(PositionIterator posIter, LayoutContext context) {
286
287         // Add word areas
288
AreaInfo ai = null;
289         int iWScount = 0;
290         int iLScount = 0;
291         int firstAreaInfoIndex = -1;
292         int lastAreaInfoIndex = 0;
293         MinOptMax realWidth = new MinOptMax(0);
294
295         /* On first area created, add any leading space.
296          * Calculate word-space stretch value.
297          */

298         while (posIter.hasNext()) {
299             LeafPosition tbpNext = (LeafPosition) posIter.next();
300             if (tbpNext == null) {
301                 continue; //Ignore elements without Positions
302
}
303             if (tbpNext.getLeafPos() != -1) {
304                 ai = (AreaInfo) vecAreaInfo.get(tbpNext.getLeafPos());
305                 if (firstAreaInfoIndex == -1) {
306                     firstAreaInfoIndex = tbpNext.getLeafPos();
307                 }
308                 iWScount += ai.iWScount;
309                 iLScount += ai.iLScount;
310                 realWidth.add(ai.ipdArea);
311                 lastAreaInfoIndex = tbpNext.getLeafPos();
312             }
313         }
314         if (ai == null) {
315             return;
316         }
317         int textLength = ai.iBreakIndex - ai.iStartIndex;
318         if (ai.iLScount == textLength
319                    && context.isLastArea()) {
320             // the line ends at a character like "/" or "-";
321
// remove the letter space after the last character
322
realWidth.add(MinOptMax.multiply(letterSpaceIPD, -1));
323             iLScount--;
324         }
325         
326         for (int i = ai.iStartIndex; i < ai.iBreakIndex; i++) {
327             MinOptMax ladj = letterAdjustArray[i + 1];
328             if (ladj != null && ladj.isElastic()) {
329                 iLScount++;
330             }
331         }
332
333         // add hyphenation character if the last word is hyphenated
334
if (context.isLastArea() && ai.bHyphenated) {
335             realWidth.add(new MinOptMax(hyphIPD));
336         }
337
338         // Calculate adjustments
339
int iDifference = 0;
340         int iTotalAdjust = 0;
341         int iWordSpaceDim = wordSpaceIPD.opt;
342         int iLetterSpaceDim = letterSpaceIPD.opt;
343         double dIPDAdjust = context.getIPDAdjust();
344         double dSpaceAdjust = context.getSpaceAdjust(); // not used
345

346         // calculate total difference between real and available width
347
if (dIPDAdjust > 0.0) {
348             iDifference = (int) ((double) (realWidth.max - realWidth.opt)
349                                 * dIPDAdjust);
350         } else {
351             iDifference = (int) ((double) (realWidth.opt - realWidth.min)
352                                 * dIPDAdjust);
353         }
354         
355         // set letter space adjustment
356
if (dIPDAdjust > 0.0) {
357             iLetterSpaceDim
358                 += (int) ((double) (letterSpaceIPD.max - letterSpaceIPD.opt)
359                          * dIPDAdjust);
360         } else {
361             iLetterSpaceDim
362                 += (int) ((double) (letterSpaceIPD.opt - letterSpaceIPD.min)
363                          * dIPDAdjust);
364         }
365         iTotalAdjust += (iLetterSpaceDim - letterSpaceIPD.opt) * iLScount;
366
367         // set word space adjustment
368
//
369
if (iWScount > 0) {
370             iWordSpaceDim += (int) ((iDifference - iTotalAdjust) / iWScount);
371         } else {
372             // there are no word spaces in this area
373
}
374         iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount;
375         if (iTotalAdjust != iDifference) {
376             // the applied adjustment is greater or smaller than the needed one
377
log.trace("TextLM.addAreas: error in word / letter space adjustment = "
378                     + (iTotalAdjust - iDifference));
379             // set iTotalAdjust = iDifference, so that the width of the TextArea
380
// will counterbalance the error and the other inline areas will be
381
// placed correctly
382
iTotalAdjust = iDifference;
383         }
384
385         TextArea t = createTextArea(realWidth, iTotalAdjust, context,
386                                     wordSpaceIPD.opt - spaceCharIPD,
387                                     firstAreaInfoIndex, lastAreaInfoIndex,
388                                     context.isLastArea());
389
390         // iWordSpaceDim is computed in relation to wordSpaceIPD.opt
391
// but the renderer needs to know the adjustment in relation
392
// to the size of the space character in the current font;
393
// moreover, the pdf renderer adds the character spacing even to
394
// the last character of a word and to space characters: in order
395
// to avoid this, we must subtract the letter space width twice;
396
// the renderer will compute the space width as:
397
// space width =
398
// = "normal" space width + letterSpaceAdjust + wordSpaceAdjust
399
// = spaceCharIPD + letterSpaceAdjust +
400
// + (iWordSpaceDim - spaceCharIPD - 2 * letterSpaceAdjust)
401
// = iWordSpaceDim - letterSpaceAdjust
402
t.setTextLetterSpaceAdjust(iLetterSpaceDim);
403         t.setTextWordSpaceAdjust(iWordSpaceDim - spaceCharIPD
404                                  - 2 * t.getTextLetterSpaceAdjust());
405         if (context.getIPDAdjust() != 0) {
406             // add information about space width
407
t.setSpaceDifference(wordSpaceIPD.opt - spaceCharIPD
408                                  - 2 * t.getTextLetterSpaceAdjust());
409         }
410         parentLM.addChildArea(t);
411     }
412
413     /**
414      * Create an inline word area.
415      * This creates a TextArea and sets up the various attributes.
416      *
417      * @param width the MinOptMax width of the content
418      * @param adjust the total ipd adjustment with respect to the optimal width
419      * @param context the layout context
420      * @param spaceDiff unused
421      * @param firstIndex the index of the first AreaInfo used for the TextArea
422      * @param lastIndex the index of the last AreaInfo used for the TextArea
423      * @param isLastArea is this TextArea the last in a line?
424      * @return the new text area
425      */

426     protected TextArea createTextArea(MinOptMax width, int adjust,
427                                       LayoutContext context, int spaceDiff,
428                                       int firstIndex, int lastIndex, boolean isLastArea) {
429         TextArea textArea;
430         if (context.getIPDAdjust() == 0.0) {
431             // create just a TextArea
432
textArea = new TextArea();
433         } else {
434             // justified area: create a TextArea with extra info
435
// about potential adjustments
436
textArea = new TextArea(width.max - width.opt,
437                                     width.opt - width.min,
438                                     adjust);
439         }
440         textArea.setIPD(width.opt + adjust);
441         textArea.setBPD(font.getAscender() - font.getDescender());
442         textArea.setBaselineOffset(font.getAscender());
443         if (textArea.getBPD() == alignmentContext.getHeight()) {
444             textArea.setOffset(0);
445         } else {
446             textArea.setOffset(alignmentContext.getOffset());
447         }
448
449         // set the text of the TextArea, split into words and spaces
450
int wordStartIndex = -1;
451         AreaInfo areaInfo;
452         for (int i = firstIndex; i <= lastIndex; i++) {
453             areaInfo = (AreaInfo) vecAreaInfo.get(i);
454             if (areaInfo.isSpace) {
455                 // areaInfo stores information about spaces
456
// add the spaces to the TextArea
457
for (int j = areaInfo.iStartIndex; j < areaInfo.iBreakIndex; j++) {
458                     char spaceChar = textArray[j];
459                     textArea.addSpace(spaceChar, 0,
460                             CharUtilities.isAdjustableSpace(spaceChar));
461                 }
462             } else {
463                 // areaInfo stores information about a word fragment
464
if (wordStartIndex == -1) {
465                     // here starts a new word
466
wordStartIndex = areaInfo.iStartIndex;
467                 }
468                 if (i == lastIndex || ((AreaInfo) vecAreaInfo.get(i + 1)).isSpace) {
469                     // here ends a new word
470
// add a word to the TextArea
471
int len = areaInfo.iBreakIndex - wordStartIndex;
472                     String JavaDoc wordChars = new String JavaDoc(textArray, wordStartIndex, len);
473                     if (isLastArea
474                         && i == lastIndex
475                         && areaInfo.bHyphenated) {
476                         // add the hyphenation character
477
wordChars += foText.getCommonHyphenation().hyphenationCharacter;
478                     }
479                     int[] letterAdjust = new int[wordChars.length()];
480                     int lsCount = areaInfo.iLScount;
481                     for (int letter = 0; letter < len; letter++) {
482                         MinOptMax adj = letterAdjustArray[letter + wordStartIndex];
483                         if (letter > 0) {
484                             letterAdjust[letter] = (adj != null ? adj.opt : 0);
485                         }
486                         if (lsCount > 0) {
487                             letterAdjust[letter] += textArea.getTextLetterSpaceAdjust();
488                             lsCount--;
489                         }
490                     }
491                     textArea.addWord(wordChars, 0, letterAdjust);
492                     wordStartIndex = -1;
493                 }
494             }
495         }
496         TraitSetter.addFontTraits(textArea, font);
497         textArea.addTrait(Trait.COLOR, foText.getColor());
498         
499         TraitSetter.addTextDecoration(textArea, foText.getTextDecoration());
500         
501         return textArea;
502     }
503     
504     private void addToLetterAdjust(int index, int width) {
505         if (letterAdjustArray[index] == null) {
506             letterAdjustArray[index] = new MinOptMax(width);
507         } else {
508             letterAdjustArray[index].add(width);
509         }
510     }
511
512     private void addToLetterAdjust(int index, MinOptMax width) {
513         if (letterAdjustArray[index] == null) {
514             letterAdjustArray[index] = new MinOptMax(width);
515         } else {
516             letterAdjustArray[index].add(width);
517         }
518     }
519
520     /**
521      * Indicates whether a character is a space in terms of this layout manager.
522      * @param ch the character
523      * @return true if it's a space
524      */

525     private static boolean isSpace(final char ch) {
526         return ch == CharUtilities.SPACE
527             || ch == CharUtilities.NBSPACE
528             || CharUtilities.isFixedWidthSpace(ch);
529     }
530     
531     private static boolean isBreakChar(final char ch) {
532         return (BREAK_CHARS.indexOf(ch) >= 0);
533     }
534     
535     /** @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(LayoutContext, int) */
536     public LinkedList JavaDoc getNextKnuthElements(LayoutContext context, int alignment) {
537         lineStartBAP = context.getLineStartBorderAndPaddingWidth();
538         lineEndBAP = context.getLineEndBorderAndPaddingWidth();
539         alignmentContext = context.getAlignmentContext();
540
541         LinkedList JavaDoc returnList = new LinkedList JavaDoc();
542         KnuthSequence sequence = new InlineKnuthSequence();
543         AreaInfo ai = null;
544         returnList.add(sequence);
545
546         while (iNextStart < textArray.length) {
547             char ch = textArray[iNextStart];
548             if (ch == CharUtilities.SPACE
549                 && foText.getWhitespaceTreatment() != Constants.EN_PRESERVE) {
550                 // normal non preserved space - collect them all
551
// advance to the next character
552
iThisStart = iNextStart;
553                 iNextStart++;
554                 while (iNextStart < textArray.length
555                     && textArray[iNextStart] == CharUtilities.SPACE) {
556                     iNextStart++;
557                 }
558                 // create the AreaInfo object
559
ai = new AreaInfo(iThisStart, (short) (iNextStart),
560                         (short) (iNextStart - iThisStart), (short) 0,
561                         MinOptMax.multiply(wordSpaceIPD, iNextStart - iThisStart),
562                         false, true);
563                 vecAreaInfo.add(ai);
564
565                 // create the elements
566
sequence.addAll
567                     (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1));
568
569             } else if (ch == CharUtilities.SPACE || ch == CharUtilities.NBSPACE) {
570                 // preserved space or non-breaking space:
571
// create the AreaInfo object
572
ai = new AreaInfo(iNextStart, (short) (iNextStart + 1),
573                         (short) 1, (short) 0,
574                         wordSpaceIPD, false, true);
575                 vecAreaInfo.add(ai);
576
577                 // create the elements
578
sequence.addAll
579                     (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1));
580
581                 // advance to the next character
582
iNextStart++;
583             } else if (CharUtilities.isFixedWidthSpace(ch)) {
584                 // create the AreaInfo object
585
MinOptMax ipd = new MinOptMax(font.getCharWidth(ch));
586                 ai = new AreaInfo(iNextStart, (short) (iNextStart + 1),
587                         (short) 0, (short) 0,
588                         ipd, false, true);
589                 vecAreaInfo.add(ai);
590
591                 // create the elements
592
sequence.addAll
593                     (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1));
594
595                 // advance to the next character
596
iNextStart++;
597             } else if (ch == NEWLINE) {
598                 // linefeed; this can happen when linefeed-treatment="preserve"
599
// add a penalty item to the list and start a new sequence
600
if (lineEndBAP != 0) {
601                     sequence.add
602                         (new KnuthGlue(lineEndBAP, 0, 0,
603                                        new LeafPosition(this, -1), true));
604                 }
605                 sequence.endSequence();
606                 sequence = new InlineKnuthSequence();
607                 returnList.add(sequence);
608
609                 // advance to the next character
610
iNextStart++;
611             } else {
612                 // the beginning of a word
613
iThisStart = iNextStart;
614                 iTempStart = iNextStart;
615                 for (; iTempStart < textArray.length
616                         && !isSpace(textArray[iTempStart])
617                         && textArray[iTempStart] != NEWLINE
618                         && !(iTempStart > iNextStart
619                              && isBreakChar(textArray[iTempStart - 1]));
620                         iTempStart++) {
621                     //nop, just find the word boundary
622
}
623                 
624                 //Word boundary found, process widths and kerning
625
int wordLength = iTempStart - iThisStart;
626                 boolean kerning = font.hasKerning();
627                 MinOptMax wordIPD = new MinOptMax(0);
628                 for (int i = iThisStart; i < iTempStart; i++) {
629                     char c = textArray[i];
630                     
631                     //character width
632
int charWidth = font.getCharWidth(c);
633                     wordIPD.add(charWidth);
634                     
635                     //kerning
636
int kern = 0;
637                     if (kerning && (i > iThisStart)) {
638                         char previous = textArray[i - 1];
639                         kern = font.getKernValue(previous, c) * font.getFontSize() / 1000;
640                         if (kern != 0) {
641                             //log.info("Kerning between " + previous + " and " + c + ": " + kern);
642
addToLetterAdjust(i, kern);
643                         }
644                         wordIPD.add(kern);
645                     }
646                 }
647                 
648                 int iLetterSpaces = wordLength - 1;
649                 // if the last character is '-' or '/' and the next one
650
// is not a space, it could be used as a line end;
651
// add one more letter space, in case other text follows
652
if (isBreakChar(textArray[iTempStart - 1])
653                         && iTempStart < textArray.length
654                         && !isSpace(textArray[iTempStart])) {
655                     iLetterSpaces++;
656                 }
657                 wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces));
658
659                 // create the AreaInfo object
660
ai = new AreaInfo(iThisStart, iTempStart, (short) 0,
661                         (short) iLetterSpaces,
662                         wordIPD, false, false);
663                 vecAreaInfo.add(ai);
664
665                 // create the elements
666
sequence.addAll(createElementsForAWordFragment(alignment, ai,
667                         vecAreaInfo.size() - 1, letterSpaceIPD));
668
669                 // advance to the next character
670
iNextStart = iTempStart;
671             }
672         } // end of while
673
if (((List JavaDoc)returnList.getLast()).size() == 0) {
674             //Remove an empty sequence because of a trailing newline
675
returnList.removeLast();
676         }
677         setFinished(true);
678         if (returnList.size() > 0) {
679             return returnList;
680         } else {
681             return null;
682         }
683     }
684
685     /** @see InlineLevelLayoutManager#addALetterSpaceTo(List) */
686     public List JavaDoc addALetterSpaceTo(List JavaDoc oldList) {
687         // old list contains only a box, or the sequence: box penalty glue box;
688
// look at the Position stored in the first element in oldList
689
// which is always a box
690
ListIterator JavaDoc oldListIterator = oldList.listIterator();
691         KnuthElement el = (KnuthElement)oldListIterator.next();
692         LeafPosition pos = (LeafPosition) ((KnuthBox) el).getPosition();
693         AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
694         ai.iLScount++;
695         ai.ipdArea.add(letterSpaceIPD);
696         if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0) {
697             // the last character could be used as a line break
698
// append new elements to oldList
699
oldListIterator = oldList.listIterator(oldList.size());
700             oldListIterator.add(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
701                                                  new LeafPosition(this, -1), false));
702             oldListIterator.add(new KnuthGlue(letterSpaceIPD.opt,
703                                        letterSpaceIPD.max - letterSpaceIPD.opt,
704                                        letterSpaceIPD.opt - letterSpaceIPD.min,
705                                        new LeafPosition(this, -1), false));
706         } else if (letterSpaceIPD.min == letterSpaceIPD.max) {
707             // constant letter space: replace the box
708
oldListIterator.set(new KnuthInlineBox(ai.ipdArea.opt, alignmentContext, pos, false));
709         } else {
710             // adjustable letter space: replace the glue
711
oldListIterator.next(); // this would return the penalty element
712
oldListIterator.next(); // this would return the glue element
713
oldListIterator.set(new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
714                                               ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
715                                               ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
716                                               new LeafPosition(this, -1), true));
717         }
718         return oldList;
719     }
720
721     /**
722      * remove the AreaInfo object represented by the given elements,
723      * so that it won't generate any element when getChangedKnuthElements
724      * will be called
725      *
726      * @param oldList the elements representing the word space
727      */

728     public void removeWordSpace(List JavaDoc oldList) {
729         // find the element storing the Position whose value
730
// points to the AreaInfo object
731
ListIterator JavaDoc oldListIterator = oldList.listIterator();
732         if (((KnuthElement) ((LinkedList JavaDoc) oldList).getFirst()).isPenalty()) {
733             // non breaking space: oldList starts with a penalty
734
oldListIterator.next();
735         }
736         if (oldList.size() > 2) {
737             // alignment is either center, start or end:
738
// the first two elements does not store the needed Position
739
oldListIterator.next();
740             oldListIterator.next();
741         }
742         int leafValue = ((LeafPosition) ((KnuthElement) oldListIterator.next()).getPosition()).getLeafPos();
743         // only the last word space can be a trailing space!
744
if (leafValue == vecAreaInfo.size() - 1) {
745             vecAreaInfo.remove(leafValue);
746         } else {
747             log.error("trying to remove a non-trailing word space");
748         }
749     }
750
751     /** @see InlineLevelLayoutManager#hyphenate(Position, HyphContext) */
752     public void hyphenate(Position pos, HyphContext hc) {
753         AreaInfo ai
754             = (AreaInfo) vecAreaInfo.get(((LeafPosition) pos).getLeafPos());
755         int iStartIndex = ai.iStartIndex;
756         int iStopIndex;
757         boolean bNothingChanged = true;
758
759         while (iStartIndex < ai.iBreakIndex) {
760             MinOptMax newIPD = new MinOptMax(0);
761             boolean bHyphenFollows;
762
763             if (hc.hasMoreHyphPoints()
764                 && (iStopIndex = iStartIndex + hc.getNextHyphPoint())
765                 <= ai.iBreakIndex) {
766                 // iStopIndex is the index of the first character
767
// after a hyphenation point
768
bHyphenFollows = true;
769             } else {
770                 // there are no more hyphenation points,
771
// or the next one is after ai.iBreakIndex
772
bHyphenFollows = false;
773                 iStopIndex = ai.iBreakIndex;
774             }
775
776             hc.updateOffset(iStopIndex - iStartIndex);
777
778             //log.info("Word: " + new String(textArray, iStartIndex, iStopIndex - iStartIndex));
779
for (int i = iStartIndex; i < iStopIndex; i++) {
780                 char c = textArray[i];
781                 newIPD.add(new MinOptMax(font.getCharWidth(c)));
782                 //if (i > iStartIndex) {
783
if (i < iStopIndex) {
784                     MinOptMax la = this.letterAdjustArray[i + 1];
785                     if ((i == iStopIndex - 1) && bHyphenFollows) {
786                         //the letter adjust here needs to be handled further down during
787
//element generation because it depends on hyph/no-hyph condition
788
la = null;
789                     }
790                     if (la != null) {
791                         newIPD.add(la);
792                     }
793                 }
794             }
795             // add letter spaces
796
boolean bIsWordEnd
797                 = iStopIndex == ai.iBreakIndex
798                 && ai.iLScount < (ai.iBreakIndex - ai.iStartIndex);
799             newIPD.add(MinOptMax.multiply(letterSpaceIPD,
800                                           (bIsWordEnd
801                                            ? (iStopIndex - iStartIndex - 1)
802                                            : (iStopIndex - iStartIndex))));
803
804             if (!(bNothingChanged
805                   && iStopIndex == ai.iBreakIndex
806                   && bHyphenFollows == false)) {
807                 // the new AreaInfo object is not equal to the old one
808
if (changeList == null) {
809                     changeList = new LinkedList JavaDoc();
810                 }
811                 changeList.add
812                     (new PendingChange
813                      (new AreaInfo((short) iStartIndex, (short) iStopIndex,
814                                    (short) 0,
815                                    (short) (bIsWordEnd
816                                             ? (iStopIndex - iStartIndex - 1)
817                                             : (iStopIndex - iStartIndex)),
818                                    newIPD, bHyphenFollows, false),
819                       ((LeafPosition) pos).getLeafPos()));
820                 bNothingChanged = false;
821             }
822             iStartIndex = iStopIndex;
823         }
824         if (!bChanged && !bNothingChanged) {
825             bChanged = true;
826         }
827     }
828
829     /** @see InlineLevelLayoutManager#applyChanges(List) */
830     public boolean applyChanges(List JavaDoc oldList) {
831         setFinished(false);
832
833         if (changeList != null) {
834             int iAddedAI = 0;
835             int iRemovedAI = 0;
836             int iOldIndex = -1;
837             PendingChange currChange = null;
838             ListIterator JavaDoc changeListIterator = changeList.listIterator();
839             while (changeListIterator.hasNext()) {
840                 currChange = (PendingChange) changeListIterator.next();
841                 if (currChange.index != iOldIndex) {
842                     iRemovedAI++;
843                     iAddedAI++;
844                     iOldIndex = currChange.index;
845                     vecAreaInfo.remove(currChange.index + iAddedAI - iRemovedAI);
846                     vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI,
847                                     currChange.ai);
848                 } else {
849                     iAddedAI++;
850                     vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI,
851                                     currChange.ai);
852                 }
853             }
854             changeList.clear();
855         }
856
857         iReturnedIndex = 0;
858         return bChanged;
859     }
860
861     /** @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(List, int) */
862     public LinkedList JavaDoc getChangedKnuthElements(List JavaDoc oldList,
863                                               int alignment) {
864         if (isFinished()) {
865             return null;
866         }
867
868         LinkedList JavaDoc returnList = new LinkedList JavaDoc();
869
870         while (iReturnedIndex < vecAreaInfo.size()) {
871             AreaInfo ai = (AreaInfo) vecAreaInfo.get(iReturnedIndex);
872             if (ai.iWScount == 0) {
873                 // ai refers either to a word or a word fragment
874
returnList.addAll
875                 (createElementsForAWordFragment(alignment, ai, iReturnedIndex, letterSpaceIPD));
876             } else {
877                 // ai refers to a space
878
returnList.addAll
879                 (createElementsForASpace(alignment, ai, iReturnedIndex));
880             }
881             iReturnedIndex++;
882         } // end of while
883
setFinished(true);
884         //ElementListObserver.observe(returnList, "text-changed", null);
885
return returnList;
886     }
887
888     /** @see InlineLevelLayoutManager#getWordChars(StringBuffer, Position) */
889     public void getWordChars(StringBuffer JavaDoc sbChars, Position pos) {
890         int iLeafValue = ((LeafPosition) pos).getLeafPos();
891         if (iLeafValue != -1) {
892             AreaInfo ai = (AreaInfo) vecAreaInfo.get(iLeafValue);
893             sbChars.append(new String JavaDoc(textArray, ai.iStartIndex,
894                                       ai.iBreakIndex - ai.iStartIndex));
895         }
896     }
897
898     private LinkedList JavaDoc createElementsForASpace(int alignment,
899             AreaInfo ai, int leafValue) {
900         LinkedList JavaDoc spaceElements = new LinkedList JavaDoc();
901         LeafPosition mainPosition = new LeafPosition(this, leafValue);
902         
903         if (textArray[ai.iStartIndex] == CharUtilities.NBSPACE) {
904             // a non-breaking space
905
//TODO: other kinds of non-breaking spaces
906
if (alignment == EN_JUSTIFY) {
907                 // the space can stretch and shrink, and must be preserved
908
// when starting a line
909
spaceElements.add(new KnuthInlineBox(0, null,
910                         notifyPos(new LeafPosition(this, -1)), true));
911                 spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE,
912                         false, new LeafPosition(this, -1), false));
913                 spaceElements.add(new KnuthGlue(ai.ipdArea.opt, ai.ipdArea.max - ai.ipdArea.opt,
914                         ai.ipdArea.opt - ai.ipdArea.min, mainPosition, false));
915             } else {
916                 // the space does not need to stretch or shrink, and must be
917
// preserved when starting a line
918
spaceElements.add(new KnuthInlineBox(ai.ipdArea.opt, null,
919                         mainPosition, true));
920             }
921         } else if (textArray[ai.iStartIndex] == CharUtilities.SPACE
922                     && foText.getWhitespaceTreatment() == Constants.EN_PRESERVE) {
923             // a breaking space that needs to be preserved
924
switch (alignment) {
925             case EN_CENTER:
926                 // centered text:
927
// if the second element is chosen as a line break these elements
928
// add a constant amount of stretch at the end of a line and at the
929
// beginning of the next one, otherwise they don't add any stretch
930
spaceElements.add(new KnuthGlue(lineEndBAP,
931                         3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
932                         new LeafPosition(this, -1), false));
933                 spaceElements
934                         .add(new KnuthPenalty(
935                                 0,
936                                 (textArray[ai.iStartIndex] == CharUtilities.NBSPACE
937                                         ? KnuthElement.INFINITE
938                                         : 0), false,
939                                 new LeafPosition(this, -1), false));
940                 spaceElements.add(new KnuthGlue(
941                         - (lineStartBAP + lineEndBAP), -6
942                         * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
943                         new LeafPosition(this, -1), false));
944                 spaceElements.add(new KnuthInlineBox(0, null,
945                         notifyPos(new LeafPosition(this, -1)), false));
946                 spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE,
947                         false, new LeafPosition(this, -1), false));
948                 spaceElements.add(new KnuthGlue(ai.ipdArea.opt + lineStartBAP,
949                         3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
950                         mainPosition, false));
951                 break;
952
953             case EN_START: // fall through
954
case EN_END:
955                 // left- or right-aligned text:
956
// if the second element is chosen as a line break these elements
957
// add a constant amount of stretch at the end of a line, otherwise
958
// they don't add any stretch
959
spaceElements.add(new KnuthGlue(lineEndBAP,
960                         3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
961                         new LeafPosition(this, -1), false));
962                 spaceElements.add(new KnuthPenalty(0, 0, false,
963                         new LeafPosition(this, -1), false));
964                 spaceElements.add(new KnuthGlue(
965                         - (lineStartBAP + lineEndBAP), -3
966                         * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
967                         new LeafPosition(this, -1), false));
968                 spaceElements.add(new KnuthInlineBox(0, null,
969                         notifyPos(new LeafPosition(this, -1)), false));
970                 spaceElements.add(new KnuthPenalty(0,
971                         KnuthElement.INFINITE, false, new LeafPosition(
972                                 this, -1), false));
973                 spaceElements.add(new KnuthGlue(ai.ipdArea.opt + lineStartBAP, 0, 0,
974                         mainPosition, false));
975                 break;
976
977             case EN_JUSTIFY:
978                 // justified text:
979
// the stretch and shrink depends on the space width
980
spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0,
981                         new LeafPosition(this, -1), false));
982                 spaceElements.add(new KnuthPenalty(0, 0, false,
983                         new LeafPosition(this, -1), false));
984                 spaceElements.add(new KnuthGlue(
985                         - (lineStartBAP + lineEndBAP), ai.ipdArea.max
986                         - ai.ipdArea.opt, ai.ipdArea.opt - ai.ipdArea.min,
987                         new LeafPosition(this, -1), false));
988                 spaceElements.add(new KnuthInlineBox(0, null,
989                         notifyPos(new LeafPosition(this, -1)), false));
990                 spaceElements.add(new KnuthPenalty(0,
991                         KnuthElement.INFINITE, false, new LeafPosition(
992                                 this, -1), false));
993                 spaceElements.add(new KnuthGlue(lineStartBAP + ai.ipdArea.opt, 0, 0,
994                         mainPosition, false));
995                 break;
996
997             default:
998                 // last line justified, the other lines unjustified:
999
// use only the space stretch
1000
spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0,
1001                        new LeafPosition(this, -1), false));
1002                spaceElements.add(new KnuthPenalty(0, 0, false,
1003                        new LeafPosition(this, -1), false));
1004                spaceElements.add(new KnuthGlue(
1005                        - (lineStartBAP + lineEndBAP), ai.ipdArea.max
1006                        - ai.ipdArea.opt, 0,
1007                        new LeafPosition(this, -1), false));
1008                spaceElements.add(new KnuthInlineBox(0, null,
1009                        notifyPos(new LeafPosition(this, -1)), false));
1010                spaceElements.add(new KnuthPenalty(0,
1011                        KnuthElement.INFINITE, false, new LeafPosition(
1012                                this, -1), false));
1013                spaceElements.add(new KnuthGlue(lineStartBAP + ai.ipdArea.opt, 0, 0,
1014                        mainPosition, false));
1015            }
1016        } else {
1017            // a (possible block) of breaking spaces
1018
switch (alignment) {
1019            case EN_CENTER:
1020                // centered text:
1021
// if the second element is chosen as a line break these elements
1022
// add a constant amount of stretch at the end of a line and at the
1023
// beginning of the next one, otherwise they don't add any stretch
1024
spaceElements.add(new KnuthGlue(lineEndBAP,
1025                        3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1026                        new LeafPosition(this, -1), false));
1027                spaceElements
1028                        .add(new KnuthPenalty(
1029                                0, 0, false,
1030                                new LeafPosition(this, -1), false));
1031                spaceElements.add(new KnuthGlue(ai.ipdArea.opt
1032                        - (lineStartBAP + lineEndBAP), -6
1033                        * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1034                        mainPosition, false));
1035                spaceElements.add(new KnuthInlineBox(0, null,
1036                        notifyPos(new LeafPosition(this, -1)), false));
1037                spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE,
1038                        false, new LeafPosition(this, -1), false));
1039                spaceElements.add(new KnuthGlue(lineStartBAP,
1040                        3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1041                        new LeafPosition(this, -1), false));
1042                break;
1043
1044            case EN_START: // fall through
1045
case EN_END:
1046                // left- or right-aligned text:
1047
// if the second element is chosen as a line break these elements
1048
// add a constant amount of stretch at the end of a line, otherwise
1049
// they don't add any stretch
1050
if (lineStartBAP != 0 || lineEndBAP != 0) {
1051                    spaceElements.add(new KnuthGlue(lineEndBAP,
1052                            3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1053                            new LeafPosition(this, -1), false));
1054                    spaceElements.add(new KnuthPenalty(0, 0, false,
1055                            new LeafPosition(this, -1), false));
1056                    spaceElements.add(new KnuthGlue(ai.ipdArea.opt
1057                            - (lineStartBAP + lineEndBAP), -3
1058                            * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1059                            mainPosition, false));
1060                    spaceElements.add(new KnuthInlineBox(0, null,
1061                            notifyPos(new LeafPosition(this, -1)), false));
1062                    spaceElements.add(new KnuthPenalty(0,
1063                            KnuthElement.INFINITE, false, new LeafPosition(
1064                                    this, -1), false));
1065                    spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0,
1066                            new LeafPosition(this, -1), false));
1067                } else {
1068                    spaceElements.add(new KnuthGlue(0,
1069                            3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1070                            new LeafPosition(this, -1), false));
1071                    spaceElements.add(new KnuthPenalty(0, 0, false,
1072                            new LeafPosition(this, -1), false));
1073                    spaceElements.add(new KnuthGlue(ai.ipdArea.opt, -3
1074                            * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1075                            mainPosition, false));
1076                }
1077                break;
1078
1079            case EN_JUSTIFY:
1080                // justified text:
1081
// the stretch and shrink depends on the space width
1082
if (lineStartBAP != 0 || lineEndBAP != 0) {
1083                    spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0,
1084                            new LeafPosition(this, -1), false));
1085                    spaceElements.add(new KnuthPenalty(0, 0, false,
1086                            new LeafPosition(this, -1), false));
1087                    spaceElements.add(new KnuthGlue(
1088                            ai.ipdArea.opt - (lineStartBAP + lineEndBAP),
1089                            ai.ipdArea.max - ai.ipdArea.opt,
1090                            ai.ipdArea.opt - ai.ipdArea.min,
1091                            mainPosition, false));
1092                    spaceElements.add(new KnuthInlineBox(0, null,
1093                            notifyPos(new LeafPosition(this, -1)), false));
1094                    spaceElements.add(new KnuthPenalty(0,
1095                            KnuthElement.INFINITE, false, new LeafPosition(
1096                                    this, -1), false));
1097                    spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0,
1098                            new LeafPosition(this, -1), false));
1099                } else {
1100                    spaceElements.add(new KnuthGlue(ai.ipdArea.opt,
1101                            ai.ipdArea.max - ai.ipdArea.opt,
1102                            ai.ipdArea.opt - ai.ipdArea.min,
1103                            mainPosition, false));
1104                }
1105                break;
1106
1107            default:
1108                // last line justified, the other lines unjustified:
1109
// use only the space stretch
1110
if (lineStartBAP != 0 || lineEndBAP != 0) {
1111                    spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0,
1112                            new LeafPosition(this, -1), false));
1113                    spaceElements.add(new KnuthPenalty(0, 0, false,
1114                            new LeafPosition(this, -1), false));
1115                    spaceElements.add(new KnuthGlue(
1116                            ai.ipdArea.opt - (lineStartBAP + lineEndBAP),
1117                            ai.ipdArea.max - ai.ipdArea.opt,
1118                            0, mainPosition, false));
1119                    spaceElements.add(new KnuthInlineBox(0, null,
1120                            notifyPos(new LeafPosition(this, -1)), false));
1121                    spaceElements.add(new KnuthPenalty(0,
1122                            KnuthElement.INFINITE, false, new LeafPosition(
1123                                    this, -1), false));
1124                    spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0,
1125                            new LeafPosition(this, -1), false));
1126                } else {
1127                    spaceElements.add(new KnuthGlue(ai.ipdArea.opt,
1128                            ai.ipdArea.max - ai.ipdArea.opt, 0,
1129                            mainPosition, false));
1130                }
1131            }
1132        }
1133        
1134        return spaceElements;
1135    }
1136
1137    private LinkedList JavaDoc createElementsForAWordFragment(int alignment,
1138            AreaInfo ai, int leafValue, MinOptMax letterSpaceWidth) {
1139        LinkedList JavaDoc wordElements = new LinkedList JavaDoc();
1140        LeafPosition mainPosition = new LeafPosition(this, leafValue);
1141
1142        // if the last character of the word fragment is '-' or '/',
1143
// the fragment could end a line; in this case, it loses one
1144
// of its letter spaces;
1145
boolean bSuppressibleLetterSpace
1146            = /*ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)
1147                &&*/
isBreakChar(textArray[ai.iBreakIndex - 1]);
1148
1149        if (letterSpaceWidth.min == letterSpaceWidth.max) {
1150            // constant letter spacing
1151
wordElements.add
1152                (new KnuthInlineBox(
1153                        bSuppressibleLetterSpace
1154                                ? ai.ipdArea.opt - letterSpaceWidth.opt
1155                                : ai.ipdArea.opt,
1156                        alignmentContext,
1157                        notifyPos(mainPosition), false));
1158        } else {
1159            // adjustable letter spacing
1160
int unsuppressibleLetterSpaces
1161                = bSuppressibleLetterSpace ? ai.iLScount - 1 : ai.iLScount;
1162            wordElements.add
1163                (new KnuthInlineBox(ai.ipdArea.opt
1164                        - ai.iLScount * letterSpaceWidth.opt,
1165                        alignmentContext,
1166                        notifyPos(mainPosition), false));
1167            wordElements.add
1168                (new KnuthPenalty(0, KnuthElement.INFINITE, false,
1169                        new LeafPosition(this, -1), true));
1170            wordElements.add
1171                (new KnuthGlue(unsuppressibleLetterSpaces * letterSpaceWidth.opt,
1172                        unsuppressibleLetterSpaces * (letterSpaceWidth.max - letterSpaceWidth.opt),
1173                        unsuppressibleLetterSpaces * (letterSpaceWidth.opt - letterSpaceWidth.min),
1174                        new LeafPosition(this, -1), true));
1175            wordElements.add
1176                (new KnuthInlineBox(0, null,
1177                              notifyPos(new LeafPosition(this, -1)), true));
1178        }
1179 
1180        // extra-elements if the word fragment is the end of a syllable,
1181
// or it ends with a character that can be used as a line break
1182
if (ai.bHyphenated) {
1183            MinOptMax widthIfNoBreakOccurs = null;
1184            if (ai.iBreakIndex < textArray.length) {
1185                //Add in kerning in no-break condition
1186
widthIfNoBreakOccurs = letterAdjustArray[ai.iBreakIndex];
1187            }
1188            //if (ai.iBreakIndex)
1189

1190            // the word fragment ends at the end of a syllable:
1191
// if a break occurs the content width increases,
1192
// otherwise nothing happens
1193
wordElements.addAll(createElementsForAHyphen(alignment, hyphIPD, widthIfNoBreakOccurs));
1194        } else if (bSuppressibleLetterSpace) {
1195            // the word fragment ends with a character that acts as a hyphen
1196
// if a break occurs the width does not increase,
1197
// otherwise there is one more letter space
1198
wordElements.addAll(createElementsForAHyphen(alignment, 0, letterSpaceWidth));
1199        }
1200        return wordElements;
1201    }
1202
1203    private LinkedList JavaDoc createElementsForAHyphen(int alignment,
1204            int widthIfBreakOccurs, MinOptMax widthIfNoBreakOccurs) {
1205        if (widthIfNoBreakOccurs == null) {
1206            widthIfNoBreakOccurs = ZERO_MINOPTMAX;
1207        }
1208        LinkedList JavaDoc hyphenElements = new LinkedList JavaDoc();
1209        
1210        switch (alignment) {
1211        case EN_CENTER :
1212            // centered text:
1213
/*
1214            hyphenElements.add
1215                (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1216                        new LeafPosition(this, -1), false));
1217            hyphenElements.add
1218                (new KnuthPenalty(hyphIPD,
1219                        KnuthPenalty.FLAGGED_PENALTY, true,
1220                        new LeafPosition(this, -1), false));
1221            hyphenElements.add
1222                (new KnuthGlue(0,
1223                        - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1224                        new LeafPosition(this, -1), false));
1225            hyphenElements.add
1226                (new KnuthInlineBox(0, 0, 0, 0,
1227                        new LeafPosition(this, -1), false));
1228            hyphenElements.add
1229                (new KnuthPenalty(0, KnuthElement.INFINITE, true,
1230                        new LeafPosition(this, -1), false));
1231            hyphenElements.add
1232                (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1233                        new LeafPosition(this, -1), false));
1234            */

1235            hyphenElements.add
1236                (new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1237                               new LeafPosition(this, -1), true));
1238            hyphenElements.add
1239                (new KnuthPenalty(hyphIPD,
1240                        KnuthPenalty.FLAGGED_PENALTY, true,
1241                        new LeafPosition(this, -1), false));
1242            hyphenElements.add
1243                (new KnuthGlue(-(lineEndBAP + lineStartBAP),
1244                        -6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1245                        new LeafPosition(this, -1), false));
1246            hyphenElements.add
1247                (new KnuthInlineBox(0, null,
1248                                    notifyPos(new LeafPosition(this, -1)), true));
1249            hyphenElements.add
1250               (new KnuthPenalty(0, KnuthElement.INFINITE, false,
1251                                 new LeafPosition(this, -1), true));
1252            hyphenElements.add
1253                (new KnuthGlue(lineStartBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1254                               new LeafPosition(this, -1), true));
1255            break;
1256            
1257        case EN_START : // fall through
1258
case EN_END :
1259            // left- or right-aligned text:
1260
/*
1261            hyphenElements.add
1262                (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1263                        new LeafPosition(this, -1), false));
1264            hyphenElements.add
1265                (new KnuthPenalty(widthIfBreakOccurs,
1266                        KnuthPenalty.FLAGGED_PENALTY, true,
1267                        new LeafPosition(this, -1), false));
1268             hyphenElements.add
1269                (new KnuthGlue(widthIfNoBreakOccurs.opt,
1270                        - 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1271                        new LeafPosition(this, -1), false));
1272            */

1273            if (lineStartBAP != 0 || lineEndBAP != 0) {
1274                hyphenElements.add
1275                    (new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1276                                   new LeafPosition(this, -1), false));
1277                hyphenElements.add
1278                    (new KnuthPenalty(widthIfBreakOccurs,
1279                            KnuthPenalty.FLAGGED_PENALTY, true,
1280                            new LeafPosition(this, -1), false));
1281                hyphenElements.add
1282                    (new KnuthGlue(widthIfNoBreakOccurs.opt - (lineStartBAP + lineEndBAP),
1283                                   -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1284                                   new LeafPosition(this, -1), false));
1285                hyphenElements.add
1286                    (new KnuthInlineBox(0, null,
1287                                        notifyPos(new LeafPosition(this, -1)), false));
1288                hyphenElements.add
1289                   (new KnuthPenalty(0, KnuthElement.INFINITE, false,
1290                                     new LeafPosition(this, -1), false));
1291                hyphenElements.add
1292                    (new KnuthGlue(lineStartBAP, 0, 0,
1293                                   new LeafPosition(this, -1), false));
1294            } else {
1295                hyphenElements.add
1296                    (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1297                            new LeafPosition(this, -1), false));
1298                hyphenElements.add
1299                    (new KnuthPenalty(widthIfBreakOccurs,
1300                            KnuthPenalty.FLAGGED_PENALTY, true,
1301                            new LeafPosition(this, -1), false));
1302                hyphenElements.add
1303                    (new KnuthGlue(widthIfNoBreakOccurs.opt,
1304                            -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
1305                            new LeafPosition(this, -1), false));
1306            }
1307            break;
1308            
1309        default:
1310            // justified text, or last line justified:
1311
// just a flagged penalty
1312
/*
1313            hyphenElements.add
1314                (new KnuthPenalty(widthIfBreakOccurs,
1315                        KnuthPenalty.FLAGGED_PENALTY, true,
1316                        new LeafPosition(this, -1), false));
1317            */

1318            if (lineStartBAP != 0 || lineEndBAP != 0) {
1319                hyphenElements.add
1320                    (new KnuthGlue(lineEndBAP, 0, 0,
1321                                   new LeafPosition(this, -1), false));
1322                hyphenElements.add
1323                    (new KnuthPenalty(widthIfBreakOccurs,
1324                            KnuthPenalty.FLAGGED_PENALTY, true,
1325                            new LeafPosition(this, -1), false));
1326                // extra elements representing a letter space that is suppressed
1327
// if a break occurs
1328
if (widthIfNoBreakOccurs.min != 0
1329                    || widthIfNoBreakOccurs.max != 0) {
1330                    hyphenElements.add
1331                        (new KnuthGlue(widthIfNoBreakOccurs.opt - (lineStartBAP + lineEndBAP),
1332                                widthIfNoBreakOccurs.max - widthIfNoBreakOccurs.opt,
1333                                widthIfNoBreakOccurs.opt - widthIfNoBreakOccurs.min,
1334                                new LeafPosition(this, -1), false));
1335                } else {
1336                    hyphenElements.add
1337                        (new KnuthGlue(-(lineStartBAP + lineEndBAP), 0, 0,
1338                                       new LeafPosition(this, -1), false));
1339                }
1340                hyphenElements.add
1341                    (new KnuthInlineBox(0, null,
1342                                        notifyPos(new LeafPosition(this, -1)), false));
1343                hyphenElements.add
1344                    (new KnuthPenalty(0, KnuthElement.INFINITE, false,
1345                                      new LeafPosition(this, -1), false));
1346                hyphenElements.add
1347                    (new KnuthGlue(lineStartBAP, 0, 0,
1348                                   new LeafPosition(this, -1), false));
1349            } else {
1350                hyphenElements.add
1351                    (new KnuthPenalty(widthIfBreakOccurs,
1352                            KnuthPenalty.FLAGGED_PENALTY, true,
1353                            new LeafPosition(this, -1), false));
1354                // extra elements representing a letter space that is suppressed
1355
// if a break occurs
1356
if (widthIfNoBreakOccurs.min != 0
1357                    || widthIfNoBreakOccurs.max != 0) {
1358                    hyphenElements.add
1359                        (new KnuthGlue(widthIfNoBreakOccurs.opt,
1360                                widthIfNoBreakOccurs.max - widthIfNoBreakOccurs.opt,
1361                                widthIfNoBreakOccurs.opt - widthIfNoBreakOccurs.min,
1362                                new LeafPosition(this, -1), false));
1363                }
1364            }
1365        }
1366        
1367        return hyphenElements;
1368    }
1369}
1370
1371
Popular Tags