You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-commits@xmlgraphics.apache.org by de...@apache.org on 2006/03/05 03:50:36 UTC

svn commit: r383270 - in /xmlgraphics/batik/branches/svg11: lib/ sources/org/apache/batik/css/engine/sac/ sources/org/apache/batik/gvt/text/

Author: deweese
Date: Sat Mar  4 18:50:35 2006
New Revision: 383270

URL: http://svn.apache.org/viewcvs?rev=383270&view=rev
Log:
1) Updated LICENSE.js.txt to reference license for tree table classes.
2) 38785 - Strange equals method in sac selectors. Thanks dvholten!
3) 38497 - enhancements to ArabicTextHandler (actually includes
           several additional enhancements). Thanks dvholten!

Modified:
    xmlgraphics/batik/branches/svg11/lib/LICENSE.js.txt
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractAttributeCondition.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractCombinatorCondition.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractDescendantSelector.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractElementSelector.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractSiblingSelector.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSAttributeCondition.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSConditionalSelector.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSLangCondition.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSPseudoClassCondition.java
    xmlgraphics/batik/branches/svg11/sources/org/apache/batik/gvt/text/ArabicTextHandler.java

Modified: xmlgraphics/batik/branches/svg11/lib/LICENSE.js.txt
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/lib/LICENSE.js.txt?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/lib/LICENSE.js.txt (original)
+++ xmlgraphics/batik/branches/svg11/lib/LICENSE.js.txt Sat Mar  4 18:50:35 2006
@@ -8,6 +8,57 @@
 Rhino is licensed under the NPL (Netscape Public License) which 
 is duplicated below.
 
+The Rhino jar also includes four classes:
+  org.mozilla.javascript.tools.debugger.downloaded.AbstractCellEditor.java
+  org.mozilla.javascript.tools.debugger.downloaded.JTreeTable.java
+  org.mozilla.javascript.tools.debugger.downloaded.TreeTableModel.java
+  org.mozilla.javascript.tools.debugger.downloaded.TreeTableModelAdapter.java
+Which comes from:
+  http://java.sun.com/products/jfc/tsc/articles/treetable2
+
+Under the following license:
+
+Code sample
+License
+Copyright 1994-2006 Sun Microsystems, Inc. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ 
+
+    * Redistribution of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistribution 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.
+
+ 
+Neither the name of Sun Microsystems, Inc. or the names of
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+ 
+This software is provided "AS IS," without a warranty of any kind. ALL
+EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ 
+You acknowledge that this software is not designed, licensed or
+intended for use in the design, construction, operation or maintenance
+of any nuclear facility.
+
+
+
 ==============================================================================
 
 AMENDMENTS

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractAttributeCondition.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractAttributeCondition.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractAttributeCondition.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractAttributeCondition.java Sat Mar  4 18:50:35 2006
@@ -47,11 +47,11 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (obj == null || !(obj.getClass() != getClass())) {
-	    return false;
-	}
-	AbstractAttributeCondition c = (AbstractAttributeCondition)obj;
-	return c.value.equals(value);
+        if (obj == null || (obj.getClass() != getClass())) {
+            return false;
+        }
+        AbstractAttributeCondition c = (AbstractAttributeCondition)obj;
+        return c.value.equals(value);
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractCombinatorCondition.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractCombinatorCondition.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractCombinatorCondition.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractCombinatorCondition.java Sat Mar  4 18:50:35 2006
@@ -54,12 +54,12 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (obj == null || !(obj.getClass() != getClass())) {
-	    return false;
-	}
-	AbstractCombinatorCondition c = (AbstractCombinatorCondition)obj;
-	return c.firstCondition.equals(firstCondition) &&
-	       c.secondCondition.equals(secondCondition);
+        if (obj == null || (obj.getClass() != getClass())) {
+            return false;
+        }
+        AbstractCombinatorCondition c = (AbstractCombinatorCondition)obj;
+        return (c.firstCondition.equals(firstCondition) &&
+                c.secondCondition.equals(secondCondition));
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractDescendantSelector.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractDescendantSelector.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractDescendantSelector.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractDescendantSelector.java Sat Mar  4 18:50:35 2006
@@ -56,11 +56,11 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (obj == null || !(obj.getClass() != getClass())) {
-	    return false;
-	}
-	AbstractDescendantSelector s = (AbstractDescendantSelector)obj;
-	return s.simpleSelector.equals(simpleSelector);
+        if (obj == null || (obj.getClass() != getClass())) {
+            return false;
+        }
+        AbstractDescendantSelector s = (AbstractDescendantSelector)obj;
+        return s.simpleSelector.equals(simpleSelector);
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractElementSelector.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractElementSelector.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractElementSelector.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractElementSelector.java Sat Mar  4 18:50:35 2006
@@ -46,8 +46,8 @@
      * Creates a new ElementSelector object.
      */
     protected AbstractElementSelector(String uri, String name) {
-	namespaceURI = uri;
-	localName    = name;
+        namespaceURI = uri;
+        localName    = name;
     }
 
     /**
@@ -55,12 +55,12 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (obj == null || !(obj.getClass() != getClass())) {
-	    return false;
-	}
-	AbstractElementSelector s = (AbstractElementSelector)obj;
-	return s.namespaceURI.equals(namespaceURI) &&
-	       s.localName.equals(localName);
+        if (obj == null || (obj.getClass() != getClass())) {
+            return false;
+        }
+        AbstractElementSelector s = (AbstractElementSelector)obj;
+        return (s.namespaceURI.equals(namespaceURI) &&
+                s.localName.equals(localName));
     }
 
     /**
@@ -68,7 +68,7 @@
      * org.w3c.css.sac.ElementSelector#getNamespaceURI()}.
      */
     public String getNamespaceURI() {
-	return namespaceURI;
+        return namespaceURI;
     }
 
     /**
@@ -76,7 +76,7 @@
      * org.w3c.css.sac.ElementSelector#getLocalName()}.
      */
     public String getLocalName() {
-	return localName;
+        return localName;
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractSiblingSelector.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractSiblingSelector.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractSiblingSelector.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/AbstractSiblingSelector.java Sat Mar  4 18:50:35 2006
@@ -70,11 +70,11 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (obj == null || !(obj.getClass() != getClass())) {
-	    return false;
-	}
-	AbstractSiblingSelector s = (AbstractSiblingSelector)obj;
-	return s.simpleSelector.equals(simpleSelector);
+        if (obj == null || (obj.getClass() != getClass())) {
+            return false;
+        }
+        AbstractSiblingSelector s = (AbstractSiblingSelector)obj;
+        return s.simpleSelector.equals(simpleSelector);
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSAttributeCondition.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSAttributeCondition.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSAttributeCondition.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSAttributeCondition.java Sat Mar  4 18:50:35 2006
@@ -62,13 +62,13 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (!super.equals(obj)) {
-	    return false;
-	}
-	CSSAttributeCondition c = (CSSAttributeCondition)obj;
-	return c.namespaceURI.equals(namespaceURI) &&
-	       c.localName.equals(localName) &&
-	       c.specified == specified;
+        if (!super.equals(obj)) {
+            return false;
+        }
+        CSSAttributeCondition c = (CSSAttributeCondition)obj;
+        return (c.namespaceURI.equals(namespaceURI) &&
+                c.localName.equals(localName)       &&
+                c.specified == specified);
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSConditionalSelector.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSConditionalSelector.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSConditionalSelector.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSConditionalSelector.java Sat Mar  4 18:50:35 2006
@@ -58,12 +58,12 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (obj == null || !(obj.getClass() != getClass())) {
-	    return false;
-	}
-	CSSConditionalSelector s = (CSSConditionalSelector)obj;
-	return s.simpleSelector.equals(simpleSelector) &&
-	       s.condition.equals(condition);
+        if (obj == null || (obj.getClass() != getClass())) {
+            return false;
+        }
+        CSSConditionalSelector s = (CSSConditionalSelector)obj;
+        return (s.simpleSelector.equals(simpleSelector) &&
+                s.condition.equals(condition));
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSLangCondition.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSLangCondition.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSLangCondition.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSLangCondition.java Sat Mar  4 18:50:35 2006
@@ -50,11 +50,11 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (obj == null || !(obj.getClass() != getClass())) {
-	    return false;
-	}
-	CSSLangCondition c = (CSSLangCondition)obj;
-	return c.lang.equals(lang);
+        if (obj == null || (obj.getClass() != getClass())) {
+            return false;
+        }
+        CSSLangCondition c = (CSSLangCondition)obj;
+        return c.lang.equals(lang);
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSPseudoClassCondition.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSPseudoClassCondition.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSPseudoClassCondition.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/css/engine/sac/CSSPseudoClassCondition.java Sat Mar  4 18:50:35 2006
@@ -48,11 +48,11 @@
      * @param obj the reference object with which to compare.
      */
     public boolean equals(Object obj) {
-	if (!super.equals(obj)) {
-	    return false;
-	}
-	CSSPseudoClassCondition c = (CSSPseudoClassCondition)obj;
-	return c.namespaceURI.equals(namespaceURI);
+        if (!super.equals(obj)) {
+            return false;
+        }
+        CSSPseudoClassCondition c = (CSSPseudoClassCondition)obj;
+        return c.namespaceURI.equals(namespaceURI);
     }
 
     /**

Modified: xmlgraphics/batik/branches/svg11/sources/org/apache/batik/gvt/text/ArabicTextHandler.java
URL: http://svn.apache.org/viewcvs/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/gvt/text/ArabicTextHandler.java?rev=383270&r1=383269&r2=383270&view=diff
==============================================================================
--- xmlgraphics/batik/branches/svg11/sources/org/apache/batik/gvt/text/ArabicTextHandler.java (original)
+++ xmlgraphics/batik/branches/svg11/sources/org/apache/batik/gvt/text/ArabicTextHandler.java Sat Mar  4 18:50:35 2006
@@ -1,6 +1,6 @@
 /*
 
-   Copyright 2001,2003  The Apache Software Foundation 
+   Copyright 2001,2003,2006  The Apache Software Foundation
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -53,12 +53,19 @@
         GVTAttributedCharacterIterator.TextAttribute.ARABIC_MEDIAL;
 
     /**
+     * private ctor prevents unnecessary instantiation of this class.
+     */
+    private ArabicTextHandler() {
+
+    }
+
+    /*
      * If the AttributedString contains any arabic chars, assigns an
      * arabic form attribute, i.e. initial|medial|terminal|isolated,
      * to each arabic char.
      *
      * @param as The string to attach the arabic form attributes to.
-     * @return An attributed string with arabic form attributes.  
+     * @return An attributed string with arabic form attributes.
      */
     public static AttributedString assignArabicForms(AttributedString as) {
 
@@ -73,47 +80,54 @@
         // reorder that part of the string so that it becomes tAB
         // construct the reordered ACI
         AttributedCharacterIterator aci = as.getIterator();
-        boolean didSomeReordering = false;
         int numChars = aci.getEndIndex() - aci.getBeginIndex();
-        int charOrder[] = new int[numChars];
-        for (int i = 0; i < numChars; i++) {
-            charOrder[i] = i + aci.getBeginIndex();
-        }
-        for (int i = 1; i < numChars-1; i++) {
-            char c = aci.setIndex(aci.getBeginIndex() + i);
-            if (arabicCharTransparent(c)) {
-                char prevChar = aci.setIndex(aci.getBeginIndex() + i-1);
-                char nextChar = aci.setIndex(aci.getBeginIndex() + i+1);
-                if (charMap.get("" + prevChar + nextChar) != null) {
-                    // found a ligature, separated by a transparent char
-                    didSomeReordering = true;
-                    int temp = charOrder[i];
-                    charOrder[i] = charOrder[i-1];
-                    charOrder[i-1] = temp;
+        int charOrder[] = null;
+        if (numChars >= 3) {
+            char prevChar = aci.first();
+            char c        = aci.next(); 
+            int  i        = 1;
+            for (char nextChar = aci.next(); 
+                 nextChar != AttributedCharacterIterator.DONE; 
+                 prevChar = c, c = nextChar, nextChar = aci.next(), i++) {
+                if (arabicCharTransparent(c)) {
+                    if (hasSubstitute(prevChar, nextChar)) {
+                        // found a ligature, separated by a transparent char
+                        if (charOrder == null) {
+                            charOrder = new int[numChars];
+                            for (int j = 0; j < numChars; i++) {
+                                charOrder[i] = j + aci.getBeginIndex();
+                            }
+                        }
+                        int temp = charOrder[i];
+                        charOrder[i] = charOrder[i-1];
+                        charOrder[i-1] = temp;
+                    }
                 }
             }
         }
 
-        if (didSomeReordering) {
+        if (charOrder != null) {
             // need to reconstruct the reordered attributed string
-            String reorderedString = "";
+            StringBuffer reorderedString = new StringBuffer(numChars);
             char c;
             for (int i = 0; i < numChars; i++) {
                 c = aci.setIndex(charOrder[i]);
-                reorderedString += c;
+                reorderedString.append( c );
             }
-            AttributedString reorderedAS = new AttributedString(reorderedString);
+            AttributedString reorderedAS;
+            reorderedAS = new AttributedString(reorderedString.toString());
+
             for (int i = 0; i < numChars; i++) {
                 aci.setIndex(charOrder[i]);
                 Map attributes = aci.getAttributes();
                 reorderedAS.addAttributes(attributes, i, i+1);
             }
-            if ((charOrder[0] == (aci.getBeginIndex()+1)) && 
-                (charOrder[1] ==  aci.getBeginIndex())) {
-                // have swapped the first 2 chars, may need to move
+
+            if (charOrder[0] != aci.getBeginIndex()) {
+                // have swapped the first char. Need to move
                 // any position attributes
 
-                aci.first();
+                aci.setIndex(charOrder[0]);
                 Float x = (Float) aci.getAttribute(
                     GVTAttributedCharacterIterator.TextAttribute.X);
                 Float y = (Float) aci.getAttribute(
@@ -122,17 +136,17 @@
                 if (x != null && !x.isNaN()) {
                     reorderedAS.addAttribute
                         (GVTAttributedCharacterIterator.TextAttribute.X,
-                         new Float(Float.NaN), 1, 2);
+                         new Float(Float.NaN), charOrder[0], charOrder[0]+1);
                     reorderedAS.addAttribute
-                        (GVTAttributedCharacterIterator.TextAttribute.X, 
+                        (GVTAttributedCharacterIterator.TextAttribute.X,
                          x, 0, 1);
                 }
                 if (y != null && !y.isNaN()) {
                     reorderedAS.addAttribute
                         (GVTAttributedCharacterIterator.TextAttribute.Y,
-                         new Float(Float.NaN), 1, 2);
+                         new Float(Float.NaN), charOrder[0], charOrder[0]+1);
                     reorderedAS.addAttribute
-                        (GVTAttributedCharacterIterator.TextAttribute.Y, 
+                        (GVTAttributedCharacterIterator.TextAttribute.Y,
                          y, 0, 1);
                 }
             }
@@ -140,15 +154,23 @@
         }
 
         // first assign none to all arabic letters
-        int c;
         aci = as.getIterator();
-        for (int i = aci.getBeginIndex(); i < aci.getEndIndex(); i++) {
-            c = aci.setIndex(i);
+        int runStart = -1;
+        int idx = aci.getBeginIndex();
+        for (int c = aci.first(); 
+             c != AttributedCharacterIterator.DONE;
+             c = aci.next(), idx++) {
             if ((c >= arabicStart) && (c <= arabicEnd)) {
-                as.addAttribute(ARABIC_FORM, ARABIC_NONE,i, i+1);
+                if (runStart == -1)
+                    runStart = idx;
+            } else if (runStart != -1) {
+                as.addAttribute(ARABIC_FORM, ARABIC_NONE, runStart, idx);
+                runStart = -1;
             }
         }
-        
+        if (runStart != -1) 
+            as.addAttribute(ARABIC_FORM, ARABIC_NONE, runStart, idx);
+
         aci = as.getIterator();  // Make sure ACI tracks ARABIC_FORM
         int end   = aci.getBeginIndex();
 
@@ -162,16 +184,16 @@
 
             if (currentForm == null) {
                 // only modify if the chars in the run are arabic
-                continue; 
+                continue;
             }
-            
+
 
             int currentIndex = start;
             int prevCharIndex = start-1;
             while (currentIndex < end) {
                 char prevChar = currentChar;
                 currentChar=  aci.setIndex(currentIndex);
-                while (arabicCharTransparent(currentChar) && 
+                while (arabicCharTransparent(currentChar) &&
                        (currentIndex < end)) {
                     currentIndex++;
                     currentChar = aci.setIndex(currentIndex);
@@ -188,7 +210,7 @@
                         && arabicCharShapesLeft(currentChar)) {
                         // Increment the form of the previous char
                         prevForm = new Integer(prevForm.intValue()+1);
-                        as.addAttribute(ARABIC_FORM, prevForm, 
+                        as.addAttribute(ARABIC_FORM, prevForm,
                                         prevCharIndex, prevCharIndex+1);
 
                         // and set the form of the current char to INITIAL
@@ -245,9 +267,9 @@
      * @return True if at least one char is arabic, false otherwise.
      */
     public static boolean containsArabic(AttributedCharacterIterator aci) {
-        char c;
-        for (int i = aci.getBeginIndex(); i < aci.getEndIndex(); i++) {
-            c = aci.setIndex(i);
+        for (char c = aci.first(); 
+             c != AttributedCharacterIterator.DONE;
+             c = aci.next()) {
             if (arabicChar(c)) {
                 return true;
             }
@@ -263,7 +285,7 @@
      */
     public static boolean arabicCharTransparent(char c) {
         int charVal = c;
-        if ((charVal  < 0x064B) || (charVal > 0x06ED)) 
+        if ((charVal  < 0x064B) || (charVal > 0x06ED))
             return false;
 
         if ((charVal <= 0x0655)                      ||
@@ -353,6 +375,17 @@
         return arabicCharShapesRight(c);
     }
 
+    public static boolean hasSubstitute(char ch1, char ch2) {
+        if ((ch1 < doubleCharFirst) || (ch1 > doubleCharLast)) return false;
+
+        int [][]remaps = doubleCharRemappings[ch1-doubleCharFirst];
+        if (remaps == null) return false;
+        for (int i=0; i<remaps.length; i++) {
+            if (remaps[i][0] == ch2)
+                return true;
+        }
+        return false;
+    }
 
     /**
      * Will try and find a substitute character of the specified form.
@@ -365,16 +398,29 @@
      * @return The unicode value of the substutute char, or -1 if no susbtitue
      * exists.
      */
-    public static int getSubstituteChar(String unicode, int form) {
+    public static int getSubstituteChar(char ch1, char ch2, int form) {
         if (form == 0) return -1;
-        int chars[] = (int[])charMap.get(unicode);
-        if (chars == null) return -1;
+        if ((ch1 < doubleCharFirst) || (ch1 > doubleCharLast)) return -1;
 
-        if (chars[form-1] > 0)
-            return chars[form-1];
+        int [][]remaps = doubleCharRemappings[ch1-doubleCharFirst];
+        if (remaps == null) return -1;
+        for (int i=0; i<remaps.length; i++) {
+            if (remaps[i][0] == ch2)
+                return remaps[i][form];
+        }
         return -1;
     }
 
+    public static int getSubstituteChar(char ch, int form) {
+        if (form == 0) return -1;
+        if ((ch < singleCharFirst) || (ch > singleCharLast)) return -1;
+
+        int chars[] = singleCharRemappings[ch-singleCharFirst];
+        if (chars == null) return -1;
+
+        return chars[form-1];
+    }
+
     /**
      * Where possible substitues plain arabic glyphs with their shaped
      * forms.  This is needed when the arabic text is rendered using
@@ -388,66 +434,68 @@
      * @return A String containing the shaped versions of the arabic characters
      */
     public static String createSubstituteString(AttributedCharacterIterator aci) {
-
-        String substString = "";
-        for (int i = aci.getBeginIndex(); i < aci.getEndIndex(); i++) {
+        int start = aci.getBeginIndex();
+        int end   = aci.getEndIndex();
+        int numChar = end-start;
+        StringBuffer substString = new StringBuffer(numChar);
+        for (int i=start; i< end; i++) {
             char c = aci.setIndex(i);
-            if (arabicChar(c)) {
-                Integer form = (Integer)aci.getAttribute(ARABIC_FORM);
-                // see if the c is the start of a ligature
-                if (charStartsLigature(c) && i < aci.getEndIndex()) {
-                    char nextChar = aci.setIndex(i+1);
-                    Integer nextForm = (Integer)aci.getAttribute(ARABIC_FORM);
-                    if (form != null && nextForm != null) {
-                        if (form.equals(ARABIC_TERMINAL)
-                            && nextForm.equals(ARABIC_INITIAL)) {
-                            // look for an isolated ligature
-                            int substChar = ArabicTextHandler.getSubstituteChar
-                                ("" + c + nextChar,ARABIC_ISOLATED.intValue());
-                            if (substChar > -1) {
-                                substString += (char)substChar;
-                                i++;
-                                continue;
-                            }
-                        } else if (form.equals(ARABIC_TERMINAL)) {
-                            // look for a terminal ligature
-                            int substChar = ArabicTextHandler.getSubstituteChar
-                                ("" + c + nextChar,ARABIC_TERMINAL.intValue());
-                            if (substChar > -1) {
-                                substString += (char)substChar;
-                                i++;
-                                continue;
-                            }
-                        } else if (form.equals(ARABIC_MEDIAL)
-                                && nextForm.equals(ARABIC_MEDIAL)) {
-                            // look for a medial ligature
-                            int substChar = ArabicTextHandler.getSubstituteChar
-                                ("" + c + nextChar,ARABIC_MEDIAL.intValue());
-                            if (substChar > -1) {
-                                substString += (char)substChar;
-                                i++;
-                                continue;
-                            }
+            if (!arabicChar(c)) {
+                substString.append(c);
+                continue;
+            }
+
+            Integer form = (Integer)aci.getAttribute(ARABIC_FORM);
+            // see if the c is the start of a ligature
+            if (charStartsLigature(c) && (i+1 < end)) {
+                char nextChar = aci.setIndex(i+1);
+                Integer nextForm = (Integer)aci.getAttribute(ARABIC_FORM);
+                if (form != null && nextForm != null) {
+                    if (form.equals(ARABIC_TERMINAL)
+                        && nextForm.equals(ARABIC_INITIAL)) {
+                        // look for an isolated ligature
+                        int substChar = ArabicTextHandler.getSubstituteChar
+                            (c, nextChar,ARABIC_ISOLATED.intValue());
+                        if (substChar > -1) {
+                            substString.append((char)substChar);
+                            i++;
+                            continue;
+                        }
+                    } else if (form.equals(ARABIC_TERMINAL)) {
+                        // look for a terminal ligature
+                        int substChar = ArabicTextHandler.getSubstituteChar
+                            (c, nextChar,ARABIC_TERMINAL.intValue());
+                        if (substChar > -1) {
+                            substString.append((char)substChar);
+                            i++;
+                            continue;
+                        }
+                    } else if (form.equals(ARABIC_MEDIAL)
+                               && nextForm.equals(ARABIC_MEDIAL)) {
+                        // look for a medial ligature
+                        int substChar = ArabicTextHandler.getSubstituteChar
+                            (c, nextChar,ARABIC_MEDIAL.intValue());
+                        if (substChar > -1) {
+                            substString.append((char)substChar);
+                            i++;
+                            continue;
                         }
                     }
                 }
+            }
 
-                // couln't find a matching ligature so  just look for a simple substitution
-                if (form != null && form.intValue() > 0) {
-                    int substChar = ArabicTextHandler.getSubstituteChar(""+c, form.intValue());
-                    if (substChar > -1) {
-                        substString += (char)substChar;
-                    } else {
-                        substString += c;
-                    }
-                } else {
-                    substString += c;
+            // couldn't find a matching ligature so just look for a
+            // simple substitution
+            if (form != null && form.intValue() > 0) {
+                int substChar = getSubstituteChar(c, form.intValue());
+                if (substChar > -1) {
+                    c = (char)substChar;
                 }
-            } else {
-                substString += c;
             }
+            substString.append(c);
         }
-        return substString;
+
+        return substString.toString();
     }
 
     /**
@@ -493,7 +541,7 @@
      */
     public static boolean isLigature(char c) {
         int charVal = c;
-        if ((charVal < 0xFE70) || (charVal > 0xFEFC)) 
+        if ((charVal < 0xFE70) || (charVal > 0xFEFC))
             return false;
 
         if ((charVal <= 0xFE72)                      ||
@@ -506,176 +554,114 @@
     }
 
 
-    static {
-
-        // constructs the character map that maps arabic characters and
-        // ligature to their various forms
-        // NOTE: the unicode values for ligatures are stored here in
-        // visual order (not logical order)
-
-        int chars1[] = {0xFE70, -1, -1, -1};  //    isolated, final, initial, medial
-        charMap.put(new String("" + (char)0x064B + (char)0x0020), chars1);
-
-        int chars2[] = {-1, -1, -1, 0xFE71};
-        charMap.put(new String("" + (char)0x064B + (char)0x0640), chars2);
-
-        int chars3[] = {0xFE72, -1, -1, -1};
-        charMap.put(new String("" + (char)0x064C + (char)0x0020), chars3);
-
-        int chars4[] = {0xFE74, -1, -1, -1};
-        charMap.put(new String("" + (char)0x064D + (char)0x0020), chars4);
-
-        int chars5[] = {0xFE76, -1, -1, -1};
-        charMap.put(new String("" + (char)0x064E + (char)0x0020), chars5);
-
-        int chars6[] = {-1, -1, -1, 0xFE77};
-        charMap.put(new String("" + (char)0x064E + (char)0x0640), chars6);
-
-        int chars7[] = {0xFE78, -1, -1, -1};
-        charMap.put(new String("" + (char)0x064F + (char)0x0020), chars7);
-
-        int chars8[] = {-1, -1, -1, 0xFE79};
-        charMap.put(new String("" + (char)0x064F + (char)0x0640), chars8);
-
-        int chars9[] = {0xFE7A, -1, -1, -1};
-        charMap.put(new String("" + (char)0x0650 + (char)0x0020), chars9);
-
-        int chars10[] = {-1, -1, -1, 0xFE7B};
-        charMap.put(new String("" + (char)0x0650 + (char)0x0640), chars10);
-
-        int chars11[] = {0xFE7C, -1, -1, -1};
-        charMap.put(new String("" + (char)0x0651 + (char)0x0020), chars11);
-
-        int chars12[] = {-1, -1, -1, 0xFE7D};
-        charMap.put(new String("" + (char)0x0651 + (char)0x0640), chars12);
-
-        int chars13[] = {0xFE7E, -1, -1, -1};
-        charMap.put(new String("" + (char)0x0652 + (char)0x0020), chars13);
-
-        int chars14[] = {-1, -1, -1, 0xFE7F};
-        charMap.put(new String("" + (char)0x0652 + (char)0x0640), chars14);
-
-        int chars15[] = {0xFE80, -1, -1, -1};
-        charMap.put(new String("" + (char)0x0621), chars15);
-
-        int chars16[] = {0xFE81, 0xFE82, -1, -1};
-        charMap.put(new String("" + (char)0x0622), chars16);
-
-        int chars17[] = {0xFE83, 0xFE84, -1, -1};
-        charMap.put(new String("" + (char)0x0623), chars17);
-
-        int chars18[] = {0xFE85, 0xFE86, -1, -1};
-        charMap.put(new String("" + (char)0x0624), chars18);
-
-        int chars19[] = {0xFE87, 0xFE88, -1, -1};
-        charMap.put(new String("" + (char)0x0625), chars19);
-
-        int chars20[] = {0xFE89, 0xFE8A, 0xFE8B, 0xFE8C};
-        charMap.put(new String("" + (char)0x0626), chars20);
-
-        int chars21[] = {0xFE8D, 0xFE8E, -1, -1};
-        charMap.put(new String("" + (char)0x0627), chars21);
-
-        int chars22[] = {0xFE8F, 0xFE90, 0xFE91, 0xFE92};
-        charMap.put(new String("" + (char)0x0628), chars22);
-
-        int chars23[] = {0xFE93, 0xFE94, -1, -1};
-        charMap.put(new String("" + (char)0x0629), chars23);
-
-        int chars24[] = {0xFE95, 0xFE96, 0xFE97, 0xFE98};
-        charMap.put(new String("" + (char)0x062A), chars24);
-
-        int chars25[] = {0xFE99, 0xFE9A,  0xFE9B, 0xFE9C};
-        charMap.put(new String("" + (char)0x062B), chars25);
-
-        int chars26[] = {0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0};
-        charMap.put(new String("" + (char)0x062C), chars26);
-
-        int chars27[] = {0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4};
-        charMap.put(new String("" + (char)0x062D), chars27);
-
-        int chars28[] = {0xFEA5,  0xFEA6, 0xFEA7, 0xFEA8};
-        charMap.put(new String("" + (char)0x062E), chars28);
-
-        int chars29[] = {0xFEA9, 0xFEAA, -1, -1};
-        charMap.put(new String("" + (char)0x062F), chars29);
-
-        int chars30[] = {0xFEAB, 0xFEAC, -1, -1};
-        charMap.put(new String("" + (char)0x0630), chars30);
-
-        int chars31[] = {0xFEAD, 0xFEAE, -1, -1};
-        charMap.put(new String("" + (char)0x0631), chars31);
-
-        int chars32[] = {0xFEAF,  0xFEB0, -1, -1};
-        charMap.put(new String("" + (char)0x0632), chars32);
-
-        int chars33[] = {0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4};
-        charMap.put(new String("" + (char)0x0633), chars33);
-
-        int chars34[] = {0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8};
-        charMap.put(new String("" + (char)0x0634), chars34);
-
-        int chars35[] = {0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC};
-        charMap.put(new String("" + (char)0x0635), chars35);
-
-        int chars36[] = {0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0};
-        charMap.put(new String("" + (char)0x0636), chars36);
-
-        int chars37[] = {0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4};
-        charMap.put(new String("" + (char)0x0637), chars37);
-
-        int chars38[] = {0xFEC5,  0xFEC6, 0xFEC7, 0xFEC8};
-        charMap.put(new String("" + (char)0x0638), chars38);
-
-        int chars39[] = {0xFEC9,  0xFECA, 0xFECB, 0xFECC};
-        charMap.put(new String("" + (char)0x0639), chars39);
-
-        int chars40[] = { 0xFECD,  0xFECE, 0xFECF,  0xFED0};
-        charMap.put(new String("" + (char)0x063A), chars40);
-
-        int chars41[] = {0xFED1, 0xFED2, 0xFED3, 0xFED4};
-        charMap.put(new String("" + (char)0x0641), chars41);
-
-        int chars42[] = {0xFED5, 0xFED6, 0xFED7, 0xFED8};
-        charMap.put(new String("" + (char)0x0642), chars42);
-
-        int chars43[] = {0xFED9, 0xFEDA,  0xFEDB, 0xFEDC};
-        charMap.put(new String("" + (char)0x0643), chars43);
-
-        int chars44[] = {0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0};
-        charMap.put(new String("" + (char)0x0644), chars44);
-
-        int chars45[] = {0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4};
-        charMap.put(new String("" + (char)0x0645), chars45);
-
-        int chars46[] = {0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8};
-        charMap.put(new String("" + (char)0x0646), chars46);
-
-        int chars47[] = {0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC};
-        charMap.put(new String("" + (char)0x0647), chars47);
-
-        int chars48[] = {0xFEED, 0xFEEE, -1, -1};
-        charMap.put(new String("" + (char)0x0648), chars48);
-
-        int chars49[] = {0xFEEF, 0xFEF0, -1, -1};
-        charMap.put(new String("" + (char)0x0649), chars49);
-
-        int chars50[] = {0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4};
-        charMap.put(new String("" + (char)0x064A), chars50);
-
-         int chars51[] = {0xFEF5, 0xFEF6, -1, -1};
-        charMap.put(new String("" + (char)0x0622 + (char)0x0644), chars51);
-
-        int chars52[] = {0xFEF7, 0xFEF8, -1, -1};
-        charMap.put(new String("" + (char)0x0623 + (char)0x0644), chars52);
-
-        int chars53[] = {0xFEF9, 0xFEFA, -1, -1};
-        charMap.put(new String("" + (char)0x0625 + (char)0x0644), chars53);
-
-        int chars54[] = {0xFEFB,  0xFEFC, -1, -1};
-        charMap.put(new String("" + (char)0x0627 + (char)0x0644), chars54);
-
-    }
-
-
+    // constructs the character map that maps arabic characters and
+    // ligature to their various forms
+    // NOTE: the unicode values for ligatures are stored here in
+    // visual order (not logical order)
+    
+    // Single char remappings:
+    static int singleCharFirst=0x0621;
+    static int singleCharLast =0x064A;
+    static int [][] singleCharRemappings = {
+     // isolated, final, initial, medial
+        {0xFE80,     -1,     -1,     -1},  // 0x0621
+        {0xFE81, 0xFE82,     -1,     -1},  // 0x0622
+        {0xFE83, 0xFE84,     -1,     -1},  // 0x0623
+        {0xFE85, 0xFE86,     -1,     -1},  // 0x0624
+        {0xFE87, 0xFE88,     -1,     -1},  // 0x0625
+        {0xFE89, 0xFE8A, 0xFE8B, 0xFE8C},  // 0x0626
+        {0xFE8D, 0xFE8E,     -1,     -1},  // 0x0627
+        {0xFE8F, 0xFE90, 0xFE91, 0xFE92},  // 0x0628
+        {0xFE93, 0xFE94,     -1,     -1},  // 0x0629
+        {0xFE95, 0xFE96, 0xFE97, 0xFE98},  // 0x062A
+        {0xFE99, 0xFE9A, 0xFE9B, 0xFE9C},  // 0x062B
+        {0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0},  // 0x062C
+        {0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4},  // 0x062D
+        {0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8},  // 0x062E
+        {0xFEA9, 0xFEAA,     -1,     -1},  // 0x062F
+        {0xFEAB, 0xFEAC,     -1,     -1},  // 0x0630
+        {0xFEAD, 0xFEAE,     -1,     -1},  // 0x0631
+        {0xFEAF, 0xFEB0,     -1,     -1},  // 0x0632
+        {0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4},  // 0x0633
+        {0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8},  // 0x0634
+        {0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC},  // 0x0635
+        {0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0},  // 0x0636
+        {0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4},  // 0x0637
+        {0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8},  // 0x0638
+        {0xFEC9, 0xFECA, 0xFECB, 0xFECC},  // 0x0639
+        {0xFECD, 0xFECE, 0xFECF, 0xFED0},  // 0x063A
+
+        null,  // 0x063B
+        null,  // 0x063C
+        null,  // 0x063D
+        null,  // 0x063E
+        null,  // 0x063F
+        null,  // 0x0640
+
+        {0xFED1, 0xFED2, 0xFED3, 0xFED4},  // 0x0641
+        {0xFED5, 0xFED6, 0xFED7, 0xFED8},  // 0x0642
+        {0xFED9, 0xFEDA, 0xFEDB, 0xFEDC},  // 0x0643
+        {0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0},  // 0x0644
+        {0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4},  // 0x0645
+        {0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8},  // 0x0646
+        {0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC},  // 0x0647
+        {0xFEED, 0xFEEE,     -1,     -1},  // 0x0648
+        {0xFEEF, 0xFEF0,     -1,     -1},  // 0x0649
+        {0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}}; // 0x064A
+
+    static int doubleCharFirst=0x0622;
+    static int doubleCharLast =0x0652;
+    static int [][][] doubleCharRemappings = {
+      // 2nd Char, isolated, final, initial, medial
+        {{0x0644,   0xFEF5, 0xFEF6,     -1,     -1}},  // 0x0622
+        {{0x0644,   0xFEF7, 0xFEF8,     -1,     -1}},  // 0x0623
+        null,                                          // 0x0624
+        {{0x0644,   0xFEF9, 0xFEFA,     -1,     -1}},  // 0x0625
+        null,                                          // 0x0626
+        {{0x0644,   0xFEFB, 0xFEFC,     -1,     -1}},  // 0x0627
+
+        null,                                          // 0x0628
+        null,                                          // 0x0629
+        null,                                          // 0x0630
+        null,                                          // 0x0631
+        null,                                          // 0x0632
+        null,                                          // 0x0633
+        null,                                          // 0x0634
+        null,                                          // 0x0635
+        null,                                          // 0x0636
+        null,                                          // 0x0637
+        null,                                          // 0x0638
+        null,                                          // 0x0639
+        null,                                          // 0x063A
+        null,                                          // 0x063B
+        null,                                          // 0x063C
+        null,                                          // 0x063D
+        null,                                          // 0x063E
+        null,                                          // 0x063F
+        null,                                          // 0x0640
+        null,                                          // 0x0641
+        null,                                          // 0x0642
+        null,                                          // 0x0643
+        null,                                          // 0x0644
+        null,                                          // 0x0645
+        null,                                          // 0x0646
+        null,                                          // 0x0647
+        null,                                          // 0x0648
+        null,                                          // 0x0649
+        null,                                          // 0x064A
+
+        {{0x0020,   0xFE70,     -1,     -1,     -1},   // 0x064B
+         {0x0640,       -1,     -1,     -1, 0xFE71}},
+        {{0x0020,   0xFE72,     -1,     -1,     -1}},  // 0x064C
+        {{0x0020,   0xFE74,     -1,     -1,     -1}},  // 0x064D
+        {{0x0020,   0xFE76,     -1,     -1,     -1},   // 0x064E
+         {0x0640,       -1,     -1,     -1, 0xFE77}},
+        {{0x0020,   0xFE78,     -1,     -1,     -1},   // 0x064F
+         {0x0640,       -1,     -1,     -1, 0xFE79}},
+        {{0x0020,   0xFE7A,     -1,     -1,     -1},   // 0x0650
+         {0x0640,       -1,     -1,     -1, 0xFE7B}},
+        {{0x0020,   0xFE7C,     -1,     -1,     -1},   // 0x0651
+         {0x0640,       -1,     -1,     -1, 0xFE7D}},
+        {{0x0020,   0xFE7E,     -1,     -1,     -1},   // 0x0652
+         {0x0640,       -1,     -1,     -1, 0xFE7F}}};
 }