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.
boolean needsBIDI = true;
if (false) {
// Believe it or not this is much slower than the else case
// but the two are exactly equivilent (including the stripping
// of null keys/values).
as = new AttributedString(aci);
} else {
StringBuffer strB = new StringBuffer();
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());
Iterator i = attrMap.keySet().iterator();
while (i.hasNext()) {
// Font doesn't like getting attribute sets with
// null keys or values so we strip them here.
Object key = i.next();
if (key == null) continue;
Object value = attrMap.get(key);
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,
new Integer(currBiDi), runStart, i);
runStart = i;
currBiDi = newBiDi;
if (newBiDi > maxBiDi) maxBiDi = newBiDi;
}
}
as.addAttribute
(GVTAttributedCharacterIterator.TextAttribute.BIDI_LEVEL,
new Integer(currBiDi), runStart, numChars);
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.aci = this.reorderedACI = as.getIterator();
newCharOrder = new int[numChars];
for (int i=0; i<numChars; i++)
newCharOrder[i] = chunkStart+i;
return;
}
this.aci = as.getIterator();
// work out the new character order
newCharOrder = doBidiReorder(charIndices, charLevels,
numChars, maxBiDi);
// construct the string in the new order
StringBuffer reorderedString = new StringBuffer();
char c;
for (int i = 0; i < numChars; i++) {
c = this.aci.setIndex(newCharOrder[i]);
// check for mirrored char
int bidiLevel = tl.getCharacterLevel(newCharOrder[i]);
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=this.aci.getBeginIndex();
int end =this.aci.getEndIndex();
int index = start;
while (index < end) {
this.aci.setIndex(index);
Map attrMap = this.aci.getAttributes();
int extent = this.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
this.aci.first();
Float x = (Float) this.aci.getAttribute
(GVTAttributedCharacterIterator.TextAttribute.X);
Float y = (Float) this.aci.getAttribute
(GVTAttributedCharacterIterator.TextAttribute.Y);
if (x != null && !x.isNaN()) {
reorderedAS.addAttribute
(GVTAttributedCharacterIterator.TextAttribute.X,
new Float(Float.NaN), newCharOrder[0], newCharOrder[0]+1);
reorderedAS.addAttribute
(GVTAttributedCharacterIterator.TextAttribute.X, x, 0, 1);
}
if (y != null && !y.isNaN()) {
reorderedAS.addAttribute
(GVTAttributedCharacterIterator.TextAttribute.Y,
new Float(Float.NaN), newCharOrder[0], newCharOrder[0]+1);
reorderedAS.addAttribute
(GVTAttributedCharacterIterator.TextAttribute.Y, y, 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();
}