1 18 package org.apache.batik.gvt.text; 19 20 import java.awt.font.FontRenderContext ; 21 import java.awt.font.TextLayout ; 22 import java.text.AttributedCharacterIterator ; 23 import java.text.AttributedString ; 24 import java.util.HashMap ; 25 import java.util.Iterator ; 26 import java.util.Map ; 27 import java.util.Set ; 28 29 39 public class BidiAttributedCharacterIterator implements AttributedCharacterIterator { 40 41 private AttributedCharacterIterator reorderedACI; 42 private FontRenderContext frc; 43 private int chunkStart; 44 private int [] newCharOrder; 45 46 private final static Map mirroredGlyphs = new HashMap (50); 47 48 49 protected BidiAttributedCharacterIterator 50 (AttributedCharacterIterator reorderedACI, 51 FontRenderContext frc, 52 int chunkStart, 53 int [] newCharOrder) { 54 this.reorderedACI = reorderedACI; 55 this.frc = frc; 56 this.chunkStart = chunkStart; 57 this.newCharOrder = newCharOrder; 58 } 59 60 61 69 public BidiAttributedCharacterIterator(AttributedCharacterIterator aci, 70 FontRenderContext frc, 71 int chunkStart) { 72 73 this.frc = frc; 74 this.chunkStart = chunkStart; 75 aci.first(); 76 int numChars = aci.getEndIndex()-aci.getBeginIndex(); 77 AttributedString as; 78 79 if (false) { 82 as = new AttributedString (aci); 86 } else { 87 StringBuffer strB = new StringBuffer (); 88 char c = aci.first(); 89 for (int i = 0; i < numChars; i++) { 90 strB.append(c); 91 c = aci.next(); 92 } 93 as = new AttributedString (strB.toString()); 94 int start=aci.getBeginIndex(); 95 int end =aci.getEndIndex(); 96 int index = start; 97 while (index < end) { 98 aci.setIndex(index); 99 Map attrMap = aci.getAttributes(); 100 int extent = aci.getRunLimit(); 101 Map destMap = new HashMap (attrMap.size()); 102 Iterator i = attrMap.keySet().iterator(); 103 while (i.hasNext()) { 104 Object key = i.next(); 107 if (key == null) continue; 108 Object value = attrMap.get(key); 109 if (value == null) continue; 110 destMap.put(key, value); 111 } 112 as.addAttributes (destMap, index-start, extent-start); 115 index = extent; 116 } 117 } 118 119 TextLayout tl = new TextLayout (as.getIterator(), frc); 122 123 int[] charIndices = new int[numChars]; 124 int[] charLevels = new int[numChars]; 125 126 int runStart = 0; 127 int currBiDi = tl.getCharacterLevel(0); 128 charIndices[0] = 0; 129 charLevels [0] = currBiDi; 130 int maxBiDi = currBiDi; 131 132 for (int i = 1; i < numChars; i++) { 133 int newBiDi = tl.getCharacterLevel(i); 134 charIndices[i] = i; 135 charLevels [i] = newBiDi; 136 137 if (newBiDi != currBiDi) { 138 as.addAttribute 139 (GVTAttributedCharacterIterator.TextAttribute.BIDI_LEVEL, 140 new Integer (currBiDi), runStart, i); 141 runStart = i; 142 currBiDi = newBiDi; 143 if (newBiDi > maxBiDi) maxBiDi = newBiDi; 144 } 145 } 146 as.addAttribute 147 (GVTAttributedCharacterIterator.TextAttribute.BIDI_LEVEL, 148 new Integer (currBiDi), runStart, numChars); 149 150 aci = as.getIterator(); 151 152 if ((runStart == 0) && (currBiDi==0)) { 153 this.reorderedACI = aci; 157 newCharOrder = new int[numChars]; 158 for (int i=0; i<numChars; i++) 159 newCharOrder[i] = chunkStart+i; 160 return; 161 } 162 163 newCharOrder = doBidiReorder(charIndices, charLevels, 165 numChars, maxBiDi); 166 167 StringBuffer reorderedString = new StringBuffer (); 169 char c; 170 for (int i = 0; i < numChars; i++) { 171 c = aci.setIndex(newCharOrder[i]); 172 173 int bidiLevel = tl.getCharacterLevel(newCharOrder[i]); 175 if ((bidiLevel & 0x01) != 0) { 176 c = (char)mirrorChar(c); 180 } 181 182 reorderedString.append(c); 183 } 184 185 AttributedString reorderedAS 187 = new AttributedString (reorderedString.toString()); 188 Map [] attrs = new Map [numChars]; 189 int start=aci.getBeginIndex(); 190 int end =aci.getEndIndex(); 191 int index = start; 192 while (index < end) { 193 aci.setIndex(index); 194 Map attrMap = aci.getAttributes(); 195 int extent = aci.getRunLimit(); 196 for (int i=index; i<extent; i++) 197 attrs[i-start] = attrMap; 198 index = extent; 199 } 200 201 runStart=0; 202 Map prevAttrMap = attrs[newCharOrder[0]]; 203 for (int i = 1; i < numChars; i++) { 204 Map attrMap = attrs[newCharOrder[i]]; 205 if (attrMap != prevAttrMap) { 206 reorderedAS.addAttributes(prevAttrMap, runStart, i); 208 prevAttrMap = attrMap; 209 runStart = i; 210 } 211 } 212 reorderedAS.addAttributes(prevAttrMap, runStart, numChars); 213 214 aci.first(); 216 Float x = (Float ) aci.getAttribute 217 (GVTAttributedCharacterIterator.TextAttribute.X); 218 Float y = (Float ) aci.getAttribute 219 (GVTAttributedCharacterIterator.TextAttribute.Y); 220 221 if (x != null && !x.isNaN()) { 222 reorderedAS.addAttribute 223 (GVTAttributedCharacterIterator.TextAttribute.X, 224 new Float (Float.NaN), newCharOrder[0], newCharOrder[0]+1); 225 reorderedAS.addAttribute 226 (GVTAttributedCharacterIterator.TextAttribute.X, x, 0, 1); 227 } 228 if (y != null && !y.isNaN()) { 229 reorderedAS.addAttribute 230 (GVTAttributedCharacterIterator.TextAttribute.Y, 231 new Float (Float.NaN), newCharOrder[0], newCharOrder[0]+1); 232 reorderedAS.addAttribute 233 (GVTAttributedCharacterIterator.TextAttribute.Y, y, 0, 1); 234 } 235 236 reorderedAS = ArabicTextHandler.assignArabicForms(reorderedAS); 238 239 for (int i=0; i<newCharOrder.length; i++) { 241 newCharOrder[i] += chunkStart; 242 } 243 reorderedACI = reorderedAS.getIterator(); 244 } 245 246 public int[] getCharMap() { return newCharOrder; } 249 250 260 private int[] doBidiReorder(int[] charIndices, int[] charLevels, 261 int numChars, int highestLevel) { 262 if (highestLevel == 0) return charIndices; 263 264 int currentIndex = 0; 267 while (currentIndex < numChars) { 268 269 while ((currentIndex < numChars) && 271 (charLevels[currentIndex] < highestLevel)) { 272 currentIndex++; 273 } 274 if (currentIndex == numChars) { 275 break; 277 } 278 int startIndex = currentIndex; 279 280 currentIndex++; 281 while ((currentIndex < numChars) && 283 (charLevels[currentIndex] == highestLevel)) { 284 currentIndex++; 285 } 286 int endIndex = currentIndex-1; 287 288 290 int middle = ((endIndex-startIndex)>>1)+1; 294 int tmp; 295 for (int i = 0; i<middle; i++) { 296 tmp = charIndices[startIndex+i]; 297 charIndices[startIndex+i] = charIndices[endIndex-i]; 298 charIndices[endIndex -i] = tmp; 299 300 charLevels [startIndex+i] = highestLevel-1; 301 charLevels [endIndex -i] = highestLevel-1; 302 } 303 } 304 return doBidiReorder(charIndices, charLevels, numChars, highestLevel-1); 305 } 306 307 308 309 312 public Set getAllAttributeKeys() { 313 return reorderedACI.getAllAttributeKeys(); 314 } 315 316 320 public Object getAttribute(AttributedCharacterIterator.Attribute attribute) { 321 return reorderedACI.getAttribute(attribute); 322 } 323 324 328 public Map getAttributes() { 329 return reorderedACI.getAttributes(); 330 } 331 332 337 public int getRunLimit() { 338 return reorderedACI.getRunLimit(); 339 } 340 341 346 public int getRunLimit(AttributedCharacterIterator.Attribute attribute) { 347 return reorderedACI.getRunLimit(attribute); 348 } 349 350 355 public int getRunLimit(Set attributes) { 356 return reorderedACI.getRunLimit(attributes); 357 } 358 359 363 public int getRunStart() { 364 return reorderedACI.getRunStart(); 365 } 366 367 373 public int getRunStart(AttributedCharacterIterator.Attribute attribute) { 374 return reorderedACI.getRunStart(attribute); 375 } 376 377 382 public int getRunStart(Set attributes) { 383 return reorderedACI.getRunStart(attributes); 384 } 385 386 389 public Object clone() { 390 return new BidiAttributedCharacterIterator 391 ((AttributedCharacterIterator )reorderedACI.clone(), 392 frc, chunkStart, (int [])newCharOrder.clone()); 393 } 394 395 398 public char current() { 399 return reorderedACI.current(); 400 } 401 402 406 public char first() { 407 return reorderedACI.first(); 408 } 409 410 413 public int getBeginIndex() { 414 return reorderedACI.getBeginIndex(); 415 } 416 417 420 public int getEndIndex() { 421 return reorderedACI.getEndIndex(); 422 } 423 424 427 public int getIndex() { 428 return reorderedACI.getIndex(); 429 } 430 431 435 public char last() { 436 return reorderedACI.last(); 437 } 438 439 443 public char next() { 444 return reorderedACI.next(); 445 } 446 447 450 public char previous() { 451 return reorderedACI.previous(); 452 } 453 454 457 public char setIndex(int position) { 458 return reorderedACI.setIndex(position); 459 } 460 461 462 public static int mirrorChar(int c) { 463 switch(c) { 464 case 0x0028: return 0x0029; case 0x0029: return 0x0028; case 0x003C: return 0x003E; case 0x003E: return 0x003C; case 0x005B: return 0x005D; case 0x005D: return 0x005B; case 0x007B: return 0x007D; case 0x007D: return 0x007B; case 0x00AB: return 0x00BB; case 0x00BB: return 0x00AB; case 0x2039: return 0x203A; case 0x203A: return 0x2039; case 0x2045: return 0x2046; case 0x2046: return 0x2045; case 0x207D: return 0x207E; case 0x207E: return 0x207D; case 0x208D: return 0x208E; case 0x208E: return 0x208D; case 0x2208: return 0x220B; case 0x2209: return 0x220C; case 0x220A: return 0x220D; case 0x220B: return 0x2208; case 0x220C: return 0x2209; case 0x220D: return 0x220A; case 0x223C: return 0x223D; case 0x223D: return 0x223C; case 0x2243: return 0x22CD; case 0x2252: return 0x2253; case 0x2253: return 0x2252; case 0x2254: return 0x2255; case 0x2255: return 0x2254; case 0x2264: return 0x2265; case 0x2265: return 0x2264; case 0x2266: return 0x2267; case 0x2267: return 0x2266; case 0x2268: return 0x2269; case 0x2269: return 0x2268; case 0x226A: return 0x226B; case 0x226B: return 0x226A; case 0x226E: return 0x226F; case 0x226F: return 0x226E; case 0x2270: return 0x2271; case 0x2271: return 0x2270; case 0x2272: return 0x2273; case 0x2273: return 0x2272; case 0x2274: return 0x2275; case 0x2275: return 0x2274; case 0x2276: return 0x2277; case 0x2277: return 0x2276; case 0x2278: return 0x2279; case 0x2279: return 0x2278; case 0x227A: return 0x227B; case 0x227B: return 0x227A; case 0x227C: return 0x227D; case 0x227D: return 0x227C; case 0x227E: return 0x227F; case 0x227F: return 0x227E; case 0x2280: return 0x2281; case 0x2281: return 0x2280; case 0x2282: return 0x2283; case 0x2283: return 0x2282; case 0x2284: return 0x2285; case 0x2285: return 0x2284; case 0x2286: return 0x2287; case 0x2287: return 0x2286; case 0x2288: return 0x2289; case 0x2289: return 0x2288; case 0x228A: return 0x228B; case 0x228B: return 0x228A; case 0x228F: return 0x2290; case 0x2290: return 0x228F; case 0x2291: return 0x2292; case 0x2292: return 0x2291; case 0x22A2: return 0x22A3; case 0x22A3: return 0x22A2; case 0x22B0: return 0x22B1; case 0x22B1: return 0x22B0; case 0x22B2: return 0x22B3; case 0x22B3: return 0x22B2; case 0x22B4: return 0x22B5; case 0x22B5: return 0x22B4; case 0x22B6: return 0x22B7; case 0x22B7: return 0x22B6; case 0x22C9: return 0x22CA; case 0x22CA: return 0x22C9; case 0x22CB: return 0x22CC; case 0x22CC: return 0x22CB; case 0x22CD: return 0x2243; case 0x22D0: return 0x22D1; case 0x22D1: return 0x22D0; case 0x22D6: return 0x22D7; case 0x22D7: return 0x22D6; case 0x22D8: return 0x22D9; case 0x22D9: return 0x22D8; case 0x22DA: return 0x22DB; case 0x22DB: return 0x22DA; case 0x22DC: return 0x22DD; case 0x22DD: return 0x22DC; case 0x22DE: return 0x22DF; case 0x22DF: return 0x22DE; case 0x22E0: return 0x22E1; case 0x22E1: return 0x22E0; case 0x22E2: return 0x22E3; case 0x22E3: return 0x22E2; case 0x22E4: return 0x22E5; case 0x22E5: return 0x22E4; case 0x22E6: return 0x22E7; case 0x22E7: return 0x22E6; case 0x22E8: return 0x22E9; case 0x22E9: return 0x22E8; case 0x22EA: return 0x22EB; case 0x22EB: return 0x22EA; case 0x22EC: return 0x22ED; case 0x22ED: return 0x22EC; case 0x22F0: return 0x22F1; case 0x22F1: return 0x22F0; case 0x2308: return 0x2309; case 0x2309: return 0x2308; case 0x230A: return 0x230B; case 0x230B: return 0x230A; case 0x2329: return 0x232A; case 0x232A: return 0x2329; case 0x3008: return 0x3009; case 0x3009: return 0x3008; case 0x300A: return 0x300B; case 0x300B: return 0x300A; case 0x300C: return 0x300D; case 0x300D: return 0x300C; case 0x300E: return 0x300F; case 0x300F: return 0x300E; case 0x3010: return 0x3011; case 0x3011: return 0x3010; case 0x3014: return 0x3015; case 0x3015: return 0x3014; case 0x3016: return 0x3017; case 0x3017: return 0x3016; case 0x3018: return 0x3019; case 0x3019: return 0x3018; case 0x301A: return 0x301B; case 0x301B: return 0x301A; default: break; 606 } 607 return c; 608 } 609 } 610 | Popular Tags |