--- /dev/null
+/*
+ * $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.fo;
+
+// FOP
+import org.apache.fop.apps.FOPException;
+import java.util.NoSuchElementException;
+
+public abstract class AbstractCharIterator implements CharIterator, Cloneable {
+
+ abstract public boolean hasNext();
+
+ abstract public char nextChar() throws NoSuchElementException ;
+
+ public Object next() throws NoSuchElementException {
+ return new Character(nextChar());
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+
+ public void replaceChar(char c) {
+ }
+
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ return null;
+ }
+ }
+};
--- /dev/null
+/*
+ * $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.fo;
+
+
+/**
+ * A character class allowing to distinguish whitespace, LF, other text.
+ */
+public class CharClass {
+
+ /** Character code used to signal a character boundary in
+ * inline content, such as an inline with borders and padding
+ * or a nested block object.
+ */
+ public static final char CODE_EOT=0;
+
+ public static final int UCWHITESPACE=0; // unicode white space
+ public static final int LINEFEED=1;
+ public static final int EOT=2; // Boundary beteween text runs
+ public static final int NONWHITESPACE=3;
+ public static final int XMLWHITESPACE=4;
+
+
+ /**
+ * Return the appropriate CharClass constant for the type
+ * of the passed character.
+ */
+ public static int classOf(char c) {
+ if (c == CODE_EOT) return EOT;
+ if (c == '\n') return LINEFEED;
+ if ( c==' '|| c == '\r' || c=='\t' ) return XMLWHITESPACE;
+ if (isAnySpace(c)) return UCWHITESPACE;
+ return NONWHITESPACE;
+ }
+
+
+ /**
+ * Helper method to determine if the character is a
+ * space with normal behaviour. Normal behaviour means that
+ * it's not non-breaking
+ */
+ private static boolean isSpace(char c) {
+ return (c == ' ' ||
+ (c >= '\u2000' && c <= '\u200B'));
+// 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
+
+ }
+
+
+ /**
+ * Method to determine if the character is a nonbreaking
+ * space.
+ */
+ private static boolean isNBSP(char c) {
+ return (c == '\u00A0' || // normal no-break space
+ c == '\u202F' || // narrow no-break space
+ c == '\u3000' || // ideographic space
+ c == '\uFEFF') ; // zero width no-break space
+ }
+
+ /**
+ * @return true if the character represents any kind of space
+ */
+ private static boolean isAnySpace(char c) {
+ return (isSpace(c) || isNBSP(c));
+ }
+
+}
+
--- /dev/null
+/*
+ * $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.fo;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+
+public interface CharIterator extends Iterator {
+
+ char nextChar() throws NoSuchElementException ;
+ void replaceChar(char c);
+ Object clone();
+}
import org.xml.sax.Attributes;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
/**
* base class for nodes in the XML tree
*
return this.parent;
}
+ /**
+ * Return an iterator over all the children of this FObj.
+ * @return A ListIterator.
+ */
+ public ListIterator getChildren() {
+ return null;
+ }
+
+ /**
+ * Return an iterator over the object's children starting
+ * at the pased node.
+ * @param childNode First node in the iterator
+ * @return A ListIterator or null if childNode isn't a child of
+ * this FObj.
+ */
+ public ListIterator getChildren(FONode childNode) {
+ return null;
+ }
+
+ public CharIterator charIterator() {
+ return new OneCharIterator(CharClass.CODE_EOT);
+ }
+
}
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.TextLayoutManager;
+import java.util.NoSuchElementException;
+
/**
* a text node in the formatting object tree
*
}
public LayoutManager getLayoutManager() {
+ // What if nothing left (length=0)?
+ if (length < ca.length) {
+ char[] tmp = ca;
+ ca = new char[length];
+ System.arraycopy(tmp, 0, ca, 0, length);
+ }
return new TextLayoutManager(this, ca, textInfo);
}
+
+ public CharIterator charIterator() {
+ return new TextCharIterator();
+ }
+
+ private class TextCharIterator extends AbstractCharIterator {
+ int curIndex = 0;
+ public boolean hasNext() {
+ return (curIndex < length);
+ }
+
+ public char nextChar() {
+ if (curIndex < length) {
+ // Just a char class? Don't actually care about the value!
+ return ca[curIndex++];
+ }
+ else throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ if (curIndex>0 && curIndex < length) {
+ // copy from curIndex to end to curIndex-1
+ System.arraycopy(ca, curIndex, ca, curIndex-1,
+ length-curIndex);
+ length--;
+ curIndex--;
+ }
+ else if (curIndex == length) {
+ curIndex = --length;
+ }
+ }
+
+
+ public void replaceChar(char c) {
+ if (curIndex>0 && curIndex <= length) {
+ ca[curIndex-1]=c;
+ }
+ }
+
+
+ }
}
return null;
}
-
+ /**
+ * Return an iterator over all the children of this FObj.
+ * @return A ListIterator.
+ */
public ListIterator getChildren() {
return children.listIterator();
}
+ /**
+ * Return an iterator over the object's children starting
+ * at the pased node.
+ * @param childNode First node in the iterator
+ * @return A ListIterator or null if childNode isn't a child of
+ * this FObj.
+ */
+ public ListIterator getChildren(FONode childNode) {
+ int i = children.indexOf(childNode);
+ if (i >= 0) {
+ return children.listIterator(i);
+ }
+ else return null;
+ }
+
public void setIsInTableCell() {
this.isInTableCell = true;
// made recursive by Eric Schaeffer
import org.apache.fop.apps.StreamRenderer;
import org.apache.fop.datatypes.ColorType;
+
/**
* base class for representation of mixed content formatting objects
* and their processing
return new Status(Status.OK);
}
+ public CharIterator charIterator() {
+ return new RecursiveCharIterator(this);
+ }
+
+
+
}
--- /dev/null
+package org.apache.fop.fo;
+
+import org.apache.fop.layout.BorderAndPadding;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+
+public class InlineCharIterator extends RecursiveCharIterator {
+ private boolean bStartBoundary=false;
+ private boolean bEndBoundary=false;
+
+ public InlineCharIterator(FObj fobj, BorderAndPadding bap) {
+ super(fobj);
+ checkBoundaries(bap);
+ }
+
+
+ private void checkBoundaries(BorderAndPadding bap) {
+ // TODO! use start and end in BAP!!
+ bStartBoundary = (bap.getBorderLeftWidth(false)>0 ||
+ bap.getPaddingLeft(false)>0);
+ bEndBoundary = (bap.getBorderRightWidth(false)>0 ||
+ bap.getPaddingRight(false)>0);
+ }
+
+ public boolean hasNext() {
+ if (bStartBoundary) return true;
+ return (super.hasNext() || bEndBoundary);
+ /* If super.hasNext() returns false,
+ * we return true if we are going to return a "boundary" signal
+ * else false.
+ */
+ }
+
+ public char nextChar() throws NoSuchElementException {
+ if (bStartBoundary) {
+ bStartBoundary=false;
+ return CharClass.CODE_EOT;
+ }
+ try {
+ return super.nextChar();
+ }
+ catch (NoSuchElementException e) {
+ // Underlying has nothing more to return
+ // Check end boundary char
+ if (bEndBoundary) {
+ bEndBoundary=false;
+ return CharClass.CODE_EOT;
+ }
+ else throw e;
+ }
+ }
+}
--- /dev/null
+/*
+ * $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.fo;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+
+public class OneCharIterator extends AbstractCharIterator {
+
+ private boolean bFirst=true;
+ private char charCode;
+
+ public OneCharIterator(char c) {
+ this.charCode = c;
+ }
+
+ public boolean hasNext() {
+ return bFirst;
+ }
+
+ public char nextChar() throws NoSuchElementException {
+ if (bFirst) {
+ bFirst=false;
+ return charCode;
+ }
+ else throw new NoSuchElementException();
+ }
+
+}
--- /dev/null
+package org.apache.fop.fo;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+
+public class RecursiveCharIterator extends AbstractCharIterator {
+ Iterator childIter = null; // Child flow objects
+ CharIterator curCharIter = null; // Children's characters
+ private FONode fobj;
+ private FONode curChild;
+
+ public RecursiveCharIterator(FObj fobj) {
+ // Set up first child iterator
+ this.fobj = fobj;
+ this.childIter = fobj.getChildren();
+ getNextCharIter();
+ }
+
+ public RecursiveCharIterator(FObj fobj, FONode child) {
+ // Set up first child iterator
+ this.fobj = fobj;
+ this.childIter = fobj.getChildren(child);
+ getNextCharIter();
+ }
+
+ public CharIterator mark() {
+ return (CharIterator) this.clone();
+ }
+
+ public Object clone() {
+ RecursiveCharIterator ci = (RecursiveCharIterator)super.clone();
+ ci.childIter = fobj.getChildren(ci.curChild);
+ ci.curCharIter = (CharIterator)curCharIter.clone();
+ return ci;
+ }
+
+
+ public void replaceChar(char c) {
+ if (curCharIter != null) {
+ curCharIter.replaceChar(c);
+ }
+ }
+
+
+ private void getNextCharIter() {
+ if (childIter.hasNext()) {
+ this.curChild = (FONode)childIter.next();
+ this.curCharIter = curChild.charIterator();
+ }
+ else {
+ curChild = null;
+ curCharIter = null;
+ }
+ }
+
+ public boolean hasNext() {
+ while (curCharIter != null) {
+ if (curCharIter.hasNext()==false) {
+ getNextCharIter();
+ }
+ else return true;
+ }
+ return false;
+ }
+
+ public char nextChar() throws NoSuchElementException {
+ if (curCharIter != null) {
+ return curCharIter.nextChar();
+ }
+ else throw new NoSuchElementException();
+ }
+
+
+ public void remove() {
+ if (curCharIter != null) {
+ curCharIter.remove();
+ }
+ }
+}
String id;
int span;
+ private int wsTreatment; //ENUMERATION
+ private int lfTreatment; //ENUMERATION
+ private boolean bWScollapse; //true if white-space-collapse=true
// this may be helpful on other FOs too
boolean anythingLaidOut = false;
+ /**
+ * Index of first inline-type FO seen in a sequence.
+ * Used during FO tree building to do white-space handling.
+ */
+ private FONode firstInlineChild = null;
+
public Block(FONode parent) {
super(parent);
}
public void handleAttrs(Attributes attlist) throws FOPException {
super.handleAttrs(attlist);
this.span = this.properties.get("span").getEnum();
+ this.wsTreatment = this.properties.get("white-space-treatment").getEnum();
+ this.bWScollapse = (this.properties.get("white-space-collapse").getEnum() ==
+ Constants.TRUE);
+ this.lfTreatment = this.properties.get("linefeed-treatment").getEnum();
}
public Status layout(Area area) throws FOPException {
// this.properties.get("line-height-shift-adjustment");
// this.properties.get("line-stacking-strategy");
// this.properties.get("orphans");
- // this.properties.get("space-treatment");
+ // this.properties.get("white-space-treatment");
// this.properties.get("span");
// this.properties.get("text-align");
// this.properties.get("text-align-last");
public boolean generatesInlineAreas() {
return false;
}
+
+
+ public void addChild(FONode child) {
+ // Handle whitespace based on values of properties
+ // Handle a sequence of inline-producing children in
+ // one pass
+ if (((FObj)child).generatesInlineAreas()) {
+ if (firstInlineChild == null) {
+ firstInlineChild = child;
+ }
+ // lastInlineChild = children.size();
+ }
+ else {
+ // Handle whitespace in preceeding inline areas if any
+ handleWhiteSpace();
+ }
+ super.addChild(child);
+ }
+
+ public void end() {
+ handleWhiteSpace();
+ }
+
+ private void handleWhiteSpace() {
+ log.debug("fo:block: handleWhiteSpace");
+ if (firstInlineChild != null) {
+ boolean bInWS=false;
+ boolean bPrevWasLF=false;
+ RecursiveCharIterator charIter =
+ new RecursiveCharIterator(this, firstInlineChild);
+ LFchecker lfCheck = new LFchecker(charIter);
+
+ while (charIter.hasNext()) {
+ switch (CharClass.classOf(charIter.nextChar())) {
+ case CharClass.XMLWHITESPACE:
+ /* Some kind of whitespace character, except linefeed. */
+ boolean bIgnore=false;
+
+ switch (wsTreatment) {
+ case Constants.IGNORE:
+ bIgnore=true;
+ break;
+ case Constants.IGNORE_IF_BEFORE_LINEFEED:
+ bIgnore = lfCheck.nextIsLF();
+ break;
+ case Constants.IGNORE_IF_SURROUNDING_LINEFEED:
+ bIgnore = (bPrevWasLF || lfCheck.nextIsLF());
+ break;
+ case Constants.IGNORE_IF_AFTER_LINEFEED:
+ bIgnore = bPrevWasLF;
+ break;
+ }
+ // Handle ignore
+ if (bIgnore) {
+ charIter.remove();
+ }
+ else if (bWScollapse) {
+ if (bInWS || (lfTreatment == Constants.PRESERVE &&
+ (bPrevWasLF || lfCheck.nextIsLF()))) {
+ charIter.remove();
+ }
+ else {
+ bInWS = true;
+ }
+ }
+ break;
+
+ case CharClass.LINEFEED:
+ /* A linefeed */
+ lfCheck.reset();
+ bPrevWasLF=true; // for following whitespace
+
+ switch (lfTreatment) {
+ case Constants.IGNORE:
+ charIter.remove();
+ break;
+ case Constants.TREAT_AS_SPACE:
+ if (bInWS) {
+ // only if bWScollapse=true
+ charIter.remove();
+ }
+ else {
+ if (bWScollapse) bInWS=true;
+ charIter.replaceChar('\u0020');
+ }
+ break;
+ case Constants.TREAT_AS_ZERO_WIDTH_SPACE:
+ charIter.replaceChar('\u200b');
+ // Fall through: this isn't XML whitespace
+ case Constants.PRESERVE:
+ bInWS=false;
+ break;
+ }
+ break;
+
+ case CharClass.EOT:
+ // A "boundary" objects such as non-character inline
+ // or nested block object was encountered.
+ // If any whitespace run in progress, finish it.
+ // FALL THROUGH
+
+ case CharClass.UCWHITESPACE: // Non XML-whitespace
+ case CharClass.NONWHITESPACE:
+ /* Any other character */
+ bInWS = bPrevWasLF=false;
+ lfCheck.reset();
+ break;
+ }
+ }
+ firstInlineChild = null;
+ }
+ }
+
+ private static class LFchecker {
+ private boolean bNextIsLF=false;
+ private RecursiveCharIterator charIter;
+
+ LFchecker(RecursiveCharIterator charIter) {
+ this.charIter = charIter;
+ }
+
+ boolean nextIsLF() {
+ if (bNextIsLF==false) {
+ CharIterator lfIter = charIter.mark();
+ while (lfIter.hasNext()) {
+ char c = lfIter.nextChar();
+ if (c == '\n') {
+ bNextIsLF=true;
+ break;
+ }
+ else if (CharClass.classOf(c)!=CharClass.XMLWHITESPACE) {
+ break;
+ }
+ }
+ }
+ return bNextIsLF;
+ }
+
+ void reset() {
+ bNextIsLF=false;
+ }
+ }
}
public final static int OK = 0;
public final static int DOESNOT_FIT = 1;
+ private char characterValue;
+
public Character(FONode parent) {
super(parent);
this.name = "fo:character";
}
// Character specific properties
- char characterValue = this.properties.get("character").getCharacter();
+ characterValue = this.properties.get("character").getCharacter();
// initialize id
}
+ public CharIterator charIterator() {
+ return new OneCharIterator(characterValue);
+ // But what it the character is ignored due to white space handling?
+ }
+
+
}
}
}
+
+ public CharIterator charIterator() {
+ return new InlineCharIterator(this, propMgr.getBorderAndPadding());
+ }
+
}
new Rectangle(0,0,
pageWidth,pageHeight)));
- _regions = null;
+ // _regions = null; // PageSequence access SimplePageMaster....
children = null;
properties = null;
}