public BidiAttributedCharacterIterator()

in batik-gvt/src/main/java/org/apache/batik/gvt/text/BidiAttributedCharacterIterator.java [68:265]


    public BidiAttributedCharacterIterator(AttributedCharacterIterator aci,
                                           FontRenderContext           frc,
                                           int chunkStart) {

        this.frc = frc;
        this.chunkStart = chunkStart;
        aci.first();
        int   numChars    = aci.getEndIndex()-aci.getBeginIndex();
        AttributedString as;

         // Ideally we would do a 'quick' check on chars and
         // attributes to decide if we really need to do bidi or not.
        if (false) {
            // Believe it or not this is much slower than the else case
            // but the two are exactly equivalent (including the stripping
            // of null keys/values).
            as = new AttributedString(aci);
        } else {
            StringBuffer strB = new StringBuffer( numChars );
            char c = aci.first();
            for (int i = 0; i < numChars; i++) {
                strB.append(c);
                c = aci.next();
            }
            as = new AttributedString(strB.toString());
            int start=aci.getBeginIndex();
            int end  =aci.getEndIndex();
            int index = start;
            while (index < end) {
                aci.setIndex(index);
                Map attrMap = aci.getAttributes();
                int extent  = aci.getRunLimit();
                Map destMap = new HashMap(attrMap.size());
                for (Object o : attrMap.entrySet()) {
                    // Font doesn't like getting attribute sets with
                    // null keys or values so we strip them here.
                    Map.Entry e = (Map.Entry) o;
                    Object key = e.getKey();
                    if (key == null) continue;
                    Object value = e.getValue();
                    if (value == null) continue;
                    destMap.put(key, value);
                }
                // System.out.println("Run: " + (index-start) + "->" +
                //                    (extent-start) + " of " + numChars);
                as.addAttributes (destMap, index-start, extent-start);
                index = extent;
            }
        }

        // We Just want it to do BIDI for us...
        // In 1.4 we might be able to use the BIDI class...
        TextLayout tl = new TextLayout(as.getIterator(), frc);

        int[] charIndices = new int[numChars];
        int[] charLevels  = new int[numChars];

        int runStart   = 0;
        int currBiDi   = tl.getCharacterLevel(0);
        charIndices[0] = 0;
        charLevels [0] = currBiDi;
        int maxBiDi    = currBiDi;

        for (int i = 1; i < numChars; i++) {
            int newBiDi = tl.getCharacterLevel(i);
            charIndices[i] = i;
            charLevels [i] = newBiDi;

            if (newBiDi != currBiDi) {
                as.addAttribute
                    (GVTAttributedCharacterIterator.TextAttribute.BIDI_LEVEL,
                            currBiDi, runStart, i);
                runStart = i;
                currBiDi  = newBiDi;
                if (newBiDi > maxBiDi) maxBiDi = newBiDi;
            }
        }
        as.addAttribute
            (GVTAttributedCharacterIterator.TextAttribute.BIDI_LEVEL,
                    currBiDi, runStart, numChars);

        aci = as.getIterator();

        if ((runStart == 0) && (currBiDi==0)) {
            // This avoids all the mucking about we need to do when
            // bidi is actually performed for cases where it
            // is not actually needed.
            this.reorderedACI = aci;
            newCharOrder = new int[numChars];
            for (int i=0; i<numChars; i++)
                newCharOrder[i] = chunkStart+i;
            return;
        }

        //  work out the new character order
        newCharOrder = doBidiReorder(charIndices, charLevels,
                                     numChars, maxBiDi);

        // construct the string in the new order
        StringBuffer reorderedString = new StringBuffer( numChars );
        int reorderedFirstChar = 0;
        for (int i = 0; i < numChars; i++) {
            int srcIdx = newCharOrder[i];
            char c = aci.setIndex(srcIdx);
            if (srcIdx == 0) reorderedFirstChar = i;

            // check for mirrored char
            int bidiLevel = tl.getCharacterLevel(srcIdx);
            if ((bidiLevel & 0x01) != 0) {
                // bidi level is odd so writing dir is right to left
                // So get the mirror version of the char if there
                // is one.
                c = (char)mirrorChar(c);
            }

            reorderedString.append(c);
        }

        // construct the reordered ACI
        AttributedString reorderedAS
            = new AttributedString(reorderedString.toString());
        Map [] attrs = new Map[numChars];
        int start=aci.getBeginIndex();
        int end  =aci.getEndIndex();
        int index = start;
        while (index < end) {
            aci.setIndex(index);
            Map attrMap = aci.getAttributes();
            int extent  = aci.getRunLimit();
            for (int i=index; i<extent; i++)
                attrs[i-start] = attrMap;
            index = extent;
        }

        runStart=0;
        Map prevAttrMap = attrs[newCharOrder[0]];
        for (int i = 1; i < numChars; i++) {
            Map attrMap = attrs[newCharOrder[i]];
            if (attrMap != prevAttrMap) {
                // Change in attrs set last run...
                reorderedAS.addAttributes(prevAttrMap, runStart, i);
                prevAttrMap = attrMap;
                runStart = i;
            }
        }
        reorderedAS.addAttributes(prevAttrMap, runStart, numChars);

        // transfer any position atttributes to the new first char
        aci.first();
        Float x = (Float) aci.getAttribute
            (GVTAttributedCharacterIterator.TextAttribute.X);
        if (x != null && !x.isNaN()) {
            reorderedAS.addAttribute
                (GVTAttributedCharacterIterator.TextAttribute.X,
                 FLOAT_NAN, reorderedFirstChar, reorderedFirstChar+1);
            reorderedAS.addAttribute
                (GVTAttributedCharacterIterator.TextAttribute.X, x, 0, 1);
        }

        Float y = (Float) aci.getAttribute
            (GVTAttributedCharacterIterator.TextAttribute.Y);
        if (y != null && !y.isNaN()) {
            reorderedAS.addAttribute
                (GVTAttributedCharacterIterator.TextAttribute.Y,
                 FLOAT_NAN, reorderedFirstChar, reorderedFirstChar+1);
            reorderedAS.addAttribute
                (GVTAttributedCharacterIterator.TextAttribute.Y, y, 0, 1);
        }


        Float dx = (Float) aci.getAttribute
            (GVTAttributedCharacterIterator.TextAttribute.DX);
        if (dx != null && !dx.isNaN()) {
            reorderedAS.addAttribute
                (GVTAttributedCharacterIterator.TextAttribute.DX,
                 FLOAT_NAN, reorderedFirstChar, reorderedFirstChar+1);
            reorderedAS.addAttribute
                (GVTAttributedCharacterIterator.TextAttribute.DX, dx, 0, 1);
        }
        Float dy = (Float) aci.getAttribute
            (GVTAttributedCharacterIterator.TextAttribute.DY);
        if (dy != null && !dy.isNaN()) {
            reorderedAS.addAttribute
                (GVTAttributedCharacterIterator.TextAttribute.DY,
                 FLOAT_NAN, reorderedFirstChar, reorderedFirstChar+1);
            reorderedAS.addAttribute
                (GVTAttributedCharacterIterator.TextAttribute.DY, dy, 0, 1);
        }

        // assign arabic form attributes to any arabic chars in the string
        reorderedAS = ArabicTextHandler.assignArabicForms(reorderedAS);

        // Shift the values to match the source text string...
        for (int i=0; i<newCharOrder.length; i++) {
            newCharOrder[i] += chunkStart;
        }
        reorderedACI = reorderedAS.getIterator();
    }