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
26package sun.font;
27
28import java.awt.FontFormatException;
29import java.awt.font.FontRenderContext;
30import java.awt.geom.GeneralPath;
31import java.awt.geom.Rectangle2D;
32import java.util.HashMap;
33import java.util.Locale;
34import java.nio.charset.*;
35import java.nio.CharBuffer;
36import java.nio.ByteBuffer;
37
38class 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.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
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