1
48
49 package com.lowagie.text.pdf;
50
51 import java.util.ArrayList;
52
53 import com.lowagie.text.Chunk;
54 import com.lowagie.text.Utilities;
55
56
60 public class BidiLine {
61
62 protected int runDirection;
63 protected int pieceSize = 256;
64 protected char text[] = new char[pieceSize];
65 protected PdfChunk detailChunks[] = new PdfChunk[pieceSize];
66 protected int totalTextLength = 0;
67
68 protected byte orderLevels[] = new byte[pieceSize];
69 protected int indexChars[] = new int[pieceSize];
70
71 protected ArrayList chunks = new ArrayList();
72 protected int indexChunk = 0;
73 protected int indexChunkChar = 0;
74 protected int currentChar = 0;
75
76 protected int storedRunDirection;
77 protected char storedText[] = new char[0];
78 protected PdfChunk storedDetailChunks[] = new PdfChunk[0];
79 protected int storedTotalTextLength = 0;
80
81 protected byte storedOrderLevels[] = new byte[0];
82 protected int storedIndexChars[] = new int[0];
83
84 protected int storedIndexChunk = 0;
85 protected int storedIndexChunkChar = 0;
86 protected int storedCurrentChar = 0;
87
88 protected boolean shortStore;
89
90 protected static final IntHashtable mirrorChars = new IntHashtable();
91 protected int arabicOptions;
92
93
94 public BidiLine() {
95 }
96
97 public BidiLine(BidiLine org) {
98 runDirection = org.runDirection;
99 pieceSize = org.pieceSize;
100 text = (char[])org.text.clone();
101 detailChunks = (PdfChunk[])org.detailChunks.clone();
102 totalTextLength = org.totalTextLength;
103
104 orderLevels = (byte[])org.orderLevels.clone();
105 indexChars = (int[])org.indexChars.clone();
106
107 chunks = new ArrayList(org.chunks);
108 indexChunk = org.indexChunk;
109 indexChunkChar = org.indexChunkChar;
110 currentChar = org.currentChar;
111
112 storedRunDirection = org.storedRunDirection;
113 storedText = (char[])org.storedText.clone();
114 storedDetailChunks = (PdfChunk[])org.storedDetailChunks.clone();
115 storedTotalTextLength = org.storedTotalTextLength;
116
117 storedOrderLevels = (byte[])org.storedOrderLevels.clone();
118 storedIndexChars = (int[])org.storedIndexChars.clone();
119
120 storedIndexChunk = org.storedIndexChunk;
121 storedIndexChunkChar = org.storedIndexChunkChar;
122 storedCurrentChar = org.storedCurrentChar;
123
124 shortStore = org.shortStore;
125 arabicOptions = org.arabicOptions;
126 }
127
128 public boolean isEmpty() {
129 return (currentChar >= totalTextLength && indexChunk >= chunks.size());
130 }
131
132 public void clearChunks() {
133 chunks.clear();
134 totalTextLength = 0;
135 currentChar = 0;
136 }
137
138 public boolean getParagraph(int runDirection) {
139 this.runDirection = runDirection;
140 currentChar = 0;
141 totalTextLength = 0;
142 boolean hasText = false;
143 char c;
144 char uniC;
145 BaseFont bf;
146 for (; indexChunk < chunks.size(); ++indexChunk) {
147 PdfChunk ck = (PdfChunk)chunks.get(indexChunk);
148 bf = ck.font().getFont();
149 String s = ck.toString();
150 int len = s.length();
151 for (; indexChunkChar < len; ++indexChunkChar) {
152 c = s.charAt(indexChunkChar);
153 uniC = (char)bf.getUnicodeEquivalent(c);
154 if (uniC == '\r' || uniC == '\n') {
155
156 if (uniC == '\r' && indexChunkChar + 1 < len && s.charAt(indexChunkChar + 1) == '\n')
157 ++indexChunkChar;
158 ++indexChunkChar;
159 if (indexChunkChar >= len) {
160 indexChunkChar = 0;
161 ++indexChunk;
162 }
163 hasText = true;
164 if (totalTextLength == 0)
165 detailChunks[0] = ck;
166 break;
167 }
168 addPiece(c, ck);
169 }
170 if (hasText)
171 break;
172 indexChunkChar = 0;
173 }
174 if (totalTextLength == 0)
175 return hasText;
176
177
178 totalTextLength = trimRight(0, totalTextLength - 1) + 1;
179 if (totalTextLength == 0) {
180 return true;
181 }
182
183 if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) {
184 if (orderLevels.length < totalTextLength) {
185 orderLevels = new byte[pieceSize];
186 indexChars = new int[pieceSize];
187 }
188 ArabicLigaturizer.processNumbers(text, 0, totalTextLength, arabicOptions);
189 BidiOrder order = new BidiOrder(text, 0, totalTextLength, (byte)(runDirection == PdfWriter.RUN_DIRECTION_RTL ? 1 : 0));
190 byte od[] = order.getLevels();
191 for (int k = 0; k < totalTextLength; ++k) {
192 orderLevels[k] = od[k];
193 indexChars[k] = k;
194 }
195 doArabicShapping();
196 mirrorGlyphs();
197 }
198 totalTextLength = trimRightEx(0, totalTextLength - 1) + 1;
199 return true;
200 }
201
202 public void addChunk(PdfChunk chunk) {
203 chunks.add(chunk);
204 }
205
206 public void addChunks(ArrayList chunks) {
207 this.chunks.addAll(chunks);
208 }
209
210 public void addPiece(char c, PdfChunk chunk) {
211 if (totalTextLength >= pieceSize) {
212 char tempText[] = text;
213 PdfChunk tempDetailChunks[] = detailChunks;
214 pieceSize *= 2;
215 text = new char[pieceSize];
216 detailChunks = new PdfChunk[pieceSize];
217 System.arraycopy(tempText, 0, text, 0, totalTextLength);
218 System.arraycopy(tempDetailChunks, 0, detailChunks, 0, totalTextLength);
219 }
220 text[totalTextLength] = c;
221 detailChunks[totalTextLength++] = chunk;
222 }
223
224 public void save() {
225 if (indexChunk > 0) {
226 if (indexChunk >= chunks.size())
227 chunks.clear();
228 else {
229 for (--indexChunk; indexChunk >= 0; --indexChunk)
230 chunks.remove(indexChunk);
231 }
232 indexChunk = 0;
233 }
234 storedRunDirection = runDirection;
235 storedTotalTextLength = totalTextLength;
236 storedIndexChunk = indexChunk;
237 storedIndexChunkChar = indexChunkChar;
238 storedCurrentChar = currentChar;
239 shortStore = (currentChar < totalTextLength);
240 if (!shortStore) {
241
242 if (storedText.length < totalTextLength) {
243 storedText = new char[totalTextLength];
244 storedDetailChunks = new PdfChunk[totalTextLength];
245 }
246 System.arraycopy(text, 0, storedText, 0, totalTextLength);
247 System.arraycopy(detailChunks, 0, storedDetailChunks, 0, totalTextLength);
248 }
249 if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) {
250 if (storedOrderLevels.length < totalTextLength) {
251 storedOrderLevels = new byte[totalTextLength];
252 storedIndexChars = new int[totalTextLength];
253 }
254 System.arraycopy(orderLevels, currentChar, storedOrderLevels, currentChar, totalTextLength - currentChar);
255 System.arraycopy(indexChars, currentChar, storedIndexChars, currentChar, totalTextLength - currentChar);
256 }
257 }
258
259 public void restore() {
260 runDirection = storedRunDirection;
261 totalTextLength = storedTotalTextLength;
262 indexChunk = storedIndexChunk;
263 indexChunkChar = storedIndexChunkChar;
264 currentChar = storedCurrentChar;
265 if (!shortStore) {
266
267 System.arraycopy(storedText, 0, text, 0, totalTextLength);
268 System.arraycopy(storedDetailChunks, 0, detailChunks, 0, totalTextLength);
269 }
270 if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) {
271 System.arraycopy(storedOrderLevels, currentChar, orderLevels, currentChar, totalTextLength - currentChar);
272 System.arraycopy(storedIndexChars, currentChar, indexChars, currentChar, totalTextLength - currentChar);
273 }
274 }
275
276 public void mirrorGlyphs() {
277 for (int k = 0; k < totalTextLength; ++k) {
278 if ((orderLevels[k] & 1) == 1) {
279 int mirror = mirrorChars.get(text[k]);
280 if (mirror != 0)
281 text[k] = (char)mirror;
282 }
283 }
284 }
285
286 public void doArabicShapping() {
287 int src = 0;
288 int dest = 0;
289 for (;;) {
290 while (src < totalTextLength) {
291 char c = text[src];
292 if (c >= 0x0600 && c <= 0x06ff)
293 break;
294 if (src != dest) {
295 text[dest] = text[src];
296 detailChunks[dest] = detailChunks[src];
297 orderLevels[dest] = orderLevels[src];
298 }
299 ++src;
300 ++dest;
301 }
302 if (src >= totalTextLength) {
303 totalTextLength = dest;
304 return;
305 }
306 int startArabicIdx = src;
307 ++src;
308 while (src < totalTextLength) {
309 char c = text[src];
310 if (c < 0x0600 || c > 0x06ff)
311 break;
312 ++src;
313 }
314 int arabicWordSize = src - startArabicIdx;
315 int size = ArabicLigaturizer.arabic_shape(text, startArabicIdx, arabicWordSize, text, dest, arabicWordSize, arabicOptions);
316 if (startArabicIdx != dest) {
317 for (int k = 0; k < size; ++k) {
318 detailChunks[dest] = detailChunks[startArabicIdx];
319 orderLevels[dest++] = orderLevels[startArabicIdx++];
320 }
321 }
322 else
323 dest += size;
324 }
325 }
326
327 public PdfLine processLine(float leftX, float width, int alignment, int runDirection, int arabicOptions) {
328 this.arabicOptions = arabicOptions;
329 save();
330 boolean isRTL = (runDirection == PdfWriter.RUN_DIRECTION_RTL);
331 if (currentChar >= totalTextLength) {
332 boolean hasText = getParagraph(runDirection);
333 if (!hasText)
334 return null;
335 if (totalTextLength == 0) {
336 ArrayList ar = new ArrayList();
337 PdfChunk ck = new PdfChunk("", detailChunks[0]);
338 ar.add(ck);
339 return new PdfLine(0, 0, 0, alignment, true, ar, isRTL);
340 }
341 }
342 float originalWidth = width;
343 int lastSplit = -1;
344 if (currentChar != 0)
345 currentChar = trimLeftEx(currentChar, totalTextLength - 1);
346 int oldCurrentChar = currentChar;
347 int uniC = 0;
348 PdfChunk ck = null;
349 float charWidth = 0;
350 PdfChunk lastValidChunk = null;
351 boolean splitChar = false;
352 boolean surrogate = false;
353 for (; currentChar < totalTextLength; ++currentChar) {
354 ck = detailChunks[currentChar];
355 surrogate = Utilities.isSurrogatePair(text, currentChar);
356 if (surrogate)
357 uniC = ck.getUnicodeEquivalent(Utilities.convertToUtf32(text, currentChar));
358 else
359 uniC = ck.getUnicodeEquivalent(text[currentChar]);
360 if (PdfChunk.noPrint(uniC))
361 continue;
362 if (surrogate)
363 charWidth = ck.getCharWidth(uniC);
364 else
365 charWidth = ck.getCharWidth(text[currentChar]);
366 splitChar = ck.isExtSplitCharacter(oldCurrentChar, currentChar, totalTextLength, text, detailChunks);
367 if (splitChar && Character.isWhitespace((char)uniC))
368 lastSplit = currentChar;
369 if (width - charWidth < 0)
370 break;
371 if (splitChar)
372 lastSplit = currentChar;
373 width -= charWidth;
374 lastValidChunk = ck;
375 if (ck.isTab()) {
376 Object[] tab = (Object[])ck.getAttribute(Chunk.TAB);
377 float tabPosition = ((Float)tab[1]).floatValue();
378 boolean newLine = ((Boolean)tab[2]).booleanValue();
379 if (newLine && tabPosition < originalWidth - width) {
380 return new PdfLine(0, originalWidth, width, alignment, true, createArrayOfPdfChunks(oldCurrentChar, currentChar - 1), isRTL);
381 }
382 detailChunks[currentChar].adjustLeft(leftX);
383 width = originalWidth - tabPosition;
384 }
385 if (surrogate)
386 ++currentChar;
387 }
388 if (lastValidChunk == null) {
389
390 ++currentChar;
391 if (surrogate)
392 ++currentChar;
393 return new PdfLine(0, originalWidth, 0, alignment, false, createArrayOfPdfChunks(currentChar - 1, currentChar - 1), isRTL);
394 }
395 if (currentChar >= totalTextLength) {
396
397 return new PdfLine(0, originalWidth, width, alignment, true, createArrayOfPdfChunks(oldCurrentChar, totalTextLength - 1), isRTL);
398 }
399 int newCurrentChar = trimRightEx(oldCurrentChar, currentChar - 1);
400 if (newCurrentChar < oldCurrentChar) {
401
402 return new PdfLine(0, originalWidth, width, alignment, false, createArrayOfPdfChunks(oldCurrentChar, currentChar - 1), isRTL);
403 }
404 if (newCurrentChar == currentChar - 1) {
405 HyphenationEvent he = (HyphenationEvent)lastValidChunk.getAttribute(Chunk.HYPHENATION);
406 if (he != null) {
407 int word[] = getWord(oldCurrentChar, newCurrentChar);
408 if (word != null) {
409 float testWidth = width + getWidth(word[0], currentChar - 1);
410 String pre = he.getHyphenatedWordPre(new String(text, word[0], word[1] - word[0]), lastValidChunk.font().getFont(), lastValidChunk.font().size(), testWidth);
411 String post = he.getHyphenatedWordPost();
412 if (pre.length() > 0) {
413 PdfChunk extra = new PdfChunk(pre, lastValidChunk);
414 currentChar = word[1] - post.length();
415 return new PdfLine(0, originalWidth, testWidth - lastValidChunk.font().width(pre), alignment, false, createArrayOfPdfChunks(oldCurrentChar, word[0] - 1, extra), isRTL);
416 }
417 }
418 }
419 }
420 if (lastSplit == -1 || lastSplit >= newCurrentChar) {
421
422 return new PdfLine(0, originalWidth, width + getWidth(newCurrentChar + 1, currentChar - 1), alignment, false, createArrayOfPdfChunks(oldCurrentChar, newCurrentChar), isRTL);
423 }
424
425 currentChar = lastSplit + 1;
426 newCurrentChar = trimRightEx(oldCurrentChar, lastSplit);
427 if (newCurrentChar < oldCurrentChar) {
428
429 newCurrentChar = currentChar - 1;
430 }
431 return new PdfLine(0, originalWidth, originalWidth - getWidth(oldCurrentChar, newCurrentChar), alignment, false, createArrayOfPdfChunks(oldCurrentChar, newCurrentChar), isRTL);
432 }
433
434
439 public float getWidth(int startIdx, int lastIdx) {
440 char c = 0;
441 char uniC;
442 PdfChunk ck = null;
443 float width = 0;
444 for (; startIdx <= lastIdx; ++startIdx) {
445 boolean surrogate = Utilities.isSurrogatePair(text, startIdx);
446 if (surrogate) {
447 width += detailChunks[startIdx].getCharWidth(Utilities.convertToUtf32(text, startIdx));
448 ++startIdx;
449 }
450 else {
451 c = text[startIdx];
452 ck = detailChunks[startIdx];
453 if (PdfChunk.noPrint(ck.getUnicodeEquivalent(c)))
454 continue;
455 width += detailChunks[startIdx].getCharWidth(c);
456 }
457 }
458 return width;
459 }
460
461 public ArrayList createArrayOfPdfChunks(int startIdx, int endIdx) {
462 return createArrayOfPdfChunks(startIdx, endIdx, null);
463 }
464
465 public ArrayList createArrayOfPdfChunks(int startIdx, int endIdx, PdfChunk extraPdfChunk) {
466 boolean bidi = (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL);
467 if (bidi)
468 reorder(startIdx, endIdx);
469 ArrayList ar = new ArrayList();
470 PdfChunk refCk = detailChunks[startIdx];
471 PdfChunk ck = null;
472 StringBuffer buf = new StringBuffer();
473 char c;
474 int idx = 0;
475 for (; startIdx <= endIdx; ++startIdx) {
476 idx = bidi ? indexChars[startIdx] : startIdx;
477 c = text[idx];
478 ck = detailChunks[idx];
479 if (PdfChunk.noPrint(ck.getUnicodeEquivalent(c)))
480 continue;
481 if (ck.isImage() || ck.isSeparator() || ck.isTab()) {
482 if (buf.length() > 0) {
483 ar.add(new PdfChunk(buf.toString(), refCk));
484 buf = new StringBuffer();
485 }
486 ar.add(ck);
487 }
488 else if (ck == refCk) {
489 buf.append(c);
490 }
491 else {
492 if (buf.length() > 0) {
493 ar.add(new PdfChunk(buf.toString(), refCk));
494 buf = new StringBuffer();
495 }
496 if (!ck.isImage() && !ck.isSeparator() && !ck.isTab())
497 buf.append(c);
498 refCk = ck;
499 }
500 }
501 if (buf.length() > 0) {
502 ar.add(new PdfChunk(buf.toString(), refCk));
503 }
504 if (extraPdfChunk != null)
505 ar.add(extraPdfChunk);
506 return ar;
507 }
508
509 public int[] getWord(int startIdx, int idx) {
510 int last = idx;
511 int first = idx;
512
513 for (; last < totalTextLength; ++last) {
514 if (!Character.isLetter(text[last]))
515 break;
516 }
517 if (last == idx)
518 return null;
519
520 for (; first >= startIdx; --first) {
521 if (!Character.isLetter(text[first]))
522 break;
523 }
524 ++first;
525 return new int[]{first, last};
526 }
527
528 public int trimRight(int startIdx, int endIdx) {
529 int idx = endIdx;
530 char c;
531 for (; idx >= startIdx; --idx) {
532 c = (char)detailChunks[idx].getUnicodeEquivalent(text[idx]);
533 if (!isWS(c))
534 break;
535 }
536 return idx;
537 }
538
539 public int trimLeft(int startIdx, int endIdx) {
540 int idx = startIdx;
541 char c;
542 for (; idx <= endIdx; ++idx) {
543 c = (char)detailChunks[idx].getUnicodeEquivalent(text[idx]);
544 if (!isWS(c))
545 break;
546 }
547 return idx;
548 }
549
550 public int trimRightEx(int startIdx, int endIdx) {
551 int idx = endIdx;
552 char c = 0;
553 for (; idx >= startIdx; --idx) {
554 c = (char)detailChunks[idx].getUnicodeEquivalent(text[idx]);
555 if (!isWS(c) && !PdfChunk.noPrint(c))
556 break;
557 }
558 return idx;
559 }
560
561 public int trimLeftEx(int startIdx, int endIdx) {
562 int idx = startIdx;
563 char c = 0;
564 for (; idx <= endIdx; ++idx) {
565 c = (char)detailChunks[idx].getUnicodeEquivalent(text[idx]);
566 if (!isWS(c) && !PdfChunk.noPrint(c))
567 break;
568 }
569 return idx;
570 }
571
572 public void reorder(int start, int end) {
573 byte maxLevel = orderLevels[start];
574 byte minLevel = maxLevel;
575 byte onlyOddLevels = maxLevel;
576 byte onlyEvenLevels = maxLevel;
577 for (int k = start + 1; k <= end; ++k) {
578 byte b = orderLevels[k];
579 if (b > maxLevel)
580 maxLevel = b;
581 else if (b < minLevel)
582 minLevel = b;
583 onlyOddLevels &= b;
584 onlyEvenLevels |= b;
585 }
586 if ((onlyEvenLevels & 1) == 0)
587 return;
588 if ((onlyOddLevels & 1) == 1) {
589 flip(start, end + 1);
590 return;
591 }
592 minLevel |= 1;
593 for (; maxLevel >= minLevel; --maxLevel) {
594 int pstart = start;
595 for (;;) {
596 for (;pstart <= end; ++pstart) {
597 if (orderLevels[pstart] >= maxLevel)
598 break;
599 }
600 if (pstart > end)
601 break;
602 int pend = pstart + 1;
603 for (; pend <= end; ++pend) {
604 if (orderLevels[pend] < maxLevel)
605 break;
606 }
607 flip(pstart, pend);
608 pstart = pend + 1;
609 }
610 }
611 }
612
613 public void flip(int start, int end) {
614 int mid = (start + end) / 2;
615 --end;
616 for (; start < mid; ++start, --end) {
617 int temp = indexChars[start];
618 indexChars[start] = indexChars[end];
619 indexChars[end] = temp;
620 }
621 }
622
623 public static boolean isWS(char c) {
624 return (c <= ' ');
625 }
626
627 static {
628 mirrorChars.put(0x0028, 0x0029);
629 mirrorChars.put(0x0029, 0x0028);
630 mirrorChars.put(0x003C, 0x003E);
631 mirrorChars.put(0x003E, 0x003C);
632 mirrorChars.put(0x005B, 0x005D);
633 mirrorChars.put(0x005D, 0x005B);
634 mirrorChars.put(0x007B, 0x007D);
635 mirrorChars.put(0x007D, 0x007B);
636 mirrorChars.put(0x00AB, 0x00BB);
637 mirrorChars.put(0x00BB, 0x00AB);
638 mirrorChars.put(0x2039, 0x203A);
639 mirrorChars.put(0x203A, 0x2039);
640 mirrorChars.put(0x2045, 0x2046);
641 mirrorChars.put(0x2046, 0x2045);
642 mirrorChars.put(0x207D, 0x207E);
643 mirrorChars.put(0x207E, 0x207D);
644 mirrorChars.put(0x208D, 0x208E);
645 mirrorChars.put(0x208E, 0x208D);
646 mirrorChars.put(0x2208, 0x220B);
647 mirrorChars.put(0x2209, 0x220C);
648 mirrorChars.put(0x220A, 0x220D);
649 mirrorChars.put(0x220B, 0x2208);
650 mirrorChars.put(0x220C, 0x2209);
651 mirrorChars.put(0x220D, 0x220A);
652 mirrorChars.put(0x2215, 0x29F5);
653 mirrorChars.put(0x223C, 0x223D);
654 mirrorChars.put(0x223D, 0x223C);
655 mirrorChars.put(0x2243, 0x22CD);
656 mirrorChars.put(0x2252, 0x2253);
657 mirrorChars.put(0x2253, 0x2252);
658 mirrorChars.put(0x2254, 0x2255);
659 mirrorChars.put(0x2255, 0x2254);
660 mirrorChars.put(0x2264, 0x2265);
661 mirrorChars.put(0x2265, 0x2264);
662 mirrorChars.put(0x2266, 0x2267);
663 mirrorChars.put(0x2267, 0x2266);
664 mirrorChars.put(0x2268, 0x2269);
665 mirrorChars.put(0x2269, 0x2268);
666 mirrorChars.put(0x226A, 0x226B);
667 mirrorChars.put(0x226B, 0x226A);
668 mirrorChars.put(0x226E, 0x226F);
669 mirrorChars.put(0x226F, 0x226E);
670 mirrorChars.put(0x2270, 0x2271);
671 mirrorChars.put(0x2271, 0x2270);
672 mirrorChars.put(0x2272, 0x2273);
673 mirrorChars.put(0x2273, 0x2272);
674 mirrorChars.put(0x2274, 0x2275);
675 mirrorChars.put(0x2275, 0x2274);
676 mirrorChars.put(0x2276, 0x2277);
677 mirrorChars.put(0x2277, 0x2276);
678 mirrorChars.put(0x2278, 0x2279);
679 mirrorChars.put(0x2279, 0x2278);
680 mirrorChars.put(0x227A, 0x227B);
681 mirrorChars.put(0x227B, 0x227A);
682 mirrorChars.put(0x227C, 0x227D);
683 mirrorChars.put(0x227D, 0x227C);
684 mirrorChars.put(0x227E, 0x227F);
685 mirrorChars.put(0x227F, 0x227E);
686 mirrorChars.put(0x2280, 0x2281);
687 mirrorChars.put(0x2281, 0x2280);
688 mirrorChars.put(0x2282, 0x2283);
689 mirrorChars.put(0x2283, 0x2282);
690 mirrorChars.put(0x2284, 0x2285);
691 mirrorChars.put(0x2285, 0x2284);
692 mirrorChars.put(0x2286, 0x2287);
693 mirrorChars.put(0x2287, 0x2286);
694 mirrorChars.put(0x2288, 0x2289);
695 mirrorChars.put(0x2289, 0x2288);
696 mirrorChars.put(0x228A, 0x228B);
697 mirrorChars.put(0x228B, 0x228A);
698 mirrorChars.put(0x228F, 0x2290);
699 mirrorChars.put(0x2290, 0x228F);
700 mirrorChars.put(0x2291, 0x2292);
701 mirrorChars.put(0x2292, 0x2291);
702 mirrorChars.put(0x2298, 0x29B8);
703 mirrorChars.put(0x22A2, 0x22A3);
704 mirrorChars.put(0x22A3, 0x22A2);
705 mirrorChars.put(0x22A6, 0x2ADE);
706 mirrorChars.put(0x22A8, 0x2AE4);
707 mirrorChars.put(0x22A9, 0x2AE3);
708 mirrorChars.put(0x22AB, 0x2AE5);
709 mirrorChars.put(0x22B0, 0x22B1);
710 mirrorChars.put(0x22B1, 0x22B0);
711 mirrorChars.put(0x22B2, 0x22B3);
712 mirrorChars.put(0x22B3, 0x22B2);
713 mirrorChars.put(0x22B4, 0x22B5);
714 mirrorChars.put(0x22B5, 0x22B4);
715 mirrorChars.put(0x22B6, 0x22B7);
716 mirrorChars.put(0x22B7, 0x22B6);
717 mirrorChars.put(0x22C9, 0x22CA);
718 mirrorChars.put(0x22CA, 0x22C9);
719 mirrorChars.put(0x22CB, 0x22CC);
720 mirrorChars.put(0x22CC, 0x22CB);
721 mirrorChars.put(0x22CD, 0x2243);
722 mirrorChars.put(0x22D0, 0x22D1);
723 mirrorChars.put(0x22D1, 0x22D0);
724 mirrorChars.put(0x22D6, 0x22D7);
725 mirrorChars.put(0x22D7, 0x22D6);
726 mirrorChars.put(0x22D8, 0x22D9);
727 mirrorChars.put(0x22D9, 0x22D8);
728 mirrorChars.put(0x22DA, 0x22DB);
729 mirrorChars.put(0x22DB, 0x22DA);
730 mirrorChars.put(0x22DC, 0x22DD);
731 mirrorChars.put(0x22DD, 0x22DC);
732 mirrorChars.put(0x22DE, 0x22DF);
733 mirrorChars.put(0x22DF, 0x22DE);
734 mirrorChars.put(0x22E0, 0x22E1);
735 mirrorChars.put(0x22E1, 0x22E0);
736 mirrorChars.put(0x22E2, 0x22E3);
737 mirrorChars.put(0x22E3, 0x22E2);
738 mirrorChars.put(0x22E4, 0x22E5);
739 mirrorChars.put(0x22E5, 0x22E4);
740 mirrorChars.put(0x22E6, 0x22E7);
741 mirrorChars.put(0x22E7, 0x22E6);
742 mirrorChars.put(0x22E8, 0x22E9);
743 mirrorChars.put(0x22E9, 0x22E8);
744 mirrorChars.put(0x22EA, 0x22EB);
745 mirrorChars.put(0x22EB, 0x22EA);
746 mirrorChars.put(0x22EC, 0x22ED);
747 mirrorChars.put(0x22ED, 0x22EC);
748 mirrorChars.put(0x22F0, 0x22F1);
749 mirrorChars.put(0x22F1, 0x22F0);
750 mirrorChars.put(0x22F2, 0x22FA);
751 mirrorChars.put(0x22F3, 0x22FB);
752 mirrorChars.put(0x22F4, 0x22FC);
753 mirrorChars.put(0x22F6, 0x22FD);
754 mirrorChars.put(0x22F7, 0x22FE);
755 mirrorChars.put(0x22FA, 0x22F2);
756 mirrorChars.put(0x22FB, 0x22F3);
757 mirrorChars.put(0x22FC, 0x22F4);
758 mirrorChars.put(0x22FD, 0x22F6);
759 mirrorChars.put(0x22FE, 0x22F7);
760 mirrorChars.put(0x2308, 0x2309);
761 mirrorChars.put(0x2309, 0x2308);
762 mirrorChars.put(0x230A, 0x230B);
763 mirrorChars.put(0x230B, 0x230A);
764 mirrorChars.put(0x2329, 0x232A);
765 mirrorChars.put(0x232A, 0x2329);
766 mirrorChars.put(0x2768, 0x2769);
767 mirrorChars.put(0x2769, 0x2768);
768 mirrorChars.put(0x276A, 0x276B);
769 mirrorChars.put(0x276B, 0x276A);
770 mirrorChars.put(0x276C, 0x276D);
771 mirrorChars.put(0x276D, 0x276C);
772 mirrorChars.put(0x276E, 0x276F);
773 mirrorChars.put(0x276F, 0x276E);
774 mirrorChars.put(0x2770, 0x2771);
775 mirrorChars.put(0x2771, 0x2770);
776 mirrorChars.put(0x2772, 0x2773);
777 mirrorChars.put(0x2773, 0x2772);
778 mirrorChars.put(0x2774, 0x2775);
779 mirrorChars.put(0x2775, 0x2774);
780 mirrorChars.put(0x27D5, 0x27D6);
781 mirrorChars.put(0x27D6, 0x27D5);
782 mirrorChars.put(0x27DD, 0x27DE);
783 mirrorChars.put(0x27DE, 0x27DD);
784 mirrorChars.put(0x27E2, 0x27E3);
785 mirrorChars.put(0x27E3, 0x27E2);
786 mirrorChars.put(0x27E4, 0x27E5);
787 mirrorChars.put(0x27E5, 0x27E4);
788 mirrorChars.put(0x27E6, 0x27E7);
789 mirrorChars.put(0x27E7, 0x27E6);
790 mirrorChars.put(0x27E8, 0x27E9);
791 mirrorChars.put(0x27E9, 0x27E8);
792 mirrorChars.put(0x27EA, 0x27EB);
793 mirrorChars.put(0x27EB, 0x27EA);
794 mirrorChars.put(0x2983, 0x2984);
795 mirrorChars.put(0x2984, 0x2983);
796 mirrorChars.put(0x2985, 0x2986);
797 mirrorChars.put(0x2986, 0x2985);
798 mirrorChars.put(0x2987, 0x2988);
799 mirrorChars.put(0x2988, 0x2987);
800 mirrorChars.put(0x2989, 0x298A);
801 mirrorChars.put(0x298A, 0x2989);
802 mirrorChars.put(0x298B, 0x298C);
803 mirrorChars.put(0x298C, 0x298B);
804 mirrorChars.put(0x298D, 0x2990);
805 mirrorChars.put(0x298E, 0x298F);
806 mirrorChars.put(0x298F, 0x298E);
807 mirrorChars.put(0x2990, 0x298D);
808 mirrorChars.put(0x2991, 0x2992);
809 mirrorChars.put(0x2992, 0x2991);
810 mirrorChars.put(0x2993, 0x2994);
811 mirrorChars.put(0x2994, 0x2993);
812 mirrorChars.put(0x2995, 0x2996);
813 mirrorChars.put(0x2996, 0x2995);
814 mirrorChars.put(0x2997, 0x2998);
815 mirrorChars.put(0x2998, 0x2997);
816 mirrorChars.put(0x29B8, 0x2298);
817 mirrorChars.put(0x29C0, 0x29C1);
818 mirrorChars.put(0x29C1, 0x29C0);
819 mirrorChars.put(0x29C4, 0x29C5);
820 mirrorChars.put(0x29C5, 0x29C4);
821 mirrorChars.put(0x29CF, 0x29D0);
822 mirrorChars.put(0x29D0, 0x29CF);
823 mirrorChars.put(0x29D1, 0x29D2);
824 mirrorChars.put(0x29D2, 0x29D1);
825 mirrorChars.put(0x29D4, 0x29D5);
826 mirrorChars.put(0x29D5, 0x29D4);
827 mirrorChars.put(0x29D8, 0x29D9);
828 mirrorChars.put(0x29D9, 0x29D8);
829 mirrorChars.put(0x29DA, 0x29DB);
830 mirrorChars.put(0x29DB, 0x29DA);
831 mirrorChars.put(0x29F5, 0x2215);
832 mirrorChars.put(0x29F8, 0x29F9);
833 mirrorChars.put(0x29F9, 0x29F8);
834 mirrorChars.put(0x29FC, 0x29FD);
835 mirrorChars.put(0x29FD, 0x29FC);
836 mirrorChars.put(0x2A2B, 0x2A2C);
837 mirrorChars.put(0x2A2C, 0x2A2B);
838 mirrorChars.put(0x2A2D, 0x2A2C);
839 mirrorChars.put(0x2A2E, 0x2A2D);
840 mirrorChars.put(0x2A34, 0x2A35);
841 mirrorChars.put(0x2A35, 0x2A34);
842 mirrorChars.put(0x2A3C, 0x2A3D);
843 mirrorChars.put(0x2A3D, 0x2A3C);
844 mirrorChars.put(0x2A64, 0x2A65);
845 mirrorChars.put(0x2A65, 0x2A64);
846 mirrorChars.put(0x2A79, 0x2A7A);
847 mirrorChars.put(0x2A7A, 0x2A79);
848 mirrorChars.put(0x2A7D, 0x2A7E);
849 mirrorChars.put(0x2A7E, 0x2A7D);
850 mirrorChars.put(0x2A7F, 0x2A80);
851 mirrorChars.put(0x2A80, 0x2A7F);
852 mirrorChars.put(0x2A81, 0x2A82);
853 mirrorChars.put(0x2A82, 0x2A81);
854 mirrorChars.put(0x2A83, 0x2A84);
855 mirrorChars.put(0x2A84, 0x2A83);
856 mirrorChars.put(0x2A8B, 0x2A8C);
857 mirrorChars.put(0x2A8C, 0x2A8B);
858 mirrorChars.put(0x2A91, 0x2A92);
859 mirrorChars.put(0x2A92, 0x2A91);
860 mirrorChars.put(0x2A93, 0x2A94);
861 mirrorChars.put(0x2A94, 0x2A93);
862 mirrorChars.put(0x2A95, 0x2A96);
863 mirrorChars.put(0x2A96, 0x2A95);
864 mirrorChars.put(0x2A97, 0x2A98);
865 mirrorChars.put(0x2A98, 0x2A97);
866 mirrorChars.put(0x2A99, 0x2A9A);
867 mirrorChars.put(0x2A9A, 0x2A99);
868 mirrorChars.put(0x2A9B, 0x2A9C);
869 mirrorChars.put(0x2A9C, 0x2A9B);
870 mirrorChars.put(0x2AA1, 0x2AA2);
871 mirrorChars.put(0x2AA2, 0x2AA1);
872 mirrorChars.put(0x2AA6, 0x2AA7);
873 mirrorChars.put(0x2AA7, 0x2AA6);
874 mirrorChars.put(0x2AA8, 0x2AA9);
875 mirrorChars.put(0x2AA9, 0x2AA8);
876 mirrorChars.put(0x2AAA, 0x2AAB);
877 mirrorChars.put(0x2AAB, 0x2AAA);
878 mirrorChars.put(0x2AAC, 0x2AAD);
879 mirrorChars.put(0x2AAD, 0x2AAC);
880 mirrorChars.put(0x2AAF, 0x2AB0);
881 mirrorChars.put(0x2AB0, 0x2AAF);
882 mirrorChars.put(0x2AB3, 0x2AB4);
883 mirrorChars.put(0x2AB4, 0x2AB3);
884 mirrorChars.put(0x2ABB, 0x2ABC);
885 mirrorChars.put(0x2ABC, 0x2ABB);
886 mirrorChars.put(0x2ABD, 0x2ABE);
887 mirrorChars.put(0x2ABE, 0x2ABD);
888 mirrorChars.put(0x2ABF, 0x2AC0);
889 mirrorChars.put(0x2AC0, 0x2ABF);
890 mirrorChars.put(0x2AC1, 0x2AC2);
891 mirrorChars.put(0x2AC2, 0x2AC1);
892 mirrorChars.put(0x2AC3, 0x2AC4);
893 mirrorChars.put(0x2AC4, 0x2AC3);
894 mirrorChars.put(0x2AC5, 0x2AC6);
895 mirrorChars.put(0x2AC6, 0x2AC5);
896 mirrorChars.put(0x2ACD, 0x2ACE);
897 mirrorChars.put(0x2ACE, 0x2ACD);
898 mirrorChars.put(0x2ACF, 0x2AD0);
899 mirrorChars.put(0x2AD0, 0x2ACF);
900 mirrorChars.put(0x2AD1, 0x2AD2);
901 mirrorChars.put(0x2AD2, 0x2AD1);
902 mirrorChars.put(0x2AD3, 0x2AD4);
903 mirrorChars.put(0x2AD4, 0x2AD3);
904 mirrorChars.put(0x2AD5, 0x2AD6);
905 mirrorChars.put(0x2AD6, 0x2AD5);
906 mirrorChars.put(0x2ADE, 0x22A6);
907 mirrorChars.put(0x2AE3, 0x22A9);
908 mirrorChars.put(0x2AE4, 0x22A8);
909 mirrorChars.put(0x2AE5, 0x22AB);
910 mirrorChars.put(0x2AEC, 0x2AED);
911 mirrorChars.put(0x2AED, 0x2AEC);
912 mirrorChars.put(0x2AF7, 0x2AF8);
913 mirrorChars.put(0x2AF8, 0x2AF7);
914 mirrorChars.put(0x2AF9, 0x2AFA);
915 mirrorChars.put(0x2AFA, 0x2AF9);
916 mirrorChars.put(0x3008, 0x3009);
917 mirrorChars.put(0x3009, 0x3008);
918 mirrorChars.put(0x300A, 0x300B);
919 mirrorChars.put(0x300B, 0x300A);
920 mirrorChars.put(0x300C, 0x300D);
921 mirrorChars.put(0x300D, 0x300C);
922 mirrorChars.put(0x300E, 0x300F);
923 mirrorChars.put(0x300F, 0x300E);
924 mirrorChars.put(0x3010, 0x3011);
925 mirrorChars.put(0x3011, 0x3010);
926 mirrorChars.put(0x3014, 0x3015);
927 mirrorChars.put(0x3015, 0x3014);
928 mirrorChars.put(0x3016, 0x3017);
929 mirrorChars.put(0x3017, 0x3016);
930 mirrorChars.put(0x3018, 0x3019);
931 mirrorChars.put(0x3019, 0x3018);
932 mirrorChars.put(0x301A, 0x301B);
933 mirrorChars.put(0x301B, 0x301A);
934 mirrorChars.put(0xFF08, 0xFF09);
935 mirrorChars.put(0xFF09, 0xFF08);
936 mirrorChars.put(0xFF1C, 0xFF1E);
937 mirrorChars.put(0xFF1E, 0xFF1C);
938 mirrorChars.put(0xFF3B, 0xFF3D);
939 mirrorChars.put(0xFF3D, 0xFF3B);
940 mirrorChars.put(0xFF5B, 0xFF5D);
941 mirrorChars.put(0xFF5D, 0xFF5B);
942 mirrorChars.put(0xFF5F, 0xFF60);
943 mirrorChars.put(0xFF60, 0xFF5F);
944 mirrorChars.put(0xFF62, 0xFF63);
945 mirrorChars.put(0xFF63, 0xFF62);
946 }
947 }