KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > gvt > text > BidiAttributedCharacterIterator


1 /*
2
3    Copyright 2001-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.gvt.text;
19
20 import java.awt.font.FontRenderContext JavaDoc;
21 import java.awt.font.TextLayout JavaDoc;
22 import java.text.AttributedCharacterIterator JavaDoc;
23 import java.text.AttributedString JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Set JavaDoc;
28
29 /**
30  * An attributed character iterator that does the reordering of the characters
31  * for bidirectional text. It reorders the characters so they are in visual order.
32  * It also assigns a BIDI_LEVEL attribute to each character which can be used
33  * to split the reordered ACI into text runs based on direction. ie. characters
34  * in a text run will all have the same bidi level.
35  *
36  * @author <a HREF="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
37  * @version $Id: BidiAttributedCharacterIterator.java,v 1.11 2004/08/18 07:14:40 vhardy Exp $
38  */

39 public class BidiAttributedCharacterIterator implements AttributedCharacterIterator JavaDoc {
40
41     private AttributedCharacterIterator JavaDoc reorderedACI;
42     private FontRenderContext JavaDoc frc;
43     private int chunkStart;
44     private int [] newCharOrder;
45
46     private final static Map JavaDoc mirroredGlyphs = new HashMap JavaDoc(50);
47
48
49     protected BidiAttributedCharacterIterator
50         (AttributedCharacterIterator JavaDoc reorderedACI,
51          FontRenderContext JavaDoc 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     /**
62      * Constructs a character iterator that represents the visual display order
63      * of characters within bidirectional text.
64      *
65      * @param aci The character iterator containing the characters in logical
66      * order.
67      * @param frc The current font render context
68      */

69     public BidiAttributedCharacterIterator(AttributedCharacterIterator JavaDoc aci,
70                                            FontRenderContext JavaDoc 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 JavaDoc as;
78
79          // Ideally we would do a 'quick' check on chars and
80
// attributes to decide if we really need to do bidi or not.
81
if (false) {
82             // Believe it or not this is much slower than the else case
83
// but the two are exactly equivilent (including the stripping
84
// of null keys/values).
85
as = new AttributedString JavaDoc(aci);
86         } else {
87             StringBuffer JavaDoc strB = new StringBuffer JavaDoc();
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 JavaDoc(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 JavaDoc attrMap = aci.getAttributes();
100                 int extent = aci.getRunLimit();
101                 Map JavaDoc destMap = new HashMap JavaDoc(attrMap.size());
102                 Iterator JavaDoc i = attrMap.keySet().iterator();
103                 while (i.hasNext()) {
104                     // Font doesn't like getting attribute sets with
105
// null keys or values so we strip them here.
106
Object JavaDoc key = i.next();
107                     if (key == null) continue;
108                     Object JavaDoc value = attrMap.get(key);
109                     if (value == null) continue;
110                     destMap.put(key, value);
111                 }
112                 // System.out.println("Run: " + (index-start) + "->" +
113
// (extent-start) + " of " + numChars);
114
as.addAttributes (destMap, index-start, extent-start);
115                 index = extent;
116             }
117         }
118
119         // We Just want it to do BIDI for us...
120
// In 1.4 we might be able to use the BIDI class...
121
TextLayout JavaDoc tl = new TextLayout JavaDoc(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 JavaDoc(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 JavaDoc(currBiDi), runStart, numChars);
149
150         aci = as.getIterator();
151
152         if ((runStart == 0) && (currBiDi==0)) {
153             // This avoids all the mucking about we need to do when
154
// bidi is actually performed for cases where it
155
// is not actually needed.
156
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         // work out the new character order
164
newCharOrder = doBidiReorder(charIndices, charLevels,
165                                            numChars, maxBiDi);
166
167         // construct the string in the new order
168
StringBuffer JavaDoc reorderedString = new StringBuffer JavaDoc();
169         char c;
170         for (int i = 0; i < numChars; i++) {
171             c = aci.setIndex(newCharOrder[i]);
172
173             // check for mirrored char
174
int bidiLevel = tl.getCharacterLevel(newCharOrder[i]);
175             if ((bidiLevel & 0x01) != 0) {
176                 // bidi level is odd so writing dir is right to left
177
// So get the mirror version of the char if there
178
// is one.
179
c = (char)mirrorChar(c);
180             }
181
182             reorderedString.append(c);
183         }
184
185         // construct the reordered ACI
186
AttributedString JavaDoc reorderedAS
187             = new AttributedString JavaDoc(reorderedString.toString());
188         Map JavaDoc [] attrs = new Map JavaDoc[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 JavaDoc 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 JavaDoc prevAttrMap = attrs[newCharOrder[0]];
203         for (int i = 1; i < numChars; i++) {
204             Map JavaDoc attrMap = attrs[newCharOrder[i]];
205             if (attrMap != prevAttrMap) {
206                 // Change in attrs set last run...
207
reorderedAS.addAttributes(prevAttrMap, runStart, i);
208                 prevAttrMap = attrMap;
209                 runStart = i;
210             }
211         }
212         reorderedAS.addAttributes(prevAttrMap, runStart, numChars);
213
214         // transfer any position atttributes to the new first char
215
aci.first();
216         Float JavaDoc x = (Float JavaDoc) aci.getAttribute
217             (GVTAttributedCharacterIterator.TextAttribute.X);
218         Float JavaDoc y = (Float JavaDoc) aci.getAttribute
219             (GVTAttributedCharacterIterator.TextAttribute.Y);
220
221         if (x != null && !x.isNaN()) {
222             reorderedAS.addAttribute
223                 (GVTAttributedCharacterIterator.TextAttribute.X,
224                  new Float JavaDoc(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 JavaDoc(Float.NaN), newCharOrder[0], newCharOrder[0]+1);
232             reorderedAS.addAttribute
233                 (GVTAttributedCharacterIterator.TextAttribute.Y, y, 0, 1);
234         }
235
236         // assign arabic form attributes to any arabic chars in the string
237
reorderedAS = ArabicTextHandler.assignArabicForms(reorderedAS);
238
239         // Shift the values to match the source text string...
240
for (int i=0; i<newCharOrder.length; i++) {
241             newCharOrder[i] += chunkStart;
242         }
243         reorderedACI = reorderedAS.getIterator();
244     }
245
246     // Returns an array that give the character index in the source ACI for
247
// each character in this ACI.
248
public int[] getCharMap() { return newCharOrder; }
249
250     /**
251      * Calculates the display order of the characters based on the specified
252      * character levels. This method is recursive.
253      *
254      * @param charIndices An array contianing the original indices of each char.
255      * @param charLevels An array containing the current levels of each char.
256      * @param numChars The number of chars to reorder.
257      *
258      * @return An array contianing the reordered character indices.
259      */

260     private int[] doBidiReorder(int[] charIndices, int[] charLevels,
261                                 int numChars, int highestLevel) {
262         if (highestLevel == 0) return charIndices;
263
264         // find all groups of chars at the highest level and reverse
265
// their order
266
int currentIndex = 0;
267         while (currentIndex < numChars) {
268
269             // find the first char at the highest index
270
while ((currentIndex < numChars) &&
271                    (charLevels[currentIndex] < highestLevel)) {
272                 currentIndex++;
273             }
274             if (currentIndex == numChars) {
275                 // have reached the end of the string
276
break;
277             }
278             int startIndex = currentIndex;
279
280             currentIndex++;
281             // now find the index where the run at the highestLevel end
282
while ((currentIndex < numChars) &&
283                    (charLevels[currentIndex] == highestLevel)) {
284                 currentIndex++;
285             }
286             int endIndex = currentIndex-1;
287
288             // now reverse the chars between startIndex and endIndex
289

290             // Calculate the middle of the swap region, we include
291
// the middle char when region is an odd number of
292
// chars wide so we properly decriment it's charLevel.
293
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     /**
310      * Get the keys of all attributes defined on the iterator's text range.
311      */

312     public Set JavaDoc getAllAttributeKeys() {
313         return reorderedACI.getAllAttributeKeys();
314     }
315
316     /**
317      * Get the value of the named attribute for the current
318      * character.
319      */

320     public Object JavaDoc getAttribute(AttributedCharacterIterator.Attribute JavaDoc attribute) {
321         return reorderedACI.getAttribute(attribute);
322     }
323
324     /**
325      * Returns a map with the attributes defined on the current
326      * character.
327      */

328     public Map JavaDoc getAttributes() {
329         return reorderedACI.getAttributes();
330     }
331
332     /**
333      * Get the index of the first character following the
334      * run with respect to all attributes containing the current
335      * character.
336      */

337     public int getRunLimit() {
338         return reorderedACI.getRunLimit();
339     }
340
341     /**
342      * Get the index of the first character following the
343      * run with respect to the given attribute containing the current
344      * character.
345      */

346     public int getRunLimit(AttributedCharacterIterator.Attribute JavaDoc attribute) {
347         return reorderedACI.getRunLimit(attribute);
348     }
349
350     /**
351      * Get the index of the first character following the
352      * run with respect to the given attributes containing the current
353      * character.
354      */

355     public int getRunLimit(Set JavaDoc attributes) {
356         return reorderedACI.getRunLimit(attributes);
357     }
358
359     /**
360      * Get the index of the first character of the run with
361      * respect to all attributes containing the current character.
362      */

363     public int getRunStart() {
364         return reorderedACI.getRunStart();
365     }
366
367     /**
368      * Get the index of the first character of the run with
369      * respect to the given attribute containing the current character.
370      * @param attribute The attribute for whose appearance the first offset
371      * is requested.
372      */

373     public int getRunStart(AttributedCharacterIterator.Attribute JavaDoc attribute) {
374         return reorderedACI.getRunStart(attribute);
375     }
376
377     /**
378      * Get the index of the first character of the run with
379      * respect to the given attributes containing the current character.
380      * @param attributes the Set of attributes which begins at the returned index.
381      */

382     public int getRunStart(Set JavaDoc attributes) {
383         return reorderedACI.getRunStart(attributes);
384     }
385
386     /**
387      * Creates a copy of this iterator.
388      */

389     public Object JavaDoc clone() {
390         return new BidiAttributedCharacterIterator
391             ((AttributedCharacterIterator JavaDoc)reorderedACI.clone(),
392              frc, chunkStart, (int [])newCharOrder.clone());
393     }
394
395     /**
396      * Gets the character at the current position (as returned by getIndex()).
397      */

398     public char current() {
399         return reorderedACI.current();
400     }
401
402     /**
403      * Sets the position to getBeginIndex() and returns the character at
404      * that position.
405      */

406     public char first() {
407         return reorderedACI.first();
408     }
409
410     /**
411      * Returns the start index of the text.
412      */

413     public int getBeginIndex() {
414         return reorderedACI.getBeginIndex();
415     }
416
417     /**
418      * Returns the end index of the text.
419      */

420     public int getEndIndex() {
421         return reorderedACI.getEndIndex();
422     }
423
424     /**
425      * Returns the current index.
426      */

427     public int getIndex() {
428         return reorderedACI.getIndex();
429     }
430
431     /**
432      * Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty)
433      * and returns the character at that position.
434      */

435     public char last() {
436         return reorderedACI.last();
437     }
438
439     /**
440      * Increments the iterator's index by one and returns the character at
441      * the new index.
442      */

443     public char next() {
444         return reorderedACI.next();
445     }
446
447     /**
448      * Decrements the iterator's index by one and returns the character at the new index.
449      */

450     public char previous() {
451         return reorderedACI.previous();
452     }
453
454     /**
455      * Sets the position to the specified position in the text and returns that character.
456      */

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             // set up the mirrored glyph case statement;
465
case 0x0028: return 0x0029; //LEFT PARENTHESIS
466
case 0x0029: return 0x0028; //RIGHT PARENTHESIS
467
case 0x003C: return 0x003E; //LESS-THAN SIGN
468
case 0x003E: return 0x003C; //GREATER-THAN SIGN
469
case 0x005B: return 0x005D; //LEFT SQUARE BRACKET
470
case 0x005D: return 0x005B; //RIGHT SQUARE BRACKET
471
case 0x007B: return 0x007D; //LEFT CURLY BRACKET
472
case 0x007D: return 0x007B; //RIGHT CURLY BRACKET
473
case 0x00AB: return 0x00BB; //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
474
case 0x00BB: return 0x00AB; //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
475
case 0x2039: return 0x203A; //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
476
case 0x203A: return 0x2039; //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
477
case 0x2045: return 0x2046; //LEFT SQUARE BRACKET WITH QUILL
478
case 0x2046: return 0x2045; //RIGHT SQUARE BRACKET WITH QUILL
479
case 0x207D: return 0x207E; //SUPERSCRIPT LEFT PARENTHESIS
480
case 0x207E: return 0x207D; //SUPERSCRIPT RIGHT PARENTHESIS
481
case 0x208D: return 0x208E; //SUBSCRIPT LEFT PARENTHESIS
482
case 0x208E: return 0x208D; //SUBSCRIPT RIGHT PARENTHESIS
483
case 0x2208: return 0x220B; //ELEMENT OF
484
case 0x2209: return 0x220C; //NOT AN ELEMENT OF
485
case 0x220A: return 0x220D; //SMALL ELEMENT OF
486
case 0x220B: return 0x2208; //CONTAINS AS MEMBER
487
case 0x220C: return 0x2209; //DOES NOT CONTAIN AS MEMBER
488
case 0x220D: return 0x220A; //SMALL CONTAINS AS MEMBER
489
case 0x223C: return 0x223D; //TILDE OPERATOR
490
case 0x223D: return 0x223C; //REVERSED TILDE
491
case 0x2243: return 0x22CD; //ASYMPTOTICALLY EQUAL TO
492
case 0x2252: return 0x2253; //APPROXIMATELY EQUAL TO OR THE IMAGE OF
493
case 0x2253: return 0x2252; //IMAGE OF OR APPROXIMATELY EQUAL TO
494
case 0x2254: return 0x2255; //COLON EQUALS
495
case 0x2255: return 0x2254; //EQUALS COLON
496
case 0x2264: return 0x2265; //LESS-THAN OR EQUAL TO
497
case 0x2265: return 0x2264; //GREATER-THAN OR EQUAL TO
498
case 0x2266: return 0x2267; //LESS-THAN OVER EQUAL TO
499
case 0x2267: return 0x2266; //GREATER-THAN OVER EQUAL TO
500
case 0x2268: return 0x2269; //[BEST FIT] LESS-THAN BUT NOT EQUAL TO
501
case 0x2269: return 0x2268; //[BEST FIT] GREATER-THAN BUT NOT EQUAL TO
502
case 0x226A: return 0x226B; //MUCH LESS-THAN
503
case 0x226B: return 0x226A; //MUCH GREATER-THAN
504
case 0x226E: return 0x226F; //[BEST FIT] NOT LESS-THAN
505
case 0x226F: return 0x226E; //[BEST FIT] NOT GREATER-THAN
506
case 0x2270: return 0x2271; //[BEST FIT] NEITHER LESS-THAN NOR EQUAL TO
507
case 0x2271: return 0x2270; //[BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO
508
case 0x2272: return 0x2273; //[BEST FIT] LESS-THAN OR EQUIVALENT TO
509
case 0x2273: return 0x2272; //[BEST FIT] GREATER-THAN OR EQUIVALENT TO
510
case 0x2274: return 0x2275; //[BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO
511
case 0x2275: return 0x2274; //[BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO
512
case 0x2276: return 0x2277; //LESS-THAN OR GREATER-THAN
513
case 0x2277: return 0x2276; //GREATER-THAN OR LESS-THAN
514
case 0x2278: return 0x2279; //NEITHER LESS-THAN NOR GREATER-THAN
515
case 0x2279: return 0x2278; //NEITHER GREATER-THAN NOR LESS-THAN
516
case 0x227A: return 0x227B; //PRECEDES
517
case 0x227B: return 0x227A; //SUCCEEDS
518
case 0x227C: return 0x227D; //PRECEDES OR EQUAL TO
519
case 0x227D: return 0x227C; //SUCCEEDS OR EQUAL TO
520
case 0x227E: return 0x227F; //[BEST FIT] PRECEDES OR EQUIVALENT TO
521
case 0x227F: return 0x227E; //[BEST FIT] SUCCEEDS OR EQUIVALENT TO
522
case 0x2280: return 0x2281; //[BEST FIT] DOES NOT PRECEDE
523
case 0x2281: return 0x2280; //[BEST FIT] DOES NOT SUCCEED
524
case 0x2282: return 0x2283; //SUBSET OF
525
case 0x2283: return 0x2282; //SUPERSET OF
526
case 0x2284: return 0x2285; //[BEST FIT] NOT A SUBSET OF
527
case 0x2285: return 0x2284; //[BEST FIT] NOT A SUPERSET OF
528
case 0x2286: return 0x2287; //SUBSET OF OR EQUAL TO
529
case 0x2287: return 0x2286; //SUPERSET OF OR EQUAL TO
530
case 0x2288: return 0x2289; //[BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO
531
case 0x2289: return 0x2288; //[BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO
532
case 0x228A: return 0x228B; //[BEST FIT] SUBSET OF WITH NOT EQUAL TO
533
case 0x228B: return 0x228A; //[BEST FIT] SUPERSET OF WITH NOT EQUAL TO
534
case 0x228F: return 0x2290; //SQUARE IMAGE OF
535
case 0x2290: return 0x228F; //SQUARE ORIGINAL OF
536
case 0x2291: return 0x2292; //SQUARE IMAGE OF OR EQUAL TO
537
case 0x2292: return 0x2291; //SQUARE ORIGINAL OF OR EQUAL TO
538
case 0x22A2: return 0x22A3; //RIGHT TACK
539
case 0x22A3: return 0x22A2; //LEFT TACK
540
case 0x22B0: return 0x22B1; //PRECEDES UNDER RELATION
541
case 0x22B1: return 0x22B0; //SUCCEEDS UNDER RELATION
542
case 0x22B2: return 0x22B3; //NORMAL SUBGROUP OF
543
case 0x22B3: return 0x22B2; //CONTAINS AS NORMAL SUBGROUP
544
case 0x22B4: return 0x22B5; //NORMAL SUBGROUP OF OR EQUAL TO
545
case 0x22B5: return 0x22B4; //CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
546
case 0x22B6: return 0x22B7; //ORIGINAL OF
547
case 0x22B7: return 0x22B6; //IMAGE OF
548
case 0x22C9: return 0x22CA; //LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
549
case 0x22CA: return 0x22C9; //RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
550
case 0x22CB: return 0x22CC; //LEFT SEMIDIRECT PRODUCT
551
case 0x22CC: return 0x22CB; //RIGHT SEMIDIRECT PRODUCT
552
case 0x22CD: return 0x2243; //REVERSED TILDE EQUALS
553
case 0x22D0: return 0x22D1; //DOUBLE SUBSET
554
case 0x22D1: return 0x22D0; //DOUBLE SUPERSET
555
case 0x22D6: return 0x22D7; //LESS-THAN WITH DOT
556
case 0x22D7: return 0x22D6; //GREATER-THAN WITH DOT
557
case 0x22D8: return 0x22D9; //VERY MUCH LESS-THAN
558
case 0x22D9: return 0x22D8; //VERY MUCH GREATER-THAN
559
case 0x22DA: return 0x22DB; //LESS-THAN EQUAL TO OR GREATER-THAN
560
case 0x22DB: return 0x22DA; //GREATER-THAN EQUAL TO OR LESS-THAN
561
case 0x22DC: return 0x22DD; //EQUAL TO OR LESS-THAN
562
case 0x22DD: return 0x22DC; //EQUAL TO OR GREATER-THAN
563
case 0x22DE: return 0x22DF; //EQUAL TO OR PRECEDES
564
case 0x22DF: return 0x22DE; //EQUAL TO OR SUCCEEDS
565
case 0x22E0: return 0x22E1; //[BEST FIT] DOES NOT PRECEDE OR EQUAL
566
case 0x22E1: return 0x22E0; //[BEST FIT] DOES NOT SUCCEED OR EQUAL
567
case 0x22E2: return 0x22E3; //[BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO
568
case 0x22E3: return 0x22E2; //[BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO
569
case 0x22E4: return 0x22E5; //[BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO
570
case 0x22E5: return 0x22E4; //[BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO
571
case 0x22E6: return 0x22E7; //[BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO
572
case 0x22E7: return 0x22E6; //[BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO
573
case 0x22E8: return 0x22E9; //[BEST FIT] PRECEDES BUT NOT EQUIVALENT TO
574
case 0x22E9: return 0x22E8; //[BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO
575
case 0x22EA: return 0x22EB; //[BEST FIT] NOT NORMAL SUBGROUP OF
576
case 0x22EB: return 0x22EA; //[BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP
577
case 0x22EC: return 0x22ED; //[BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO
578
case 0x22ED: return 0x22EC; //[BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
579
case 0x22F0: return 0x22F1; //UP RIGHT DIAGONAL ELLIPSIS
580
case 0x22F1: return 0x22F0; //DOWN RIGHT DIAGONAL ELLIPSIS
581
case 0x2308: return 0x2309; //LEFT CEILING
582
case 0x2309: return 0x2308; //RIGHT CEILING
583
case 0x230A: return 0x230B; //LEFT FLOOR
584
case 0x230B: return 0x230A; //RIGHT FLOOR
585
case 0x2329: return 0x232A; //LEFT-POINTING ANGLE BRACKET
586
case 0x232A: return 0x2329; //RIGHT-POINTING ANGLE BRACKET
587
case 0x3008: return 0x3009; //LEFT ANGLE BRACKET
588
case 0x3009: return 0x3008; //RIGHT ANGLE BRACKET
589
case 0x300A: return 0x300B; //LEFT DOUBLE ANGLE BRACKET
590
case 0x300B: return 0x300A; //RIGHT DOUBLE ANGLE BRACKET
591
case 0x300C: return 0x300D; //[BEST FIT] LEFT CORNER BRACKET
592
case 0x300D: return 0x300C; //[BEST FIT] RIGHT CORNER BRACKET
593
case 0x300E: return 0x300F; //[BEST FIT] LEFT WHITE CORNER BRACKET
594
case 0x300F: return 0x300E; //[BEST FIT] RIGHT WHITE CORNER BRACKET
595
case 0x3010: return 0x3011; //LEFT BLACK LENTICULAR BRACKET
596
case 0x3011: return 0x3010; //RIGHT BLACK LENTICULAR BRACKET
597
case 0x3014: return 0x3015; //[BEST FIT] LEFT TORTOISE SHELL BRACKET
598
case 0x3015: return 0x3014; //[BEST FIT] RIGHT TORTOISE SHELL BRACKET
599
case 0x3016: return 0x3017; //LEFT WHITE LENTICULAR BRACKET
600
case 0x3017: return 0x3016; //RIGHT WHITE LENTICULAR BRACKET
601
case 0x3018: return 0x3019; //LEFT WHITE TORTOISE SHELL BRACKET
602
case 0x3019: return 0x3018; //RIGHT WHITE TORTOISE SHELL BRACKET
603
case 0x301A: return 0x301B; //LEFT WHITE SQUARE BRACKET
604
case 0x301B: return 0x301A; //RIGHT WHITE SQUARE BRACKET
605
default: break;
606         }
607         return c;
608     }
609 }
610
Popular Tags