123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335 |
- /*
- * $Id$
- * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
- package org.apache.fop.layout;
-
- // fop
- import org.apache.fop.render.Renderer;
- import org.apache.fop.messaging.MessageHandler;
- import org.apache.fop.layout.inline.*;
- import org.apache.fop.datatypes.IDNode;
- import org.apache.fop.fo.properties.WrapOption;
- import org.apache.fop.fo.properties.WhiteSpaceCollapse;
- import org.apache.fop.fo.properties.TextAlign;
- import org.apache.fop.fo.properties.TextAlignLast;
- import org.apache.fop.fo.properties.LeaderPattern;
- import org.apache.fop.fo.properties.Hyphenate;
- import org.apache.fop.fo.properties.CountryMaker;
- import org.apache.fop.fo.properties.LanguageMaker;
- import org.apache.fop.fo.properties.LeaderAlignment;
- import org.apache.fop.fo.properties.VerticalAlign;
- import org.apache.fop.layout.hyphenation.Hyphenation;
- import org.apache.fop.layout.hyphenation.Hyphenator;
- import org.apache.fop.configuration.Configuration;
-
- // java
- import java.util.Vector;
- import java.util.Enumeration;
- import java.util.StringTokenizer;
- import java.awt.Rectangle;
-
- public class LineArea extends Area {
-
- protected int lineHeight;
- protected int halfLeading;
- protected int nominalFontSize;
- protected int nominalGlyphHeight;
-
- protected int allocationHeight;
- protected int startIndent;
- protected int endIndent;
-
- private int placementOffset;
-
- private FontState currentFontState; // not the nominal, which is
- // in this.fontState
- private float red, green, blue;
- private int wrapOption;
- private int whiteSpaceCollapse;
- int vAlign;
-
- /* hyphenation */
- HyphenationProps hyphProps;
-
- /*
- * the width of text that has definitely made it into the line
- * area
- */
- protected int finalWidth = 0;
-
- /* the position to shift a link rectangle in order to compensate for links embedded within a word */
- protected int embeddedLinkStart = 0;
-
- /* the width of the current word so far */
- // protected int wordWidth = 0;
-
- /* values that prev (below) may take */
- protected static final int NOTHING = 0;
- protected static final int WHITESPACE = 1;
- protected static final int TEXT = 2;
-
- /* the character type of the previous character */
- protected int prev = NOTHING;
-
- /* the position in data[] of the start of the current word */
- // protected int wordStart;
-
- /* the length (in characters) of the current word */
- // protected int wordLength = 0;
-
- /* width of spaces before current word */
- protected int spaceWidth = 0;
-
- /*
- * the inline areas that have not yet been added to the line
- * because subsequent characters to come (in a different addText)
- * may be part of the same word
- */
- protected Vector pendingAreas = new Vector();
-
- /* the width of the pendingAreas */
- protected int pendingWidth = 0;
-
- /* text-decoration of the previous text */
- protected boolean prevUlState = false;
- protected boolean prevOlState = false;
- protected boolean prevLTState = false;
-
- public LineArea(FontState fontState, int lineHeight, int halfLeading,
- int allocationWidth, int startIndent, int endIndent,
- LineArea prevLineArea) {
- super(fontState);
-
- this.currentFontState = fontState;
- this.lineHeight = lineHeight;
- this.nominalFontSize = fontState.getFontSize();
- this.nominalGlyphHeight = fontState.getAscender()
- - fontState.getDescender();
-
- this.placementOffset = fontState.getAscender();
- this.contentRectangleWidth = allocationWidth - startIndent
- - endIndent;
- this.fontState = fontState;
-
- this.allocationHeight = this.nominalGlyphHeight;
- this.halfLeading = this.lineHeight - this.allocationHeight;
-
- this.startIndent = startIndent;
- this.endIndent = endIndent;
-
- if (prevLineArea != null) {
- Enumeration e = prevLineArea.pendingAreas.elements();
- Box b = null;
- // There might be InlineSpaces at the beginning
- // that should not be there - eat them
- boolean eatMoreSpace = true;
- int eatenWidth = 0;
-
- while (eatMoreSpace) {
- if (e.hasMoreElements()) {
- b = (Box)e.nextElement();
- if (b instanceof InlineSpace) {
- InlineSpace is = (InlineSpace)b;
- if (is.isEatable())
- eatenWidth += is.getSize();
- else
- eatMoreSpace = false;
- } else {
- eatMoreSpace = false;
- }
- } else {
- eatMoreSpace = false;
- b = null;
- }
- }
-
- while (b != null) {
- pendingAreas.addElement(b);
- if (e.hasMoreElements())
- b = (Box)e.nextElement();
- else
- b = null;
- }
- pendingWidth = prevLineArea.getPendingWidth() - eatenWidth;
- }
- }
-
- public int addPageNumberCitation(String refid, LinkSet ls) {
-
- /*
- * We should add code here to handle the case where the page number doesn't fit on the current line
- */
-
- // Space must be alloted to the page number, so currently we give it 3 spaces
-
- int width = currentFontState.width(currentFontState.mapChar(' '));
-
-
- PageNumberInlineArea pia = new PageNumberInlineArea(currentFontState,
- this.red, this.green, this.blue, refid, width);
-
- pia.setYOffset(placementOffset);
- pendingAreas.addElement(pia);
- pendingWidth += width;
- prev = TEXT;
-
- return -1;
- }
-
-
- /**
- * adds text to line area
- *
- * @return int character position
- */
- public int addText(char odata[], int start, int end, LinkSet ls,
- TextState textState) {
- // this prevents an array index out of bounds
- // which occurs when some text is laid out again.
- if (start == -1)
- return -1;
- boolean overrun = false;
-
- int wordStart = start;
- int wordLength = 0;
- int wordWidth = 0;
- // With CID fonts, space isn't neccesary currentFontState.width(32)
- int whitespaceWidth = getCharWidth(' ');
-
- char[] data = new char[odata.length];
- char[] dataCopy = new char[odata.length];
- System.arraycopy(odata, 0, data, 0, odata.length);
- System.arraycopy(odata, 0, dataCopy, 0, odata.length);
-
- boolean isText = false;
-
- /* iterate over each character */
- for (int i = start; i < end; i++) {
- int charWidth;
- /* get the character */
- char c = data[i];
- if (!(isSpace(c) || (c == '\n') || (c == '\r') || (c == '\t')
- || (c == '\u2028'))) {
- charWidth = getCharWidth(c);
- isText = true;
- // Add support for zero-width spaces
- if (charWidth <= 0 && c != '\u200B' && c != '\uFEFF')
- charWidth = whitespaceWidth;
- } else {
- if ((c == '\n') || (c == '\r') || (c == '\t'))
- charWidth = whitespaceWidth;
- else
- charWidth = getCharWidth(c);
-
- isText = false;
-
- if (prev == WHITESPACE) {
-
- // if current & previous are WHITESPACE
-
- if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
- if (isSpace(c)) {
- spaceWidth += getCharWidth(c);
- } else if (c == '\n' || c == '\u2028') {
- // force line break
- if (spaceWidth > 0) {
- InlineSpace is = new InlineSpace(spaceWidth);
- is.setUnderlined(textState.getUnderlined());
- is.setOverlined(textState.getOverlined());
- is.setLineThrough(textState.getLineThrough());
- addChild(is);
- finalWidth += spaceWidth;
- spaceWidth = 0;
- }
- return i + 1;
- } else if (c == '\t') {
- spaceWidth += 8 * whitespaceWidth;
- }
- } else if (c == '\u2028') {
- // Line separator
- // Breaks line even if WhiteSpaceCollapse = True
- if (spaceWidth > 0) {
- InlineSpace is = new InlineSpace(spaceWidth);
- is.setUnderlined(textState.getUnderlined());
- is.setOverlined(textState.getOverlined());
- is.setLineThrough(textState.getLineThrough());
- addChild(is);
- finalWidth += spaceWidth;
- spaceWidth = 0;
- }
- return i + 1;
- }
-
- } else if (prev == TEXT) {
-
- // if current is WHITESPACE and previous TEXT
- // the current word made it, so
- // add the space before the current word (if there
- // was some)
-
- if (spaceWidth > 0) {
- InlineSpace is = new InlineSpace(spaceWidth);
- if (prevUlState) {
- is.setUnderlined(textState.getUnderlined());
- }
- if (prevOlState) {
- is.setOverlined(textState.getOverlined());
- }
- if (prevLTState) {
- is.setLineThrough(textState.getLineThrough());
- }
- addChild(is);
- finalWidth += spaceWidth;
- spaceWidth = 0;
- }
-
- // add any pending areas
-
- Enumeration e = pendingAreas.elements();
- while (e.hasMoreElements()) {
- Box box = (Box)e.nextElement();
- if (box instanceof InlineArea) {
- if (ls != null) {
- Rectangle lr =
- new Rectangle(finalWidth, 0,
- ((InlineArea)box).getContentWidth(),
- fontState.getFontSize());
- ls.addRect(lr, this, (InlineArea)box);
- }
- }
- addChild(box);
- }
-
- finalWidth += pendingWidth;
-
- // reset pending areas array
- pendingWidth = 0;
- pendingAreas = new Vector();
-
- // add the current word
-
- if (wordLength > 0) {
- // The word might contain nonbreaking
- // spaces. Split the word and add InlineSpace
- // as necessary. All spaces inside the word
- // Have a fixed width.
- addSpacedWord(new String(data, wordStart, wordLength),
- ls, finalWidth, 0, textState, false);
- finalWidth += wordWidth;
-
- // reset word width
- wordWidth = 0;
- }
-
- // deal with this new whitespace following the
- // word we just added
- prev = WHITESPACE;
-
- embeddedLinkStart =
- 0; // reset embeddedLinkStart since a space was encountered
-
- spaceWidth = getCharWidth(c);
-
- /*
- * here is the place for space-treatment value 'ignore':
- * if (this.spaceTreatment ==
- * SpaceTreatment.IGNORE) {
- * // do nothing
- * } else {
- * spaceWidth = currentFontState.width(32);
- * }
- */
-
-
- if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
- if (c == '\n' || c == '\u2028') {
- // force a line break
- return i + 1;
- } else if (c == '\t') {
- spaceWidth = whitespaceWidth;
- }
- } else if (c == '\u2028') {
- return i + 1;
- }
- } else {
-
- // if current is WHITESPACE and no previous
-
- if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) {
- if (isSpace(c)) {
- prev = WHITESPACE;
- spaceWidth = getCharWidth(c);
- } else if (c == '\n') {
- // force line break
- // textdecoration not used because spaceWidth is 0
- InlineSpace is = new InlineSpace(spaceWidth);
- addChild(is);
- return i + 1;
- } else if (c == '\t') {
- prev = WHITESPACE;
- spaceWidth = 8 * whitespaceWidth;
- }
-
- } else {
- // skip over it
- wordStart++;
- }
- }
-
- }
-
- if (isText) { // current is TEXT
-
- if (prev == WHITESPACE) {
-
- // if current is TEXT and previous WHITESPACE
-
- wordWidth = charWidth;
- if ((finalWidth + spaceWidth + wordWidth)
- > this.getContentWidth()) {
- if (overrun)
- MessageHandler.log("area contents overflows area");
- if (this.wrapOption == WrapOption.WRAP) {
- return i;
- }
- }
- prev = TEXT;
- wordStart = i;
- wordLength = 1;
- } else if (prev == TEXT) {
- wordLength++;
- wordWidth += charWidth;
- } else { // nothing previous
-
- prev = TEXT;
- wordStart = i;
- wordLength = 1;
- wordWidth = charWidth;
- }
-
- if ((finalWidth + spaceWidth + pendingWidth + wordWidth)
- > this.getContentWidth()) {
-
- // BREAK MID WORD
- if (canBreakMidWord()) {
- addSpacedWord(new String(data, wordStart, wordLength - 1),
- ls,
- finalWidth + spaceWidth
- + embeddedLinkStart, spaceWidth,
- textState, false);
- finalWidth += wordWidth;
- wordWidth = 0;
- return i;
- }
-
- if (this.wrapOption == WrapOption.WRAP) {
-
- if (hyphProps.hyphenate == Hyphenate.TRUE) {
- int ret = wordStart;
- ret = this.doHyphenation(dataCopy, i, wordStart,
- this.getContentWidth()
- - (finalWidth
- + spaceWidth
- + pendingWidth));
-
- // current word couldn't be hypenated
- // couldn't fit first word
- // I am at the beginning of my line
- if ((ret == wordStart) &&
- (wordStart == start) &&
- (finalWidth == 0)) {
-
- MessageHandler.log("area contents overflows area");
- addSpacedWord(new String(data, wordStart, wordLength - 1),
- ls,
- finalWidth + spaceWidth
- + embeddedLinkStart,
- spaceWidth, textState, false);
-
- finalWidth += wordWidth;
- wordWidth = 0;
- ret = i;
- }
- return ret;
- } else if (wordStart == start) {
- // first word
- overrun = true;
- // if not at start of line, return word start
- // to try again on a new line
- if (finalWidth > 0) {
- return wordStart;
- }
- } else {
- return wordStart;
- }
-
- }
- }
- }
- } // end of iteration over text
-
- if (prev == TEXT) {
-
- if (spaceWidth > 0) {
- InlineSpace pis = new InlineSpace(spaceWidth);
- // Make sure that this space doesn't occur as
- // first thing in the next line
- pis.setEatable(true);
- if (prevUlState) {
- pis.setUnderlined(textState.getUnderlined());
- }
- if (prevOlState) {
- pis.setOverlined(textState.getOverlined());
- }
- if (prevLTState) {
- pis.setLineThrough(textState.getLineThrough());
- }
- pendingAreas.addElement(pis);
- pendingWidth += spaceWidth;
- spaceWidth = 0;
- }
-
- addSpacedWord(new String(data, wordStart, wordLength), ls,
- finalWidth + spaceWidth + embeddedLinkStart,
- spaceWidth, textState, true);
-
- embeddedLinkStart += wordWidth;
- wordWidth = 0;
- }
-
- if (overrun)
- MessageHandler.log("area contents overflows area");
- return -1;
- }
-
- /**
- * adds a Leader; actually the method receives the leader properties
- * and creates a leader area or an inline area which is appended to
- * the children of the containing line area. <br>
- * leader pattern use-content is not implemented.
- */
- public void addLeader(int leaderPattern, int leaderLengthMinimum,
- int leaderLengthOptimum, int leaderLengthMaximum,
- int ruleStyle, int ruleThickness,
- int leaderPatternWidth, int leaderAlignment) {
- WordArea leaderPatternArea;
- int leaderLength = 0;
- char dotIndex = '.'; // currentFontState.mapChar('.');
- int dotWidth =
- currentFontState.width(currentFontState.mapChar(dotIndex));
- char whitespaceIndex = ' '; // currentFontState.mapChar(' ');
- int whitespaceWidth =
- currentFontState.width(currentFontState.mapChar(whitespaceIndex));
-
- int remainingWidth = this.getContentWidth()
- - this.getCurrentXPosition();
-
- /**
- * checks whether leaderLenghtOptimum fits into rest of line;
- * should never overflow, as it has been checked already in BlockArea
- * first check: use remaining width if it smaller than optimum oder maximum
- */
- if ((remainingWidth <= leaderLengthOptimum)
- || (remainingWidth <= leaderLengthMaximum)) {
- leaderLength = remainingWidth;
- } else if ((remainingWidth > leaderLengthOptimum)
- && (remainingWidth > leaderLengthMaximum)) {
- leaderLength = leaderLengthMaximum;
- } else if ((leaderLengthOptimum > leaderLengthMaximum)
- && (leaderLengthOptimum < remainingWidth)) {
- leaderLength = leaderLengthOptimum;
- }
-
- // stop if leader-length is too small
- if (leaderLength <= 0) {
- return;
- }
-
- switch (leaderPattern) {
- case LeaderPattern.SPACE:
- InlineSpace spaceArea = new InlineSpace(leaderLength);
- pendingAreas.addElement(spaceArea);
- break;
- case LeaderPattern.RULE:
- LeaderArea leaderArea = new LeaderArea(fontState, red, green,
- blue, "", leaderLength,
- leaderPattern,
- ruleThickness, ruleStyle);
- leaderArea.setYOffset(placementOffset);
- pendingAreas.addElement(leaderArea);
- break;
- case LeaderPattern.DOTS:
- // if the width of a dot is larger than leader-pattern-width
- // ignore this property
- if (leaderPatternWidth < dotWidth) {
- leaderPatternWidth = 0;
- }
- // if value of leader-pattern-width is 'use-font-metrics' (0)
- if (leaderPatternWidth == 0) {
- pendingAreas.addElement(this.buildSimpleLeader(dotIndex,
- leaderLength));
- } else {
- // if leader-alignment is used, calculate space to insert before leader
- // so that all dots will be parallel.
- if (leaderAlignment == LeaderAlignment.REFERENCE_AREA) {
- int spaceBeforeLeader =
- this.getLeaderAlignIndent(leaderLength,
- leaderPatternWidth);
- // appending indent space leader-alignment
- // setting InlineSpace to false, so it is not used in line justification
- if (spaceBeforeLeader != 0) {
- pendingAreas.addElement(new InlineSpace(spaceBeforeLeader,
- false));
- pendingWidth += spaceBeforeLeader;
- // shorten leaderLength, otherwise - in case of
- // leaderLength=remaining length - it will cut off the end of
- // leaderlength
- leaderLength -= spaceBeforeLeader;
- }
- }
-
- // calculate the space to insert between the dots and create a
- // inline area with this width
- InlineSpace spaceBetweenDots =
- new InlineSpace(leaderPatternWidth - dotWidth, false);
-
- leaderPatternArea = new WordArea(currentFontState, this.red,
- this.green, this.blue,
- new String("."), dotWidth);
- leaderPatternArea.setYOffset(placementOffset);
- int dotsFactor =
- (int)Math.floor(((double)leaderLength)
- / ((double)leaderPatternWidth));
-
- // add combination of dot + space to fill leader
- // is there a way to do this in a more effective way?
- for (int i = 0; i < dotsFactor; i++) {
- pendingAreas.addElement(leaderPatternArea);
- pendingAreas.addElement(spaceBetweenDots);
- }
- // append at the end some space to fill up to leader length
- pendingAreas.addElement(new InlineSpace(leaderLength
- - dotsFactor
- * leaderPatternWidth));
- }
- break;
- // leader pattern use-content not implemented.
- case LeaderPattern.USECONTENT:
- MessageHandler.errorln("leader-pattern=\"use-content\" not "
- + "supported by this version of Fop");
- return;
- }
- // adds leader length to length of pending inline areas
- pendingWidth += leaderLength;
- // sets prev to TEXT and makes so sure, that also blocks only
- // containing leaders are processed
- prev = TEXT;
- }
-
- /**
- * adds pending inline areas to the line area
- * normally done, when the line area is filled and
- * added as child to the parent block area
- */
- public void addPending() {
- if (spaceWidth > 0) {
- addChild(new InlineSpace(spaceWidth));
- finalWidth += spaceWidth;
- spaceWidth = 0;
- }
-
- Enumeration e = pendingAreas.elements();
- while (e.hasMoreElements()) {
- Box box = (Box)e.nextElement();
- addChild(box);
- }
-
- finalWidth += pendingWidth;
-
- // reset pending areas array
- pendingWidth = 0;
- pendingAreas = new Vector();
- }
-
- /**
- * aligns line area
- *
- */
- public void align(int type) {
- int padding = 0;
-
- switch (type) {
- case TextAlign.START: // left
- padding = this.getContentWidth() - finalWidth;
- endIndent += padding;
- break;
- case TextAlign.END: // right
- padding = this.getContentWidth() - finalWidth;
- startIndent += padding;
- break;
- case TextAlign.CENTER: // center
- padding = (this.getContentWidth() - finalWidth) / 2;
- startIndent += padding;
- endIndent += padding;
- break;
- case TextAlign.JUSTIFY: // justify
- // first pass - count the spaces
- int spaceCount = 0;
- Enumeration e = children.elements();
- while (e.hasMoreElements()) {
- Box b = (Box)e.nextElement();
- if (b instanceof InlineSpace) {
- InlineSpace space = (InlineSpace)b;
- if (space.getResizeable()) {
- spaceCount++;
- }
- }
- }
- if (spaceCount > 0) {
- padding = (this.getContentWidth() - finalWidth) / spaceCount;
- } else { // no spaces
- padding = 0;
- }
- // second pass - add additional space
- spaceCount = 0;
- e = children.elements();
- while (e.hasMoreElements()) {
- Box b = (Box)e.nextElement();
- if (b instanceof InlineSpace) {
- InlineSpace space = (InlineSpace)b;
- if (space.getResizeable()) {
- space.setSize(space.getSize() + padding);
- spaceCount++;
- }
- } else if (b instanceof InlineArea) {
- ((InlineArea)b).setXOffset(spaceCount * padding);
- }
-
- }
- }
- }
-
- /**
- * Balance (vertically) the inline areas within this line.
- */
- public void verticalAlign() {
- int superHeight = -this.placementOffset;
- int maxHeight = this.allocationHeight;
- Enumeration e = children.elements();
- while (e.hasMoreElements()) {
- Box b = (Box)e.nextElement();
- if (b instanceof InlineArea) {
- InlineArea ia = (InlineArea)b;
- if (ia instanceof WordArea) {
- ia.setYOffset(placementOffset);
- }
- if (ia.getHeight() > maxHeight) {
- maxHeight = ia.getHeight();
- }
- int vert = ia.getVerticalAlign();
- if (vert == VerticalAlign.SUPER) {
- int fh = fontState.getAscender();
- ia.setYOffset((int)(placementOffset - (2 * fh / 3.0)));
- } else if (vert == VerticalAlign.SUB) {
- int fh = fontState.getAscender();
- ia.setYOffset((int)(placementOffset + (2 * fh / 3.0)));
- }
- } else {}
- }
- // adjust the height of this line to the
- // resulting alignment height.
- this.allocationHeight = maxHeight;
- }
-
- public void changeColor(float red, float green, float blue) {
- this.red = red;
- this.green = green;
- this.blue = blue;
- }
-
- public void changeFont(FontState fontState) {
- this.currentFontState = fontState;
- }
-
- public void changeWhiteSpaceCollapse(int whiteSpaceCollapse) {
- this.whiteSpaceCollapse = whiteSpaceCollapse;
- }
-
- public void changeWrapOption(int wrapOption) {
- this.wrapOption = wrapOption;
- }
-
- public void changeVerticalAlign(int vAlign) {
- this.vAlign = vAlign;
- }
-
- public int getEndIndent() {
- return endIndent;
- }
-
- public int getHeight() {
- return this.allocationHeight;
- }
-
- public int getPlacementOffset() {
- return this.placementOffset;
- }
-
- public int getStartIndent() {
- return startIndent;
- }
-
- public boolean isEmpty() {
- return !(pendingAreas.size() > 0 || children.size() > 0);
- // return (prev == NOTHING);
- }
-
- public Vector getPendingAreas() {
- return pendingAreas;
- }
-
- public int getPendingWidth() {
- return pendingWidth;
- }
-
- public void setPendingAreas(Vector areas) {
- pendingAreas = areas;
- }
-
- public void setPendingWidth(int width) {
- pendingWidth = width;
- }
-
- /**
- * sets hyphenation related traits: language, country, hyphenate, hyphenation-character
- * and minimum number of character to remain one the previous line and to be on the
- * next line.
- */
- public void changeHyphenation(HyphenationProps hyphProps) {
- this.hyphProps = hyphProps;
- }
-
-
- /**
- * creates a leader as String out of the given char and the leader length
- * and wraps it in an InlineArea which is returned
- */
- private InlineArea buildSimpleLeader(char c, int leaderLength) {
- int width = this.currentFontState.width(currentFontState.mapChar(c));
- if (width == 0) {
- MessageHandler.errorln("char " + c
- + " has width 0. Using width 100 instead.");
- width = 100;
- }
- int factor = (int)Math.floor(leaderLength / width);
- char[] leaderChars = new char[factor];
- for (int i = 0; i < factor; i++) {
- leaderChars[i] = c; // currentFontState.mapChar(c);
- }
- WordArea leaderPatternArea = new WordArea(currentFontState, this.red,
- this.green, this.blue,
- new String(leaderChars),
- leaderLength);
- leaderPatternArea.setYOffset(placementOffset);
- return leaderPatternArea;
- }
-
- /**
- * calculates the width of space which has to be inserted before the
- * start of the leader, so that all leader characters are aligned.
- * is used if property leader-align is set. At the moment only the value
- * for leader-align="reference-area" is supported.
- *
- */
- private int getLeaderAlignIndent(int leaderLength,
- int leaderPatternWidth) {
- // calculate position of used space in line area
- double position = getCurrentXPosition();
- // calculate factor of next leader pattern cycle
- double nextRepeatedLeaderPatternCycle = Math.ceil(position
- / leaderPatternWidth);
- // calculate difference between start of next leader
- // pattern cycle and already used space
- double difference =
- (leaderPatternWidth * nextRepeatedLeaderPatternCycle) - position;
- return (int)difference;
- }
-
- /**
- * calculates the used space in this line area
- */
- private int getCurrentXPosition() {
- return finalWidth + spaceWidth + startIndent + pendingWidth;
- }
-
- /**
- * extracts a complete word from the character data
- */
- private String getHyphenationWord(char[] characters, int wordStart) {
- boolean wordendFound = false;
- int counter = 0;
- char[] newWord = new char[characters.length]; // create a buffer
- while ((!wordendFound)
- && ((wordStart + counter) < characters.length)) {
- char tk = characters[wordStart + counter];
- if (Character.isLetter(tk)) {
- newWord[counter] = tk;
- counter++;
- } else {
- wordendFound = true;
- }
- }
- return new String(newWord, 0, counter);
- }
-
-
- /**
- * extracts word for hyphenation and calls hyphenation package,
- * handles cases of inword punctuation and quotation marks at the beginning
- * of words, but not in a internationalized way
- */
- public int doHyphenation(char[] characters, int position, int wordStart,
- int remainingWidth) {
- // check whether the language property has been set
- if (this.hyphProps.language.equalsIgnoreCase("none")) {
- MessageHandler.errorln("if property 'hyphenate' is used, a language must be specified");
- return wordStart;
- }
-
- /**
- * remaining part string of hyphenation
- */
- StringBuffer remainingString = new StringBuffer();
-
- /**
- * for words with some inword punctuation like / or -
- */
- StringBuffer preString = null;
-
- /**
- * char before the word, probably whitespace
- */
- char startChar = ' '; // characters[wordStart-1];
-
- /**
- * in word punctuation character
- */
- char inwordPunctuation;
-
- /**
- * the complete word handed to the hyphenator
- */
- String wordToHyphenate;
-
- // width of hyphenation character
- int hyphCharWidth =
- this.currentFontState.width(currentFontState.mapChar(this.hyphProps.hyphenationChar));
- remainingWidth -= hyphCharWidth;
-
- // handles ' or " at the beginning of the word
- if (characters[wordStart] == '"' || characters[wordStart] == '\'') {
- remainingString.append(characters[wordStart]);
- // extracts whole word from string
- wordToHyphenate = getHyphenationWord(characters, wordStart + 1);
- } else {
- wordToHyphenate = getHyphenationWord(characters, wordStart);
- }
-
- // if the extracted word is smaller than the remaining width
- // we have a non letter character inside the word. at the moment
- // we will only handle hard hyphens and slashes
- if (getWordWidth(wordToHyphenate) < remainingWidth) {
- inwordPunctuation =
- characters[wordStart + wordToHyphenate.length()];
- if (inwordPunctuation == '-' || inwordPunctuation == '/') {
- preString = new StringBuffer(wordToHyphenate);
- preString = preString.append(inwordPunctuation);
- wordToHyphenate =
- getHyphenationWord(characters,
- wordStart + wordToHyphenate.length()
- + 1);
- remainingWidth -=
- (getWordWidth(wordToHyphenate)
- + this.currentFontState.width(currentFontState.mapChar(inwordPunctuation)));
- }
- }
-
- // are there any hyphenation points
- Hyphenation hyph =
- Hyphenator.hyphenate(hyphProps.language, hyphProps.country,
- wordToHyphenate,
- hyphProps.hyphenationRemainCharacterCount,
- hyphProps.hyphenationPushCharacterCount);
- // no hyphenation points and no inword non letter character
- if (hyph == null && preString == null) {
- if (remainingString.length() > 0) {
- return wordStart - 1;
- } else {
- return wordStart;
- }
-
- // no hyphenation points, but a inword non-letter character
- } else if (hyph == null && preString != null) {
- remainingString.append(preString);
- // is.addMapWord(startChar,remainingString);
- this.addWord(startChar, remainingString);
- return wordStart + remainingString.length();
- // hyphenation points and no inword non-letter character
- } else if (hyph != null && preString == null) {
- int index = getFinalHyphenationPoint(hyph, remainingWidth);
- if (index != -1) {
- remainingString.append(hyph.getPreHyphenText(index));
- remainingString.append(this.hyphProps.hyphenationChar);
- // is.addMapWord(startChar,remainingString);
- this.addWord(startChar, remainingString);
- return wordStart + remainingString.length() - 1;
- }
- // hyphenation points and a inword non letter character
- } else if (hyph != null && preString != null) {
- int index = getFinalHyphenationPoint(hyph, remainingWidth);
- if (index != -1) {
- remainingString.append(preString.append(hyph.getPreHyphenText(index)));
- remainingString.append(this.hyphProps.hyphenationChar);
- // is.addMapWord(startChar,remainingString);
- this.addWord(startChar, remainingString);
- return wordStart + remainingString.length() - 1;
- } else {
- remainingString.append(preString);
- // is.addMapWord(startChar,remainingString);
- this.addWord(startChar, remainingString);
- return wordStart + remainingString.length();
- }
- }
- return wordStart;
- }
-
-
- /**
- * Calculates the wordWidth using the actual fontstate
- */
- private int getWordWidth(String word) {
- if (word == null)
- return 0;
- int wordLength = word.length();
- int width = 0;
- char[] characters = new char[wordLength];
- word.getChars(0, wordLength, characters, 0);
-
- for (int i = 0; i < wordLength; i++) {
- width += getCharWidth(characters[i]);
- }
- return width;
- }
-
- public int getRemainingWidth() {
- return this.getContentWidth() - this.getCurrentXPosition();
- }
-
- public void setLinkSet(LinkSet ls) {}
-
- public void addInlineArea(Area box) {
- addPending();
- addChild(box);
- prev = TEXT;
- finalWidth += box.getContentWidth();
- }
-
- public void addInlineSpace(InlineSpace is, int spaceWidth) {
- addChild(is);
- finalWidth += spaceWidth;
- // spaceWidth = 0;
- }
-
- /**
- * adds a single character to the line area tree
- */
- public int addCharacter(char data, LinkSet ls, boolean ul) {
- WordArea ia = null;
- int remainingWidth = this.getContentWidth()
- - this.getCurrentXPosition();
- int width =
- this.currentFontState.width(currentFontState.mapChar(data));
- // if it doesn't fit, return
- if (width > remainingWidth) {
- return org.apache.fop.fo.flow.Character.DOESNOT_FIT;
- } else {
- // if whitespace-collapse == true, discard character
- if (Character.isSpaceChar(data)
- && whiteSpaceCollapse == WhiteSpaceCollapse.TRUE) {
- return org.apache.fop.fo.flow.Character.OK;
- }
- // create new InlineArea
- ia = new WordArea(currentFontState, this.red, this.green,
- this.blue, new Character(data).toString(),
- width);
- ia.setYOffset(placementOffset);
- ia.setUnderlined(ul);
- pendingAreas.addElement(ia);
- if (Character.isSpaceChar(data)) {
- this.spaceWidth = +width;
- prev = LineArea.WHITESPACE;
- } else {
- pendingWidth += width;
- prev = LineArea.TEXT;
- }
- return org.apache.fop.fo.flow.Character.OK;
- }
- }
-
-
- /**
- * Same as addWord except that characters in wordBuf is mapped
- * to the current fontstate's encoding
- */
- private void addMapWord(char startChar, StringBuffer wordBuf) {
- StringBuffer mapBuf = new StringBuffer(wordBuf.length());
- for (int i = 0; i < wordBuf.length(); i++) {
- mapBuf.append(currentFontState.mapChar(wordBuf.charAt(i)));
- }
-
- addWord(startChar, mapBuf);
- }
-
- /**
- * adds a InlineArea containing the String startChar+wordBuf to the line area children.
- */
- private void addWord(char startChar, StringBuffer wordBuf) {
- String word = (wordBuf != null) ? wordBuf.toString() : "";
- WordArea hia;
- int startCharWidth = getCharWidth(startChar);
-
- if (isAnySpace(startChar)) {
- this.addChild(new InlineSpace(startCharWidth));
- } else {
- hia = new WordArea(currentFontState, this.red, this.green,
- this.blue,
- new Character(startChar).toString(), 1);
- hia.setYOffset(placementOffset);
- this.addChild(hia);
- }
- int wordWidth = this.getWordWidth(word);
- hia = new WordArea(currentFontState, this.red, this.green, this.blue,
- word, word.length());
- hia.setYOffset(placementOffset);
- this.addChild(hia);
-
- // calculate the space needed
- finalWidth += startCharWidth + wordWidth;
- }
-
-
- /**
- * extracts from a hyphenated word the best (most greedy) fit
- */
- private int getFinalHyphenationPoint(Hyphenation hyph,
- int remainingWidth) {
- int[] hyphenationPoints = hyph.getHyphenationPoints();
- int numberOfHyphenationPoints = hyphenationPoints.length;
-
- int index = -1;
- String wordBegin = "";
- int wordBeginWidth = 0;
-
- for (int i = 0; i < numberOfHyphenationPoints; i++) {
- wordBegin = hyph.getPreHyphenText(i);
- if (this.getWordWidth(wordBegin) > remainingWidth) {
- break;
- }
- index = i;
- }
- return index;
- }
-
- /**
- * Checks if it's legal to break a word in the middle
- * based on the current language property.
- * @return true if legal to break word in the middle
- */
- private boolean canBreakMidWord() {
- boolean ret = false;
- if (hyphProps != null && hyphProps.language != null
- &&!hyphProps.language.equals("NONE")) {
- String lang = hyphProps.language.toLowerCase();
- if ("zh".equals(lang) || "ja".equals(lang) || "ko".equals(lang)
- || "vi".equals(lang))
- ret = true;
- }
- return ret;
- }
-
- /**
- * Helper method for getting the width of a unicode char
- * from the current fontstate.
- * This also performs some guessing on widths on various
- * versions of space that might not exists in the font.
- */
- private int getCharWidth(char c) {
- int width = currentFontState.width(currentFontState.mapChar(c));
- if (width <= 0) {
- // Estimate the width of spaces not represented in
- // the font
- int em = currentFontState.width(currentFontState.mapChar('m'));
- int en = currentFontState.width(currentFontState.mapChar('n'));
- if (em <= 0)
- em = 500 * currentFontState.getFontSize();
- if (en <= 0)
- en = em - 10;
-
- if (c == ' ')
- width = em;
- if (c == '\u2000')
- width = en;
- if (c == '\u2001')
- width = em;
- if (c == '\u2002')
- width = em / 2;
- if (c == '\u2003')
- width = currentFontState.getFontSize();
- if (c == '\u2004')
- width = em / 3;
- if (c == '\u2005')
- width = em / 4;
- if (c == '\u2006')
- width = em / 6;
- if (c == '\u2007')
- width = getCharWidth(' ');
- if (c == '\u2008')
- width = getCharWidth('.');
- if (c == '\u2009')
- width = em / 5;
- if (c == '\u200A')
- width = 5;
- if (c == '\u200B')
- width = 100;
- if (c == '\u00A0')
- width = getCharWidth(' ');
- if (c == '\u202F')
- width = getCharWidth(' ') / 2;
- if (c == '\u3000')
- width = getCharWidth(' ') * 2;
- if ((c == '\n') || (c == '\r') || (c == '\t'))
- width = getCharWidth(' ');
- }
-
- return width;
- }
-
-
- /**
- * Helper method to determine if the character is a
- * space with normal behaviour. Normal behaviour means that
- * it's not non-breaking
- */
- private boolean isSpace(char c) {
- if (c == ' ' || c == '\u2000' || // en quad
- c == '\u2001' || // em quad
- c == '\u2002' || // en space
- c == '\u2003' || // em space
- c == '\u2004' || // three-per-em space
- c == '\u2005' || // four--per-em space
- c == '\u2006' || // six-per-em space
- c == '\u2007' || // figure space
- c == '\u2008' || // punctuation space
- c == '\u2009' || // thin space
- c == '\u200A' || // hair space
- c == '\u200B') // zero width space
- return true;
- else
- return false;
- }
-
-
- /**
- * Method to determine if the character is a nonbreaking
- * space.
- */
- private boolean isNBSP(char c) {
- if (c == '\u00A0' || c == '\u202F' || // narrow no-break space
- c == '\u3000' || // ideographic space
- c == '\uFEFF') { // zero width no-break space
- return true;
- } else
- return false;
- }
-
- /**
- * @return true if the character represents any kind of space
- */
- private boolean isAnySpace(char c) {
- boolean ret = (isSpace(c) || isNBSP(c));
- return ret;
- }
-
- /**
- * Add a word that might contain non-breaking spaces.
- * Split the word into WordArea and InlineSpace and add it.
- * If addToPending is true, add to pending areas.
- */
- private void addSpacedWord(String word, LinkSet ls, int startw,
- int spacew, TextState textState,
- boolean addToPending) {
- StringTokenizer st = new StringTokenizer(word, "\u00A0\u202F\u3000\uFEFF", true);
- int extraw = 0;
- while (st.hasMoreTokens()) {
- String currentWord = st.nextToken();
-
- if (currentWord.length() == 1
- && (isNBSP(currentWord.charAt(0)))) {
- // Add an InlineSpace
- int spaceWidth = getCharWidth(currentWord.charAt(0));
- if (spaceWidth > 0) {
- InlineSpace is = new InlineSpace(spaceWidth);
- extraw += spaceWidth;
- if (prevUlState) {
- is.setUnderlined(textState.getUnderlined());
- }
- if (prevOlState) {
- is.setOverlined(textState.getOverlined());
- }
- if (prevLTState) {
- is.setLineThrough(textState.getLineThrough());
- }
-
- if (addToPending) {
- pendingAreas.addElement(is);
- pendingWidth += spaceWidth;
- } else {
- addChild(is);
- }
- }
- } else {
- WordArea ia = new WordArea(currentFontState, this.red,
- this.green, this.blue,
- currentWord,
- getWordWidth(currentWord));
- ia.setYOffset(placementOffset);
- ia.setUnderlined(textState.getUnderlined());
- prevUlState = textState.getUnderlined();
- ia.setOverlined(textState.getOverlined());
- prevOlState = textState.getOverlined();
- ia.setLineThrough(textState.getLineThrough());
- prevLTState = textState.getLineThrough();
- ia.setVerticalAlign(vAlign);
-
- if (addToPending) {
- pendingAreas.addElement(ia);
- pendingWidth += getWordWidth(currentWord);
- } else {
- addChild(ia);
- }
- if (ls != null) {
- Rectangle lr = new Rectangle(startw + extraw, spacew,
- ia.getContentWidth(),
- fontState.getFontSize());
- ls.addRect(lr, this, ia);
- }
- }
- }
- }
-
- }
|