You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by ga...@apache.org on 2012/02/26 03:29:29 UTC
svn commit: r1293736 [5/38] - in /xmlgraphics/fop/trunk: ./
src/codegen/java/org/apache/fop/tools/
src/codegen/unicode/java/org/apache/fop/complexscripts/
src/codegen/unicode/java/org/apache/fop/complexscripts/bidi/
src/documentation/content/xdocs/trun...
Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.complexscripts.bidi;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.fo.CharIterator;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
+import org.apache.fop.traits.Direction;
+import org.apache.fop.traits.WritingModeTraits;
+import org.apache.fop.traits.WritingModeTraitsGetter;
+import org.apache.fop.util.CharUtilities;
+
+// CSOFF: EmptyForIteratorPadCheck
+// CSOFF: InnerAssignmentCheck
+// CSOFF: LineLengthCheck
+// CSOFF: NoWhitespaceAfterCheck
+
+/**
+ * The <code>DelimitedTextRange</code> class implements the "delimited text range" as described
+ * by XML-FO 1.1 §5.8, which contains a flattened sequence of characters. Any FO that generates
+ * block areas serves as a delimiter.
+ *
+ * @author Glenn Adams
+ */
+public class DelimitedTextRange {
+ private FONode fn; // node that generates this text range
+ private StringBuffer buffer; // flattened character sequence of generating FO nodes
+ private List intervals; // list of intervals over buffer of generating FO nodes
+ /**
+ * Primary constructor.
+ * @param fn node that generates this text range
+ */
+ public DelimitedTextRange ( FONode fn ) {
+ this.fn = fn;
+ this.buffer = new StringBuffer();
+ this.intervals = new Vector();
+ }
+ /**
+ * Obtain node that generated this text range.
+ * @return node that generated this text range
+ */
+ public FONode getNode() {
+ return fn;
+ }
+ /**
+ * Append interval using characters from character iterator IT.
+ * @param it character iterator
+ * @param fn node that generates interval being appended
+ */
+ public void append ( CharIterator it, FONode fn ) {
+ if ( it != null ) {
+ int s = buffer.length();
+ int e = s;
+ while ( it.hasNext() ) {
+ char c = it.nextChar();
+ buffer.append ( c );
+ e++;
+ }
+ intervals.add ( new TextInterval ( fn, s, e ) );
+ }
+ }
+ /**
+ * Append interval using character C.
+ * @param c character
+ * @param fn node that generates interval being appended
+ */
+ public void append ( char c, FONode fn ) {
+ if ( c != 0 ) {
+ int s = buffer.length();
+ int e = s + 1;
+ buffer.append ( c );
+ intervals.add ( new TextInterval ( fn, s, e ) );
+ }
+ }
+ /**
+ * Determine if range is empty.
+ * @return true if range is empty
+ */
+ public boolean isEmpty() {
+ return buffer.length() == 0;
+ }
+ /**
+ * Resolve bidirectional levels for this range.
+ */
+ public void resolve() {
+ WritingModeTraitsGetter tg;
+ if ( ( tg = WritingModeTraits.getWritingModeTraitsGetter ( getNode() ) ) != null ) {
+ resolve ( tg.getInlineProgressionDirection() );
+ }
+ }
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer ( "DR: " + fn.getLocalName() + " { <" + CharUtilities.toNCRefs ( buffer.toString() ) + ">" );
+ sb.append ( ", intervals <" );
+ boolean first = true;
+ for ( Iterator it = intervals.iterator(); it.hasNext(); ) {
+ TextInterval ti = (TextInterval) it.next();
+ if ( first ) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append ( ti.toString() );
+ }
+ sb.append("> }");
+ return sb.toString();
+ }
+ private void resolve ( Direction paragraphEmbeddingLevel ) {
+ int [] levels;
+ if ( ( levels = UnicodeBidiAlgorithm.resolveLevels ( buffer, paragraphEmbeddingLevel ) ) != null ) {
+ assignLevels ( levels );
+ assignBlockLevel ( paragraphEmbeddingLevel );
+ assignTextLevels();
+ }
+ }
+ /**
+ * <p>Assign resolved levels to all text intervals of this delimited text range.</p>
+ * <p>Has a possible side effect of replacing the intervals array with a new array
+ * containing new text intervals, such that each text interval is associated with
+ * a single level run.</p>
+ * @param levels array of levels each corresponding to each index of the delimited
+ * text range
+ */
+ private void assignLevels ( int[] levels ) {
+ Vector intervalsNew = new Vector ( intervals.size() );
+ for ( Iterator it = intervals.iterator(); it.hasNext(); ) {
+ TextInterval ti = (TextInterval) it.next();
+ intervalsNew.addAll ( assignLevels ( ti, levels ) );
+ }
+ if ( ! intervalsNew.equals ( intervals ) ) {
+ intervals = intervalsNew;
+ }
+ }
+ /**
+ * <p>Assign resolved levels to a specified text interval over this delimited text
+ * range.</p>
+ * <p>Returns a list of text intervals containing either (1) the single, input text
+ * interval or (2) two or more new text intervals obtained from sub-dividing the input
+ * text range into level runs, i.e., runs of text assigned to a single level.</p>
+ * @param ti a text interval to which levels are to be assigned
+ * @param levels array of levels each corresponding to each index of the delimited
+ * text range
+ * @return a list of text intervals as described above
+ */
+ private static final Log log = LogFactory.getLog(BidiResolver.class); // CSOK: ConstantNameCheck
+ private List assignLevels ( TextInterval ti, int[] levels ) {
+ Vector tiv = new Vector();
+ FONode fn = ti.getNode();
+ int fnStart = ti.getStart(); // start of node's text in delimited text range
+ for ( int i = fnStart, n = ti.getEnd(); i < n; ) {
+ int s = i; // inclusive start index of interval in delimited text range
+ int e = s; // exclusive end index of interval in delimited text range
+ int l = levels [ s ]; // current run level
+ while ( e < n ) { // skip to end of run level or end of interval
+ if ( levels [ e ] != l ) {
+ break;
+ } else {
+ e++;
+ }
+ }
+ if ( ( ti.getStart() == s ) && ( ti.getEnd() == e ) ) {
+ ti.setLevel ( l ); // reuse interval, assigning it single level
+ } else {
+ ti = new TextInterval ( fn, fnStart, s, e, l ); // subdivide interval
+ }
+ if (log.isDebugEnabled()) {
+ log.debug ( "AL(" + l + "): " + ti );
+ }
+ tiv.add ( ti );
+ i = e;
+ }
+ return tiv;
+ }
+ /**
+ * <p>Assign resolved levels for each interval to source #PCDATA in the associated FOText.</p>
+ */
+ private void assignTextLevels() {
+ for ( Iterator it = intervals.iterator(); it.hasNext(); ) {
+ TextInterval ti = (TextInterval) it.next();
+ ti.assignTextLevels();
+ }
+ }
+ private void assignBlockLevel ( Direction paragraphEmbeddingLevel ) {
+ int defaultLevel = ( paragraphEmbeddingLevel == Direction.RL ) ? 1 : 0;
+ for ( Iterator it = intervals.iterator(); it.hasNext(); ) {
+ TextInterval ti = (TextInterval) it.next();
+ assignBlockLevel ( ti.getNode(), defaultLevel );
+ }
+ }
+ private void assignBlockLevel ( FONode node, int defaultLevel ) {
+ for ( FONode fn = node; fn != null; fn = fn.getParent() ) {
+ if ( fn instanceof FObj ) {
+ FObj fo = (FObj) fn;
+ if ( fo.isBidiRangeBlockItem() ) {
+ if ( fo.getBidiLevel() < 0 ) {
+ fo.setBidiLevel ( defaultLevel );
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.complexscripts.bidi;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.fop.area.inline.Anchor;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.InlineBlockParent;
+import org.apache.fop.area.inline.InlineParent;
+import org.apache.fop.area.inline.InlineViewport;
+import org.apache.fop.area.inline.Leader;
+import org.apache.fop.area.inline.Space;
+import org.apache.fop.area.inline.SpaceArea;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.inline.UnresolvedPageNumber;
+import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.traits.Direction;
+import org.apache.fop.util.CharUtilities;
+
+// CSOFF: EmptyForIteratorPadCheck
+// CSOFF: InnerAssignmentCheck
+// CSOFF: NoWhitespaceAfterCheck
+// CSOFF: SimplifyBooleanReturnCheck
+
+/**
+ * The <code>InlineRun</code> class is a utility class, the instances of which are used
+ * to capture a sequence of reordering levels associated with an inline area.
+ *
+ * @author Glenn Adams
+ */
+public class InlineRun {
+ private InlineArea inline;
+ private int[] levels;
+ private int minLevel;
+ private int maxLevel;
+ private int reversals;
+ /**
+ * Primary constructor.
+ * @param inline which generated this inline run
+ * @param levels levels array
+ */
+ public InlineRun ( InlineArea inline, int[] levels ) {
+ assert inline != null;
+ assert levels != null;
+ this.inline = inline;
+ this.levels = levels;
+ setMinMax ( levels );
+ }
+ /**
+ * Alternate constructor.
+ * @param inline which generated this inline run
+ * @param level for each index
+ * @param count of indices
+ */
+ public InlineRun ( InlineArea inline, int level, int count ) {
+ this ( inline, makeLevels ( level, count ) );
+ }
+ /**
+ * Obtain inline area that generated this inline run.
+ * @return inline area that generated this inline run.
+ */
+ public InlineArea getInline() {
+ return inline;
+ }
+ /**
+ * Obtain minimum bidi level for this run.
+ * @return minimum bidi level
+ */
+ public int getMinLevel() {
+ return minLevel;
+ }
+ /**
+ * Obtain maximum bidi level for this run.
+ * @return maximum bidi level
+ */
+ public int getMaxLevel() {
+ return maxLevel;
+ }
+ private void setMinMax ( int[] levels ) {
+ int mn = Integer.MAX_VALUE;
+ int mx = Integer.MIN_VALUE;
+ if ( ( levels != null ) && ( levels.length > 0 ) ) {
+ for ( int i = 0, n = levels.length; i < n; i++ ) {
+ int l = levels [ i ];
+ if ( l < mn ) {
+ mn = l;
+ }
+ if ( l > mx ) {
+ mx = l;
+ }
+ }
+ } else {
+ mn = mx = -1;
+ }
+ this.minLevel = mn;
+ this.maxLevel = mx;
+ }
+ /**
+ * Determine if this run has homogenous (same valued) bidi levels.
+ * @return true if homogenous
+ */
+ public boolean isHomogenous() {
+ return minLevel == maxLevel;
+ }
+ /**
+ * Split this inline run into homogenous runs.
+ * @return list of new runs
+ */
+ public List split() {
+ List runs = new Vector();
+ for ( int i = 0, n = levels.length; i < n; ) {
+ int l = levels [ i ];
+ int s = i;
+ int e = s;
+ while ( e < n ) {
+ if ( levels [ e ] != l ) {
+ break;
+ } else {
+ e++;
+ }
+ }
+ if ( s < e ) {
+ runs.add ( new InlineRun ( inline, l, e - s ) );
+ }
+ i = e;
+ }
+ assert runs.size() < 2 : "heterogeneous inlines not yet supported!!";
+ return runs;
+ }
+ /**
+ * Update a min/max array to correspond with this run's min/max values.
+ * @param mm reference to min/max array
+ */
+ public void updateMinMax ( int[] mm ) {
+ if ( minLevel < mm[0] ) {
+ mm[0] = minLevel;
+ }
+ if ( maxLevel > mm[1] ) {
+ mm[1] = maxLevel;
+ }
+ }
+ /**
+ * Determine if run needs mirroring.
+ * @return true if run is homogenous and odd (i.e., right to left)
+ */
+ public boolean maybeNeedsMirroring() {
+ return ( minLevel == maxLevel ) && ( ( minLevel & 1 ) != 0 );
+ }
+ /**
+ * Reverse run (by incrementing reversal count, not actually reversing).
+ */
+ public void reverse() {
+ reversals++;
+ }
+ /**
+ * Reverse inline area if it is a word area and it requires
+ * reversal.
+ * @param mirror if true then also mirror characters
+ */
+ public void maybeReverseWord ( boolean mirror ) {
+ if ( inline instanceof WordArea ) {
+ WordArea w = (WordArea) inline;
+ // if not already reversed, then reverse now
+ if ( ! w.isReversed() ) {
+ if ( ( reversals & 1 ) != 0 ) {
+ w.reverse ( mirror );
+ } else if ( mirror && maybeNeedsMirroring() ) {
+ w.mirror();
+ }
+ }
+ }
+ }
+ @Override
+ public boolean equals ( Object o ) {
+ if ( o instanceof InlineRun ) {
+ InlineRun ir = (InlineRun) o;
+ if ( ir.inline != inline ) {
+ return false;
+ } else if ( ir.minLevel != minLevel ) {
+ return false;
+ } else if ( ir.maxLevel != maxLevel ) {
+ return false;
+ } else if ( ( ir.levels != null ) && ( levels != null ) ) {
+ if ( ir.levels.length != levels.length ) {
+ return false;
+ } else {
+ for ( int i = 0, n = levels.length; i < n; i++ ) {
+ if ( ir.levels[i] != levels[i] ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ } else if ( ( ir.levels == null ) && ( levels == null ) ) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ @Override
+ public int hashCode() {
+ int l = ( inline != null ) ? inline.hashCode() : 0;
+ l = ( l ^ minLevel ) + ( l << 19 );
+ l = ( l ^ maxLevel ) + ( l << 11 );
+ return l;
+ }
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer( "RR: { type = \'" );
+ char c;
+ String content = null;
+ if ( inline instanceof WordArea ) {
+ c = 'W';
+ content = ( (WordArea) inline ) .getWord();
+ } else if ( inline instanceof SpaceArea ) {
+ c = 'S';
+ content = ( (SpaceArea) inline ) .getSpace();
+ } else if ( inline instanceof Anchor ) {
+ c = 'A';
+ } else if ( inline instanceof Leader ) {
+ c = 'L';
+ } else if ( inline instanceof Space ) {
+ c = 'S';
+ } else if ( inline instanceof UnresolvedPageNumber ) {
+ c = '#';
+ content = ( (UnresolvedPageNumber) inline ) .getText();
+ } else if ( inline instanceof InlineBlockParent ) {
+ c = 'B';
+ } else if ( inline instanceof InlineViewport ) {
+ c = 'V';
+ } else if ( inline instanceof InlineParent ) {
+ c = 'I';
+ } else {
+ c = '?';
+ }
+ sb.append ( c );
+ sb.append ( "\', levels = \'" );
+ sb.append ( generateLevels ( levels ) );
+ sb.append ( "\', min = " );
+ sb.append ( minLevel );
+ sb.append ( ", max = " );
+ sb.append ( maxLevel );
+ sb.append ( ", reversals = " );
+ sb.append ( reversals );
+ sb.append ( ", content = <" );
+ sb.append ( CharUtilities.toNCRefs ( content ) );
+ sb.append ( "> }" );
+ return sb.toString();
+ }
+ private String generateLevels ( int[] levels ) {
+ StringBuffer lb = new StringBuffer();
+ int maxLevel = -1;
+ int numLevels = levels.length;
+ for ( int i = 0; i < numLevels; i++ ) {
+ int l = levels [ i ];
+ if ( l > maxLevel ) {
+ maxLevel = l;
+ }
+ }
+ if ( maxLevel < 0 ) {
+ // leave level buffer empty
+ } else if ( maxLevel < 10 ) {
+ // use string of decimal digits
+ for ( int i = 0; i < numLevels; i++ ) {
+ lb.append ( (char) ( '0' + levels [ i ] ) );
+ }
+ } else {
+ // use comma separated list
+ boolean first = true;
+ for ( int i = 0; i < numLevels; i++ ) {
+ if ( first ) {
+ first = false;
+ } else {
+ lb.append(',');
+ }
+ lb.append ( levels [ i ] );
+ }
+ }
+ return lb.toString();
+ }
+ private static int[] makeLevels ( int level, int count ) {
+ int[] levels = new int [ count ];
+ Arrays.fill ( levels, level );
+ return levels;
+ }
+}
+
Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.complexscripts.bidi;
+
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FOText;
+import org.apache.fop.fo.flow.AbstractPageNumberCitation;
+import org.apache.fop.fo.flow.AbstractGraphics;
+import org.apache.fop.fo.flow.BidiOverride;
+import org.apache.fop.fo.flow.Character;
+import org.apache.fop.fo.flow.Leader;
+
+// CSOFF: LineLengthCheck
+// CSOFF: SimplifyBooleanReturnCheck
+
+/**
+ * The <code>TextInterval</code> class is a utility class, the instances of which are used
+ * to record backpointers to associated nodes over sub-intervals of a delimited text range.
+ *
+ * @author Glenn Adams
+ */
+class TextInterval {
+ private FONode fn; // associated node
+ private int textStart; // starting index within delimited text range of associated node's text
+ private int start; // starting index within delimited text range
+ private int end; // ending index within delimited text range
+ private int level; // resolved level or default (-1)
+ TextInterval ( FONode fn, int start, int end ) {
+ this ( fn, start, start, end, -1 );
+ }
+ TextInterval ( FONode fn, int textStart, int start, int end, int level ) {
+ this.fn = fn;
+ this.textStart = textStart;
+ this.start = start;
+ this.end = end;
+ this.level = level;
+ }
+ FONode getNode() {
+ return fn;
+ }
+ int getTextStart() {
+ return textStart;
+ }
+ int getStart() {
+ return start;
+ }
+ int getEnd() {
+ return end;
+ }
+ int getLevel() {
+ return level;
+ }
+ void setLevel ( int level ) {
+ this.level = level;
+ }
+ public int length() {
+ return end - start;
+ }
+ public String getText() {
+ if ( fn instanceof FOText ) {
+ return ( (FOText) fn ) .getCharSequence() .toString();
+ } else if ( fn instanceof Character ) {
+ return new String ( new char[] {( (Character) fn ) .getCharacter()} );
+ } else {
+ return null;
+ }
+ }
+ public void assignTextLevels() {
+ if ( fn instanceof FOText ) {
+ ( (FOText) fn ) .setBidiLevel ( level, start - textStart, end - textStart );
+ } else if ( fn instanceof Character ) {
+ ( (Character) fn ) .setBidiLevel ( level );
+ } else if ( fn instanceof AbstractPageNumberCitation ) {
+ ( (AbstractPageNumberCitation) fn ) .setBidiLevel ( level );
+ } else if ( fn instanceof AbstractGraphics ) {
+ ( (AbstractGraphics) fn ) .setBidiLevel ( level );
+ } else if ( fn instanceof Leader ) {
+ ( (Leader) fn ) .setBidiLevel ( level );
+ }
+ }
+ public boolean equals ( Object o ) {
+ if ( o instanceof TextInterval ) {
+ TextInterval ti = (TextInterval) o;
+ if ( ti.getNode() != fn ) {
+ return false;
+ } else if ( ti.getStart() != start ) {
+ return false;
+ } else if ( ti.getEnd() != end ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+ public int hashCode() {
+ int l = ( fn != null ) ? fn.hashCode() : 0;
+ l = ( l ^ start ) + ( l << 19 );
+ l = ( l ^ end ) + ( l << 11 );
+ return l;
+ }
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ char c;
+ if ( fn instanceof FOText ) {
+ c = 'T';
+ } else if ( fn instanceof Character ) {
+ c = 'C';
+ } else if ( fn instanceof BidiOverride ) {
+ c = 'B';
+ } else if ( fn instanceof AbstractPageNumberCitation ) {
+ c = '#';
+ } else if ( fn instanceof AbstractGraphics ) {
+ c = 'G';
+ } else if ( fn instanceof Leader ) {
+ c = 'L';
+ } else {
+ c = '?';
+ }
+ sb.append ( c );
+ sb.append ( "[" + start + "," + end + "][" + textStart + "](" + level + ")" );
+ return sb.toString();
+ }
+}
+
Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.complexscripts.bidi;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.fop.area.Area;
+import org.apache.fop.area.LinkResolver;
+import org.apache.fop.area.inline.BasicLinkArea;
+import org.apache.fop.area.inline.FilledArea;
+import org.apache.fop.area.inline.InlineArea;
+import org.apache.fop.area.inline.InlineParent;
+import org.apache.fop.area.inline.SpaceArea;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.inline.UnresolvedPageNumber;
+
+// CSOFF: EmptyForIteratorPadCheck
+// CSOFF: LineLengthCheck
+// CSOFF: NoWhitespaceAfterCheck
+// CSOFF: SimplifyBooleanReturnCheck
+
+/**
+ * The <code>UnflattenProcessor</code> class is used to reconstruct (by unflattening) a line
+ * area's internal area hierarachy after leaf inline area reordering is completed.
+ *
+ * @author Glenn Adams
+ */
+class UnflattenProcessor {
+ private List<InlineArea> il; // list of flattened inline areas being unflattened
+ private List<InlineArea> ilNew; // list of unflattened inline areas being constructed
+ private int iaLevelLast; // last (previous) level of current inline area (if applicable) or -1
+ private TextArea tcOrig; // original text area container
+ private TextArea tcNew; // new text area container being constructed
+ private Stack<InlineParent> icOrig; // stack of original inline parent containers
+ private Stack<InlineParent> icNew; // stack of new inline parent containers being constructed
+ UnflattenProcessor ( List<InlineArea> inlines ) {
+ this.il = inlines;
+ this.ilNew = new ArrayList<InlineArea>();
+ this.iaLevelLast = -1;
+ this.icOrig = new Stack<InlineParent>();
+ this.icNew = new Stack<InlineParent>();
+ }
+ List unflatten() {
+ if ( il != null ) {
+ for ( Iterator<InlineArea> it = il.iterator(); it.hasNext(); ) {
+ process ( it.next() );
+ }
+ }
+ finishAll();
+ return ilNew;
+ }
+ private void process ( InlineArea ia ) {
+ process ( findInlineContainers ( ia ), findTextContainer ( ia ), ia );
+ }
+ private void process ( List<InlineParent> ich, TextArea tc, InlineArea ia ) {
+ if ( ( tcNew == null ) || ( tc != tcNew ) ) {
+ maybeFinishTextContainer ( tc, ia );
+ maybeFinishInlineContainers ( ich, tc, ia );
+ update ( ich, tc, ia );
+ } else {
+ // skip inline area whose text container is the current new text container,
+ // which occurs in the context of the inline runs produced by a filled area
+ }
+ }
+ private boolean shouldFinishTextContainer ( TextArea tc, InlineArea ia ) {
+ if ( ( tcOrig != null ) && ( tc != tcOrig ) ) {
+ return true;
+ } else if ( ( iaLevelLast != -1 ) && ( ia.getBidiLevel() != iaLevelLast ) ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ private void finishTextContainer() {
+ finishTextContainer ( null, null );
+ }
+ private void finishTextContainer ( TextArea tc, InlineArea ia ) {
+ if ( tcNew != null ) {
+ updateIPD ( tcNew );
+ if ( ! icNew.empty() ) {
+ icNew.peek().addChildArea ( tcNew );
+ } else {
+ ilNew.add ( tcNew );
+ }
+ }
+ tcNew = null;
+ }
+ private void maybeFinishTextContainer ( TextArea tc, InlineArea ia ) {
+ if ( shouldFinishTextContainer ( tc, ia ) ) {
+ finishTextContainer ( tc, ia );
+ }
+ }
+ private boolean shouldFinishInlineContainer ( List<InlineParent> ich, TextArea tc, InlineArea ia ) {
+ if ( ( ich == null ) || ich.isEmpty() ) {
+ return ! icOrig.empty();
+ } else {
+ if ( ! icOrig.empty() ) {
+ InlineParent ic = ich.get(0);
+ InlineParent ic0 = icOrig.peek();
+ return ( ic != ic0 ) && ! isInlineParentOf ( ic, ic0 );
+ } else {
+ return false;
+ }
+ }
+ }
+ private void finishInlineContainer() {
+ finishInlineContainer ( null, null, null );
+ }
+ private void finishInlineContainer ( List<InlineParent> ich, TextArea tc, InlineArea ia ) {
+ if ( ( ich != null ) && ! ich.isEmpty() ) { // finish non-matching inner inline container(s)
+ for ( Iterator<InlineParent> it = ich.iterator(); it.hasNext(); ) {
+ InlineParent ic = it.next();
+ InlineParent ic0 = icOrig.empty() ? null : icOrig.peek();
+ if ( ic0 == null ) {
+ assert icNew.empty();
+ } else if ( ic != ic0 ) {
+ assert ! icNew.empty();
+ InlineParent icO0 = icOrig.pop();
+ InlineParent icN0 = icNew.pop();
+ assert icO0 != null;
+ assert icN0 != null;
+ if ( icNew.empty() ) {
+ ilNew.add ( icN0 );
+ } else {
+ icNew.peek().addChildArea ( icN0 );
+ }
+ if ( ! icOrig.empty() && ( icOrig.peek() == ic ) ) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ } else { // finish all inline containers
+ while ( ! icNew.empty() ) {
+ InlineParent icO0 = icOrig.pop();
+ InlineParent icN0 = icNew.pop();
+ assert icO0 != null;
+ assert icN0 != null;
+ if ( icNew.empty() ) {
+ ilNew.add ( icN0 );
+ } else {
+ icNew.peek().addChildArea ( icN0 );
+ }
+ }
+ }
+ }
+ private void maybeFinishInlineContainers ( List<InlineParent> ich, TextArea tc, InlineArea ia ) {
+ if ( shouldFinishInlineContainer ( ich, tc, ia ) ) {
+ finishInlineContainer ( ich, tc, ia );
+ }
+ }
+ private void finishAll() {
+ finishTextContainer();
+ finishInlineContainer();
+ }
+ private void update ( List<InlineParent> ich, TextArea tc, InlineArea ia ) {
+ if ( ! alreadyUnflattened ( ia ) ) {
+ if ( ( ich != null ) && ! ich.isEmpty() ) {
+ pushInlineContainers ( ich );
+ }
+ if ( tc != null ) {
+ pushTextContainer ( tc, ia );
+ } else {
+ pushNonTextInline ( ia );
+ }
+ iaLevelLast = ia.getBidiLevel();
+ tcOrig = tc;
+ } else if ( tcNew != null ) {
+ finishTextContainer();
+ tcOrig = null;
+ } else {
+ tcOrig = null;
+ }
+ }
+ private boolean alreadyUnflattened ( InlineArea ia ) {
+ for ( Iterator<InlineArea> it = ilNew.iterator(); it.hasNext(); ) {
+ if ( ia.isAncestorOrSelf ( it.next() ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ private void pushInlineContainers ( List<InlineParent> ich ) {
+ LinkedList<InlineParent> icl = new LinkedList<InlineParent>();
+ for ( Iterator<InlineParent> it = ich.iterator(); it.hasNext(); ) {
+ InlineParent ic = it.next();
+ if ( icOrig.search ( ic ) >= 0 ) {
+ break;
+ } else {
+ icl.addFirst ( ic );
+ }
+ }
+ for ( Iterator<InlineParent> it = icl.iterator(); it.hasNext(); ) {
+ InlineParent ic = it.next();
+ icOrig.push ( ic );
+ icNew.push ( generateInlineContainer ( ic ) );
+ }
+ }
+ private void pushTextContainer ( TextArea tc, InlineArea ia ) {
+ if ( tc instanceof UnresolvedPageNumber ) {
+ tcNew = tc;
+ } else {
+ if ( tcNew == null ) {
+ tcNew = generateTextContainer ( tc );
+ }
+ tcNew.addChildArea ( ia );
+ }
+ }
+ private void pushNonTextInline ( InlineArea ia ) {
+ if ( icNew.empty() ) {
+ ilNew.add ( ia );
+ } else {
+ icNew.peek().addChildArea ( ia );
+ }
+ }
+ private InlineParent generateInlineContainer ( InlineParent i ) {
+ if ( i instanceof BasicLinkArea ) {
+ return generateBasicLinkArea ( (BasicLinkArea) i );
+ } else if ( i instanceof FilledArea ) {
+ return generateFilledArea ( (FilledArea) i );
+ } else {
+ return generateInlineContainer0 ( i );
+ }
+ }
+ private InlineParent generateBasicLinkArea ( BasicLinkArea l ) {
+ BasicLinkArea lc = new BasicLinkArea();
+ if ( l != null ) {
+ initializeInlineContainer ( lc, l );
+ initializeLinkArea ( lc, l );
+ }
+ return lc;
+ }
+ private void initializeLinkArea ( BasicLinkArea lc, BasicLinkArea l ) {
+ assert lc != null;
+ assert l != null;
+ LinkResolver r = l.getResolver();
+ if ( r != null ) {
+ String[] idrefs = r.getIDRefs();
+ if ( idrefs.length > 0 ) {
+ String idref = idrefs[0];
+ LinkResolver lr = new LinkResolver ( idref, lc );
+ lc.setResolver ( lr );
+ r.addDependent ( lr );
+ }
+ }
+ }
+ private InlineParent generateFilledArea ( FilledArea f ) {
+ FilledArea fc = new FilledArea();
+ if ( f != null ) {
+ initializeInlineContainer ( fc, f );
+ initializeFilledArea ( fc, f );
+ }
+ return fc;
+ }
+ private void initializeFilledArea ( FilledArea fc, FilledArea f ) {
+ assert fc != null;
+ assert f != null;
+ fc.setIPD ( f.getIPD() );
+ fc.setUnitWidth ( f.getUnitWidth() );
+ }
+ private InlineParent generateInlineContainer0 ( InlineParent i ) {
+ InlineParent ic = new InlineParent();
+ if ( i != null ) {
+ initializeInlineContainer ( ic, i );
+ }
+ return ic;
+ }
+ private void initializeInlineContainer ( InlineParent ic, InlineParent i ) {
+ assert ic != null;
+ assert i != null;
+ ic.setTraits ( i.getTraits() );
+ ic.setBPD ( i.getBPD() );
+ ic.setBlockProgressionOffset ( i.getBlockProgressionOffset() );
+ }
+ private TextArea generateTextContainer ( TextArea t ) {
+ TextArea tc = new TextArea();
+ if ( t != null ) {
+ tc.setTraits ( t.getTraits() );
+ tc.setBPD ( t.getBPD() );
+ tc.setBlockProgressionOffset ( t.getBlockProgressionOffset() );
+ tc.setBaselineOffset ( t.getBaselineOffset() );
+ tc.setTextWordSpaceAdjust ( t.getTextWordSpaceAdjust() );
+ tc.setTextLetterSpaceAdjust ( t.getTextLetterSpaceAdjust() );
+ }
+ return tc;
+ }
+ private void updateIPD ( TextArea tc ) {
+ int numAdjustable = 0;
+ for ( Iterator it = tc.getChildAreas().iterator(); it.hasNext(); ) {
+ InlineArea ia = (InlineArea) it.next();
+ if ( ia instanceof SpaceArea ) {
+ SpaceArea sa = (SpaceArea) ia;
+ if ( sa.isAdjustable() ) {
+ numAdjustable++;
+ }
+ }
+ }
+ if ( numAdjustable > 0 ) {
+ tc.setIPD ( tc.getIPD() + ( numAdjustable * tc.getTextWordSpaceAdjust() ) );
+ }
+ }
+ private TextArea findTextContainer ( InlineArea ia ) {
+ assert ia != null;
+ TextArea t = null;
+ while ( t == null ) {
+ if ( ia instanceof TextArea ) {
+ t = (TextArea) ia;
+ } else {
+ Area p = ia.getParentArea();
+ if ( p instanceof InlineArea ) {
+ ia = (InlineArea) p;
+ } else {
+ break;
+ }
+ }
+ }
+ return t;
+ }
+ private List<InlineParent> findInlineContainers ( InlineArea ia ) {
+ assert ia != null;
+ List<InlineParent> ich = new ArrayList<InlineParent>();
+ Area a = ia.getParentArea();
+ while ( a != null ) {
+ if ( a instanceof InlineArea ) {
+ if ( ( a instanceof InlineParent ) && ! ( a instanceof TextArea ) ) {
+ ich.add ( (InlineParent) a );
+ }
+ a = ( (InlineArea) a ) .getParentArea();
+ } else {
+ a = null;
+ }
+ }
+ return ich;
+ }
+ private boolean isInlineParentOf ( InlineParent ic0, InlineParent ic1 ) {
+ assert ic0 != null;
+ return ic0.getParentArea() == ic1;
+ }
+}
Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,839 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.complexscripts.bidi;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.traits.Direction;
+import org.apache.fop.util.CharUtilities;
+
+// CSOFF: AvoidNestedBlocksCheck
+// CSOFF: EmptyForIteratorPadCheck
+// CSOFF: InnerAssignmentCheck
+// CSOFF: LineLengthCheck
+// CSOFF: NoWhitespaceAfterCheck
+// CSOFF: ParameterNumberCheck
+
+/**
+ * The <code>UnicodeBidiAlgorithm</code> class implements functionality prescribed by
+ * the Unicode Bidirectional Algorithm, Unicode Standard Annex #9.
+ *
+ * @author Glenn Adams
+ */
+public final class UnicodeBidiAlgorithm implements BidiConstants {
+
+ /**
+ * logging instance
+ */
+ private static final Log log = LogFactory.getLog(BidiResolver.class); // CSOK: ConstantNameCheck
+
+ private UnicodeBidiAlgorithm() {
+ }
+
+ /**
+ * Resolve the directionality levels of each character in a character seqeunce.
+ * If some character is encoded in the character sequence as a Unicode Surrogate Pair,
+ * then the directionality level of each of the two members of the pair will be identical.
+ * @return null if bidirectional processing is not required; otherwise, returns an array
+ * of integers, where each integer corresponds to exactly one UTF-16
+ * encoding element present in the input character sequence, and where each integer denotes
+ * the directionality level of the corresponding encoding element
+ * @param cs input character sequence representing a UTF-16 encoded string
+ * @param defaultLevel the default paragraph level, which must be zero (LR) or one (RL)
+ */
+ public static int[] resolveLevels ( CharSequence cs, Direction defaultLevel ) {
+ int[] chars = new int [ cs.length() ];
+ if ( convertToScalar ( cs, chars ) || ( defaultLevel == Direction.RL ) ) {
+ return resolveLevels ( chars, ( defaultLevel == Direction.RL ) ? 1 : 0, new int [ chars.length ] );
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Resolve the directionality levels of each character in a character seqeunce.
+ * @return null if bidirectional processing is not required; otherwise, returns an array
+ * of integers, where each integer corresponds to exactly one UTF-16
+ * encoding element present in the input character sequence, and where each integer denotes
+ * the directionality level of the corresponding encoding element
+ * @param chars array of input characters represented as unicode scalar values
+ * @param defaultLevel the default paragraph level, which must be zero (LR) or one (RL)
+ * @param levels array to receive levels, one for each character in chars array
+ */
+ public static int[] resolveLevels ( int[] chars, int defaultLevel, int[] levels ) {
+ return resolveLevels ( chars, getClasses ( chars ), defaultLevel, levels, false );
+ }
+
+ /**
+ * Resolve the directionality levels of each character in a character seqeunce.
+ * @return null if bidirectional processing is not required; otherwise, returns an array
+ * of integers, where each integer corresponds to exactly one UTF-16
+ * encoding element present in the input character sequence, and where each integer denotes
+ * the directionality level of the corresponding encoding element
+ * @param chars array of input characters represented as unicode scalar values
+ * @param classes array containing one bidi class per character in chars array
+ * @param defaultLevel the default paragraph level, which must be zero (LR) or one (RL)
+ * @param levels array to receive levels, one for each character in chars array
+ * @param useRuleL1 true if rule L1 should be used
+ */
+ public static int[] resolveLevels ( int[] chars, int[] classes, int defaultLevel, int[] levels, boolean useRuleL1 ) {
+ int[] ica = classes;
+ int[] wca = copySequence ( ica );
+ int[] ea = new int [ levels.length ];
+ resolveExplicit ( wca, defaultLevel, ea );
+ resolveRuns ( wca, defaultLevel, ea, levelsFromEmbeddings ( ea, levels ) );
+ if ( useRuleL1 ) {
+ resolveSeparators ( ica, wca, defaultLevel, levels );
+ }
+ dump ( "RL: CC(" + ( ( chars != null ) ? chars.length : -1 ) + ")", chars, classes, defaultLevel, levels );
+ return levels;
+ }
+
+ private static int[] copySequence ( int[] ta ) {
+ int[] na = new int [ ta.length ];
+ System.arraycopy ( ta, 0, na, 0, na.length );
+ return na;
+ }
+
+ private static void resolveExplicit ( int[] wca, int defaultLevel, int[] ea ) {
+ int[] es = new int [ MAX_LEVELS ]; /* embeddings stack */
+ int ei = 0; /* embeddings stack index */
+ int ec = defaultLevel; /* current embedding level */
+ for ( int i = 0, n = wca.length; i < n; i++ ) {
+ int bc = wca [ i ]; /* bidi class of current char */
+ int el; /* embedding level to assign to current char */
+ switch ( bc ) {
+ case LRE: // start left-to-right embedding
+ case RLE: // start right-to-left embedding
+ case LRO: // start left-to-right override
+ case RLO: // start right-to-left override
+ {
+ int en; /* new embedding level */
+ if ( ( bc == RLE ) || ( bc == RLO ) ) {
+ en = ( ( ec & ~OVERRIDE ) + 1 ) | 1;
+ } else {
+ en = ( ( ec & ~OVERRIDE ) + 2 ) & ~1;
+ }
+ if ( en < ( MAX_LEVELS + 1 ) ) {
+ es [ ei++ ] = ec;
+ if ( ( bc == LRO ) || ( bc == RLO ) ) {
+ ec = en | OVERRIDE;
+ } else {
+ ec = en & ~OVERRIDE;
+ }
+ } else {
+ // max levels exceeded, so don't change level or override
+ }
+ el = ec;
+ break;
+ }
+ case PDF: // pop directional formatting
+ {
+ el = ec;
+ if ( ei > 0 ) {
+ ec = es [ --ei ];
+ } else {
+ // ignore isolated PDF
+ }
+ break;
+ }
+ case B: // paragraph separator
+ {
+ el = ec = defaultLevel;
+ ei = 0;
+ break;
+ }
+ default:
+ {
+ el = ec;
+ break;
+ }
+ }
+ switch ( bc ) {
+ case BN:
+ break;
+ case LRE: case RLE: case LRO: case RLO: case PDF:
+ wca [ i ] = BN;
+ break;
+ default:
+ if ( ( el & OVERRIDE ) != 0 ) {
+ wca [ i ] = directionOfLevel ( el );
+ }
+ break;
+ }
+ ea [ i ] = el;
+ }
+ }
+
+ private static int directionOfLevel ( int level ) {
+ return ( ( level & 1 ) != 0 ) ? R : L;
+ }
+
+ private static int levelOfEmbedding ( int embedding ) {
+ return embedding & ~OVERRIDE;
+ }
+
+ private static int[] levelsFromEmbeddings ( int[] ea, int[] la ) {
+ assert ea != null;
+ assert la != null;
+ assert la.length == ea.length;
+ for ( int i = 0, n = la.length; i < n; i++ ) {
+ la [ i ] = levelOfEmbedding ( ea [ i ] );
+ }
+ return la;
+ }
+
+ private static void resolveRuns ( int[] wca, int defaultLevel, int[] ea, int[] la ) {
+ if ( la.length != wca.length ) {
+ throw new IllegalArgumentException ( "levels sequence length must match classes sequence length" );
+ } else if ( la.length != ea.length ) {
+ throw new IllegalArgumentException ( "levels sequence length must match embeddings sequence length" );
+ } else {
+ for ( int i = 0, n = ea.length, lPrev = defaultLevel; i < n; ) {
+ int s = i;
+ int e = s;
+ int l = findNextNonRetainedFormattingLevel ( wca, ea, s, lPrev );
+ while ( e < n ) {
+ if ( la [ e ] != l ) {
+ if ( startsWithRetainedFormattingRun ( wca, ea, e ) ) {
+ e += getLevelRunLength ( ea, e );
+ } else {
+ break;
+ }
+ } else {
+ e++;
+ }
+ }
+ lPrev = resolveRun ( wca, defaultLevel, ea, la, s, e, l, lPrev );
+ i = e;
+ }
+ }
+ }
+
+ private static int findNextNonRetainedFormattingLevel ( int[] wca, int[] ea, int start, int lPrev ) {
+ int s = start;
+ int e = wca.length;
+ while ( s < e ) {
+ if ( startsWithRetainedFormattingRun ( wca, ea, s ) ) {
+ s += getLevelRunLength ( ea, s );
+ } else {
+ break;
+ }
+ }
+ if ( s < e ) {
+ return levelOfEmbedding ( ea [ s ] );
+ } else {
+ return lPrev;
+ }
+ }
+
+ private static int getLevelRunLength ( int[] ea, int start ) {
+ assert start < ea.length;
+ int nl = 0;
+ for ( int s = start, e = ea.length, l0 = levelOfEmbedding ( ea [ start ] ); s < e; s++ ) {
+ if ( levelOfEmbedding ( ea [ s ] ) == l0 ) {
+ nl++;
+ } else {
+ break;
+ }
+ }
+ return nl;
+ }
+
+ private static boolean startsWithRetainedFormattingRun ( int[] wca, int[] ea, int start ) {
+ int nl = getLevelRunLength ( ea, start );
+ if ( nl > 0 ) {
+ int nc = getRetainedFormattingRunLength ( wca, start );
+ return ( nc >= nl );
+ } else {
+ return false;
+ }
+ }
+
+ private static int getRetainedFormattingRunLength ( int[] wca, int start ) {
+ assert start < wca.length;
+ int nc = 0;
+ for ( int s = start, e = wca.length; s < e; s++ ) {
+ if ( wca [ s ] == BidiConstants.BN ) {
+ nc++;
+ } else {
+ break;
+ }
+ }
+ return nc;
+ }
+
+ private static int resolveRun ( int[] wca, int defaultLevel, int[] ea, int[] la, int start, int end, int level, int levelPrev ) {
+
+ // determine start of run direction
+ int sor = directionOfLevel ( max ( levelPrev, level ) );
+
+ // determine end of run direction
+ int le = -1;
+ if ( end == wca.length ) {
+ le = max ( level, defaultLevel );
+ } else {
+ for ( int i = end; i < wca.length; i++ ) {
+ if ( wca [ i ] != BidiConstants.BN ) {
+ le = max ( level, la [ i ] );
+ break;
+ }
+ }
+ if ( le < 0 ) {
+ le = max ( level, defaultLevel );
+ }
+ }
+ int eor = directionOfLevel ( le );
+
+ if (log.isDebugEnabled()) {
+ log.debug ( "BR[" + padLeft ( start, 3 ) + "," + padLeft ( end, 3 ) + "] :" + padLeft ( level, 2 ) + ": SOR(" + getClassName(sor) + "), EOR(" + getClassName(eor) + ")" );
+ }
+
+ resolveWeak ( wca, defaultLevel, ea, la, start, end, level, sor, eor );
+ resolveNeutrals ( wca, defaultLevel, ea, la, start, end, level, sor, eor );
+ resolveImplicit ( wca, defaultLevel, ea, la, start, end, level, sor, eor );
+
+ // if this run is all retained formatting, then return prior level, otherwise this run's level
+ return isRetainedFormatting ( wca, start, end ) ? levelPrev : level;
+ }
+
+ private static void resolveWeak ( int[] wca, int defaultLevel, int[] ea, int[] la, int start, int end, int level, int sor, int eor ) {
+
+ // W1 - X BN* NSM -> X BN* X
+ for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( bc == NSM ) {
+ wca [ i ] = bcPrev;
+ } else if ( bc != BN ) {
+ bcPrev = bc;
+ }
+ }
+
+ // W2 - AL ... EN -> AL ... AN
+ for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( bc == EN ) {
+ if ( bcPrev == AL ) {
+ wca [ i ] = AN;
+ }
+ } else if ( isStrong ( bc ) ) {
+ bcPrev = bc;
+ }
+ }
+
+ // W3 - AL -> R
+ for ( int i = start, n = end; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( bc == AL ) {
+ wca [ i ] = R;
+ }
+ }
+
+ // W4 - EN BN* ES BN* EN -> EN BN* EN BN* EN; XN BN* CS BN* XN -> XN BN* XN BN* XN
+ for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( bc == ES ) {
+ int bcNext = eor;
+ for ( int j = i + 1; j < n; j++ ) {
+ if ( ( bc = wca [ j ] ) != BN ) {
+ bcNext = bc;
+ break;
+ }
+ }
+ if ( ( bcPrev == EN ) && ( bcNext == EN ) ) {
+ wca [ i ] = EN;
+ }
+ } else if ( bc == CS ) {
+ int bcNext = eor;
+ for ( int j = i + 1; j < n; j++ ) {
+ if ( ( bc = wca [ j ] ) != BN ) {
+ bcNext = bc;
+ break;
+ }
+ }
+ if ( ( bcPrev == EN ) && ( bcNext == EN ) ) {
+ wca [ i ] = EN;
+ } else if ( ( bcPrev == AN ) && ( bcNext == AN ) ) {
+ wca [ i ] = AN;
+ }
+ }
+ if ( bc != BN ) {
+ bcPrev = bc;
+ }
+ }
+
+ // W5 - EN (ET|BN)* -> EN (EN|BN)*; (ET|BN)* EN -> (EN|BN)* EN
+ for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( bc == ET ) {
+ int bcNext = eor;
+ for ( int j = i + 1; j < n; j++ ) {
+ bc = wca [ j ];
+ if ( ( bc != BN ) && ( bc != ET ) ) {
+ bcNext = bc;
+ break;
+ }
+ }
+ if ( ( bcPrev == EN ) || ( bcNext == EN ) ) {
+ wca [ i ] = EN;
+ }
+ } else if ( ( bc != BN ) && ( bc != ET ) ) {
+ bcPrev = bc;
+ }
+ }
+
+ // W6 - BN* (ET|ES|CS) BN* -> ON* ON ON*
+ for ( int i = start, n = end; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( ( bc == ET ) || ( bc == ES ) || ( bc == CS ) ) {
+ wca [ i ] = ON;
+ resolveAdjacentBoundaryNeutrals ( wca, start, end, i, ON );
+ }
+ }
+
+ // W7 - L ... EN -> L ... L
+ for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( bc == EN ) {
+ if ( bcPrev == L ) {
+ wca [ i ] = L;
+ }
+ } else if ( ( bc == L ) || ( bc == R ) ) {
+ bcPrev = bc;
+ }
+ }
+
+ }
+
+ private static void resolveNeutrals ( int[] wca, int defaultLevel, int[] ea, int[] la, int start, int end, int level, int sor, int eor ) {
+
+ // N1 - (L|R) N+ (L|R) -> L L+ L | R R+ R; (AN|EN) N+ R -> (AN|EN) R+ R; R N+ (AN|EN) -> R R+ (AN|EN)
+ for ( int i = start, n = end, bcPrev = sor; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( isNeutral ( bc ) ) {
+ int bcNext = eor;
+ for ( int j = i + 1; j < n; j++ ) {
+ bc = wca [ j ];
+ if ( ( bc == L ) || ( bc == R ) ) {
+ bcNext = bc;
+ break;
+ } else if ( ( bc == AN ) || ( bc == EN ) ) {
+ bcNext = R;
+ break;
+ } else if ( isNeutral ( bc ) ) {
+ continue;
+ } else if ( isRetainedFormatting ( bc ) ) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if ( bcPrev == bcNext ) {
+ wca [ i ] = bcPrev;
+ resolveAdjacentBoundaryNeutrals ( wca, start, end, i, bcPrev );
+ }
+ } else if ( ( bc == L ) || ( bc == R ) ) {
+ bcPrev = bc;
+ } else if ( ( bc == AN ) || ( bc == EN ) ) {
+ bcPrev = R;
+ }
+ }
+
+ // N2 - N -> embedding level
+ for ( int i = start, n = end; i < n; i++ ) {
+ int bc = wca [ i ];
+ if ( isNeutral ( bc ) ) {
+ int bcEmbedding = directionOfLevel ( levelOfEmbedding ( ea [ i ] ) );
+ wca [ i ] = bcEmbedding;
+ resolveAdjacentBoundaryNeutrals ( wca, start, end, i, bcEmbedding );
+ }
+ }
+
+ }
+
+ private static void resolveAdjacentBoundaryNeutrals ( int[] wca, int start, int end, int index, int bcNew ) {
+ if ( ( index < start ) || ( index >= end ) ) {
+ throw new IllegalArgumentException();
+ } else {
+ for ( int i = index - 1; i >= start; i-- ) {
+ int bc = wca [ i ];
+ if ( bc == BN ) {
+ wca [ i ] = bcNew;
+ } else {
+ break;
+ }
+ }
+ for ( int i = index + 1; i < end; i++ ) {
+ int bc = wca [ i ];
+ if ( bc == BN ) {
+ wca [ i ] = bcNew;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ private static void resolveImplicit ( int[] wca, int defaultLevel, int[] ea, int[] la, int start, int end, int level, int sor, int eor ) {
+ for ( int i = start, n = end; i < n; i++ ) {
+ int bc = wca [ i ]; // bidi class
+ int el = la [ i ]; // embedding level
+ int ed = 0; // embedding level delta
+ if ( ( el & 1 ) == 0 ) { // even
+ if ( bc == R ) {
+ ed = 1;
+ } else if ( bc == AN ) {
+ ed = 2;
+ } else if ( bc == EN ) {
+ ed = 2;
+ }
+ } else { // odd
+ if ( bc == L ) {
+ ed = 1;
+ } else if ( bc == EN ) {
+ ed = 1;
+ } else if ( bc == AN ) {
+ ed = 1;
+ }
+ }
+ la [ i ] = el + ed;
+ }
+ }
+
+ /**
+ * Resolve separators and boundary neutral levels to account for UAX#9 3.4 L1 while taking into
+ * account retention of formatting codes (5.2).
+ * @param ica original input class array (sequence)
+ * @param wca working copy of original intput class array (sequence), as modified by prior steps
+ * @param dl default paragraph level
+ * @param la array of output levels to be adjusted, as produced by bidi algorithm
+ */
+ private static void resolveSeparators ( int[] ica, int[] wca, int dl, int[] la ) {
+ // steps (1) through (3)
+ for ( int i = 0, n = ica.length; i < n; i++ ) {
+ int ic = ica[i];
+ if ( ( ic == BidiConstants.S ) || ( ic == BidiConstants.B ) ) {
+ la[i] = dl;
+ for ( int k = i - 1; k >= 0; k-- ) {
+ int pc = ica[k];
+ if ( isRetainedFormatting ( pc ) ) {
+ continue;
+ } if ( pc == BidiConstants.WS ) {
+ la[k] = dl;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ // step (4) - consider end of input sequence to be end of line, but skip any trailing boundary neutrals and retained formatting codes
+ for ( int i = ica.length; i > 0; i-- ) {
+ int k = i - 1;
+ int ic = ica[k];
+ if ( isRetainedFormatting ( ic ) ) {
+ continue;
+ } else if ( ic == BidiConstants.WS ) {
+ la[k] = dl;
+ } else {
+ break;
+ }
+ }
+ // step (5) - per section 5.2
+ for ( int i = 0, n = ica.length; i < n; i++ ) {
+ int ic = ica[i];
+ if ( isRetainedFormatting ( ic ) ) {
+ if ( i == 0 ) {
+ la[i] = dl;
+ } else {
+ la[i] = la [ i - 1 ];
+ }
+ }
+ }
+ }
+
+ private static boolean isStrong ( int bc ) {
+ switch ( bc ) {
+ case L:
+ case R:
+ case AL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static boolean isNeutral ( int bc ) {
+ switch ( bc ) {
+ case WS:
+ case ON:
+ case S:
+ case B:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static boolean isRetainedFormatting ( int bc ) {
+ switch ( bc ) {
+ case LRE:
+ case LRO:
+ case RLE:
+ case RLO:
+ case PDF:
+ case BN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static boolean isRetainedFormatting ( int[] ca, int s, int e ) {
+ for ( int i = s; i < e; i++ ) {
+ if ( ! isRetainedFormatting ( ca[i] ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static int max ( int x, int y ) {
+ if ( x > y ) {
+ return x;
+ } else {
+ return y;
+ }
+ }
+
+ private static int[] getClasses ( int[] chars ) {
+ int[] classes = new int [ chars.length ];
+ int bc;
+ for ( int i = 0, n = chars.length; i < n; i++ ) {
+ int ch = chars [ i ];
+ if ( ch >= 0 ) {
+ bc = BidiClass.getBidiClass ( chars [ i ] );
+ } else {
+ bc = SURROGATE;
+ }
+ classes [ i ] = bc;
+ }
+ return classes;
+ }
+
+ /**
+ * Convert character sequence (a UTF-16 encoded string) to an array of unicode scalar values
+ * expressed as integers. If a valid UTF-16 surrogate pair is encountered, it is converted to
+ * two integers, the first being the equivalent unicode scalar value, and the second being
+ * negative one (-1). This special mechanism is used to track the use of surrogate pairs while
+ * working with unicode scalar values, and permits maintaining indices that apply both to the
+ * input UTF-16 and out scalar value sequences.
+ * @return a boolean indicating that content is present that triggers bidirectional processing
+ * @param cs a UTF-16 encoded character sequence
+ * @param chars an integer array to accept the converted scalar values, where the length of the
+ * array must be the same as the length of the input character sequence
+ * @throws IllegalArgumentException if the input sequence is not a valid UTF-16 string, e.g.,
+ * if it contains an isolated UTF-16 surrogate
+ */
+ private static boolean convertToScalar ( CharSequence cs, int[] chars ) throws IllegalArgumentException {
+ boolean triggered = false;
+ if ( chars.length != cs.length() ) {
+ throw new IllegalArgumentException ( "characters array length must match input sequence length" );
+ }
+ for ( int i = 0, n = chars.length; i < n; ) {
+ int chIn = cs.charAt ( i );
+ int chOut;
+ if ( chIn < 0xD800 ) {
+ chOut = chIn;
+ } else if ( chIn < 0xDC00 ) {
+ int chHi = chIn;
+ int chLo;
+ if ( ( i + 1 ) < n ) {
+ chLo = cs.charAt ( i + 1 );
+ if ( ( chLo >= 0xDC00 ) && ( chLo <= 0xDFFF ) ) {
+ chOut = convertToScalar ( chHi, chLo );
+ } else {
+ throw new IllegalArgumentException ( "isolated high surrogate" );
+ }
+ } else {
+ throw new IllegalArgumentException ( "truncated surrogate pair" );
+ }
+ } else if ( chIn < 0xE000 ) {
+ throw new IllegalArgumentException ( "isolated low surrogate" );
+ } else {
+ chOut = chIn;
+ }
+ if ( ! triggered && triggersBidi ( chOut ) ) {
+ triggered = true;
+ }
+ if ( ( chOut & 0xFF0000 ) == 0 ) {
+ chars [ i++ ] = chOut;
+ } else {
+ chars [ i++ ] = chOut;
+ chars [ i++ ] = -1;
+ }
+ }
+ return triggered;
+ }
+
+ /**
+ * Convert UTF-16 surrogate pair to unicode scalar valuee.
+ * @return a unicode scalar value
+ * @param chHi high (most significant or first) surrogate
+ * @param chLo low (least significant or second) surrogate
+ * @throws IllegalArgumentException if one of the input surrogates is not valid
+ */
+ private static int convertToScalar ( int chHi, int chLo ) {
+ if ( ( chHi < 0xD800 ) || ( chHi > 0xDBFF ) ) {
+ throw new IllegalArgumentException ( "bad high surrogate" );
+ } else if ( ( chLo < 0xDC00 ) || ( chLo > 0xDFFF ) ) {
+ throw new IllegalArgumentException ( "bad low surrogate" );
+ } else {
+ return ( ( ( chHi & 0x03FF ) << 10 ) | ( chLo & 0x03FF ) ) + 0x10000;
+ }
+ }
+
+ /**
+ * Determine of character CH triggers bidirectional processing. Bidirectional
+ * processing is deemed triggerable if CH is a strong right-to-left character,
+ * an arabic letter or number, or is a right-to-left embedding or override
+ * character.
+ * @return true if character triggers bidirectional processing
+ * @param ch a unicode scalar value
+ */
+ private static boolean triggersBidi ( int ch ) {
+ switch ( BidiClass.getBidiClass ( ch ) ) {
+ case R:
+ case AL:
+ case AN:
+ case RLE:
+ case RLO:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static void dump ( String header, int[] chars, int[] classes, int defaultLevel, int[] levels ) {
+ log.debug ( header );
+ log.debug ( "BD: default level(" + defaultLevel + ")" );
+ StringBuffer sb = new StringBuffer();
+ if ( chars != null ) {
+ for ( int i = 0, n = chars.length; i < n; i++ ) {
+ int ch = chars [ i ];
+ sb.setLength(0);
+ if ( ( ch > 0x20 ) && ( ch < 0x7F ) ) {
+ sb.append ( (char) ch );
+ } else {
+ sb.append ( CharUtilities.charToNCRef ( ch ) );
+ }
+ for ( int k = sb.length(); k < 12; k++ ) {
+ sb.append ( ' ' );
+ }
+ sb.append ( ": " + padRight ( getClassName ( classes[i] ), 4 ) + " " + levels[i] );
+ log.debug ( sb );
+ }
+ } else {
+ for ( int i = 0, n = classes.length; i < n; i++ ) {
+ sb.setLength(0);
+ for ( int k = sb.length(); k < 12; k++ ) {
+ sb.append ( ' ' );
+ }
+ sb.append ( ": " + padRight ( getClassName ( classes[i] ), 4 ) + " " + levels[i] );
+ log.debug ( sb );
+ }
+ }
+ }
+
+ private static String getClassName ( int bc ) {
+ switch ( bc ) {
+ case L: // left-to-right
+ return "L";
+ case LRE: // left-to-right embedding
+ return "LRE";
+ case LRO: // left-to-right override
+ return "LRO";
+ case R: // right-to-left
+ return "R";
+ case AL: // right-to-left arabic
+ return "AL";
+ case RLE: // right-to-left embedding
+ return "RLE";
+ case RLO: // right-to-left override
+ return "RLO";
+ case PDF: // pop directional formatting
+ return "PDF";
+ case EN: // european number
+ return "EN";
+ case ES: // european number separator
+ return "ES";
+ case ET: // european number terminator
+ return "ET";
+ case AN: // arabic number
+ return "AN";
+ case CS: // common number separator
+ return "CS";
+ case NSM: // non-spacing mark
+ return "NSM";
+ case BN: // boundary neutral
+ return "BN";
+ case B: // paragraph separator
+ return "B";
+ case S: // segment separator
+ return "S";
+ case WS: // whitespace
+ return "WS";
+ case ON: // other neutrals
+ return "ON";
+ case SURROGATE: // placeholder for low surrogate
+ return "SUR";
+ default:
+ return "?";
+ }
+ }
+
+ private static String padLeft ( int n, int width ) {
+ return padLeft ( Integer.toString ( n ), width );
+ }
+
+ private static String padLeft ( String s, int width ) {
+ StringBuffer sb = new StringBuffer();
+ for ( int i = s.length(); i < width; i++ ) {
+ sb.append(' ');
+ }
+ sb.append ( s );
+ return sb.toString();
+ }
+
+ /* not used yet
+ private static String padRight ( int n, int width ) {
+ return padRight ( Integer.toString ( n ), width );
+ }
+ */
+
+ private static String padRight ( String s, int width ) {
+ StringBuffer sb = new StringBuffer ( s );
+ for ( int i = sb.length(); i < width; i++ ) {
+ sb.append(' ');
+ }
+ return sb.toString();
+ }
+
+}
Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/AdvancedTypographicTableFormatException.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/AdvancedTypographicTableFormatException.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/AdvancedTypographicTableFormatException.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/AdvancedTypographicTableFormatException.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.complexscripts.fonts;
+
+/**
+ * Exception thrown when attempting to decode a truetype font file and a format
+ * constraint is violated.
+ * @author Glenn Adams
+ */
+public class AdvancedTypographicTableFormatException extends RuntimeException {
+ /**
+ * Instantiate ATT format exception.
+ */
+ public AdvancedTypographicTableFormatException() {
+ super();
+ }
+ /**
+ * Instantiate ATT format exception.
+ * @param message a message string
+ */
+ public AdvancedTypographicTableFormatException(String message) {
+ super(message);
+ }
+ /**
+ * Instantiate ATT format exception.
+ * @param message a message string
+ * @param cause a <code>Throwable</code> that caused this exception
+ */
+ public AdvancedTypographicTableFormatException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/GlyphClassMapping.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/GlyphClassMapping.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/GlyphClassMapping.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/GlyphClassMapping.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.complexscripts.fonts;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * The <code>GlyphClassMapping</code> interface provides glyph identifier to class
+ * index mapping support.
+ * @author Glenn Adams
+ */
+public interface GlyphClassMapping {
+
+ /**
+ * Obtain size of class table, i.e., ciMax + 1, where ciMax is the maximum
+ * class index.
+ * @param set for coverage set based class mappings, indicates set index, otherwise ignored
+ * @return size of class table
+ */
+ int getClassSize ( int set );
+
+ /**
+ * Map glyph identifier (code) to coverge index. Returns -1 if glyph identifier is not in the domain of
+ * the class table.
+ * @param gid glyph identifier (code)
+ * @param set for coverage set based class mappings, indicates set index, otherwise ignored
+ * @return non-negative glyph class index or -1 if glyph identifiers is not mapped by table
+ */
+ int getClassIndex ( int gid, int set );
+
+}
Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java?rev=1293736&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java Sun Feb 26 02:29:01 2012
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.complexscripts.fonts;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Iterator;
+
+// CSOFF: LineLengthCheck
+// CSOFF: NoWhitespaceAfterCheck
+
+/**
+ * Base class implementation of glyph class table.
+ * @author Glenn Adams
+ */
+public final class GlyphClassTable extends GlyphMappingTable implements GlyphClassMapping {
+
+ /** empty mapping table */
+ public static final int GLYPH_CLASS_TYPE_EMPTY = GLYPH_MAPPING_TYPE_EMPTY;
+
+ /** mapped mapping table */
+ public static final int GLYPH_CLASS_TYPE_MAPPED = GLYPH_MAPPING_TYPE_MAPPED;
+
+ /** range based mapping table */
+ public static final int GLYPH_CLASS_TYPE_RANGE = GLYPH_MAPPING_TYPE_RANGE;
+
+ /** empty mapping table */
+ public static final int GLYPH_CLASS_TYPE_COVERAGE_SET = 3;
+
+ private GlyphClassMapping cm;
+
+ private GlyphClassTable ( GlyphClassMapping cm ) {
+ assert cm != null;
+ assert cm instanceof GlyphMappingTable;
+ this.cm = cm;
+ }
+
+ /** {@inheritDoc} */
+ public int getType() {
+ return ( (GlyphMappingTable) cm ) .getType();
+ }
+
+ /** {@inheritDoc} */
+ public List getEntries() {
+ return ( (GlyphMappingTable) cm ) .getEntries();
+ }
+
+ /** {@inheritDoc} */
+ public int getClassSize ( int set ) {
+ return cm.getClassSize ( set );
+ }
+
+ /** {@inheritDoc} */
+ public int getClassIndex ( int gid, int set ) {
+ return cm.getClassIndex ( gid, set );
+ }
+
+ /**
+ * Create glyph class table.
+ * @param entries list of mapped or ranged class entries, or null or empty list
+ * @return a new covera table instance
+ */
+ public static GlyphClassTable createClassTable ( List entries ) {
+ GlyphClassMapping cm;
+ if ( ( entries == null ) || ( entries.size() == 0 ) ) {
+ cm = new EmptyClassTable ( entries );
+ } else if ( isMappedClass ( entries ) ) {
+ cm = new MappedClassTable ( entries );
+ } else if ( isRangeClass ( entries ) ) {
+ cm = new RangeClassTable ( entries );
+ } else if ( isCoverageSetClass ( entries ) ) {
+ cm = new CoverageSetClassTable ( entries );
+ } else {
+ cm = null;
+ }
+ assert cm != null : "unknown class type";
+ return new GlyphClassTable ( cm );
+ }
+
+ private static boolean isMappedClass ( List entries ) {
+ if ( ( entries == null ) || ( entries.size() == 0 ) ) {
+ return false;
+ } else {
+ for ( Iterator it = entries.iterator(); it.hasNext();) {
+ Object o = it.next();
+ if ( ! ( o instanceof Integer ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private static boolean isRangeClass ( List entries ) {
+ if ( ( entries == null ) || ( entries.size() == 0 ) ) {
+ return false;
+ } else {
+ for ( Iterator it = entries.iterator(); it.hasNext();) {
+ Object o = it.next();
+ if ( ! ( o instanceof MappingRange ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private static boolean isCoverageSetClass ( List entries ) {
+ if ( ( entries == null ) || ( entries.size() == 0 ) ) {
+ return false;
+ } else {
+ for ( Iterator it = entries.iterator(); it.hasNext();) {
+ Object o = it.next();
+ if ( ! ( o instanceof GlyphCoverageTable ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private static class EmptyClassTable extends GlyphMappingTable.EmptyMappingTable implements GlyphClassMapping {
+ public EmptyClassTable ( List entries ) {
+ super ( entries );
+ }
+ /** {@inheritDoc} */
+ public int getClassSize ( int set ) {
+ return 0;
+ }
+ /** {@inheritDoc} */
+ public int getClassIndex ( int gid, int set ) {
+ return -1;
+ }
+ }
+
+ private static class MappedClassTable extends GlyphMappingTable.MappedMappingTable implements GlyphClassMapping {
+ private int firstGlyph;
+ private int[] gca;
+ private int gcMax = -1;
+ public MappedClassTable ( List entries ) {
+ populate ( entries );
+ }
+ /** {@inheritDoc} */
+ public List getEntries() {
+ List entries = new java.util.ArrayList();
+ entries.add ( Integer.valueOf ( firstGlyph ) );
+ if ( gca != null ) {
+ for ( int i = 0, n = gca.length; i < n; i++ ) {
+ entries.add ( Integer.valueOf ( gca [ i ] ) );
+ }
+ }
+ return entries;
+ }
+ /** {@inheritDoc} */
+ public int getMappingSize() {
+ return gcMax + 1;
+ }
+ /** {@inheritDoc} */
+ public int getMappedIndex ( int gid ) {
+ int i = gid - firstGlyph;
+ if ( ( i >= 0 ) && ( i < gca.length ) ) {
+ return gca [ i ];
+ } else {
+ return -1;
+ }
+ }
+ /** {@inheritDoc} */
+ public int getClassSize ( int set ) {
+ return getMappingSize();
+ }
+ /** {@inheritDoc} */
+ public int getClassIndex ( int gid, int set ) {
+ return getMappedIndex ( gid );
+ }
+ private void populate ( List entries ) {
+ // obtain entries iterator
+ Iterator it = entries.iterator();
+ // extract first glyph
+ int firstGlyph = 0;
+ if ( it.hasNext() ) {
+ Object o = it.next();
+ if ( o instanceof Integer ) {
+ firstGlyph = ( (Integer) o ) . intValue();
+ } else {
+ throw new AdvancedTypographicTableFormatException ( "illegal entry, first entry must be Integer denoting first glyph value, but is: " + o );
+ }
+ }
+ // extract glyph class array
+ int i = 0, n = entries.size() - 1, gcMax = -1;
+ int[] gca = new int [ n ];
+ while ( it.hasNext() ) {
+ Object o = it.next();
+ if ( o instanceof Integer ) {
+ int gc = ( (Integer) o ) . intValue();
+ gca [ i++ ] = gc;
+ if ( gc > gcMax ) {
+ gcMax = gc;
+ }
+ } else {
+ throw new AdvancedTypographicTableFormatException ( "illegal mapping entry, must be Integer: " + o );
+ }
+ }
+ assert i == n;
+ assert this.gca == null;
+ this.firstGlyph = firstGlyph;
+ this.gca = gca;
+ this.gcMax = gcMax;
+ }
+ /** {@inheritDoc} */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("{ firstGlyph = " + firstGlyph + ", classes = {");
+ for ( int i = 0, n = gca.length; i < n; i++ ) {
+ if ( i > 0 ) {
+ sb.append(',');
+ }
+ sb.append ( Integer.toString ( gca [ i ] ) );
+ }
+ sb.append("} }");
+ return sb.toString();
+ }
+ }
+
+ private static class RangeClassTable extends GlyphMappingTable.RangeMappingTable implements GlyphClassMapping {
+ public RangeClassTable ( List entries ) {
+ super ( entries );
+ }
+ /** {@inheritDoc} */
+ public int getMappedIndex ( int gid, int s, int m ) {
+ return m;
+ }
+ /** {@inheritDoc} */
+ public int getClassSize ( int set ) {
+ return getMappingSize();
+ }
+ /** {@inheritDoc} */
+ public int getClassIndex ( int gid, int set ) {
+ return getMappedIndex ( gid );
+ }
+ }
+
+ private static class CoverageSetClassTable extends GlyphMappingTable.EmptyMappingTable implements GlyphClassMapping {
+ public CoverageSetClassTable ( List entries ) {
+ throw new UnsupportedOperationException ( "coverage set class table not yet supported" );
+ }
+ /** {@inheritDoc} */
+ public int getType() {
+ return GLYPH_CLASS_TYPE_COVERAGE_SET;
+ }
+ /** {@inheritDoc} */
+ public int getClassSize ( int set ) {
+ return 0;
+ }
+ /** {@inheritDoc} */
+ public int getClassIndex ( int gid, int set ) {
+ return -1;
+ }
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org