Open Source Repository

Home /itextpdf/itextpdf-5.1.2 | Repository Home



com/itextpdf/text/pdf/PdfEncodings.java
/*
 * $Id: PdfEncodings.java 4784 2011-03-15 08:33:00Z blowagie $
 *
 * This file is part of the iText (R) project.
 * Copyright (c) 1998-2011 1T3XT BVBA
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program 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 Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: [email protected]
 */
package com.itextpdf.text.pdf;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;

import com.itextpdf.text.ExceptionConverter;
import com.itextpdf.text.error_messages.MessageLocalization;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
/** Supports fast encodings for winansi and PDFDocEncoding.
 * Supports conversions from CJK encodings to CID.
 * Supports custom encodings.
 @author Paulo Soares
 */
public class PdfEncodings {
    protected static final int CIDNONE = 0;
    protected static final int CIDRANGE = 1;
    protected static final int CIDCHAR = 2;

    static final char winansiByteToChar[] {
        0123456789101112131415,
        16171819202122232425262728293031,
        32333435363738394041424344454647,
        48495051525354555657585960616263,
        64656667686970717273747576777879,
        80818283848586878889909192939495,
        96979899100101102103104105106107108109110111,
        112113114115116117118119120121122123124125126127,
        83646553382184028222823082248225710824035282493386553338165533,
        6553382168217822082218226821182127328482353825033965533382376,
        160161162163164165166167168169170171172173174175,
        176177178179180181182183184185186187188189190191,
        192193194195196197198199200201202203204205206207,
        208209210211212213214215216217218219220221222223,
        224225226227228229230231232233234235236237238239,
        240241242243244245246247248249250251252253254255};

    static final char pdfEncodingByteToChar[] {
        0123456789101112131415,
        16171819202122232425262728293031,
        32333435363738394041424344454647,
        48495051525354555657585960616263,
        64656667686970717273747576777879,
        80818283848586878889909192939495,
        96979899100101102103104105106107108109110111,
        112113114115116117118119120121122123124125126127,
        0x20220x20200x20210x20260x20140x20130x01920x20440x20390x203a0x22120x20300x201e0x201c0x201d0x2018,
        0x20190x201a0x21220xfb010xfb020x01410x01520x01600x01780x017d0x01310x01420x01530x01610x017e65533,
        0x20ac161162163164165166167168169170171172173174175,
        176177178179180181182183184185186187188189190191,
        192193194195196197198199200201202203204205206207,
        208209210211212213214215216217218219220221222223,
        224225226227228229230231232233234235236237238239,
        240241242243244245246247248249250251252253254255};

    static final IntHashtable winansi = new IntHashtable();

    static final IntHashtable pdfEncoding = new IntHashtable();

    static HashMap<String, ExtraEncoding> extraEncodings = new HashMap<String, ExtraEncoding>();

    static {
        for (int k = 128; k < 161; ++k) {
            char c = winansiByteToChar[k];
            if (c != 65533)
                winansi.put(c, k);
        }

        for (int k = 128; k < 161; ++k) {
            char c = pdfEncodingByteToChar[k];
            if (c != 65533)
                pdfEncoding.put(c, k);
        }

        addExtraEncoding("Wingdings"new WingdingsConversion());
        addExtraEncoding("Symbol"new SymbolConversion(true));
        addExtraEncoding("ZapfDingbats"new SymbolConversion(false));
        addExtraEncoding("SymbolTT"new SymbolTTConversion());
        addExtraEncoding("Cp437"new Cp437Conversion());
    }

    /** Converts a <CODE>String</CODE> to a </CODE>byte</CODE> array according
     * to the font's encoding.
     @return an array of <CODE>byte</CODE> representing the conversion according to the font's encoding
     @param encoding the encoding
     @param text the <CODE>String</CODE> to be converted
     */
    public static final byte[] convertToBytes(String text, String encoding) {
        if (text == null)
            return new byte[0];
        if (encoding == null || encoding.length() == 0) {
            int len = text.length();
            byte b[] new byte[len];
            for (int k = 0; k < len; ++k)
                b[k(byte)text.charAt(k);
            return b;
        }
        ExtraEncoding extra = extraEncodings.get(encoding.toLowerCase());
        if (extra != null) {
            byte b[] = extra.charToByte(text, encoding);
            if (b != null)
                return b;
        }
        IntHashtable hash = null;
        if (encoding.equals(BaseFont.WINANSI))
            hash = winansi;
        else if (encoding.equals(PdfObject.TEXT_PDFDOCENCODING))
            hash = pdfEncoding;
        if (hash != null) {
            char cc[] = text.toCharArray();
            int len = cc.length;
            int ptr = 0;
            byte b[] new byte[len];
            int c = 0;
            for (int k = 0; k < len; ++k) {
                char char1 = cc[k];
                if (char1 < 128 || char1 > 160 && char1 <= 255)
                    c = char1;
                else
                    c = hash.get(char1);
                if (c != 0)
                    b[ptr++(byte)c;
            }
            if (ptr == len)
                return b;
            byte b2[] new byte[ptr];
            System.arraycopy(b, 0, b2, 0, ptr);
            return b2;
        }
        if (encoding.equals(PdfObject.TEXT_UNICODE)) {
            // workaround for jdk 1.2.2 bug
            char cc[] = text.toCharArray();
            int len = cc.length;
            byte b[] new byte[cc.length * 2];
            b[0= -2;
            b[1= -1;
            int bptr = 2;
            for (int k = 0; k < len; ++k) {
                char c = cc[k];
                b[bptr++(byte)(c >> 8);
                b[bptr++(byte)(c & 0xff);
            }
            return b;
        }
        try {
            Charset cc = Charset.forName(encoding);
            CharsetEncoder ce = cc.newEncoder();
            ce.onUnmappableCharacter(CodingErrorAction.IGNORE);
            CharBuffer cb = CharBuffer.wrap(text.toCharArray());
            java.nio.ByteBuffer bb = ce.encode(cb);
            bb.rewind();
            int lim = bb.limit();
            byte[] br = new byte[lim];
            bb.get(br);
            return br;
        }
        catch (IOException e) {
            throw new ExceptionConverter(e);
        }
    }

    /** Converts a <CODE>String</CODE> to a </CODE>byte</CODE> array according
     * to the font's encoding.
     @return an array of <CODE>byte</CODE> representing the conversion according to the font's encoding
     @param encoding the encoding
     @param char1 the <CODE>char</CODE> to be converted
     */
    public static final byte[] convertToBytes(char char1, String encoding) {
        if (encoding == null || encoding.length() == 0)
            return new byte[]{(byte)char1};
        ExtraEncoding extra = extraEncodings.get(encoding.toLowerCase());
        if (extra != null) {
            byte b[] = extra.charToByte(char1, encoding);
            if (b != null)
                return b;
        }
        IntHashtable hash = null;
        if (encoding.equals(BaseFont.WINANSI))
            hash = winansi;
        else if (encoding.equals(PdfObject.TEXT_PDFDOCENCODING))
            hash = pdfEncoding;
        if (hash != null) {
            int c = 0;
            if (char1 < 128 || char1 > 160 && char1 <= 255)
                c = char1;
            else
                c = hash.get(char1);
            if (c != 0)
                return new byte[]{(byte)c};
            else
                return new byte[0];
        }
        if (encoding.equals(PdfObject.TEXT_UNICODE)) {
            // workaround for jdk 1.2.2 bug
            byte b[] new byte[4];
            b[0= -2;
            b[1= -1;
            b[2(byte)(char1 >> 8);
            b[3(byte)(char1 & 0xff);
            return b;
        }
        try {
            Charset cc = Charset.forName(encoding);
            CharsetEncoder ce = cc.newEncoder();
            ce.onUnmappableCharacter(CodingErrorAction.IGNORE);
            CharBuffer cb = CharBuffer.wrap(new char[]{char1});
            java.nio.ByteBuffer bb = ce.encode(cb);
            bb.rewind();
            int lim = bb.limit();
            byte[] br = new byte[lim];
            bb.get(br);
            return br;
        }
        catch (IOException e) {
            throw new ExceptionConverter(e);
        }
    }

    /** Converts a </CODE>byte</CODE> array to a <CODE>String</CODE> according
     * to the some encoding.
     @param bytes the bytes to convert
     @param encoding the encoding
     @return the converted <CODE>String</CODE>
     */
    public static final String convertToString(byte bytes[], String encoding) {
        if (bytes == null)
            return PdfObject.NOTHING;
        if (encoding == null || encoding.length() == 0) {
            char c[] new char[bytes.length];
            for (int k = 0; k < bytes.length; ++k)
                c[k(char)(bytes[k0xff);
            return new String(c);
        }
        ExtraEncoding extra = extraEncodings.get(encoding.toLowerCase());
        if (extra != null) {
            String text = extra.byteToChar(bytes, encoding);
            if (text != null)
                return text;
        }
        char ch[] null;
        if (encoding.equals(BaseFont.WINANSI))
            ch = winansiByteToChar;
        else if (encoding.equals(PdfObject.TEXT_PDFDOCENCODING))
            ch = pdfEncodingByteToChar;
        if (ch != null) {
            int len = bytes.length;
            char c[] new char[len];
            for (int k = 0; k < len; ++k) {
                c[k= ch[bytes[k0xff];
            }
            return new String(c);
        }
        try {
            return new String(bytes, encoding);
        }
        catch (UnsupportedEncodingException e) {
            throw new ExceptionConverter(e);
        }
    }

    /** Checks is <CODE>text</CODE> only has PdfDocEncoding characters.
     @param text the <CODE>String</CODE> to test
     @return <CODE>true</CODE> if only PdfDocEncoding characters are present
     */
    public static boolean isPdfDocEncoding(String text) {
        if (text == null)
            return true;
        int len = text.length();
        for (int k = 0; k < len; ++k) {
            char char1 = text.charAt(k);
            if (char1 < 128 || char1 > 160 && char1 <= 255)
                continue;
            if (!pdfEncoding.containsKey(char1))
                return false;
        }
        return true;
    }

    static final HashMap<String, char[][]> cmaps = new HashMap<String, char[][]>();
    /** Assumes that '\\n' and '\\r\\n' are the newline sequences. It may not work for
     * all CJK encodings. To be used with loadCmap().
     */
    public static final byte CRLF_CID_NEWLINE[][] new byte[][]{{(byte)'\n'}{(byte)'\r'(byte)'\n'}};

    /** Clears the CJK cmaps from the cache. If <CODE>name</CODE> is the
     * empty string then all the cache is cleared. Calling this method
     * has no consequences other than the need to reload the cmap
     * if needed.
     @param name the name of the cmap to clear or all the cmaps if the empty string
     */
    public static void clearCmap(String name) {
        synchronized (cmaps) {
            if (name.length() == 0)
                cmaps.clear();
            else
                cmaps.remove(name);
        }
    }

    /** Loads a CJK cmap to the cache with the option of associating
     * sequences to the newline.
     @param name the CJK cmap name
     @param newline the sequences to be replaced by a newline in the resulting CID. See <CODE>CRLF_CID_NEWLINE</CODE>
     */
    public static void loadCmap(String name, byte newline[][]) {
        try {
            char planes[][] null;
            synchronized (cmaps) {
                planes = cmaps.get(name);
            }
            if (planes == null) {
                planes = readCmap(name, newline);
                synchronized (cmaps) {
                    cmaps.put(name, planes);
                }
            }
        }
        catch (IOException e) {
            throw new ExceptionConverter(e);
        }
    }

    /** Converts a <CODE>byte</CODE> array encoded as <CODE>name</CODE>
     * to a CID string. This is needed to reach some CJK characters
     * that don't exist in 16 bit Unicode.</p>
     * The font to use this result must use the encoding "Identity-H"
     * or "Identity-V".</p>
     * See ftp://ftp.oreilly.com/pub/examples/nutshell/cjkv/adobe/.
     @param name the CJK encoding name
     @param seq the <CODE>byte</CODE> array to be decoded
     @return the CID string
     */
    public static String convertCmap(String name, byte seq[]) {
        return convertCmap(name, seq, 0, seq.length);
    }

    /** Converts a <CODE>byte</CODE> array encoded as <CODE>name</CODE>
     * to a CID string. This is needed to reach some CJK characters
     * that don't exist in 16 bit Unicode.</p>
     * The font to use this result must use the encoding "Identity-H"
     * or "Identity-V".</p>
     * See ftp://ftp.oreilly.com/pub/examples/nutshell/cjkv/adobe/.
     @param name the CJK encoding name
     @param start the start offset in the data
     @param length the number of bytes to convert
     @param seq the <CODE>byte</CODE> array to be decoded
     @return the CID string
     */
    public static String convertCmap(String name, byte seq[]int start, int length) {
        try {
            char planes[][] null;
            synchronized (cmaps) {
                planes = cmaps.get(name);
            }
            if (planes == null) {
                planes = readCmap(name, (byte[][])null);
                synchronized (cmaps) {
                    cmaps.put(name, planes);
                }
            }
            return decodeSequence(seq, start, length, planes);
        }
        catch (IOException e) {
            throw new ExceptionConverter(e);
        }
    }

    static String decodeSequence(byte seq[]int start, int length, char planes[][]) {
        StringBuffer buf = new StringBuffer();
        int end = start + length;
        int currentPlane = 0;
        for (int k = start; k < end; ++k) {
            int one = seq[k0xff;
            char plane[] = planes[currentPlane];
            int cid = plane[one];
            if ((cid & 0x8000== 0) {
                buf.append((char)cid);
                currentPlane = 0;
            }
            else
                currentPlane = cid & 0x7fff;
        }
        return buf.toString();
    }

    static char[][] readCmap(String name, byte newline[][]) throws IOException {
        ArrayList<char[]> planes = new ArrayList<char[]>();
        planes.add(new char[256]);
        readCmap(name, planes);
        if (newline != null) {
            for (int k = 0; k < newline.length; ++k)
                encodeSequence(newline[k].length, newline[k], BaseFont.CID_NEWLINE, planes);
        }
        char ret[][] new char[planes.size()][];
        return planes.toArray(ret);
    }

    static void readCmap(String name, ArrayList<char[]> planesthrows IOException {
        String fullName = BaseFont.RESOURCE_PATH + "cmaps/" + name;
        InputStream in = BaseFont.getResourceStream(fullName);
        if (in == null)
            throw new IOException(MessageLocalization.getComposedMessage("the.cmap.1.was.not.found", name));
        encodeStream(in, planes);
        in.close();
    }

    static void encodeStream(InputStream in, ArrayList<char[]> planesthrows IOException {
        BufferedReader rd = new BufferedReader(new InputStreamReader(in, "iso-8859-1"));
        String line = null;
        int state = CIDNONE;
        byte seqs[] new byte[7];
        while ((line = rd.readLine()) != null) {
            if (line.length() 6)
                continue;
            switch (state) {
                case CIDNONE: {
                    if (line.indexOf("begincidrange">= 0)
                        state = CIDRANGE;
                    else if (line.indexOf("begincidchar">= 0)
                        state = CIDCHAR;
                    else if (line.indexOf("usecmap">= 0) {
                        StringTokenizer tk = new StringTokenizer(line);
                        String t = tk.nextToken();
                        readCmap(t.substring(1), planes);
                    }
                    break;
                }
                case CIDRANGE: {
                    if (line.indexOf("endcidrange">= 0) {
                        state = CIDNONE;
                        break;
                    }
                    StringTokenizer tk = new StringTokenizer(line);
                    String t = tk.nextToken();
                    int size = t.length() 1;
                    long start = Long.parseLong(t.substring(1, t.length() 1)16);
                    t = tk.nextToken();
                    long end = Long.parseLong(t.substring(1, t.length() 1)16);
                    t = tk.nextToken();
                    int cid = Integer.parseInt(t);
                    for (long k = start; k <= end; ++k) {
                        breakLong(k, size, seqs);
                        encodeSequence(size, seqs, (char)cid, planes);
                        ++cid;
                    }
                    break;
                }
                case CIDCHAR: {
                    if (line.indexOf("endcidchar">= 0) {
                        state = CIDNONE;
                        break;
                    }
                    StringTokenizer tk = new StringTokenizer(line);
                    String t = tk.nextToken();
                    int size = t.length() 1;
                    long start = Long.parseLong(t.substring(1, t.length() 1)16);
                    t = tk.nextToken();
                    int cid = Integer.parseInt(t);
                    breakLong(start, size, seqs);
                    encodeSequence(size, seqs, (char)cid, planes);
                    break;
                }
            }
        }
    }

    static void breakLong(long n, int size, byte seqs[]) {
        for (int k = 0; k < size; ++k) {
            seqs[k(byte)(n >> (size - - k8);
        }
    }

    static void encodeSequence(int size, byte seqs[]char cid, ArrayList<char[]> planes) {
        --size;
        int nextPlane = 0;
        for (int idx = 0; idx < size; ++idx) {
            char plane[] = planes.get(nextPlane);
            int one = seqs[idx0xff;
            char c = plane[one];
            if (c != && (c & 0x8000== 0)
                throw new RuntimeException(MessageLocalization.getComposedMessage("inconsistent.mapping"));
            if (c == 0) {
                planes.add(new char[256]);
                c = (char)(planes.size() 0x8000);
                plane[one= c;
            }
            nextPlane = c & 0x7fff;
        }
        char plane[] = planes.get(nextPlane);
        int one = seqs[size0xff;
        char c = plane[one];
        if ((c & 0x8000!= 0)
            throw new RuntimeException(MessageLocalization.getComposedMessage("inconsistent.mapping"));
        plane[one= cid;
    }

    /** Adds an extra encoding.
     @param name the name of the encoding. The encoding recognition is case insensitive
     @param enc the conversion class
     */
    @SuppressWarnings("unchecked")
    public static void addExtraEncoding(String name, ExtraEncoding enc) {
        synchronized (extraEncodings) { // This serializes concurrent updates
            HashMap<String, ExtraEncoding> newEncodings = (HashMap<String, ExtraEncoding>)extraEncodings.clone();
            newEncodings.put(name.toLowerCase(), enc);
            extraEncodings = newEncodings;  // This swap does not require synchronization with reader
        }
    }

    private static class WingdingsConversion implements ExtraEncoding {

        public byte[] charToByte(char char1, String encoding) {
            if (char1 == ' ')
                return new byte[]{(byte)char1};
            else if (char1 >= '\u2701' && char1 <= '\u27BE') {
                byte v = table[char1 - 0x2700];
                if (v != 0)
                    return new byte[]{v};
            }
            return new byte[0];
        }

        public byte[] charToByte(String text, String encoding) {
            char cc[] = text.toCharArray();
            byte b[] new byte[cc.length];
            int ptr = 0;
            int len = cc.length;
            for (int k = 0; k < len; ++k) {
                char c = cc[k];
                if (c == ' ')
                    b[ptr++(byte)c;
                else if (c >= '\u2701' && c <= '\u27BE') {
                    byte v = table[c - 0x2700];
                    if (v != 0)
                        b[ptr++= v;
                }
            }
            if (ptr == len)
                return b;
            byte b2[] new byte[ptr];
            System.arraycopy(b, 0, b2, 0, ptr);
            return b2;
        }

        public String byteToChar(byte[] b, String encoding) {
            return null;
        }

        private final static byte table[] {
            0353400041628142,
            00656300000, -4,
            000, -5000000,
            8608889000000,
            00, -7500000, -740,
            00, -83, -81, -8400000,
            000124123000840,
            0000000, -9000,
            0113114000117000,
            00012512600000,
            0000000000,
            00000000, -116, -115,
            -114, -113, -112, -111, -110, -109, -108, -107, -127, -126,
            -125, -124, -123, -122, -121, -120, -119, -118, -116, -115,
            -114, -113, -112, -111, -110, -109, -108, -107, -240,
            0000000000,
            0, -24, -4000, -60, -5800, -16,
            000000000, -36,
            0000000000,
            0
        };
    }

    private static class Cp437Conversion implements ExtraEncoding {
        private static IntHashtable c2b = new IntHashtable();

        public byte[] charToByte(String text, String encoding) {
            char cc[] = text.toCharArray();
            byte b[] new byte[cc.length];
            int ptr = 0;
            int len = cc.length;
            for (int k = 0; k < len; ++k) {
                char c = cc[k];
                if (c < 128)
                    b[ptr++(byte)c;
                else {
                    byte v = (byte)c2b.get(c);
                    if (v != 0)
                        b[ptr++= v;
                }
            }
            if (ptr == len)
                return b;
            byte b2[] new byte[ptr];
            System.arraycopy(b, 0, b2, 0, ptr);
            return b2;
        }

        public byte[] charToByte(char char1, String encoding) {
            if (char1 < 128)
                return new byte[]{(byte)char1};
            else {
                byte v = (byte)c2b.get(char1);
                if (v != 0)
                    return new byte[]{v};
                else
                    return new byte[0];
            }
        }

        public String byteToChar(byte[] b, String encoding) {
            int len = b.length;
            char cc[] new char[len];
            int ptr = 0;
            for (int k = 0; k < len; ++k) {
                int c = b[k0xff;
                if (c < ' ')
                    continue;
                if (c < 128)
                    cc[ptr++(char)c;
                else {
                    char v = table[c - 128];
                    cc[ptr++= v;
                }
            }
            return new String(cc, 0, ptr);
        }

        private final static char table[] {
            '\u00C7''\u00FC''\u00E9''\u00E2''\u00E4''\u00E0''\u00E5''\u00E7''\u00EA''\u00EB''\u00E8''\u00EF''\u00EE''\u00EC''\u00C4''\u00C5',
            '\u00C9''\u00E6''\u00C6''\u00F4''\u00F6''\u00F2''\u00FB''\u00F9''\u00FF''\u00D6''\u00DC''\u00A2''\u00A3''\u00A5''\u20A7''\u0192',
            '\u00E1''\u00ED''\u00F3''\u00FA''\u00F1''\u00D1''\u00AA''\u00BA''\u00BF''\u2310''\u00AC''\u00BD''\u00BC''\u00A1''\u00AB''\u00BB',
            '\u2591''\u2592''\u2593''\u2502''\u2524''\u2561''\u2562''\u2556''\u2555''\u2563''\u2551''\u2557''\u255D''\u255C''\u255B''\u2510',
            '\u2514''\u2534''\u252C''\u251C''\u2500''\u253C''\u255E''\u255F''\u255A''\u2554''\u2569''\u2566''\u2560''\u2550''\u256C''\u2567',
            '\u2568''\u2564''\u2565''\u2559''\u2558''\u2552''\u2553''\u256B''\u256A''\u2518''\u250C''\u2588''\u2584''\u258C''\u2590''\u2580',
            '\u03B1''\u00DF''\u0393''\u03C0''\u03A3''\u03C3''\u00B5''\u03C4''\u03A6''\u0398''\u03A9''\u03B4''\u221E''\u03C6''\u03B5''\u2229',
            '\u2261''\u00B1''\u2265''\u2264''\u2320''\u2321''\u00F7''\u2248''\u00B0''\u2219''\u00B7''\u221A''\u207F''\u00B2''\u25A0''\u00A0'
        };

        static {
            for (int k = 0; k < table.length; ++k)
                c2b.put(table[k], k + 128);
        }
    }

    private static class SymbolConversion implements ExtraEncoding {

        private static final IntHashtable t1 = new IntHashtable();
        private static final IntHashtable t2 = new IntHashtable();
        private IntHashtable translation;

        SymbolConversion(boolean symbol) {
            if (symbol)
                translation = t1;
            else
                translation = t2;
        }

        public byte[] charToByte(String text, String encoding) {
            char cc[] = text.toCharArray();
            byte b[] new byte[cc.length];
            int ptr = 0;
            int len = cc.length;
            for (int k = 0; k < len; ++k) {
                char c = cc[k];
                byte v = (byte)translation.get(c);
                if (v != 0)
                    b[ptr++= v;
            }
            if (ptr == len)
                return b;
            byte b2[] new byte[ptr];
            System.arraycopy(b, 0, b2, 0, ptr);
            return b2;
        }

        public byte[] charToByte(char char1, String encoding) {
            byte v = (byte)translation.get(char1);
            if (v != 0)
                return new byte[]{v};
            else
                return new byte[0];
        }

        public String byteToChar(byte[] b, String encoding) {
            return null;
        }

        private final static char table1[] {
            ' ','!','\u2200','#','\u2203','%','&','\u220b','(',')','*','+',',','-','.','/',
            '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
            '\u2245','\u0391','\u0392','\u03a7','\u0394','\u0395','\u03a6','\u0393','\u0397','\u0399','\u03d1','\u039a','\u039b','\u039c','\u039d','\u039f',
            '\u03a0','\u0398','\u03a1','\u03a3','\u03a4','\u03a5','\u03c2','\u03a9','\u039e','\u03a8','\u0396','[','\u2234',']','\u22a5','_',
            '\u0305','\u03b1','\u03b2','\u03c7','\u03b4','\u03b5','\u03d5','\u03b3','\u03b7','\u03b9','\u03c6','\u03ba','\u03bb','\u03bc','\u03bd','\u03bf',
            '\u03c0','\u03b8','\u03c1','\u03c3','\u03c4','\u03c5','\u03d6','\u03c9','\u03be','\u03c8','\u03b6','{','|','}','~','\0',
            '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
            '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
            '\u20ac','\u03d2','\u2032','\u2264','\u2044','\u221e','\u0192','\u2663','\u2666','\u2665','\u2660','\u2194','\u2190','\u2191','\u2192','\u2193',
            '\u00b0','\u00b1','\u2033','\u2265','\u00d7','\u221d','\u2202','\u2022','\u00f7','\u2260','\u2261','\u2248','\u2026','\u2502','\u2500','\u21b5',
            '\u2135','\u2111','\u211c','\u2118','\u2297','\u2295','\u2205','\u2229','\u222a','\u2283','\u2287','\u2284','\u2282','\u2286','\u2208','\u2209',
            '\u2220','\u2207','\u00ae','\u00a9','\u2122','\u220f','\u221a','\u2022','\u00ac','\u2227','\u2228','\u21d4','\u21d0','\u21d1','\u21d2','\u21d3',
            '\u25ca','\u2329','\0','\0','\0','\u2211','\u239b','\u239c','\u239d','\u23a1','\u23a2','\u23a3','\u23a7','\u23a8','\u23a9','\u23aa',
            '\0','\u232a','\u222b','\u2320','\u23ae','\u2321','\u239e','\u239f','\u23a0','\u23a4','\u23a5','\u23a6','\u23ab','\u23ac','\u23ad','\0'
        };

        private final static char table2[] {
            '\u0020','\u2701','\u2702','\u2703','\u2704','\u260e','\u2706','\u2707','\u2708','\u2709','\u261b','\u261e','\u270C','\u270D','\u270E','\u270F',
            '\u2710','\u2711','\u2712','\u2713','\u2714','\u2715','\u2716','\u2717','\u2718','\u2719','\u271A','\u271B','\u271C','\u271D','\u271E','\u271F',
            '\u2720','\u2721','\u2722','\u2723','\u2724','\u2725','\u2726','\u2727','\u2605','\u2729','\u272A','\u272B','\u272C','\u272D','\u272E','\u272F',
            '\u2730','\u2731','\u2732','\u2733','\u2734','\u2735','\u2736','\u2737','\u2738','\u2739','\u273A','\u273B','\u273C','\u273D','\u273E','\u273F',
            '\u2740','\u2741','\u2742','\u2743','\u2744','\u2745','\u2746','\u2747','\u2748','\u2749','\u274A','\u274B','\u25cf','\u274D','\u25a0','\u274F',
            '\u2750','\u2751','\u2752','\u25b2','\u25bc','\u25c6','\u2756','\u25d7','\u2758','\u2759','\u275A','\u275B','\u275C','\u275D','\u275E','\u0000',
            '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
            '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
            '\u0000','\u2761','\u2762','\u2763','\u2764','\u2765','\u2766','\u2767','\u2663','\u2666','\u2665','\u2660','\u2460','\u2461','\u2462','\u2463',
            '\u2464','\u2465','\u2466','\u2467','\u2468','\u2469','\u2776','\u2777','\u2778','\u2779','\u277A','\u277B','\u277C','\u277D','\u277E','\u277F',
            '\u2780','\u2781','\u2782','\u2783','\u2784','\u2785','\u2786','\u2787','\u2788','\u2789','\u278A','\u278B','\u278C','\u278D','\u278E','\u278F',
            '\u2790','\u2791','\u2792','\u2793','\u2794','\u2192','\u2194','\u2195','\u2798','\u2799','\u279A','\u279B','\u279C','\u279D','\u279E','\u279F',
            '\u27A0','\u27A1','\u27A2','\u27A3','\u27A4','\u27A5','\u27A6','\u27A7','\u27A8','\u27A9','\u27AA','\u27AB','\u27AC','\u27AD','\u27AE','\u27AF',
            '\u0000','\u27B1','\u27B2','\u27B3','\u27B4','\u27B5','\u27B6','\u27B7','\u27B8','\u27B9','\u27BA','\u27BB','\u27BC','\u27BD','\u27BE','\u0000'
        };

        static {
            for (int k = 0; k < table1.length; ++k) {
                int v = table1[k];
                if (v != 0)
                    t1.put(v, k + 32);
            }
            for (int k = 0; k < table2.length; ++k) {
                int v = table2[k];
                if (v != 0)
                    t2.put(v, k + 32);
            }
        }
    }

    private static class SymbolTTConversion implements ExtraEncoding {

        public byte[] charToByte(char char1, String encoding) {
            if ((char1 & 0xff00== || (char1 & 0xff00== 0xf000)
                return new byte[]{(byte)char1};
            else
                return new byte[0];
        }

        public byte[] charToByte(String text, String encoding) {
            char ch[] = text.toCharArray();
            byte b[] new byte[ch.length];
            int ptr = 0;
            int len = ch.length;
            for (int k = 0; k < len; ++k) {
                char c = ch[k];
                if ((c & 0xff00== || (c & 0xff00== 0xf000)
                    b[ptr++(byte)c;
            }
            if (ptr == len)
                return b;
            byte b2[] new byte[ptr];
            System.arraycopy(b, 0, b2, 0, ptr);
            return b2;
        }

        public String byteToChar(byte[] b, String encoding) {
            return null;
        }

    }
}