| /* | |
| * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code 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 General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.font; | |
| import java.util.HashMap; | |
| import java.nio.charset.*; | |
| import java.nio.CharBuffer; | |
| import java.nio.ByteBuffer; | |
| final class XMap { | |
| private static HashMap<String, XMap> xMappers = new HashMap<>(); | |
| /* ConvertedGlyphs has unicode code points as indexes and values | |
| * are platform-encoded multi-bytes chars packed into java chars. | |
| * These platform-encoded characters are equated to glyph ids, although | |
| * that's not strictly true, as X11 only supports using chars. | |
| * The assumption carried over from the native implementation that | |
| * a char is big enough to hold an X11 glyph id (ie platform char). | |
| */ | |
| char[] convertedGlyphs; | |
| static synchronized XMap getXMapper(String encoding) { | |
| XMap mapper = xMappers.get(encoding); | |
| if (mapper == null) { | |
| mapper = getXMapperInternal(encoding); | |
| xMappers.put(encoding, mapper); | |
| } | |
| return mapper; | |
| } | |
| static final int SINGLE_BYTE = 1; | |
| static final int DOUBLE_BYTE = 2; | |
| private static XMap getXMapperInternal(String encoding) { | |
| String jclass = null; | |
| int nBytes = SINGLE_BYTE; | |
| int maxU = 0xffff; | |
| int minU = 0; | |
| boolean addAscii = false; | |
| boolean lowPartOnly = false; | |
| if (encoding.equals("dingbats")) { | |
| jclass = "sun.font.X11Dingbats"; | |
| minU = 0x2701; | |
| maxU = 0x27be; | |
| } else if (encoding.equals("symbol")){ | |
| jclass = "sun.awt.Symbol"; | |
| minU = 0x0391; | |
| maxU = 0x22ef; | |
| } else if (encoding.equals("iso8859-1")) { | |
| maxU = 0xff; | |
| } else if (encoding.equals("iso8859-2")) { | |
| jclass = "ISO8859_2"; | |
| } else if (encoding.equals("jisx0208.1983-0")) { | |
| jclass = "JIS0208"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.equals("jisx0201.1976-0")) { | |
| jclass = "JIS0201"; | |
| // this is mapping the latin supplement range 128->255 which | |
| // doesn't exist in JIS0201. This needs examination. | |
| // it was also overwriting a couple of the mappings of | |
| // 7E and A5 which in JIS201 are different chars than in | |
| // Latin 1. I have revised AddAscii to not overwrite chars | |
| // which are already converted. | |
| addAscii = true; | |
| lowPartOnly = true; | |
| } else if (encoding.equals("jisx0212.1990-0")) { | |
| jclass = "JIS0212"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.equals("iso8859-4")) { | |
| jclass = "ISO8859_4"; | |
| } else if (encoding.equals("iso8859-5")) { | |
| jclass = "ISO8859_5"; | |
| } else if (encoding.equals("koi8-r")) { | |
| jclass = "KOI8_R"; | |
| } else if (encoding.equals("ansi-1251")) { | |
| jclass = "windows-1251"; | |
| } else if (encoding.equals("iso8859-6")) { | |
| jclass = "ISO8859_6"; | |
| } else if (encoding.equals("iso8859-7")) { | |
| jclass = "ISO8859_7"; | |
| } else if (encoding.equals("iso8859-8")) { | |
| jclass = "ISO8859_8"; | |
| } else if (encoding.equals("iso8859-9")) { | |
| jclass = "ISO8859_9"; | |
| } else if (encoding.equals("iso8859-13")) { | |
| jclass = "ISO8859_13"; | |
| } else if (encoding.equals("iso8859-15")) { | |
| jclass = "ISO8859_15"; | |
| } else if (encoding.equals("ksc5601.1987-0")) { | |
| jclass ="sun.font.X11KSC5601"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.equals("ksc5601.1992-3")) { | |
| jclass ="sun.font.X11Johab"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.equals("ksc5601.1987-1")) { | |
| jclass ="EUC_KR"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.equals("cns11643-1")) { | |
| jclass = "sun.font.X11CNS11643P1"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.equals("cns11643-2")) { | |
| jclass = "sun.font.X11CNS11643P2"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.equals("cns11643-3")) { | |
| jclass = "sun.font.X11CNS11643P3"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.equals("gb2312.1980-0")) { | |
| jclass = "sun.font.X11GB2312"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.contains("big5")) { | |
| jclass = "Big5"; | |
| nBytes = DOUBLE_BYTE; | |
| addAscii = true; | |
| } else if (encoding.equals("tis620.2533-0")) { | |
| jclass = "TIS620"; | |
| } else if (encoding.equals("gbk-0")) { | |
| jclass = "sun.font.X11GBK"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.contains("sun.unicode-0")) { | |
| jclass = "sun.font.X11SunUnicode_0"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.contains("gb18030.2000-1")) { | |
| jclass = "sun.font.X11GB18030_1"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.contains("gb18030.2000-0")) { | |
| jclass = "sun.font.X11GB18030_0"; | |
| nBytes = DOUBLE_BYTE; | |
| } else if (encoding.contains("hkscs")) { | |
| jclass = "MS950_HKSCS_XP"; | |
| nBytes = DOUBLE_BYTE; | |
| } | |
| return new XMap(jclass, minU, maxU, nBytes, addAscii, lowPartOnly); | |
| } | |
| private static final char SURR_MIN = '\uD800'; | |
| private static final char SURR_MAX = '\uDFFF'; | |
| private XMap(String className, int minU, int maxU, int nBytes, | |
| boolean addAscii, boolean lowPartOnly) { | |
| CharsetEncoder enc = null; | |
| if (className != null) { | |
| try { | |
| if (className.startsWith("sun.awt")) { | |
| enc = ((Charset)Class.forName(className).getDeclaredConstructor(). | |
| newInstance()).newEncoder(); | |
| } else { | |
| enc = Charset.forName(className).newEncoder(); | |
| } | |
| } catch (Exception x) {x.printStackTrace();} | |
| } | |
| if (enc == null) { | |
| convertedGlyphs = new char[256]; | |
| for (int i=0; i<256; i++) { | |
| convertedGlyphs[i] = (char)i; | |
| } | |
| return; | |
| } else { | |
| /* chars is set to the unicode values to convert, | |
| * bytes is where the X11 character codes will be output. | |
| * Finally we pack the byte pairs into chars. | |
| */ | |
| int count = maxU - minU + 1; | |
| byte[] bytes = new byte[count*nBytes]; | |
| char[] chars = new char[count]; | |
| for (int i=0; i<count; i++) { | |
| chars[i] = (char)(minU+i); | |
| } | |
| int startCharIndex = 0; | |
| /* For multi-byte encodings, single byte chars should be skipped */ | |
| if (nBytes > SINGLE_BYTE && minU < 256) { | |
| startCharIndex = 256-minU; | |
| } | |
| byte[] rbytes = new byte[nBytes]; | |
| try { | |
| int cbLen = 0; | |
| int bbLen = 0; | |
| // Since we don't support surrogates in any X11 encoding, skip | |
| // the surrogate area, otherwise the sequence of "Oxdbff0xdc00" | |
| // will accidentally cause the surrogate-aware nio charset to treat | |
| // them as a legal pair and then undesirably skip 2 "chars" | |
| // for one "unmappable character" | |
| if (startCharIndex < SURR_MIN && startCharIndex + count >SURR_MAX) { | |
| cbLen = SURR_MIN - startCharIndex; | |
| bbLen = cbLen * nBytes; | |
| enc.onMalformedInput(CodingErrorAction.REPLACE) | |
| .onUnmappableCharacter(CodingErrorAction.REPLACE) | |
| .replaceWith(rbytes) | |
| .encode(CharBuffer.wrap(chars, startCharIndex, cbLen), | |
| ByteBuffer.wrap(bytes, startCharIndex * nBytes, bbLen), | |
| true); | |
| startCharIndex = SURR_MAX + 1; | |
| } | |
| cbLen = count - startCharIndex; | |
| bbLen = cbLen * nBytes; | |
| enc.onMalformedInput(CodingErrorAction.REPLACE) | |
| .onUnmappableCharacter(CodingErrorAction.REPLACE) | |
| .replaceWith(rbytes) | |
| .encode(CharBuffer.wrap(chars, startCharIndex, cbLen), | |
| ByteBuffer.wrap(bytes, startCharIndex * nBytes, bbLen), | |
| true); | |
| } catch (Exception e) { e.printStackTrace();} | |
| convertedGlyphs = new char[65536]; | |
| for (int i=0; i<count; i++) { | |
| if (nBytes == 1) { | |
| convertedGlyphs[i+minU] = (char)(bytes[i]&0xff); | |
| } else { | |
| convertedGlyphs[i+minU] = | |
| (char)(((bytes[i*2]&0xff) << 8) + (bytes[i*2+1]&0xff)); | |
| } | |
| } | |
| } | |
| int max = (lowPartOnly) ? 128 : 256; | |
| if (addAscii && convertedGlyphs.length >= 256) { | |
| for (int i=0;i<max;i++) { | |
| if (convertedGlyphs[i] == 0) { | |
| convertedGlyphs[i] = (char)i; | |
| } | |
| } | |
| } | |
| } | |
| } |