123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362 |
- /*
- * Copyright 1999-2006 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.layoutmgr.inline;
-
- import java.util.ArrayList;
- import java.util.List;
- import java.util.LinkedList;
- import java.util.ListIterator;
-
- import org.apache.fop.area.Trait;
- import org.apache.fop.area.inline.TextArea;
- import org.apache.fop.fo.Constants;
- import org.apache.fop.fo.FOText;
- import org.apache.fop.fo.flow.Inline;
- import org.apache.fop.fonts.Font;
- import org.apache.fop.layoutmgr.InlineKnuthSequence;
- import org.apache.fop.layoutmgr.KnuthBox;
- import org.apache.fop.layoutmgr.KnuthElement;
- import org.apache.fop.layoutmgr.KnuthGlue;
- import org.apache.fop.layoutmgr.KnuthPenalty;
- import org.apache.fop.layoutmgr.KnuthSequence;
- import org.apache.fop.layoutmgr.LayoutContext;
- import org.apache.fop.layoutmgr.LeafPosition;
- import org.apache.fop.layoutmgr.Position;
- import org.apache.fop.layoutmgr.PositionIterator;
- import org.apache.fop.layoutmgr.TraitSetter;
- import org.apache.fop.traits.MinOptMax;
- import org.apache.fop.traits.SpaceVal;
- import org.apache.fop.util.CharUtilities;
-
- /**
- * LayoutManager for text (a sequence of characters) which generates one
- * or more inline areas.
- */
- public class TextLayoutManager extends LeafNodeLayoutManager {
-
- /**
- * Store information about each potential text area.
- * Index of character which ends the area, IPD of area, including
- * any word-space and letter-space.
- * Number of word-spaces?
- */
- private class AreaInfo {
- private short iStartIndex;
- private short iBreakIndex;
- private short iWScount;
- private short iLScount;
- private MinOptMax ipdArea;
- private boolean bHyphenated;
- private boolean isSpace;
- public AreaInfo(short iSIndex, short iBIndex, short iWS, short iLS,
- MinOptMax ipd, boolean bHyph, boolean isSpace) {
- iStartIndex = iSIndex;
- iBreakIndex = iBIndex;
- iWScount = iWS;
- iLScount = iLS;
- ipdArea = ipd;
- bHyphenated = bHyph;
- this.isSpace = isSpace;
- }
-
- public String toString() {
- return "[ lscnt=" + iLScount
- + ", wscnt=" + iWScount
- + ", ipd=" + ipdArea.toString()
- + ", sidx=" + iStartIndex
- + ", bidx=" + iBreakIndex
- + ", hyph=" + bHyphenated
- + ", space=" + isSpace
- + "]";
- }
-
- }
-
- // this class stores information about changes in vecAreaInfo
- // which are not yet applied
- private class PendingChange {
- public AreaInfo ai;
- public int index;
-
- public PendingChange(AreaInfo ai, int index) {
- this.ai = ai;
- this.index = index;
- }
- }
-
- // Hold all possible breaks for the text in this LM's FO.
- private ArrayList vecAreaInfo;
-
- /** Non-space characters on which we can end a line. */
- private static final String BREAK_CHARS = "-/";
-
- /** Used to reduce instantiation of MinOptMax with zero length. Do not modify! */
- private static final MinOptMax ZERO_MINOPTMAX = new MinOptMax(0);
-
- private FOText foText;
- private char[] textArray;
- /**
- * Contains an array of widths to adjust for kerning. The first entry can
- * be used to influence the start position of the first letter. The entry i+1 defines the
- * cursor advancement after the character i. A null entry means no special advancement.
- */
- private MinOptMax[] letterAdjustArray; //size = textArray.length + 1
-
- private static final char NEWLINE = '\n';
-
- private Font font = null;
- /** Start index of first character in this parent Area */
- private short iAreaStart = 0;
- /** Start index of next TextArea */
- private short iNextStart = 0;
- /** Size since last makeArea call, except for last break */
- private MinOptMax ipdTotal;
- /** Size including last break possibility returned */
- // private MinOptMax nextIPD = new MinOptMax(0);
- /** size of a space character (U+0020) glyph in current font */
- private int spaceCharIPD;
- private MinOptMax wordSpaceIPD;
- private MinOptMax letterSpaceIPD;
- /** size of the hyphen character glyph in current font */
- private int hyphIPD;
- /** 1/1 of word-spacing value */
- private SpaceVal ws;
- /** 1/2 of word-spacing value */
- private SpaceVal halfWS;
- /** 1/2 of letter-spacing value */
- private SpaceVal halfLS;
- /** Number of space characters after previous possible break position. */
- private int iNbSpacesPending;
-
- private boolean bChanged = false;
- private int iReturnedIndex = 0;
- private short iThisStart = 0;
- private short iTempStart = 0;
- private LinkedList changeList = null;
-
- private AlignmentContext alignmentContext = null;
-
- private int lineStartBAP = 0;
- private int lineEndBAP = 0;
-
- /**
- * Create a Text layout manager.
- *
- * @param node The FOText object to be rendered
- */
- public TextLayoutManager(FOText node) {
- super();
- foText = node;
-
- textArray = new char[node.endIndex - node.startIndex];
- System.arraycopy(node.ca, node.startIndex, textArray, 0,
- node.endIndex - node.startIndex);
- letterAdjustArray = new MinOptMax[textArray.length + 1];
-
- vecAreaInfo = new java.util.ArrayList();
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager#initialize */
- public void initialize() {
- font = foText.getCommonFont().getFontState(foText.getFOEventHandler().getFontInfo(), this);
-
- // With CID fonts, space isn't neccesary currentFontState.width(32)
- spaceCharIPD = font.getCharWidth(' ');
- // Use hyphenationChar property
- hyphIPD = font.getCharWidth(foText.getCommonHyphenation().hyphenationCharacter);
-
- SpaceVal ls = SpaceVal.makeLetterSpacing(foText.getLetterSpacing());
- halfLS = new SpaceVal(MinOptMax.multiply(ls.getSpace(), 0.5),
- ls.isConditional(), ls.isForcing(), ls.getPrecedence());
-
- ws = SpaceVal.makeWordSpacing(foText.getWordSpacing(), ls, font);
- // Make half-space: <space> on either side of a word-space)
- halfWS = new SpaceVal(MinOptMax.multiply(ws.getSpace(), 0.5),
- ws.isConditional(), ws.isForcing(), ws.getPrecedence());
-
- // letter space applies only to consecutive non-space characters,
- // while word space applies to space characters;
- // i.e. the spaces in the string "A SIMPLE TEST" are:
- // A<<ws>>S<ls>I<ls>M<ls>P<ls>L<ls>E<<ws>>T<ls>E<ls>S<ls>T
- // there is no letter space after the last character of a word,
- // nor after a space character
- // NOTE: The above is not quite correct. Read on in XSL 1.0, 7.16.2, letter-spacing
-
- // set letter space and word space dimension;
- // the default value "normal" was converted into a MinOptMax value
- // in the SpaceVal.makeWordSpacing() method
- letterSpaceIPD = ls.getSpace();
- wordSpaceIPD = MinOptMax.add(new MinOptMax(spaceCharIPD), ws.getSpace());
-
- // if the text node is son of an inline, set vertical align
- if (foText.getParent() instanceof Inline) {
- Inline fobj = (Inline)foText.getParent();
- }
- }
-
- /**
- * Reset position for returning next BreakPossibility.
- *
- * @param prevPos the position to reset to
- */
- public void resetPosition(Position prevPos) {
- if (prevPos != null) {
- // ASSERT (prevPos.getLM() == this)
- if (prevPos.getLM() != this) {
- log.error("TextLayoutManager.resetPosition: "
- + "LM mismatch!!!");
- }
- LeafPosition tbp = (LeafPosition) prevPos;
- AreaInfo ai = (AreaInfo) vecAreaInfo.get(tbp.getLeafPos());
- if (ai.iBreakIndex != iNextStart) {
- iNextStart = ai.iBreakIndex;
- vecAreaInfo.ensureCapacity(tbp.getLeafPos() + 1);
- // TODO: reset or recalculate total IPD = sum of all word IPD
- // up to the break position
- ipdTotal = ai.ipdArea;
- setFinished(false);
- }
- } else {
- // Reset to beginning!
- vecAreaInfo.clear();
- iNextStart = 0;
- setFinished(false);
- }
- }
-
- // TODO: see if we can use normal getNextBreakPoss for this with
- // extra hyphenation information in LayoutContext
- private boolean getHyphenIPD(HyphContext hc, MinOptMax hyphIPD) {
- // Skip leading word-space before calculating count?
- boolean bCanHyphenate = true;
- int iStopIndex = iNextStart + hc.getNextHyphPoint();
-
- if (textArray.length < iStopIndex) {
- iStopIndex = textArray.length;
- bCanHyphenate = false;
- }
- hc.updateOffset(iStopIndex - iNextStart);
-
- for (; iNextStart < iStopIndex; iNextStart++) {
- char c = textArray[iNextStart];
- hyphIPD.opt += font.getCharWidth(c);
- // letter-space?
- }
- // Need to include hyphen size too, but don't count it in the
- // stored running total, since it would be double counted
- // with later hyphenation points
- return bCanHyphenate;
- }
-
- /**
- * Generate and add areas to parent area.
- * This can either generate an area for each TextArea and each space, or
- * an area containing all text with a parameter controlling the size of
- * the word space. The latter is most efficient for PDF generation.
- * Set size of each area.
- * @param posIter Iterator over Position information returned
- * by this LayoutManager.
- * @param context LayoutContext for adjustments
- */
- public void addAreas(PositionIterator posIter, LayoutContext context) {
-
- // Add word areas
- AreaInfo ai = null;
- int iWScount = 0;
- int iLScount = 0;
- int firstAreaInfoIndex = -1;
- int lastAreaInfoIndex = 0;
- MinOptMax realWidth = new MinOptMax(0);
-
- /* On first area created, add any leading space.
- * Calculate word-space stretch value.
- */
- while (posIter.hasNext()) {
- LeafPosition tbpNext = (LeafPosition) posIter.next();
- if (tbpNext == null) {
- continue; //Ignore elements without Positions
- }
- if (tbpNext.getLeafPos() != -1) {
- ai = (AreaInfo) vecAreaInfo.get(tbpNext.getLeafPos());
- if (firstAreaInfoIndex == -1) {
- firstAreaInfoIndex = tbpNext.getLeafPos();
- }
- iWScount += ai.iWScount;
- iLScount += ai.iLScount;
- realWidth.add(ai.ipdArea);
- lastAreaInfoIndex = tbpNext.getLeafPos();
- }
- }
- if (ai == null) {
- return;
- }
- int textLength = ai.iBreakIndex - ai.iStartIndex;
- if (ai.iLScount == textLength
- && context.isLastArea()) {
- // the line ends at a character like "/" or "-";
- // remove the letter space after the last character
- realWidth.add(MinOptMax.multiply(letterSpaceIPD, -1));
- iLScount--;
- }
-
- for (int i = ai.iStartIndex; i < ai.iBreakIndex; i++) {
- MinOptMax ladj = letterAdjustArray[i + 1];
- if (ladj != null && ladj.isElastic()) {
- iLScount++;
- }
- }
-
- // add hyphenation character if the last word is hyphenated
- if (context.isLastArea() && ai.bHyphenated) {
- realWidth.add(new MinOptMax(hyphIPD));
- }
-
- // Calculate adjustments
- int iDifference = 0;
- int iTotalAdjust = 0;
- int iWordSpaceDim = wordSpaceIPD.opt;
- int iLetterSpaceDim = letterSpaceIPD.opt;
- double dIPDAdjust = context.getIPDAdjust();
- double dSpaceAdjust = context.getSpaceAdjust(); // not used
-
- // calculate total difference between real and available width
- if (dIPDAdjust > 0.0) {
- iDifference = (int) ((double) (realWidth.max - realWidth.opt)
- * dIPDAdjust);
- } else {
- iDifference = (int) ((double) (realWidth.opt - realWidth.min)
- * dIPDAdjust);
- }
-
- // set letter space adjustment
- if (dIPDAdjust > 0.0) {
- iLetterSpaceDim
- += (int) ((double) (letterSpaceIPD.max - letterSpaceIPD.opt)
- * dIPDAdjust);
- } else {
- iLetterSpaceDim
- += (int) ((double) (letterSpaceIPD.opt - letterSpaceIPD.min)
- * dIPDAdjust);
- }
- iTotalAdjust += (iLetterSpaceDim - letterSpaceIPD.opt) * iLScount;
-
- // set word space adjustment
- //
- if (iWScount > 0) {
- iWordSpaceDim += (int) ((iDifference - iTotalAdjust) / iWScount);
- } else {
- // there are no word spaces in this area
- }
- iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount;
- if (iTotalAdjust != iDifference) {
- // the applied adjustment is greater or smaller than the needed one
- log.trace("TextLM.addAreas: error in word / letter space adjustment = "
- + (iTotalAdjust - iDifference));
- // set iTotalAdjust = iDifference, so that the width of the TextArea
- // will counterbalance the error and the other inline areas will be
- // placed correctly
- iTotalAdjust = iDifference;
- }
-
- TextArea t = createTextArea(realWidth, iTotalAdjust, context,
- wordSpaceIPD.opt - spaceCharIPD,
- firstAreaInfoIndex, lastAreaInfoIndex,
- context.isLastArea());
-
- // iWordSpaceDim is computed in relation to wordSpaceIPD.opt
- // but the renderer needs to know the adjustment in relation
- // to the size of the space character in the current font;
- // moreover, the pdf renderer adds the character spacing even to
- // the last character of a word and to space characters: in order
- // to avoid this, we must subtract the letter space width twice;
- // the renderer will compute the space width as:
- // space width =
- // = "normal" space width + letterSpaceAdjust + wordSpaceAdjust
- // = spaceCharIPD + letterSpaceAdjust +
- // + (iWordSpaceDim - spaceCharIPD - 2 * letterSpaceAdjust)
- // = iWordSpaceDim - letterSpaceAdjust
- t.setTextLetterSpaceAdjust(iLetterSpaceDim);
- t.setTextWordSpaceAdjust(iWordSpaceDim - spaceCharIPD
- - 2 * t.getTextLetterSpaceAdjust());
- if (context.getIPDAdjust() != 0) {
- // add information about space width
- t.setSpaceDifference(wordSpaceIPD.opt - spaceCharIPD
- - 2 * t.getTextLetterSpaceAdjust());
- }
- parentLM.addChildArea(t);
- }
-
- /**
- * Create an inline word area.
- * This creates a TextArea and sets up the various attributes.
- *
- * @param width the MinOptMax width of the content
- * @param adjust the total ipd adjustment with respect to the optimal width
- * @param context the layout context
- * @param spaceDiff unused
- * @param firstIndex the index of the first AreaInfo used for the TextArea
- * @param lastIndex the index of the last AreaInfo used for the TextArea
- * @param isLastArea is this TextArea the last in a line?
- * @return the new text area
- */
- protected TextArea createTextArea(MinOptMax width, int adjust,
- LayoutContext context, int spaceDiff,
- int firstIndex, int lastIndex, boolean isLastArea) {
- TextArea textArea;
- if (context.getIPDAdjust() == 0.0) {
- // create just a TextArea
- textArea = new TextArea();
- } else {
- // justified area: create a TextArea with extra info
- // about potential adjustments
- textArea = new TextArea(width.max - width.opt,
- width.opt - width.min,
- adjust);
- }
- textArea.setIPD(width.opt + adjust);
- textArea.setBPD(font.getAscender() - font.getDescender());
- textArea.setBaselineOffset(font.getAscender());
- if (textArea.getBPD() == alignmentContext.getHeight()) {
- textArea.setOffset(0);
- } else {
- textArea.setOffset(alignmentContext.getOffset());
- }
-
- // set the text of the TextArea, split into words and spaces
- int wordStartIndex = -1;
- AreaInfo areaInfo;
- for (int i = firstIndex; i <= lastIndex; i++) {
- areaInfo = (AreaInfo) vecAreaInfo.get(i);
- if (areaInfo.isSpace) {
- // areaInfo stores information about spaces
- // add the spaces to the TextArea
- for (int j = areaInfo.iStartIndex; j < areaInfo.iBreakIndex; j++) {
- char spaceChar = textArray[j];
- textArea.addSpace(spaceChar, 0,
- CharUtilities.isAdjustableSpace(spaceChar));
- }
- } else {
- // areaInfo stores information about a word fragment
- if (wordStartIndex == -1) {
- // here starts a new word
- wordStartIndex = areaInfo.iStartIndex;
- }
- if (i == lastIndex || ((AreaInfo) vecAreaInfo.get(i + 1)).isSpace) {
- // here ends a new word
- // add a word to the TextArea
- int len = areaInfo.iBreakIndex - wordStartIndex;
- String wordChars = new String(textArray, wordStartIndex, len);
- if (isLastArea
- && i == lastIndex
- && areaInfo.bHyphenated) {
- // add the hyphenation character
- wordChars += foText.getCommonHyphenation().hyphenationCharacter;
- }
- int[] letterAdjust = new int[wordChars.length()];
- int lsCount = areaInfo.iLScount;
- for (int letter = 0; letter < len; letter++) {
- MinOptMax adj = letterAdjustArray[letter + wordStartIndex];
- if (letter > 0) {
- letterAdjust[letter] = (adj != null ? adj.opt : 0);
- }
- if (lsCount > 0) {
- letterAdjust[letter] += textArea.getTextLetterSpaceAdjust();
- lsCount--;
- }
- }
- textArea.addWord(wordChars, 0, letterAdjust);
- wordStartIndex = -1;
- }
- }
- }
- TraitSetter.addFontTraits(textArea, font);
- textArea.addTrait(Trait.COLOR, foText.getColor());
-
- TraitSetter.addTextDecoration(textArea, foText.getTextDecoration());
-
- return textArea;
- }
-
- private void addToLetterAdjust(int index, int width) {
- if (letterAdjustArray[index] == null) {
- letterAdjustArray[index] = new MinOptMax(width);
- } else {
- letterAdjustArray[index].add(width);
- }
- }
-
- private void addToLetterAdjust(int index, MinOptMax width) {
- if (letterAdjustArray[index] == null) {
- letterAdjustArray[index] = new MinOptMax(width);
- } else {
- letterAdjustArray[index].add(width);
- }
- }
-
- /**
- * Indicates whether a character is a space in terms of this layout manager.
- * @param ch the character
- * @return true if it's a space
- */
- private static boolean isSpace(final char ch) {
- return ch == CharUtilities.SPACE
- || ch == CharUtilities.NBSPACE
- || CharUtilities.isFixedWidthSpace(ch);
- }
-
- private static boolean isBreakChar(final char ch) {
- return (BREAK_CHARS.indexOf(ch) >= 0);
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(LayoutContext, int) */
- public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
- lineStartBAP = context.getLineStartBorderAndPaddingWidth();
- lineEndBAP = context.getLineEndBorderAndPaddingWidth();
- alignmentContext = context.getAlignmentContext();
-
- LinkedList returnList = new LinkedList();
- KnuthSequence sequence = new InlineKnuthSequence();
- AreaInfo ai = null;
- returnList.add(sequence);
-
- while (iNextStart < textArray.length) {
- char ch = textArray[iNextStart];
- if (ch == CharUtilities.SPACE
- && foText.getWhitespaceTreatment() != Constants.EN_PRESERVE) {
- // normal non preserved space - collect them all
- // advance to the next character
- iThisStart = iNextStart;
- iNextStart++;
- while (iNextStart < textArray.length
- && textArray[iNextStart] == CharUtilities.SPACE) {
- iNextStart++;
- }
- // create the AreaInfo object
- ai = new AreaInfo(iThisStart, (short) (iNextStart),
- (short) (iNextStart - iThisStart), (short) 0,
- MinOptMax.multiply(wordSpaceIPD, iNextStart - iThisStart),
- false, true);
- vecAreaInfo.add(ai);
-
- // create the elements
- sequence.addAll
- (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1));
-
- } else if (ch == CharUtilities.SPACE || ch == CharUtilities.NBSPACE) {
- // preserved space or non-breaking space:
- // create the AreaInfo object
- ai = new AreaInfo(iNextStart, (short) (iNextStart + 1),
- (short) 1, (short) 0,
- wordSpaceIPD, false, true);
- vecAreaInfo.add(ai);
-
- // create the elements
- sequence.addAll
- (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1));
-
- // advance to the next character
- iNextStart++;
- } else if (CharUtilities.isFixedWidthSpace(ch)) {
- // create the AreaInfo object
- MinOptMax ipd = new MinOptMax(font.getCharWidth(ch));
- ai = new AreaInfo(iNextStart, (short) (iNextStart + 1),
- (short) 0, (short) 0,
- ipd, false, true);
- vecAreaInfo.add(ai);
-
- // create the elements
- sequence.addAll
- (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1));
-
- // advance to the next character
- iNextStart++;
- } else if (ch == NEWLINE) {
- // linefeed; this can happen when linefeed-treatment="preserve"
- // add a penalty item to the list and start a new sequence
- if (lineEndBAP != 0) {
- sequence.add
- (new KnuthGlue(lineEndBAP, 0, 0,
- new LeafPosition(this, -1), true));
- }
- sequence.endSequence();
- sequence = new InlineKnuthSequence();
- returnList.add(sequence);
-
- // advance to the next character
- iNextStart++;
- } else {
- // the beginning of a word
- iThisStart = iNextStart;
- iTempStart = iNextStart;
- for (; iTempStart < textArray.length
- && !isSpace(textArray[iTempStart])
- && textArray[iTempStart] != NEWLINE
- && !(iTempStart > iNextStart
- && isBreakChar(textArray[iTempStart - 1]));
- iTempStart++) {
- //nop, just find the word boundary
- }
-
- //Word boundary found, process widths and kerning
- int wordLength = iTempStart - iThisStart;
- boolean kerning = font.hasKerning();
- MinOptMax wordIPD = new MinOptMax(0);
- for (int i = iThisStart; i < iTempStart; i++) {
- char c = textArray[i];
-
- //character width
- int charWidth = font.getCharWidth(c);
- wordIPD.add(charWidth);
-
- //kerning
- int kern = 0;
- if (kerning && (i > iThisStart)) {
- char previous = textArray[i - 1];
- kern = font.getKernValue(previous, c) * font.getFontSize() / 1000;
- if (kern != 0) {
- //log.info("Kerning between " + previous + " and " + c + ": " + kern);
- addToLetterAdjust(i, kern);
- }
- wordIPD.add(kern);
- }
- }
-
- int iLetterSpaces = wordLength - 1;
- // if the last character is '-' or '/' and the next one
- // is not a space, it could be used as a line end;
- // add one more letter space, in case other text follows
- if (isBreakChar(textArray[iTempStart - 1])
- && iTempStart < textArray.length
- && !isSpace(textArray[iTempStart])) {
- iLetterSpaces++;
- }
- wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces));
-
- // create the AreaInfo object
- ai = new AreaInfo(iThisStart, iTempStart, (short) 0,
- (short) iLetterSpaces,
- wordIPD, false, false);
- vecAreaInfo.add(ai);
-
- // create the elements
- sequence.addAll(createElementsForAWordFragment(alignment, ai,
- vecAreaInfo.size() - 1, letterSpaceIPD));
-
- // advance to the next character
- iNextStart = iTempStart;
- }
- } // end of while
- if (((List)returnList.getLast()).size() == 0) {
- //Remove an empty sequence because of a trailing newline
- returnList.removeLast();
- }
- setFinished(true);
- if (returnList.size() > 0) {
- return returnList;
- } else {
- return null;
- }
- }
-
- /** @see InlineLevelLayoutManager#addALetterSpaceTo(List) */
- public List addALetterSpaceTo(List oldList) {
- // old list contains only a box, or the sequence: box penalty glue box;
- // look at the Position stored in the first element in oldList
- // which is always a box
- ListIterator oldListIterator = oldList.listIterator();
- KnuthElement el = (KnuthElement)oldListIterator.next();
- LeafPosition pos = (LeafPosition) ((KnuthBox) el).getPosition();
- AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
- ai.iLScount++;
- ai.ipdArea.add(letterSpaceIPD);
- if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0) {
- // the last character could be used as a line break
- // append new elements to oldList
- oldListIterator = oldList.listIterator(oldList.size());
- oldListIterator.add(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- oldListIterator.add(new KnuthGlue(letterSpaceIPD.opt,
- letterSpaceIPD.max - letterSpaceIPD.opt,
- letterSpaceIPD.opt - letterSpaceIPD.min,
- new LeafPosition(this, -1), false));
- } else if (letterSpaceIPD.min == letterSpaceIPD.max) {
- // constant letter space: replace the box
- oldListIterator.set(new KnuthInlineBox(ai.ipdArea.opt, alignmentContext, pos, false));
- } else {
- // adjustable letter space: replace the glue
- oldListIterator.next(); // this would return the penalty element
- oldListIterator.next(); // this would return the glue element
- oldListIterator.set(new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
- ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
- ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
- new LeafPosition(this, -1), true));
- }
- return oldList;
- }
-
- /**
- * remove the AreaInfo object represented by the given elements,
- * so that it won't generate any element when getChangedKnuthElements
- * will be called
- *
- * @param oldList the elements representing the word space
- */
- public void removeWordSpace(List oldList) {
- // find the element storing the Position whose value
- // points to the AreaInfo object
- ListIterator oldListIterator = oldList.listIterator();
- if (((KnuthElement) ((LinkedList) oldList).getFirst()).isPenalty()) {
- // non breaking space: oldList starts with a penalty
- oldListIterator.next();
- }
- if (oldList.size() > 2) {
- // alignment is either center, start or end:
- // the first two elements does not store the needed Position
- oldListIterator.next();
- oldListIterator.next();
- }
- int leafValue = ((LeafPosition) ((KnuthElement) oldListIterator.next()).getPosition()).getLeafPos();
- // only the last word space can be a trailing space!
- if (leafValue == vecAreaInfo.size() - 1) {
- vecAreaInfo.remove(leafValue);
- } else {
- log.error("trying to remove a non-trailing word space");
- }
- }
-
- /** @see InlineLevelLayoutManager#hyphenate(Position, HyphContext) */
- public void hyphenate(Position pos, HyphContext hc) {
- AreaInfo ai
- = (AreaInfo) vecAreaInfo.get(((LeafPosition) pos).getLeafPos());
- int iStartIndex = ai.iStartIndex;
- int iStopIndex;
- boolean bNothingChanged = true;
-
- while (iStartIndex < ai.iBreakIndex) {
- MinOptMax newIPD = new MinOptMax(0);
- boolean bHyphenFollows;
-
- if (hc.hasMoreHyphPoints()
- && (iStopIndex = iStartIndex + hc.getNextHyphPoint())
- <= ai.iBreakIndex) {
- // iStopIndex is the index of the first character
- // after a hyphenation point
- bHyphenFollows = true;
- } else {
- // there are no more hyphenation points,
- // or the next one is after ai.iBreakIndex
- bHyphenFollows = false;
- iStopIndex = ai.iBreakIndex;
- }
-
- hc.updateOffset(iStopIndex - iStartIndex);
-
- //log.info("Word: " + new String(textArray, iStartIndex, iStopIndex - iStartIndex));
- for (int i = iStartIndex; i < iStopIndex; i++) {
- char c = textArray[i];
- newIPD.add(new MinOptMax(font.getCharWidth(c)));
- //if (i > iStartIndex) {
- if (i < iStopIndex) {
- MinOptMax la = this.letterAdjustArray[i + 1];
- if ((i == iStopIndex - 1) && bHyphenFollows) {
- //the letter adjust here needs to be handled further down during
- //element generation because it depends on hyph/no-hyph condition
- la = null;
- }
- if (la != null) {
- newIPD.add(la);
- }
- }
- }
- // add letter spaces
- boolean bIsWordEnd
- = iStopIndex == ai.iBreakIndex
- && ai.iLScount < (ai.iBreakIndex - ai.iStartIndex);
- newIPD.add(MinOptMax.multiply(letterSpaceIPD,
- (bIsWordEnd
- ? (iStopIndex - iStartIndex - 1)
- : (iStopIndex - iStartIndex))));
-
- if (!(bNothingChanged
- && iStopIndex == ai.iBreakIndex
- && bHyphenFollows == false)) {
- // the new AreaInfo object is not equal to the old one
- if (changeList == null) {
- changeList = new LinkedList();
- }
- changeList.add
- (new PendingChange
- (new AreaInfo((short) iStartIndex, (short) iStopIndex,
- (short) 0,
- (short) (bIsWordEnd
- ? (iStopIndex - iStartIndex - 1)
- : (iStopIndex - iStartIndex)),
- newIPD, bHyphenFollows, false),
- ((LeafPosition) pos).getLeafPos()));
- bNothingChanged = false;
- }
- iStartIndex = iStopIndex;
- }
- if (!bChanged && !bNothingChanged) {
- bChanged = true;
- }
- }
-
- /** @see InlineLevelLayoutManager#applyChanges(List) */
- public boolean applyChanges(List oldList) {
- setFinished(false);
-
- if (changeList != null) {
- int iAddedAI = 0;
- int iRemovedAI = 0;
- int iOldIndex = -1;
- PendingChange currChange = null;
- ListIterator changeListIterator = changeList.listIterator();
- while (changeListIterator.hasNext()) {
- currChange = (PendingChange) changeListIterator.next();
- if (currChange.index != iOldIndex) {
- iRemovedAI++;
- iAddedAI++;
- iOldIndex = currChange.index;
- vecAreaInfo.remove(currChange.index + iAddedAI - iRemovedAI);
- vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI,
- currChange.ai);
- } else {
- iAddedAI++;
- vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI,
- currChange.ai);
- }
- }
- changeList.clear();
- }
-
- iReturnedIndex = 0;
- return bChanged;
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(List, int) */
- public LinkedList getChangedKnuthElements(List oldList,
- int alignment) {
- if (isFinished()) {
- return null;
- }
-
- LinkedList returnList = new LinkedList();
-
- while (iReturnedIndex < vecAreaInfo.size()) {
- AreaInfo ai = (AreaInfo) vecAreaInfo.get(iReturnedIndex);
- if (ai.iWScount == 0) {
- // ai refers either to a word or a word fragment
- returnList.addAll
- (createElementsForAWordFragment(alignment, ai, iReturnedIndex, letterSpaceIPD));
- } else {
- // ai refers to a space
- returnList.addAll
- (createElementsForASpace(alignment, ai, iReturnedIndex));
- }
- iReturnedIndex++;
- } // end of while
- setFinished(true);
- //ElementListObserver.observe(returnList, "text-changed", null);
- return returnList;
- }
-
- /** @see InlineLevelLayoutManager#getWordChars(StringBuffer, Position) */
- public void getWordChars(StringBuffer sbChars, Position pos) {
- int iLeafValue = ((LeafPosition) pos).getLeafPos();
- if (iLeafValue != -1) {
- AreaInfo ai = (AreaInfo) vecAreaInfo.get(iLeafValue);
- sbChars.append(new String(textArray, ai.iStartIndex,
- ai.iBreakIndex - ai.iStartIndex));
- }
- }
-
- private LinkedList createElementsForASpace(int alignment,
- AreaInfo ai, int leafValue) {
- LinkedList spaceElements = new LinkedList();
- LeafPosition mainPosition = new LeafPosition(this, leafValue);
-
- if (textArray[ai.iStartIndex] == CharUtilities.NBSPACE) {
- // a non-breaking space
- //TODO: other kinds of non-breaking spaces
- if (alignment == EN_JUSTIFY) {
- // the space can stretch and shrink, and must be preserved
- // when starting a line
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), true));
- spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE,
- false, new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(ai.ipdArea.opt, ai.ipdArea.max - ai.ipdArea.opt,
- ai.ipdArea.opt - ai.ipdArea.min, mainPosition, false));
- } else {
- // the space does not need to stretch or shrink, and must be
- // preserved when starting a line
- spaceElements.add(new KnuthInlineBox(ai.ipdArea.opt, null,
- mainPosition, true));
- }
- } else if (textArray[ai.iStartIndex] == CharUtilities.SPACE
- && foText.getWhitespaceTreatment() == Constants.EN_PRESERVE) {
- // a breaking space that needs to be preserved
- switch (alignment) {
- case EN_CENTER:
- // centered text:
- // if the second element is chosen as a line break these elements
- // add a constant amount of stretch at the end of a line and at the
- // beginning of the next one, otherwise they don't add any stretch
- spaceElements.add(new KnuthGlue(lineEndBAP,
- 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- spaceElements
- .add(new KnuthPenalty(
- 0,
- (textArray[ai.iStartIndex] == CharUtilities.NBSPACE
- ? KnuthElement.INFINITE
- : 0), false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(
- - (lineStartBAP + lineEndBAP), -6
- * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE,
- false, new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(ai.ipdArea.opt + lineStartBAP,
- 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- mainPosition, false));
- break;
-
- case EN_START: // fall through
- case EN_END:
- // left- or right-aligned text:
- // if the second element is chosen as a line break these elements
- // add a constant amount of stretch at the end of a line, otherwise
- // they don't add any stretch
- spaceElements.add(new KnuthGlue(lineEndBAP,
- 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthPenalty(0, 0, false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(
- - (lineStartBAP + lineEndBAP), -3
- * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- spaceElements.add(new KnuthPenalty(0,
- KnuthElement.INFINITE, false, new LeafPosition(
- this, -1), false));
- spaceElements.add(new KnuthGlue(ai.ipdArea.opt + lineStartBAP, 0, 0,
- mainPosition, false));
- break;
-
- case EN_JUSTIFY:
- // justified text:
- // the stretch and shrink depends on the space width
- spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthPenalty(0, 0, false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(
- - (lineStartBAP + lineEndBAP), ai.ipdArea.max
- - ai.ipdArea.opt, ai.ipdArea.opt - ai.ipdArea.min,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- spaceElements.add(new KnuthPenalty(0,
- KnuthElement.INFINITE, false, new LeafPosition(
- this, -1), false));
- spaceElements.add(new KnuthGlue(lineStartBAP + ai.ipdArea.opt, 0, 0,
- mainPosition, false));
- break;
-
- default:
- // last line justified, the other lines unjustified:
- // use only the space stretch
- spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthPenalty(0, 0, false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(
- - (lineStartBAP + lineEndBAP), ai.ipdArea.max
- - ai.ipdArea.opt, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- spaceElements.add(new KnuthPenalty(0,
- KnuthElement.INFINITE, false, new LeafPosition(
- this, -1), false));
- spaceElements.add(new KnuthGlue(lineStartBAP + ai.ipdArea.opt, 0, 0,
- mainPosition, false));
- }
- } else {
- // a (possible block) of breaking spaces
- switch (alignment) {
- case EN_CENTER:
- // centered text:
- // if the second element is chosen as a line break these elements
- // add a constant amount of stretch at the end of a line and at the
- // beginning of the next one, otherwise they don't add any stretch
- spaceElements.add(new KnuthGlue(lineEndBAP,
- 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- spaceElements
- .add(new KnuthPenalty(
- 0, 0, false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(ai.ipdArea.opt
- - (lineStartBAP + lineEndBAP), -6
- * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- mainPosition, false));
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE,
- false, new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(lineStartBAP,
- 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- break;
-
- case EN_START: // fall through
- case EN_END:
- // left- or right-aligned text:
- // if the second element is chosen as a line break these elements
- // add a constant amount of stretch at the end of a line, otherwise
- // they don't add any stretch
- if (lineStartBAP != 0 || lineEndBAP != 0) {
- spaceElements.add(new KnuthGlue(lineEndBAP,
- 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthPenalty(0, 0, false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(ai.ipdArea.opt
- - (lineStartBAP + lineEndBAP), -3
- * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- mainPosition, false));
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- spaceElements.add(new KnuthPenalty(0,
- KnuthElement.INFINITE, false, new LeafPosition(
- this, -1), false));
- spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0,
- new LeafPosition(this, -1), false));
- } else {
- spaceElements.add(new KnuthGlue(0,
- 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthPenalty(0, 0, false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(ai.ipdArea.opt, -3
- * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- mainPosition, false));
- }
- break;
-
- case EN_JUSTIFY:
- // justified text:
- // the stretch and shrink depends on the space width
- if (lineStartBAP != 0 || lineEndBAP != 0) {
- spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthPenalty(0, 0, false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(
- ai.ipdArea.opt - (lineStartBAP + lineEndBAP),
- ai.ipdArea.max - ai.ipdArea.opt,
- ai.ipdArea.opt - ai.ipdArea.min,
- mainPosition, false));
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- spaceElements.add(new KnuthPenalty(0,
- KnuthElement.INFINITE, false, new LeafPosition(
- this, -1), false));
- spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0,
- new LeafPosition(this, -1), false));
- } else {
- spaceElements.add(new KnuthGlue(ai.ipdArea.opt,
- ai.ipdArea.max - ai.ipdArea.opt,
- ai.ipdArea.opt - ai.ipdArea.min,
- mainPosition, false));
- }
- break;
-
- default:
- // last line justified, the other lines unjustified:
- // use only the space stretch
- if (lineStartBAP != 0 || lineEndBAP != 0) {
- spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthPenalty(0, 0, false,
- new LeafPosition(this, -1), false));
- spaceElements.add(new KnuthGlue(
- ai.ipdArea.opt - (lineStartBAP + lineEndBAP),
- ai.ipdArea.max - ai.ipdArea.opt,
- 0, mainPosition, false));
- spaceElements.add(new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- spaceElements.add(new KnuthPenalty(0,
- KnuthElement.INFINITE, false, new LeafPosition(
- this, -1), false));
- spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0,
- new LeafPosition(this, -1), false));
- } else {
- spaceElements.add(new KnuthGlue(ai.ipdArea.opt,
- ai.ipdArea.max - ai.ipdArea.opt, 0,
- mainPosition, false));
- }
- }
- }
-
- return spaceElements;
- }
-
- private LinkedList createElementsForAWordFragment(int alignment,
- AreaInfo ai, int leafValue, MinOptMax letterSpaceWidth) {
- LinkedList wordElements = new LinkedList();
- LeafPosition mainPosition = new LeafPosition(this, leafValue);
-
- // if the last character of the word fragment is '-' or '/',
- // the fragment could end a line; in this case, it loses one
- // of its letter spaces;
- boolean bSuppressibleLetterSpace
- = /*ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)
- &&*/ isBreakChar(textArray[ai.iBreakIndex - 1]);
-
- if (letterSpaceWidth.min == letterSpaceWidth.max) {
- // constant letter spacing
- wordElements.add
- (new KnuthInlineBox(
- bSuppressibleLetterSpace
- ? ai.ipdArea.opt - letterSpaceWidth.opt
- : ai.ipdArea.opt,
- alignmentContext,
- notifyPos(mainPosition), false));
- } else {
- // adjustable letter spacing
- int unsuppressibleLetterSpaces
- = bSuppressibleLetterSpace ? ai.iLScount - 1 : ai.iLScount;
- wordElements.add
- (new KnuthInlineBox(ai.ipdArea.opt
- - ai.iLScount * letterSpaceWidth.opt,
- alignmentContext,
- notifyPos(mainPosition), false));
- wordElements.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- wordElements.add
- (new KnuthGlue(unsuppressibleLetterSpaces * letterSpaceWidth.opt,
- unsuppressibleLetterSpaces * (letterSpaceWidth.max - letterSpaceWidth.opt),
- unsuppressibleLetterSpaces * (letterSpaceWidth.opt - letterSpaceWidth.min),
- new LeafPosition(this, -1), true));
- wordElements.add
- (new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), true));
- }
-
- // extra-elements if the word fragment is the end of a syllable,
- // or it ends with a character that can be used as a line break
- if (ai.bHyphenated) {
- MinOptMax widthIfNoBreakOccurs = null;
- if (ai.iBreakIndex < textArray.length) {
- //Add in kerning in no-break condition
- widthIfNoBreakOccurs = letterAdjustArray[ai.iBreakIndex];
- }
- //if (ai.iBreakIndex)
-
- // the word fragment ends at the end of a syllable:
- // if a break occurs the content width increases,
- // otherwise nothing happens
- wordElements.addAll(createElementsForAHyphen(alignment, hyphIPD, widthIfNoBreakOccurs));
- } else if (bSuppressibleLetterSpace) {
- // the word fragment ends with a character that acts as a hyphen
- // if a break occurs the width does not increase,
- // otherwise there is one more letter space
- wordElements.addAll(createElementsForAHyphen(alignment, 0, letterSpaceWidth));
- }
- return wordElements;
- }
-
- private LinkedList createElementsForAHyphen(int alignment,
- int widthIfBreakOccurs, MinOptMax widthIfNoBreakOccurs) {
- if (widthIfNoBreakOccurs == null) {
- widthIfNoBreakOccurs = ZERO_MINOPTMAX;
- }
- LinkedList hyphenElements = new LinkedList();
-
- switch (alignment) {
- case EN_CENTER :
- // centered text:
- /*
- hyphenElements.add
- (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthPenalty(hyphIPD,
- KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthGlue(0,
- - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthInlineBox(0, 0, 0, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, true,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- */
- hyphenElements.add
- (new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), true));
- hyphenElements.add
- (new KnuthPenalty(hyphIPD,
- KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthGlue(-(lineEndBAP + lineStartBAP),
- -6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), true));
- hyphenElements.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), true));
- hyphenElements.add
- (new KnuthGlue(lineStartBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), true));
- break;
-
- case EN_START : // fall through
- case EN_END :
- // left- or right-aligned text:
- /*
- hyphenElements.add
- (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthPenalty(widthIfBreakOccurs,
- KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthGlue(widthIfNoBreakOccurs.opt,
- - 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- */
- if (lineStartBAP != 0 || lineEndBAP != 0) {
- hyphenElements.add
- (new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthPenalty(widthIfBreakOccurs,
- KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthGlue(widthIfNoBreakOccurs.opt - (lineStartBAP + lineEndBAP),
- -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- hyphenElements.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthGlue(lineStartBAP, 0, 0,
- new LeafPosition(this, -1), false));
- } else {
- hyphenElements.add
- (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthPenalty(widthIfBreakOccurs,
- KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthGlue(widthIfNoBreakOccurs.opt,
- -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
- new LeafPosition(this, -1), false));
- }
- break;
-
- default:
- // justified text, or last line justified:
- // just a flagged penalty
- /*
- hyphenElements.add
- (new KnuthPenalty(widthIfBreakOccurs,
- KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- */
- if (lineStartBAP != 0 || lineEndBAP != 0) {
- hyphenElements.add
- (new KnuthGlue(lineEndBAP, 0, 0,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthPenalty(widthIfBreakOccurs,
- KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- // extra elements representing a letter space that is suppressed
- // if a break occurs
- if (widthIfNoBreakOccurs.min != 0
- || widthIfNoBreakOccurs.max != 0) {
- hyphenElements.add
- (new KnuthGlue(widthIfNoBreakOccurs.opt - (lineStartBAP + lineEndBAP),
- widthIfNoBreakOccurs.max - widthIfNoBreakOccurs.opt,
- widthIfNoBreakOccurs.opt - widthIfNoBreakOccurs.min,
- new LeafPosition(this, -1), false));
- } else {
- hyphenElements.add
- (new KnuthGlue(-(lineStartBAP + lineEndBAP), 0, 0,
- new LeafPosition(this, -1), false));
- }
- hyphenElements.add
- (new KnuthInlineBox(0, null,
- notifyPos(new LeafPosition(this, -1)), false));
- hyphenElements.add
- (new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new LeafPosition(this, -1), false));
- hyphenElements.add
- (new KnuthGlue(lineStartBAP, 0, 0,
- new LeafPosition(this, -1), false));
- } else {
- hyphenElements.add
- (new KnuthPenalty(widthIfBreakOccurs,
- KnuthPenalty.FLAGGED_PENALTY, true,
- new LeafPosition(this, -1), false));
- // extra elements representing a letter space that is suppressed
- // if a break occurs
- if (widthIfNoBreakOccurs.min != 0
- || widthIfNoBreakOccurs.max != 0) {
- hyphenElements.add
- (new KnuthGlue(widthIfNoBreakOccurs.opt,
- widthIfNoBreakOccurs.max - widthIfNoBreakOccurs.opt,
- widthIfNoBreakOccurs.opt - widthIfNoBreakOccurs.min,
- new LeafPosition(this, -1), false));
- }
- }
- }
-
- return hyphenElements;
- }
- }
|