using System; using System.Collections; using System.Text; /* * * Copyright 2002 Paulo Soares * * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the License. * * The Original Code is 'iText, a free JAVA-PDF library'. * * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. * All Rights Reserved. * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. * * Contributor(s): all the names of the contributors are added in the source code * where applicable. * * Alternatively, the contents of this file may be used under the terms of the * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the * provisions of LGPL are applicable instead of those above. If you wish to * allow use of your version of this file only under the terms of the LGPL * License and not to allow others to use your version of this file under * the MPL, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the LGPL. * If you do not delete the provisions above, a recipient may use your version * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. * * This library is free software; you can redistribute it and/or modify it * under the terms of the MPL as stated above or under the terms of the GNU * Library General Public License as published by the Free Software Foundation; * either version 2 of the License, or any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more * details. * * If you didn't download this code from the following link, you should check if * you aren't using an obsolete version: * http://www.lowagie.com/iText/ */ namespace iTextSharp.text.pdf { /** Does all the line bidirectional processing with PdfChunk assembly. * * @author Paulo Soares (psoares@consiste.pt) */ public class BidiLine { private const int pieceSizeStart = 256; protected int runDirection; protected int pieceSize = pieceSizeStart; protected char[] text = new char[pieceSizeStart]; protected PdfChunk[] detailChunks = new PdfChunk[pieceSizeStart]; protected int totalTextLength = 0; protected byte[] orderLevels = new byte[pieceSizeStart]; protected int[] indexChars = new int[pieceSizeStart]; protected ArrayList chunks = new ArrayList(); protected int indexChunk = 0; protected int indexChunkChar = 0; protected int currentChar = 0; protected int storedRunDirection; protected char[] storedText = new char[0]; protected PdfChunk[] storedDetailChunks = new PdfChunk[0]; protected int storedTotalTextLength = 0; protected byte[] storedOrderLevels = new byte[0]; protected int[] storedIndexChars = new int[0]; protected int storedIndexChunk = 0; protected int storedIndexChunkChar = 0; protected int storedCurrentChar = 0; protected bool shortStore; protected static IntHashtable mirrorChars = new IntHashtable(); protected int arabicOptions; /** Creates new BidiLine */ public BidiLine() { } public BidiLine(BidiLine org) { runDirection = org.runDirection; pieceSize = org.pieceSize; text = (char[])org.text.Clone(); detailChunks = (PdfChunk[])org.detailChunks.Clone(); totalTextLength = org.totalTextLength; orderLevels = (byte[])org.orderLevels.Clone(); indexChars = (int[])org.indexChars.Clone(); chunks = new ArrayList(org.chunks); indexChunk = org.indexChunk; indexChunkChar = org.indexChunkChar; currentChar = org.currentChar; storedRunDirection = org.storedRunDirection; storedText = (char[])org.storedText.Clone(); storedDetailChunks = (PdfChunk[])org.storedDetailChunks.Clone(); storedTotalTextLength = org.storedTotalTextLength; storedOrderLevels = (byte[])org.storedOrderLevels.Clone(); storedIndexChars = (int[])org.storedIndexChars.Clone(); storedIndexChunk = org.storedIndexChunk; storedIndexChunkChar = org.storedIndexChunkChar; storedCurrentChar = org.storedCurrentChar; shortStore = org.shortStore; arabicOptions = org.arabicOptions; } public bool IsEmpty() { return (currentChar >= totalTextLength && indexChunk >= chunks.Count); } public void ClearChunks() { chunks.Clear(); totalTextLength = 0; currentChar = 0; } public bool GetParagraph(int runDirection) { this.runDirection = runDirection; currentChar = 0; totalTextLength = 0; bool hasText = false; char c; char uniC; BaseFont bf; for (; indexChunk < chunks.Count; ++indexChunk) { PdfChunk ck = (PdfChunk)chunks[indexChunk]; bf = ck.Font.Font; string s = ck.ToString(); int len = s.Length; for (; indexChunkChar < len; ++indexChunkChar) { c = s[indexChunkChar]; uniC = (char)bf.GetUnicodeEquivalent(c); if (uniC == '\r' || uniC == '\n') { // next condition is never true for CID if (uniC == '\r' && indexChunkChar + 1 < len && s[indexChunkChar + 1] == '\n') ++indexChunkChar; ++indexChunkChar; if (indexChunkChar >= len) { indexChunkChar = 0; ++indexChunk; } hasText = true; if (totalTextLength == 0) detailChunks[0] = ck; break; } AddPiece(c, ck); } if (hasText) break; indexChunkChar = 0; } if (totalTextLength == 0) return hasText; // remove trailing WS totalTextLength = TrimRight(0, totalTextLength - 1) + 1; if (totalTextLength == 0) return true; if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) { if (orderLevels.Length < totalTextLength) { orderLevels = new byte[pieceSize]; indexChars = new int[pieceSize]; } ArabicLigaturizer.ProcessNumbers(text, 0, totalTextLength, arabicOptions); BidiOrder order = new BidiOrder(text, 0, totalTextLength, (sbyte)(runDirection == PdfWriter.RUN_DIRECTION_RTL ? 1 : 0)); byte[] od = order.GetLevels(); for (int k = 0; k < totalTextLength; ++k) { orderLevels[k] = od[k]; indexChars[k] = k; } DoArabicShapping(); MirrorGlyphs(); } totalTextLength = TrimRightEx(0, totalTextLength - 1) + 1; return true; } public void AddChunk(PdfChunk chunk) { chunks.Add(chunk); } public void AddChunks(ArrayList chunks) { this.chunks.AddRange(chunks); } public void AddPiece(char c, PdfChunk chunk) { if (totalTextLength >= pieceSize) { char[] tempText = text; PdfChunk[] tempDetailChunks = detailChunks; pieceSize *= 2; text = new char[pieceSize]; detailChunks = new PdfChunk[pieceSize]; Array.Copy(tempText, 0, text, 0, totalTextLength); Array.Copy(tempDetailChunks, 0, detailChunks, 0, totalTextLength); } text[totalTextLength] = c; detailChunks[totalTextLength++] = chunk; } public void Save() { if (indexChunk > 0) { if (indexChunk >= chunks.Count) chunks.Clear(); else { for (--indexChunk; indexChunk >= 0; --indexChunk) chunks.RemoveAt(indexChunk); } indexChunk = 0; } storedRunDirection = runDirection; storedTotalTextLength = totalTextLength; storedIndexChunk = indexChunk; storedIndexChunkChar = indexChunkChar; storedCurrentChar = currentChar; shortStore = (currentChar < totalTextLength); if (!shortStore) { // long save if (storedText.Length < totalTextLength) { storedText = new char[totalTextLength]; storedDetailChunks = new PdfChunk[totalTextLength]; } Array.Copy(text, 0, storedText, 0, totalTextLength); Array.Copy(detailChunks, 0, storedDetailChunks, 0, totalTextLength); } if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) { if (storedOrderLevels.Length < totalTextLength) { storedOrderLevels = new byte[totalTextLength]; storedIndexChars = new int[totalTextLength]; } Array.Copy(orderLevels, currentChar, storedOrderLevels, currentChar, totalTextLength - currentChar); Array.Copy(indexChars, currentChar, storedIndexChars, currentChar, totalTextLength - currentChar); } } public void Restore() { runDirection = storedRunDirection; totalTextLength = storedTotalTextLength; indexChunk = storedIndexChunk; indexChunkChar = storedIndexChunkChar; currentChar = storedCurrentChar; if (!shortStore) { // long restore Array.Copy(storedText, 0, text, 0, totalTextLength); Array.Copy(storedDetailChunks, 0, detailChunks, 0, totalTextLength); } if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) { Array.Copy(storedOrderLevels, currentChar, orderLevels, currentChar, totalTextLength - currentChar); Array.Copy(storedIndexChars, currentChar, indexChars, currentChar, totalTextLength - currentChar); } } public void MirrorGlyphs() { for (int k = 0; k < totalTextLength; ++k) { if ((orderLevels[k] & 1) == 1) { int mirror = mirrorChars[text[k]]; if (mirror != 0) text[k] = (char)mirror; } } } public void DoArabicShapping() { int src = 0; int dest = 0; for (;;) { while (src < totalTextLength) { char c = text[src]; if (c >= 0x0600 && c <= 0x06ff) break; if (src != dest) { text[dest] = text[src]; detailChunks[dest] = detailChunks[src]; orderLevels[dest] = orderLevels[src]; } ++src; ++dest; } if (src >= totalTextLength) { totalTextLength = dest; return; } int startArabicIdx = src; ++src; while (src < totalTextLength) { char c = text[src]; if (c < 0x0600 || c > 0x06ff) break; ++src; } int arabicWordSize = src - startArabicIdx; int size = ArabicLigaturizer.Arabic_shape(text, startArabicIdx, arabicWordSize, text, dest, arabicWordSize, arabicOptions); if (startArabicIdx != dest) { for (int k = 0; k < size; ++k) { detailChunks[dest] = detailChunks[startArabicIdx]; orderLevels[dest++] = orderLevels[startArabicIdx++]; } } else dest += size; } } public PdfLine ProcessLine(float leftX, float width, int alignment, int runDirection, int arabicOptions) { this.arabicOptions = arabicOptions; Save(); bool isRTL = (runDirection == PdfWriter.RUN_DIRECTION_RTL); if (currentChar >= totalTextLength) { bool hasText = GetParagraph(runDirection); if (!hasText) return null; if (totalTextLength == 0) { ArrayList ar = new ArrayList(); PdfChunk ckx = new PdfChunk("", detailChunks[0]); ar.Add(ckx); return new PdfLine(0, 0, 0, alignment, true, ar, isRTL); } } float originalWidth = width; int lastSplit = -1; if (currentChar != 0) currentChar = TrimLeftEx(currentChar, totalTextLength - 1); int oldCurrentChar = currentChar; int uniC = 0; PdfChunk ck = null; float charWidth = 0; PdfChunk lastValidChunk = null; bool splitChar = false; bool surrogate = false; for (; currentChar < totalTextLength; ++currentChar) { ck = detailChunks[currentChar]; surrogate = Utilities.IsSurrogatePair(text, currentChar); if (surrogate) uniC = ck.GetUnicodeEquivalent(Utilities.ConvertToUtf32(text, currentChar)); else uniC = ck.GetUnicodeEquivalent(text[currentChar]); if (PdfChunk.NoPrint(uniC)) continue; if (surrogate) charWidth = ck.GetCharWidth(uniC); else charWidth = ck.GetCharWidth(text[currentChar]); splitChar = ck.IsExtSplitCharacter(oldCurrentChar, currentChar, totalTextLength, text, detailChunks); if (splitChar && Char.IsWhiteSpace((char)uniC)) lastSplit = currentChar; if (width - charWidth < 0) break; if (splitChar) lastSplit = currentChar; width -= charWidth; lastValidChunk = ck; if (surrogate) ++currentChar; if (ck.IsTab()) { Object[] tab = (Object[])ck.GetAttribute(Chunk.TAB); float tabPosition = (float)tab[1]; bool newLine = (bool)tab[2]; if (newLine && tabPosition < originalWidth - width) { return new PdfLine(0, originalWidth, width, alignment, true, CreateArrayOfPdfChunks(oldCurrentChar, currentChar - 1), isRTL); } detailChunks[currentChar].AdjustLeft(leftX); width = originalWidth - tabPosition; } } if (lastValidChunk == null) { // not even a single char fit; must output the first char ++currentChar; if (surrogate) ++currentChar; return new PdfLine(0, originalWidth, 0, alignment, false, CreateArrayOfPdfChunks(currentChar - 1, currentChar - 1), isRTL); } if (currentChar >= totalTextLength) { // there was more line than text return new PdfLine(0, originalWidth, width, alignment, true, CreateArrayOfPdfChunks(oldCurrentChar, totalTextLength - 1), isRTL); } int newCurrentChar = TrimRightEx(oldCurrentChar, currentChar - 1); if (newCurrentChar < oldCurrentChar) { // only WS return new PdfLine(0, originalWidth, width, alignment, false, CreateArrayOfPdfChunks(oldCurrentChar, currentChar - 1), isRTL); } if (newCurrentChar == currentChar - 1) { // middle of word IHyphenationEvent he = (IHyphenationEvent)lastValidChunk.GetAttribute(Chunk.HYPHENATION); if (he != null) { int[] word = GetWord(oldCurrentChar, newCurrentChar); if (word != null) { float testWidth = width + GetWidth(word[0], currentChar - 1); String pre = he.GetHyphenatedWordPre(new String(text, word[0], word[1] - word[0]), lastValidChunk.Font.Font, lastValidChunk.Font.Size, testWidth); String post = he.HyphenatedWordPost; if (pre.Length > 0) { PdfChunk extra = new PdfChunk(pre, lastValidChunk); currentChar = word[1] - post.Length; return new PdfLine(0, originalWidth, testWidth - lastValidChunk.Font.Width(pre), alignment, false, CreateArrayOfPdfChunks(oldCurrentChar, word[0] - 1, extra), isRTL); } } } } if (lastSplit == -1 || lastSplit >= newCurrentChar) { // no split point or split point ahead of end return new PdfLine(0, originalWidth, width + GetWidth(newCurrentChar + 1, currentChar - 1), alignment, false, CreateArrayOfPdfChunks(oldCurrentChar, newCurrentChar), isRTL); } // standard split currentChar = lastSplit + 1; newCurrentChar = TrimRightEx(oldCurrentChar, lastSplit); if (newCurrentChar < oldCurrentChar) { // only WS again newCurrentChar = currentChar - 1; } return new PdfLine(0, originalWidth, originalWidth - GetWidth(oldCurrentChar, newCurrentChar), alignment, false, CreateArrayOfPdfChunks(oldCurrentChar, newCurrentChar), isRTL); } /** Gets the width of a range of characters. * @param startIdx the first index to calculate * @param lastIdx the last inclusive index to calculate * @return the sum of all widths */ public float GetWidth(int startIdx, int lastIdx) { char c = (char)0; PdfChunk ck = null; float width = 0; for (; startIdx <= lastIdx; ++startIdx) { bool surrogate = Utilities.IsSurrogatePair(text, startIdx); if (surrogate) { width += detailChunks[startIdx].GetCharWidth(Utilities.ConvertToUtf32(text, startIdx)); ++startIdx; } else { c = text[startIdx]; ck = detailChunks[startIdx]; if (PdfChunk.NoPrint(ck.GetUnicodeEquivalent(c))) continue; width += detailChunks[startIdx].GetCharWidth(c); } } return width; } public ArrayList CreateArrayOfPdfChunks(int startIdx, int endIdx) { return CreateArrayOfPdfChunks(startIdx, endIdx, null); } public ArrayList CreateArrayOfPdfChunks(int startIdx, int endIdx, PdfChunk extraPdfChunk) { bool bidi = (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL); if (bidi) Reorder(startIdx, endIdx); ArrayList ar = new ArrayList(); PdfChunk refCk = detailChunks[startIdx]; PdfChunk ck = null; StringBuilder buf = new StringBuilder(); char c; int idx = 0; for (; startIdx <= endIdx; ++startIdx) { idx = bidi ? indexChars[startIdx] : startIdx; c = text[idx]; ck = detailChunks[idx]; if (PdfChunk.NoPrint(ck.GetUnicodeEquivalent(c))) continue; if (ck.IsImage() || ck.IsSeparator() || ck.IsTab()) { if (buf.Length > 0) { ar.Add(new PdfChunk(buf.ToString(), refCk)); buf = new StringBuilder(); } ar.Add(ck); } else if (ck == refCk) { buf.Append(c); } else { if (buf.Length > 0) { ar.Add(new PdfChunk(buf.ToString(), refCk)); buf = new StringBuilder(); } if (!ck.IsImage() && !ck.IsSeparator() && !ck.IsTab()) buf.Append(c); refCk = ck; } } if (buf.Length > 0) { ar.Add(new PdfChunk(buf.ToString(), refCk)); } if (extraPdfChunk != null) ar.Add(extraPdfChunk); return ar; } public int[] GetWord(int startIdx, int idx) { int last = idx; int first = idx; // forward for (; last < totalTextLength; ++last) { if (!char.IsLetter(text[last])) break; } if (last == idx) return null; // backward for (; first >= startIdx; --first) { if (!char.IsLetter(text[first])) break; } ++first; return new int[]{first, last}; } public int TrimRight(int startIdx, int endIdx) { int idx = endIdx; char c; for (; idx >= startIdx; --idx) { c = (char)detailChunks[idx].GetUnicodeEquivalent(text[idx]); if (!IsWS(c)) break; } return idx; } public int TrimLeft(int startIdx, int endIdx) { int idx = startIdx; char c; for (; idx <= endIdx; ++idx) { c = (char)detailChunks[idx].GetUnicodeEquivalent(text[idx]); if (!IsWS(c)) break; } return idx; } public int TrimRightEx(int startIdx, int endIdx) { int idx = endIdx; char c = (char)0; for (; idx >= startIdx; --idx) { c = (char)detailChunks[idx].GetUnicodeEquivalent(text[idx]); if (!IsWS(c) && !PdfChunk.NoPrint(c)) break; } return idx; } public int TrimLeftEx(int startIdx, int endIdx) { int idx = startIdx; char c = (char)0; for (; idx <= endIdx; ++idx) { c = (char)detailChunks[idx].GetUnicodeEquivalent(text[idx]); if (!IsWS(c) && !PdfChunk.NoPrint(c)) break; } return idx; } public void Reorder(int start, int end) { byte maxLevel = orderLevels[start]; byte minLevel = maxLevel; byte onlyOddLevels = maxLevel; byte onlyEvenLevels = maxLevel; for (int k = start + 1; k <= end; ++k) { byte b = orderLevels[k]; if (b > maxLevel) maxLevel = b; else if (b < minLevel) minLevel = b; onlyOddLevels &= b; onlyEvenLevels |= b; } if ((onlyEvenLevels & 1) == 0) // nothing to do return; if ((onlyOddLevels & 1) == 1) { // single inversion Flip(start, end + 1); return; } minLevel |= 1; for (; maxLevel >= minLevel; --maxLevel) { int pstart = start; for (;;) { for (;pstart <= end; ++pstart) { if (orderLevels[pstart] >= maxLevel) break; } if (pstart > end) break; int pend = pstart + 1; for (; pend <= end; ++pend) { if (orderLevels[pend] < maxLevel) break; } Flip(pstart, pend); pstart = pend + 1; } } } public void Flip(int start, int end) { int mid = (start + end) / 2; --end; for (; start < mid; ++start, --end) { int temp = indexChars[start]; indexChars[start] = indexChars[end]; indexChars[end] = temp; } } public static bool IsWS(char c) { return (c <= ' '); } static BidiLine() { mirrorChars[0x0028] = 0x0029; // LEFT PARENTHESIS mirrorChars[0x0029] = 0x0028; // RIGHT PARENTHESIS mirrorChars[0x003C] = 0x003E; // LESS-THAN SIGN mirrorChars[0x003E] = 0x003C; // GREATER-THAN SIGN mirrorChars[0x005B] = 0x005D; // LEFT SQUARE BRACKET mirrorChars[0x005D] = 0x005B; // RIGHT SQUARE BRACKET mirrorChars[0x007B] = 0x007D; // LEFT CURLY BRACKET mirrorChars[0x007D] = 0x007B; // RIGHT CURLY BRACKET mirrorChars[0x00AB] = 0x00BB; // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK mirrorChars[0x00BB] = 0x00AB; // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK mirrorChars[0x2039] = 0x203A; // SINGLE LEFT-POINTING ANGLE QUOTATION MARK mirrorChars[0x203A] = 0x2039; // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK mirrorChars[0x2045] = 0x2046; // LEFT SQUARE BRACKET WITH QUILL mirrorChars[0x2046] = 0x2045; // RIGHT SQUARE BRACKET WITH QUILL mirrorChars[0x207D] = 0x207E; // SUPERSCRIPT LEFT PARENTHESIS mirrorChars[0x207E] = 0x207D; // SUPERSCRIPT RIGHT PARENTHESIS mirrorChars[0x208D] = 0x208E; // SUBSCRIPT LEFT PARENTHESIS mirrorChars[0x208E] = 0x208D; // SUBSCRIPT RIGHT PARENTHESIS mirrorChars[0x2208] = 0x220B; // ELEMENT OF mirrorChars[0x2209] = 0x220C; // NOT AN ELEMENT OF mirrorChars[0x220A] = 0x220D; // SMALL ELEMENT OF mirrorChars[0x220B] = 0x2208; // CONTAINS AS MEMBER mirrorChars[0x220C] = 0x2209; // DOES NOT CONTAIN AS MEMBER mirrorChars[0x220D] = 0x220A; // SMALL CONTAINS AS MEMBER mirrorChars[0x2215] = 0x29F5; // DIVISION SLASH mirrorChars[0x223C] = 0x223D; // TILDE OPERATOR mirrorChars[0x223D] = 0x223C; // REVERSED TILDE mirrorChars[0x2243] = 0x22CD; // ASYMPTOTICALLY EQUAL TO mirrorChars[0x2252] = 0x2253; // APPROXIMATELY EQUAL TO OR THE IMAGE OF mirrorChars[0x2253] = 0x2252; // IMAGE OF OR APPROXIMATELY EQUAL TO mirrorChars[0x2254] = 0x2255; // COLON EQUALS mirrorChars[0x2255] = 0x2254; // EQUALS COLON mirrorChars[0x2264] = 0x2265; // LESS-THAN OR EQUAL TO mirrorChars[0x2265] = 0x2264; // GREATER-THAN OR EQUAL TO mirrorChars[0x2266] = 0x2267; // LESS-THAN OVER EQUAL TO mirrorChars[0x2267] = 0x2266; // GREATER-THAN OVER EQUAL TO mirrorChars[0x2268] = 0x2269; // [BEST FIT] LESS-THAN BUT NOT EQUAL TO mirrorChars[0x2269] = 0x2268; // [BEST FIT] GREATER-THAN BUT NOT EQUAL TO mirrorChars[0x226A] = 0x226B; // MUCH LESS-THAN mirrorChars[0x226B] = 0x226A; // MUCH GREATER-THAN mirrorChars[0x226E] = 0x226F; // [BEST FIT] NOT LESS-THAN mirrorChars[0x226F] = 0x226E; // [BEST FIT] NOT GREATER-THAN mirrorChars[0x2270] = 0x2271; // [BEST FIT] NEITHER LESS-THAN NOR EQUAL TO mirrorChars[0x2271] = 0x2270; // [BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO mirrorChars[0x2272] = 0x2273; // [BEST FIT] LESS-THAN OR EQUIVALENT TO mirrorChars[0x2273] = 0x2272; // [BEST FIT] GREATER-THAN OR EQUIVALENT TO mirrorChars[0x2274] = 0x2275; // [BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO mirrorChars[0x2275] = 0x2274; // [BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO mirrorChars[0x2276] = 0x2277; // LESS-THAN OR GREATER-THAN mirrorChars[0x2277] = 0x2276; // GREATER-THAN OR LESS-THAN mirrorChars[0x2278] = 0x2279; // NEITHER LESS-THAN NOR GREATER-THAN mirrorChars[0x2279] = 0x2278; // NEITHER GREATER-THAN NOR LESS-THAN mirrorChars[0x227A] = 0x227B; // PRECEDES mirrorChars[0x227B] = 0x227A; // SUCCEEDS mirrorChars[0x227C] = 0x227D; // PRECEDES OR EQUAL TO mirrorChars[0x227D] = 0x227C; // SUCCEEDS OR EQUAL TO mirrorChars[0x227E] = 0x227F; // [BEST FIT] PRECEDES OR EQUIVALENT TO mirrorChars[0x227F] = 0x227E; // [BEST FIT] SUCCEEDS OR EQUIVALENT TO mirrorChars[0x2280] = 0x2281; // [BEST FIT] DOES NOT PRECEDE mirrorChars[0x2281] = 0x2280; // [BEST FIT] DOES NOT SUCCEED mirrorChars[0x2282] = 0x2283; // SUBSET OF mirrorChars[0x2283] = 0x2282; // SUPERSET OF mirrorChars[0x2284] = 0x2285; // [BEST FIT] NOT A SUBSET OF mirrorChars[0x2285] = 0x2284; // [BEST FIT] NOT A SUPERSET OF mirrorChars[0x2286] = 0x2287; // SUBSET OF OR EQUAL TO mirrorChars[0x2287] = 0x2286; // SUPERSET OF OR EQUAL TO mirrorChars[0x2288] = 0x2289; // [BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO mirrorChars[0x2289] = 0x2288; // [BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO mirrorChars[0x228A] = 0x228B; // [BEST FIT] SUBSET OF WITH NOT EQUAL TO mirrorChars[0x228B] = 0x228A; // [BEST FIT] SUPERSET OF WITH NOT EQUAL TO mirrorChars[0x228F] = 0x2290; // SQUARE IMAGE OF mirrorChars[0x2290] = 0x228F; // SQUARE ORIGINAL OF mirrorChars[0x2291] = 0x2292; // SQUARE IMAGE OF OR EQUAL TO mirrorChars[0x2292] = 0x2291; // SQUARE ORIGINAL OF OR EQUAL TO mirrorChars[0x2298] = 0x29B8; // CIRCLED DIVISION SLASH mirrorChars[0x22A2] = 0x22A3; // RIGHT TACK mirrorChars[0x22A3] = 0x22A2; // LEFT TACK mirrorChars[0x22A6] = 0x2ADE; // ASSERTION mirrorChars[0x22A8] = 0x2AE4; // TRUE mirrorChars[0x22A9] = 0x2AE3; // FORCES mirrorChars[0x22AB] = 0x2AE5; // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE mirrorChars[0x22B0] = 0x22B1; // PRECEDES UNDER RELATION mirrorChars[0x22B1] = 0x22B0; // SUCCEEDS UNDER RELATION mirrorChars[0x22B2] = 0x22B3; // NORMAL SUBGROUP OF mirrorChars[0x22B3] = 0x22B2; // CONTAINS AS NORMAL SUBGROUP mirrorChars[0x22B4] = 0x22B5; // NORMAL SUBGROUP OF OR EQUAL TO mirrorChars[0x22B5] = 0x22B4; // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO mirrorChars[0x22B6] = 0x22B7; // ORIGINAL OF mirrorChars[0x22B7] = 0x22B6; // IMAGE OF mirrorChars[0x22C9] = 0x22CA; // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT mirrorChars[0x22CA] = 0x22C9; // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT mirrorChars[0x22CB] = 0x22CC; // LEFT SEMIDIRECT PRODUCT mirrorChars[0x22CC] = 0x22CB; // RIGHT SEMIDIRECT PRODUCT mirrorChars[0x22CD] = 0x2243; // REVERSED TILDE EQUALS mirrorChars[0x22D0] = 0x22D1; // DOUBLE SUBSET mirrorChars[0x22D1] = 0x22D0; // DOUBLE SUPERSET mirrorChars[0x22D6] = 0x22D7; // LESS-THAN WITH DOT mirrorChars[0x22D7] = 0x22D6; // GREATER-THAN WITH DOT mirrorChars[0x22D8] = 0x22D9; // VERY MUCH LESS-THAN mirrorChars[0x22D9] = 0x22D8; // VERY MUCH GREATER-THAN mirrorChars[0x22DA] = 0x22DB; // LESS-THAN EQUAL TO OR GREATER-THAN mirrorChars[0x22DB] = 0x22DA; // GREATER-THAN EQUAL TO OR LESS-THAN mirrorChars[0x22DC] = 0x22DD; // EQUAL TO OR LESS-THAN mirrorChars[0x22DD] = 0x22DC; // EQUAL TO OR GREATER-THAN mirrorChars[0x22DE] = 0x22DF; // EQUAL TO OR PRECEDES mirrorChars[0x22DF] = 0x22DE; // EQUAL TO OR SUCCEEDS mirrorChars[0x22E0] = 0x22E1; // [BEST FIT] DOES NOT PRECEDE OR EQUAL mirrorChars[0x22E1] = 0x22E0; // [BEST FIT] DOES NOT SUCCEED OR EQUAL mirrorChars[0x22E2] = 0x22E3; // [BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO mirrorChars[0x22E3] = 0x22E2; // [BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO mirrorChars[0x22E4] = 0x22E5; // [BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO mirrorChars[0x22E5] = 0x22E4; // [BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO mirrorChars[0x22E6] = 0x22E7; // [BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO mirrorChars[0x22E7] = 0x22E6; // [BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO mirrorChars[0x22E8] = 0x22E9; // [BEST FIT] PRECEDES BUT NOT EQUIVALENT TO mirrorChars[0x22E9] = 0x22E8; // [BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO mirrorChars[0x22EA] = 0x22EB; // [BEST FIT] NOT NORMAL SUBGROUP OF mirrorChars[0x22EB] = 0x22EA; // [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP mirrorChars[0x22EC] = 0x22ED; // [BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO mirrorChars[0x22ED] = 0x22EC; // [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL mirrorChars[0x22F0] = 0x22F1; // UP RIGHT DIAGONAL ELLIPSIS mirrorChars[0x22F1] = 0x22F0; // DOWN RIGHT DIAGONAL ELLIPSIS mirrorChars[0x22F2] = 0x22FA; // ELEMENT OF WITH LONG HORIZONTAL STROKE mirrorChars[0x22F3] = 0x22FB; // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE mirrorChars[0x22F4] = 0x22FC; // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE mirrorChars[0x22F6] = 0x22FD; // ELEMENT OF WITH OVERBAR mirrorChars[0x22F7] = 0x22FE; // SMALL ELEMENT OF WITH OVERBAR mirrorChars[0x22FA] = 0x22F2; // CONTAINS WITH LONG HORIZONTAL STROKE mirrorChars[0x22FB] = 0x22F3; // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE mirrorChars[0x22FC] = 0x22F4; // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE mirrorChars[0x22FD] = 0x22F6; // CONTAINS WITH OVERBAR mirrorChars[0x22FE] = 0x22F7; // SMALL CONTAINS WITH OVERBAR mirrorChars[0x2308] = 0x2309; // LEFT CEILING mirrorChars[0x2309] = 0x2308; // RIGHT CEILING mirrorChars[0x230A] = 0x230B; // LEFT FLOOR mirrorChars[0x230B] = 0x230A; // RIGHT FLOOR mirrorChars[0x2329] = 0x232A; // LEFT-POINTING ANGLE BRACKET mirrorChars[0x232A] = 0x2329; // RIGHT-POINTING ANGLE BRACKET mirrorChars[0x2768] = 0x2769; // MEDIUM LEFT PARENTHESIS ORNAMENT mirrorChars[0x2769] = 0x2768; // MEDIUM RIGHT PARENTHESIS ORNAMENT mirrorChars[0x276A] = 0x276B; // MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT mirrorChars[0x276B] = 0x276A; // MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT mirrorChars[0x276C] = 0x276D; // MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT mirrorChars[0x276D] = 0x276C; // MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT mirrorChars[0x276E] = 0x276F; // HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT mirrorChars[0x276F] = 0x276E; // HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT mirrorChars[0x2770] = 0x2771; // HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT mirrorChars[0x2771] = 0x2770; // HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT mirrorChars[0x2772] = 0x2773; // LIGHT LEFT TORTOISE SHELL BRACKET mirrorChars[0x2773] = 0x2772; // LIGHT RIGHT TORTOISE SHELL BRACKET mirrorChars[0x2774] = 0x2775; // MEDIUM LEFT CURLY BRACKET ORNAMENT mirrorChars[0x2775] = 0x2774; // MEDIUM RIGHT CURLY BRACKET ORNAMENT mirrorChars[0x27D5] = 0x27D6; // LEFT OUTER JOIN mirrorChars[0x27D6] = 0x27D5; // RIGHT OUTER JOIN mirrorChars[0x27DD] = 0x27DE; // LONG RIGHT TACK mirrorChars[0x27DE] = 0x27DD; // LONG LEFT TACK mirrorChars[0x27E2] = 0x27E3; // WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK mirrorChars[0x27E3] = 0x27E2; // WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK mirrorChars[0x27E4] = 0x27E5; // WHITE SQUARE WITH LEFTWARDS TICK mirrorChars[0x27E5] = 0x27E4; // WHITE SQUARE WITH RIGHTWARDS TICK mirrorChars[0x27E6] = 0x27E7; // MATHEMATICAL LEFT WHITE SQUARE BRACKET mirrorChars[0x27E7] = 0x27E6; // MATHEMATICAL RIGHT WHITE SQUARE BRACKET mirrorChars[0x27E8] = 0x27E9; // MATHEMATICAL LEFT ANGLE BRACKET mirrorChars[0x27E9] = 0x27E8; // MATHEMATICAL RIGHT ANGLE BRACKET mirrorChars[0x27EA] = 0x27EB; // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET mirrorChars[0x27EB] = 0x27EA; // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET mirrorChars[0x2983] = 0x2984; // LEFT WHITE CURLY BRACKET mirrorChars[0x2984] = 0x2983; // RIGHT WHITE CURLY BRACKET mirrorChars[0x2985] = 0x2986; // LEFT WHITE PARENTHESIS mirrorChars[0x2986] = 0x2985; // RIGHT WHITE PARENTHESIS mirrorChars[0x2987] = 0x2988; // Z NOTATION LEFT IMAGE BRACKET mirrorChars[0x2988] = 0x2987; // Z NOTATION RIGHT IMAGE BRACKET mirrorChars[0x2989] = 0x298A; // Z NOTATION LEFT BINDING BRACKET mirrorChars[0x298A] = 0x2989; // Z NOTATION RIGHT BINDING BRACKET mirrorChars[0x298B] = 0x298C; // LEFT SQUARE BRACKET WITH UNDERBAR mirrorChars[0x298C] = 0x298B; // RIGHT SQUARE BRACKET WITH UNDERBAR mirrorChars[0x298D] = 0x2990; // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER mirrorChars[0x298E] = 0x298F; // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER mirrorChars[0x298F] = 0x298E; // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER mirrorChars[0x2990] = 0x298D; // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER mirrorChars[0x2991] = 0x2992; // LEFT ANGLE BRACKET WITH DOT mirrorChars[0x2992] = 0x2991; // RIGHT ANGLE BRACKET WITH DOT mirrorChars[0x2993] = 0x2994; // LEFT ARC LESS-THAN BRACKET mirrorChars[0x2994] = 0x2993; // RIGHT ARC GREATER-THAN BRACKET mirrorChars[0x2995] = 0x2996; // DOUBLE LEFT ARC GREATER-THAN BRACKET mirrorChars[0x2996] = 0x2995; // DOUBLE RIGHT ARC LESS-THAN BRACKET mirrorChars[0x2997] = 0x2998; // LEFT BLACK TORTOISE SHELL BRACKET mirrorChars[0x2998] = 0x2997; // RIGHT BLACK TORTOISE SHELL BRACKET mirrorChars[0x29B8] = 0x2298; // CIRCLED REVERSE SOLIDUS mirrorChars[0x29C0] = 0x29C1; // CIRCLED LESS-THAN mirrorChars[0x29C1] = 0x29C0; // CIRCLED GREATER-THAN mirrorChars[0x29C4] = 0x29C5; // SQUARED RISING DIAGONAL SLASH mirrorChars[0x29C5] = 0x29C4; // SQUARED FALLING DIAGONAL SLASH mirrorChars[0x29CF] = 0x29D0; // LEFT TRIANGLE BESIDE VERTICAL BAR mirrorChars[0x29D0] = 0x29CF; // VERTICAL BAR BESIDE RIGHT TRIANGLE mirrorChars[0x29D1] = 0x29D2; // BOWTIE WITH LEFT HALF BLACK mirrorChars[0x29D2] = 0x29D1; // BOWTIE WITH RIGHT HALF BLACK mirrorChars[0x29D4] = 0x29D5; // TIMES WITH LEFT HALF BLACK mirrorChars[0x29D5] = 0x29D4; // TIMES WITH RIGHT HALF BLACK mirrorChars[0x29D8] = 0x29D9; // LEFT WIGGLY FENCE mirrorChars[0x29D9] = 0x29D8; // RIGHT WIGGLY FENCE mirrorChars[0x29DA] = 0x29DB; // LEFT DOUBLE WIGGLY FENCE mirrorChars[0x29DB] = 0x29DA; // RIGHT DOUBLE WIGGLY FENCE mirrorChars[0x29F5] = 0x2215; // REVERSE SOLIDUS OPERATOR mirrorChars[0x29F8] = 0x29F9; // BIG SOLIDUS mirrorChars[0x29F9] = 0x29F8; // BIG REVERSE SOLIDUS mirrorChars[0x29FC] = 0x29FD; // LEFT-POINTING CURVED ANGLE BRACKET mirrorChars[0x29FD] = 0x29FC; // RIGHT-POINTING CURVED ANGLE BRACKET mirrorChars[0x2A2B] = 0x2A2C; // MINUS SIGN WITH FALLING DOTS mirrorChars[0x2A2C] = 0x2A2B; // MINUS SIGN WITH RISING DOTS mirrorChars[0x2A2D] = 0x2A2C; // PLUS SIGN IN LEFT HALF CIRCLE mirrorChars[0x2A2E] = 0x2A2D; // PLUS SIGN IN RIGHT HALF CIRCLE mirrorChars[0x2A34] = 0x2A35; // MULTIPLICATION SIGN IN LEFT HALF CIRCLE mirrorChars[0x2A35] = 0x2A34; // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE mirrorChars[0x2A3C] = 0x2A3D; // INTERIOR PRODUCT mirrorChars[0x2A3D] = 0x2A3C; // RIGHTHAND INTERIOR PRODUCT mirrorChars[0x2A64] = 0x2A65; // Z NOTATION DOMAIN ANTIRESTRICTION mirrorChars[0x2A65] = 0x2A64; // Z NOTATION RANGE ANTIRESTRICTION mirrorChars[0x2A79] = 0x2A7A; // LESS-THAN WITH CIRCLE INSIDE mirrorChars[0x2A7A] = 0x2A79; // GREATER-THAN WITH CIRCLE INSIDE mirrorChars[0x2A7D] = 0x2A7E; // LESS-THAN OR SLANTED EQUAL TO mirrorChars[0x2A7E] = 0x2A7D; // GREATER-THAN OR SLANTED EQUAL TO mirrorChars[0x2A7F] = 0x2A80; // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE mirrorChars[0x2A80] = 0x2A7F; // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE mirrorChars[0x2A81] = 0x2A82; // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE mirrorChars[0x2A82] = 0x2A81; // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE mirrorChars[0x2A83] = 0x2A84; // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT mirrorChars[0x2A84] = 0x2A83; // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT mirrorChars[0x2A8B] = 0x2A8C; // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN mirrorChars[0x2A8C] = 0x2A8B; // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN mirrorChars[0x2A91] = 0x2A92; // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL mirrorChars[0x2A92] = 0x2A91; // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL mirrorChars[0x2A93] = 0x2A94; // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL mirrorChars[0x2A94] = 0x2A93; // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL mirrorChars[0x2A95] = 0x2A96; // SLANTED EQUAL TO OR LESS-THAN mirrorChars[0x2A96] = 0x2A95; // SLANTED EQUAL TO OR GREATER-THAN mirrorChars[0x2A97] = 0x2A98; // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE mirrorChars[0x2A98] = 0x2A97; // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE mirrorChars[0x2A99] = 0x2A9A; // DOUBLE-LINE EQUAL TO OR LESS-THAN mirrorChars[0x2A9A] = 0x2A99; // DOUBLE-LINE EQUAL TO OR GREATER-THAN mirrorChars[0x2A9B] = 0x2A9C; // DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN mirrorChars[0x2A9C] = 0x2A9B; // DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN mirrorChars[0x2AA1] = 0x2AA2; // DOUBLE NESTED LESS-THAN mirrorChars[0x2AA2] = 0x2AA1; // DOUBLE NESTED GREATER-THAN mirrorChars[0x2AA6] = 0x2AA7; // LESS-THAN CLOSED BY CURVE mirrorChars[0x2AA7] = 0x2AA6; // GREATER-THAN CLOSED BY CURVE mirrorChars[0x2AA8] = 0x2AA9; // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL mirrorChars[0x2AA9] = 0x2AA8; // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL mirrorChars[0x2AAA] = 0x2AAB; // SMALLER THAN mirrorChars[0x2AAB] = 0x2AAA; // LARGER THAN mirrorChars[0x2AAC] = 0x2AAD; // SMALLER THAN OR EQUAL TO mirrorChars[0x2AAD] = 0x2AAC; // LARGER THAN OR EQUAL TO mirrorChars[0x2AAF] = 0x2AB0; // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN mirrorChars[0x2AB0] = 0x2AAF; // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN mirrorChars[0x2AB3] = 0x2AB4; // PRECEDES ABOVE EQUALS SIGN mirrorChars[0x2AB4] = 0x2AB3; // SUCCEEDS ABOVE EQUALS SIGN mirrorChars[0x2ABB] = 0x2ABC; // DOUBLE PRECEDES mirrorChars[0x2ABC] = 0x2ABB; // DOUBLE SUCCEEDS mirrorChars[0x2ABD] = 0x2ABE; // SUBSET WITH DOT mirrorChars[0x2ABE] = 0x2ABD; // SUPERSET WITH DOT mirrorChars[0x2ABF] = 0x2AC0; // SUBSET WITH PLUS SIGN BELOW mirrorChars[0x2AC0] = 0x2ABF; // SUPERSET WITH PLUS SIGN BELOW mirrorChars[0x2AC1] = 0x2AC2; // SUBSET WITH MULTIPLICATION SIGN BELOW mirrorChars[0x2AC2] = 0x2AC1; // SUPERSET WITH MULTIPLICATION SIGN BELOW mirrorChars[0x2AC3] = 0x2AC4; // SUBSET OF OR EQUAL TO WITH DOT ABOVE mirrorChars[0x2AC4] = 0x2AC3; // SUPERSET OF OR EQUAL TO WITH DOT ABOVE mirrorChars[0x2AC5] = 0x2AC6; // SUBSET OF ABOVE EQUALS SIGN mirrorChars[0x2AC6] = 0x2AC5; // SUPERSET OF ABOVE EQUALS SIGN mirrorChars[0x2ACD] = 0x2ACE; // SQUARE LEFT OPEN BOX OPERATOR mirrorChars[0x2ACE] = 0x2ACD; // SQUARE RIGHT OPEN BOX OPERATOR mirrorChars[0x2ACF] = 0x2AD0; // CLOSED SUBSET mirrorChars[0x2AD0] = 0x2ACF; // CLOSED SUPERSET mirrorChars[0x2AD1] = 0x2AD2; // CLOSED SUBSET OR EQUAL TO mirrorChars[0x2AD2] = 0x2AD1; // CLOSED SUPERSET OR EQUAL TO mirrorChars[0x2AD3] = 0x2AD4; // SUBSET ABOVE SUPERSET mirrorChars[0x2AD4] = 0x2AD3; // SUPERSET ABOVE SUBSET mirrorChars[0x2AD5] = 0x2AD6; // SUBSET ABOVE SUBSET mirrorChars[0x2AD6] = 0x2AD5; // SUPERSET ABOVE SUPERSET mirrorChars[0x2ADE] = 0x22A6; // SHORT LEFT TACK mirrorChars[0x2AE3] = 0x22A9; // DOUBLE VERTICAL BAR LEFT TURNSTILE mirrorChars[0x2AE4] = 0x22A8; // VERTICAL BAR DOUBLE LEFT TURNSTILE mirrorChars[0x2AE5] = 0x22AB; // DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE mirrorChars[0x2AEC] = 0x2AED; // DOUBLE STROKE NOT SIGN mirrorChars[0x2AED] = 0x2AEC; // REVERSED DOUBLE STROKE NOT SIGN mirrorChars[0x2AF7] = 0x2AF8; // TRIPLE NESTED LESS-THAN mirrorChars[0x2AF8] = 0x2AF7; // TRIPLE NESTED GREATER-THAN mirrorChars[0x2AF9] = 0x2AFA; // DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO mirrorChars[0x2AFA] = 0x2AF9; // DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO mirrorChars[0x3008] = 0x3009; // LEFT ANGLE BRACKET mirrorChars[0x3009] = 0x3008; // RIGHT ANGLE BRACKET mirrorChars[0x300A] = 0x300B; // LEFT DOUBLE ANGLE BRACKET mirrorChars[0x300B] = 0x300A; // RIGHT DOUBLE ANGLE BRACKET mirrorChars[0x300C] = 0x300D; // [BEST FIT] LEFT CORNER BRACKET mirrorChars[0x300D] = 0x300C; // [BEST FIT] RIGHT CORNER BRACKET mirrorChars[0x300E] = 0x300F; // [BEST FIT] LEFT WHITE CORNER BRACKET mirrorChars[0x300F] = 0x300E; // [BEST FIT] RIGHT WHITE CORNER BRACKET mirrorChars[0x3010] = 0x3011; // LEFT BLACK LENTICULAR BRACKET mirrorChars[0x3011] = 0x3010; // RIGHT BLACK LENTICULAR BRACKET mirrorChars[0x3014] = 0x3015; // LEFT TORTOISE SHELL BRACKET mirrorChars[0x3015] = 0x3014; // RIGHT TORTOISE SHELL BRACKET mirrorChars[0x3016] = 0x3017; // LEFT WHITE LENTICULAR BRACKET mirrorChars[0x3017] = 0x3016; // RIGHT WHITE LENTICULAR BRACKET mirrorChars[0x3018] = 0x3019; // LEFT WHITE TORTOISE SHELL BRACKET mirrorChars[0x3019] = 0x3018; // RIGHT WHITE TORTOISE SHELL BRACKET mirrorChars[0x301A] = 0x301B; // LEFT WHITE SQUARE BRACKET mirrorChars[0x301B] = 0x301A; // RIGHT WHITE SQUARE BRACKET mirrorChars[0xFF08] = 0xFF09; // FULLWIDTH LEFT PARENTHESIS mirrorChars[0xFF09] = 0xFF08; // FULLWIDTH RIGHT PARENTHESIS mirrorChars[0xFF1C] = 0xFF1E; // FULLWIDTH LESS-THAN SIGN mirrorChars[0xFF1E] = 0xFF1C; // FULLWIDTH GREATER-THAN SIGN mirrorChars[0xFF3B] = 0xFF3D; // FULLWIDTH LEFT SQUARE BRACKET mirrorChars[0xFF3D] = 0xFF3B; // FULLWIDTH RIGHT SQUARE BRACKET mirrorChars[0xFF5B] = 0xFF5D; // FULLWIDTH LEFT CURLY BRACKET mirrorChars[0xFF5D] = 0xFF5B; // FULLWIDTH RIGHT CURLY BRACKET mirrorChars[0xFF5F] = 0xFF60; // FULLWIDTH LEFT WHITE PARENTHESIS mirrorChars[0xFF60] = 0xFF5F; // FULLWIDTH RIGHT WHITE PARENTHESIS mirrorChars[0xFF62] = 0xFF63; // [BEST FIT] HALFWIDTH LEFT CORNER BRACKET mirrorChars[0xFF63] = 0xFF62; // [BEST FIT] HALFWIDTH RIGHT CORNER BRACKET } } }