From ef546444273e24a2a71c0689b2fcb1e99e76225a Mon Sep 17 00:00:00 2001 From: arved Date: Sun, 11 Feb 2001 05:37:21 +0000 Subject: [PATCH] Modified JPFOP line area class git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194057 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/org/apache/fop/layout/LineArea.java | 1088 +++++++++++++++++ 1 file changed, 1088 insertions(+) create mode 100644 jpfop-0.17.0/src/org/apache/fop/layout/LineArea.java diff --git a/jpfop-0.17.0/src/org/apache/fop/layout/LineArea.java b/jpfop-0.17.0/src/org/apache/fop/layout/LineArea.java new file mode 100644 index 000000000..598908b80 --- /dev/null +++ b/jpfop-0.17.0/src/org/apache/fop/layout/LineArea.java @@ -0,0 +1,1088 @@ +/*-- $Id$ -- + + ============================================================================ + The Apache Software License, Version 1.1 + ============================================================================ + + Copyright (C) 1999 The Apache Software Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without modifica- + tion, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The end-user documentation included with the redistribution, if any, must + include the following acknowledgment: "This product includes software + developed by the Apache Software Foundation (http://www.apache.org/)." + Alternately, this acknowledgment may appear in the software itself, if + and wherever such third-party acknowledgments normally appear. + + 4. The names "FOP" and "Apache Software Foundation" must not be used to + endorse or promote products derived from this software without prior + written permission. For written permission, please contact + apache@apache.org. + + 5. Products derived from this software may not be called "Apache", nor may + "Apache" appear in their name, without prior written permission of the + Apache Software Foundation. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This software consists of voluntary contributions made by many individuals + on behalf of the Apache Software Foundation and was originally created by + James Tauber . For more information on the Apache + Software Foundation, please see . + + */ + +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; + +//java +import java.util.Vector; +import java.util.Enumeration; +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*/ + protected int hyphenate; + protected char hyphenationChar; + protected int hyphenationPushCharacterCount; + protected int hyphenationRemainCharacterCount; + protected String language; + protected String country; + + /* 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(); + while (e.hasMoreElements()) { + pendingAreas.addElement(e.nextElement()); + } + pendingWidth = prevLineArea.getPendingWidth(); + } + } + + public void render(Renderer renderer) { + renderer.renderLineArea(this); + } + + 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(32) * 3; + + 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; + + //ForJAPANIZE + boolean isCJKChar = false; + + int wordStart = start; + int wordLength = 0; + int wordWidth = 0; + char[] data = new char[odata.length]; + for (int count = 0; count < odata.length; count++) { + data[count] = odata[count]; + } + + /* iterate over each character */ + for (int i = start; i < end; i++) { + int charWidth; + /* get the character */ + char c = data[i]; + + if (c > 127) { + /* this class shouldn't be hard coded */ + char d = org.apache.fop.render.pdf.CodePointMapping.map[c]; + if (d != 0) { + c = data[i] = d; + } else { + // ForJAPANIZE + // MessageHandler.error("ch" + (int) c + "?"); + // c = data[i] = '#'; + isCJKChar = true; + } + } + + charWidth = currentFontState.width(c); + + if ((c == ' ') || (c == '\n') || (c == '\r') || + (c == '\t')) { // whitespace + + if (prev == WHITESPACE) { + + // if current & previous are WHITESPACE + + if (this.whiteSpaceCollapse == + WhiteSpaceCollapse.FALSE) { + if (c == ' ') { + spaceWidth += currentFontState.width(32); + } else if (c == '\n') { + // force line break + return i; + } else if (c == '\t') { + spaceWidth += 8 * currentFontState.width(32); + } + } // else ignore it + + } 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); + //ForJAPANIZE + isCJKChar = false; + 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); + } + } + addChild(box); + //ForJAPANIZE + isCJKChar = false; + } + + finalWidth += pendingWidth; + + // reset pending areas array + pendingWidth = 0; + pendingAreas = new Vector(); + + // add the current word + + if (wordLength > 0) { + WordArea ia = new WordArea(currentFontState, + this.red, this.green, this.blue, + new String(data, wordStart, + wordLength), wordWidth); + 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); + + addChild(ia); + //ForJAPANIZE + isCJKChar = false; + if (ls != null) { + Rectangle lr = new Rectangle(finalWidth, 0, + ia.getContentWidth(), + fontState.getFontSize()); + ls.addRect(lr, this); + } + 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 = currentFontState.width(32); + + /* + 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') { + // force a line break + return i; + } else if (c == '\t') { + spaceWidth = currentFontState.width(32); + } + } + + } else { + + // if current is WHITESPACE and no previous + + if (this.whiteSpaceCollapse == + WhiteSpaceCollapse.FALSE) { + prev = WHITESPACE; + spaceWidth = currentFontState.width(32); + } else { + // skip over it + start++; + } + } + + } else { // current is TEXT + + if (prev == WHITESPACE) { + + // if current is TEXT and previous WHITESPACE + + wordWidth = charWidth; + if ((finalWidth + spaceWidth + wordWidth) > + this.getContentWidth()) { + if (overrun) + MessageHandler.error(">"); + 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()) { + + //ForJAPANIZE + if ( isCJKChar && this.wrapOption == WrapOption.WRAP && + charWidth <= this.getContentWidth() ) { + // The word does not separated WHITSPACE in CJK language . + // So, if the word is overran, it is nessesaly to addChild . + WordArea ia = new WordArea(currentFontState, + this.red, this.green, this.blue, + new String(data, wordStart,wordLength-1), wordWidth-charWidth); + ia.setUnderlined(textState.getUnderlined()); + addChild(ia); + isCJKChar = false; + if (ls != null) { + Rectangle lr = new Rectangle(finalWidth, 0, + ia.getContentWidth(),fontState.getFontSize()); + ls.addRect(lr, this); + } + finalWidth += wordWidth; + return i; + } + + // BREAK MID WORD + if (wordStart == start) { // if couldn't even fit + // 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 if (this.wrapOption == WrapOption.WRAP) { + if (this.hyphenate == Hyphenate.TRUE) { + return this.doHyphenation(data,i,wordStart,this.getContentWidth() - (finalWidth + spaceWidth + pendingWidth)); + } else { + return wordStart; + } + } + } + + } + } // end of iteration over text + + if (prev == TEXT) { + + if (spaceWidth > 0) { + InlineSpace pis = new InlineSpace(spaceWidth); + 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; + } + + WordArea pia = new WordArea(currentFontState, this.red, + this.green, this.blue, + new String(data, wordStart, wordLength), wordWidth); + + pia.setYOffset(placementOffset); + pia.setUnderlined(textState.getUnderlined()); + prevUlState = textState.getUnderlined(); + pia.setOverlined(textState.getOverlined()); + prevOlState = textState.getOverlined(); + pia.setLineThrough(textState.getLineThrough()); + prevLTState = textState.getLineThrough(); + pia.setVerticalAlign(vAlign); + + if (ls != null) { + Rectangle lr = new Rectangle(finalWidth + spaceWidth + + embeddedLinkStart, spaceWidth, + pia.getContentWidth(), fontState.getFontSize()); + ls.addRect(lr, this); + } + + embeddedLinkStart += wordWidth; + pendingAreas.addElement(pia); + pendingWidth += wordWidth; + wordWidth = 0; + } + + if (overrun) + MessageHandler.error(">"); + 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.
+ * 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; + 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; + } + switch (leaderPattern) { + case LeaderPattern.SPACE: + //whitespace setting must be false for this + int whiteSpaceSetting = this.whiteSpaceCollapse; + this.changeWhiteSpaceCollapse(WhiteSpaceCollapse.FALSE); + pendingAreas.addElement( + this.buildSimpleLeader(32, leaderLength)); + this.changeWhiteSpaceCollapse(whiteSpaceSetting); + 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 < this.currentFontState.width(46)) { + leaderPatternWidth = 0; + } + //if value of leader-pattern-width is 'use-font-metrics' (0) + if (leaderPatternWidth == 0) { + pendingAreas.addElement( + this.buildSimpleLeader(46, 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 - + this.currentFontState.width(46), false); + leaderPatternArea = + new WordArea(currentFontState, this.red, + this.green, this.blue, new String ("."), + this.currentFontState.width(46)); + 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 + Vector spaceList = new Vector(); + + 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()) { + spaceList.addElement(space); + spaceCount++; + } + } + } + if (spaceCount > 0) { + padding = (this.getContentWidth() - finalWidth) / + spaceCount; + } else { // no spaces + padding = 0; + } + Enumeration f = spaceList.elements(); + while (f.hasMoreElements()) { + InlineSpace space2 = (InlineSpace) f.nextElement(); + int i = space2.getSize(); + space2.setSize(i + 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 - (fh / 3.0))); + } else if(vert == VerticalAlign.SUB) { + int fh = fontState.getAscender(); + ia.setYOffset((int)(placementOffset + (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(String language, String country, + int hyphenate, char hyphenationChar, + int hyphenationPushCharacterCount, + int hyphenationRemainCharacterCount) { + this.language = language; + this.country = country; + this.hyphenate = hyphenate; + this.hyphenationChar = hyphenationChar; + this.hyphenationPushCharacterCount = hyphenationPushCharacterCount; + this.hyphenationRemainCharacterCount = + hyphenationRemainCharacterCount; + + } + + + /** + * 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(int charNumber, int leaderLength) { + int factor = (int) Math.floor (leaderLength / + this.currentFontState.width(charNumber)); + char [] leaderChars = new char [factor]; + char fillChar = (char) charNumber; + for (int i = 0; i < factor; i ++) { + leaderChars[i] = fillChar; + } + 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 [100]; //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.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(this.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(inwordPunctuation)); + } + } + + //are there any hyphenation points + Hyphenation hyph = Hyphenator.hyphenate(language,country,wordToHyphenate,hyphenationRemainCharacterCount,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); + 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.hyphenationChar); + 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.hyphenationChar); + this.addWord(startChar,remainingString); + return wordStart + remainingString.length()-1; + } else { + remainingString.append(preString) ; + this.addWord(startChar,remainingString); + return wordStart + remainingString.length(); + } + } + return wordStart; + } + + + /** calculates the wordWidth using the actual fontstate*/ + private int getWordWidth (String word) { + 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 += this.currentFontState.width(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(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; + } + } + + + /** adds a InlineArea containing the String startChar+wordBuf to the line area children. */ + private void addWord (char startChar, StringBuffer wordBuf) { + String word = wordBuf.toString(); + WordArea hia; + int startCharWidth = this.currentFontState.width(startChar); + if (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; + } + +} -- 2.39.5