1 /*
2  * Copyright (c) 2003, 2014, 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 
getXMapper(String encoding)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 
getXMapperInternal(String encoding)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.font.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 = "JIS0208";
85             nBytes = DOUBLE_BYTE;
86         } else if (encoding.equals("jisx0201.1976-0")) {
87             jclass = "JIS0201";
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 = "JIS0212";
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.font.X11KSC5601";
121             nBytes = DOUBLE_BYTE;
122         } else if (encoding.equals( "ksc5601.1992-3")) {
123             jclass ="sun.font.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.font.X11CNS11643P1";
130             nBytes = DOUBLE_BYTE;
131         } else if (encoding.equals("cns11643-2")) {
132             jclass = "sun.font.X11CNS11643P2";
133             nBytes = DOUBLE_BYTE;
134         } else if (encoding.equals("cns11643-3")) {
135             jclass = "sun.font.X11CNS11643P3";
136             nBytes = DOUBLE_BYTE;
137         } else if (encoding.equals("gb2312.1980-0")) {
138             jclass = "sun.font.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.font.X11GBK";
148             nBytes = DOUBLE_BYTE;
149         } else if (encoding.indexOf("sun.unicode-0") >= 0) {
150             jclass = "sun.font.X11SunUnicode_0";
151             nBytes = DOUBLE_BYTE;
152         } else if (encoding.indexOf("gb18030.2000-1") >= 0) {
153             jclass = "sun.font.X11GB18030_1";
154             nBytes = DOUBLE_BYTE;
155         } else if (encoding.indexOf( "gb18030.2000-0") >= 0) {
156             jclass = "sun.font.X11GB18030_0";
157             nBytes = DOUBLE_BYTE;
158         } else if (encoding.indexOf("hkscs") >= 0) {
159             jclass = "MS950_HKSCS_XP";
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 
XMap(String className, int minU, int maxU, int nBytes, boolean addAscii, boolean lowPartOnly)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).getDeclaredConstructor().
176                                                   newInstance()).newEncoder();
177                 } else {
178                     enc = Charset.forName(className).newEncoder();
179                 }
180             } catch (Exception x) {x.printStackTrace();}
181         }
182         if (enc == null) {
183             convertedGlyphs = new char[256];
184             for (int i=0; i<256; i++) {
185                 convertedGlyphs[i] = (char)i;
186             }
187             return;
188         } else {
189             /* chars is set to the unicode values to convert,
190              * bytes is where the X11 character codes will be output.
191              * Finally we pack the byte pairs into chars.
192              */
193             int count = maxU - minU + 1;
194             byte[] bytes = new byte[count*nBytes];
195             char[] chars  = new char[count];
196             for (int i=0; i<count; i++) {
197                 chars[i] = (char)(minU+i);
198             }
199             int startCharIndex = 0;
200             /* For multi-byte encodings, single byte chars should be skipped */
201             if (nBytes > SINGLE_BYTE && minU < 256) {
202                 startCharIndex = 256-minU;
203             }
204             byte[] rbytes = new byte[nBytes];
205             try {
206                 int cbLen = 0;
207                 int bbLen = 0;
208                 // Since we don't support surrogates in any X11 encoding, skip
209                 // the surrogate area, otherwise the sequence of "Oxdbff0xdc00"
210                 // will accidently cause the surrogate-aware nio charset to treat
211                 // them as a legal pair and then undesirablly skip 2 "chars"
212                 // for one "unmappable character"
213                 if (startCharIndex < SURR_MIN && startCharIndex + count >SURR_MAX) {
214                     cbLen = SURR_MIN - startCharIndex;
215                     bbLen = cbLen * nBytes;
216                     enc.onMalformedInput(CodingErrorAction.REPLACE)
217                         .onUnmappableCharacter(CodingErrorAction.REPLACE)
218                         .replaceWith(rbytes)
219                         .encode(CharBuffer.wrap(chars, startCharIndex, cbLen),
220                                 ByteBuffer.wrap(bytes, startCharIndex * nBytes, bbLen),
221                                 true);
222                     startCharIndex = SURR_MAX + 1;
223                 }
224                 cbLen = count - startCharIndex;
225                 bbLen = cbLen * nBytes;
226                 enc.onMalformedInput(CodingErrorAction.REPLACE)
227                     .onUnmappableCharacter(CodingErrorAction.REPLACE)
228                     .replaceWith(rbytes)
229                     .encode(CharBuffer.wrap(chars, startCharIndex, cbLen),
230                             ByteBuffer.wrap(bytes, startCharIndex * nBytes, bbLen),
231                             true);
232             } catch (Exception e) { e.printStackTrace();}
233 
234             convertedGlyphs = new char[65536];
235             for (int i=0; i<count; i++) {
236                 if (nBytes == 1) {
237                     convertedGlyphs[i+minU] = (char)(bytes[i]&0xff);
238                 } else {
239                     convertedGlyphs[i+minU] =
240                         (char)(((bytes[i*2]&0xff) << 8) + (bytes[i*2+1]&0xff));
241                 }
242             }
243         }
244 
245         int max = (lowPartOnly) ? 128 : 256;
246         if (addAscii && convertedGlyphs.length >= 256) {
247             for (int i=0;i<max;i++) {
248                 if (convertedGlyphs[i] == 0) {
249                     convertedGlyphs[i] = (char)i;
250                 }
251             }
252         }
253     }
254 }
255