12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061 |
- /*-- $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 <jtauber@jtauber.com>. For more information on the Apache
- Software Foundation, please see <http://www.apache.org/>.
-
- */
-
- 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(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 neccecary currentFontState.width(32)
- int whitespaceWidth =
- currentFontState.width(currentFontState.mapChar(' '));
-
- 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 == ' ') || (c == '\n') || (c == '\r') ||
- (c == '\t')))
- c = data[i] = currentFontState.mapChar(c);
-
- 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 += whitespaceWidth;
- } else if (c == '\n') {
- // force line break
- return i;
- } else if (c == '\t') {
- spaceWidth += 8 * whitespaceWidth;
- }
- } // 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);
- 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);
- }
-
- 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);
- 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 = whitespaceWidth;
-
- /*
- 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 = whitespaceWidth;
- }
- }
-
- } else {
-
- // if current is WHITESPACE and no previous
-
- if (this.whiteSpaceCollapse ==
- WhiteSpaceCollapse.FALSE) {
- prev = WHITESPACE;
- spaceWidth = whitespaceWidth;
- } 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()) {
-
- // 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. <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;
- 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:
- //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;
- }
-
- }
|