1 /*
   2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.font;
  27 
  28 import java.awt.FontFormatException;
  29 import java.awt.font.FontRenderContext;
  30 import java.awt.geom.GeneralPath;
  31 import java.awt.geom.Rectangle2D;
  32 import java.util.HashMap;
  33 import java.util.Locale;
  34 import java.nio.charset.*;
  35 import java.nio.CharBuffer;
  36 import java.nio.ByteBuffer;
  37 
  38 class XMap {
  39 
  40     private static HashMap<String, XMap> xMappers = new HashMap<>();
  41 
  42     /* ConvertedGlyphs has unicode code points as indexes and values
  43      * are platform-encoded multi-bytes chars packed into java chars.
  44      * These platform-encoded characters are equated to glyph ids, although
  45      * that's not strictly true, as X11 only supports using chars.
  46      * The assumption carried over from the native implementation that
  47      * a char is big enough to hold an X11 glyph id (ie platform char).
  48      */
  49     char[] convertedGlyphs;
  50 
  51     static synchronized XMap getXMapper(String encoding) {
  52         XMap mapper = xMappers.get(encoding);
  53         if (mapper == null) {
  54             mapper = getXMapperInternal(encoding);
  55             xMappers.put(encoding, mapper);
  56         }
  57         return mapper;
  58     }
  59 
  60     static final int SINGLE_BYTE = 1;
  61     static final int DOUBLE_BYTE = 2;
  62 
  63     private static XMap getXMapperInternal(String encoding) {
  64 
  65         String jclass = null;
  66         int nBytes = SINGLE_BYTE;
  67         int maxU = 0xffff;
  68         int minU = 0;
  69         boolean addAscii = false;
  70         boolean lowPartOnly = false;
  71         if (encoding.equals("dingbats")) {
  72             jclass = "sun.awt.motif.X11Dingbats";
  73             minU = 0x2701;
  74             maxU = 0x27be;
  75         } else if (encoding.equals("symbol")){
  76             jclass = "sun.awt.Symbol";
  77             minU = 0x0391;
  78             maxU = 0x22ef;
  79         } else if (encoding.equals("iso8859-1")) {
  80             maxU = 0xff;
  81         } else if (encoding.equals("iso8859-2")) {
  82             jclass = "ISO8859_2";
  83         } else if (encoding.equals("jisx0208.1983-0")) {
  84             jclass = "sun.awt.motif.X11JIS0208";
  85             nBytes = DOUBLE_BYTE;
  86         } else if (encoding.equals("jisx0201.1976-0")) {
  87             jclass = "sun.awt.motif.X11JIS0201";
  88             // this is mapping the latin supplement range 128->255 which
  89             // doesn't exist in JIS0201. This needs examination.
  90             // it was also overwriting a couple of the mappings of
  91             // 7E and A5 which in JIS201 are different chars than in
  92             // Latin 1. I have revised AddAscii to not overwrite chars
  93             // which are already converted.
  94             addAscii = true;
  95             lowPartOnly = true;
  96         } else if (encoding.equals("jisx0212.1990-0")) {
  97             jclass = "sun.awt.motif.X11JIS0212";
  98             nBytes = DOUBLE_BYTE;
  99         } else if (encoding.equals("iso8859-4")) {
 100             jclass = "ISO8859_4";
 101         } else if (encoding.equals("iso8859-5")) {
 102             jclass = "ISO8859_5";
 103         } else if (encoding.equals("koi8-r")) {
 104             jclass = "KOI8_R";
 105         } else if (encoding.equals("ansi-1251")) {
 106             jclass = "windows-1251";
 107         } else if (encoding.equals("iso8859-6")) {
 108             jclass = "ISO8859_6";
 109         } else if (encoding.equals("iso8859-7")) {
 110             jclass = "ISO8859_7";
 111         } else if (encoding.equals("iso8859-8")) {
 112             jclass = "ISO8859_8";
 113         } else if (encoding.equals("iso8859-9")) {
 114             jclass = "ISO8859_9";
 115         } else if (encoding.equals("iso8859-13")) {
 116             jclass = "ISO8859_13";
 117         } else if (encoding.equals("iso8859-15")) {
 118             jclass = "ISO8859_15";
 119         } else if (encoding.equals("ksc5601.1987-0")) {
 120             jclass ="sun.awt.motif.X11KSC5601";
 121             nBytes = DOUBLE_BYTE;
 122         } else if (encoding.equals( "ksc5601.1992-3")) {
 123             jclass ="sun.awt.motif.X11Johab";
 124             nBytes = DOUBLE_BYTE;
 125         } else if (encoding.equals( "ksc5601.1987-1")) {
 126             jclass ="EUC_KR";
 127             nBytes = DOUBLE_BYTE;
 128         } else if (encoding.equals( "cns11643-1")) {
 129             jclass = "sun.awt.motif.X11CNS11643P1";
 130             nBytes = DOUBLE_BYTE;
 131         } else if (encoding.equals("cns11643-2")) {
 132             jclass = "sun.awt.motif.X11CNS11643P2";
 133             nBytes = DOUBLE_BYTE;
 134         } else if (encoding.equals("cns11643-3")) {
 135             jclass = "sun.awt.motif.X11CNS11643P3";
 136             nBytes = DOUBLE_BYTE;
 137         } else if (encoding.equals("gb2312.1980-0")) {
 138             jclass = "sun.awt.motif.X11GB2312";
 139             nBytes = DOUBLE_BYTE;
 140         } else if (encoding.indexOf("big5") >= 0) {
 141             jclass = "Big5";
 142             nBytes = DOUBLE_BYTE;
 143             addAscii = true;
 144         } else if (encoding.equals("tis620.2533-0")) {
 145             jclass = "TIS620";
 146         } else if (encoding.equals("gbk-0")) {
 147             jclass = "sun.awt.motif.X11GBK";
 148             nBytes = DOUBLE_BYTE;
 149         } else if (encoding.indexOf("sun.unicode-0") >= 0) {
 150             jclass = "sun.awt.motif.X11SunUnicode_0";
 151             nBytes = DOUBLE_BYTE;
 152         } else if (encoding.indexOf("gb18030.2000-1") >= 0) {
 153             jclass = "sun.awt.motif.X11GB18030_1";
 154             nBytes = DOUBLE_BYTE;
 155         } else if (encoding.indexOf( "gb18030.2000-0") >= 0) {
 156             jclass = "sun.awt.motif.X11GB18030_0";
 157             nBytes = DOUBLE_BYTE;
 158         } else if (encoding.indexOf("hkscs") >= 0) {
 159             jclass = "sun.awt.HKSCS";
 160             nBytes = DOUBLE_BYTE;
 161         }
 162         return new XMap(jclass, minU, maxU, nBytes, addAscii, lowPartOnly);
 163     }
 164 
 165     private static final char SURR_MIN = '\uD800';
 166     private static final char SURR_MAX = '\uDFFF';
 167 
 168     private XMap(String className, int minU, int maxU, int nBytes,
 169                  boolean addAscii, boolean lowPartOnly) {
 170 
 171         CharsetEncoder enc = null;
 172         if (className != null) {
 173             try {
 174                 if (className.startsWith("sun.awt")) {
 175                     enc = ((Charset)Class.forName(className).newInstance()).newEncoder();
 176                 } else {
 177                     enc = Charset.forName(className).newEncoder();
 178                 }
 179             } catch (Exception x) {x.printStackTrace();}
 180         }
 181         if (enc == null) {
 182             convertedGlyphs = new char[256];
 183             for (int i=0; i<256; i++) {
 184                 convertedGlyphs[i] = (char)i;
 185             }
 186             return;
 187         } else {
 188             /* chars is set to the unicode values to convert,
 189              * bytes is where the X11 character codes will be output.
 190              * Finally we pack the byte pairs into chars.
 191              */
 192             int count = maxU - minU + 1;
 193             byte[] bytes = new byte[count*nBytes];
 194             char[] chars  = new char[count];
 195             for (int i=0; i<count; i++) {
 196                 chars[i] = (char)(minU+i);
 197             }
 198             int startCharIndex = 0;
 199             /* For multi-byte encodings, single byte chars should be skipped */
 200             if (nBytes > SINGLE_BYTE && minU < 256) {
 201                 startCharIndex = 256-minU;
 202             }
 203             byte[] rbytes = new byte[nBytes];
 204             try {
 205                 int cbLen = 0;
 206                 int bbLen = 0;
 207                 // Since we don't support surrogates in any X11 encoding, skip
 208                 // the surrogate area, otherwise the sequence of "Oxdbff0xdc00"
 209                 // will accidently cause the surrogate-aware nio charset to treat
 210                 // them as a legal pair and then undesirablly skip 2 "chars"
 211                 // for one "unmappable character"
 212                 if (startCharIndex < SURR_MIN && startCharIndex + count >SURR_MAX) {
 213                     cbLen = SURR_MIN - startCharIndex;
 214                     bbLen = cbLen * nBytes;
 215                     enc.onMalformedInput(CodingErrorAction.REPLACE)
 216                         .onUnmappableCharacter(CodingErrorAction.REPLACE)
 217                         .replaceWith(rbytes)
 218                         .encode(CharBuffer.wrap(chars, startCharIndex, cbLen),
 219                                 ByteBuffer.wrap(bytes, startCharIndex * nBytes, bbLen),
 220                                 true);
 221                     startCharIndex = SURR_MAX + 1;
 222                 }
 223                 cbLen = count - startCharIndex;
 224                 bbLen = cbLen * nBytes;
 225                 enc.onMalformedInput(CodingErrorAction.REPLACE)
 226                     .onUnmappableCharacter(CodingErrorAction.REPLACE)
 227                     .replaceWith(rbytes)
 228                     .encode(CharBuffer.wrap(chars, startCharIndex, cbLen),
 229                             ByteBuffer.wrap(bytes, startCharIndex * nBytes, bbLen),
 230                             true);
 231             } catch (Exception e) { e.printStackTrace();}
 232 
 233             convertedGlyphs = new char[65536];
 234             for (int i=0; i<count; i++) {
 235                 if (nBytes == 1) {
 236                     convertedGlyphs[i+minU] = (char)(bytes[i]&0xff);
 237                 } else {
 238                     convertedGlyphs[i+minU] =
 239                         (char)(((bytes[i*2]&0xff) << 8) + (bytes[i*2+1]&0xff));
 240                 }
 241             }
 242         }
 243 
 244         int max = (lowPartOnly) ? 128 : 256;
 245         if (addAscii && convertedGlyphs.length >= 256) {
 246             for (int i=0;i<max;i++) {
 247                 if (convertedGlyphs[i] == 0) {
 248                     convertedGlyphs[i] = (char)i;
 249                 }
 250             }
 251         }
 252     }
 253 }