1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /* $Id: AbstractCodePointMapping.java 1616312 2014-08-06 19:19:31Z gadams $ */
19 
20 package org.apache.fop.fonts;
21 
22 import java.util.Arrays;
23 
24 import org.apache.xmlgraphics.fonts.Glyphs;
25 
26 import org.apache.fop.util.CharUtilities;
27 
28 /**
29  * Abstract base class for code point mapping classes (1-byte character encodings).
30  */
31 public class AbstractCodePointMapping implements SingleByteEncoding {
32 
33     private final String name;
34     private char[] latin1Map;
35     private char[] characters;
36     private char[] codepoints;
37     private char[] unicodeMap; //code point to Unicode char
38     private String[] charNameMap; //all character names in the encoding
39 
40     /**
41      * Main constructor.
42      * @param name the name of the encoding
43      * @param table the table ([code point, unicode scalar value]+) with the mapping
44      */
AbstractCodePointMapping(String name, int[] table)45     public AbstractCodePointMapping(String name, int[] table) {
46         this(name, table, null);
47     }
48 
49     /**
50      * Extended constructor.
51      * @param name the name of the encoding
52      * @param table the table ([code point, unicode scalar value]+) with the mapping
53      * @param charNameMap all character names in the encoding (a value of null will be converted
54      *                  to ".notdef")
55      */
AbstractCodePointMapping(String name, int[] table, String[] charNameMap)56     public AbstractCodePointMapping(String name, int[] table, String[] charNameMap) {
57         this.name = name;
58         buildFromTable(table);
59         if (charNameMap != null) {
60             this.charNameMap = new String[256];
61             for (int i = 0; i < 256; i++) {
62                 String charName = charNameMap[i];
63                 if (charName == null) {
64                     this.charNameMap[i] = Glyphs.NOTDEF;
65                 } else {
66                     this.charNameMap[i] = charName;
67                 }
68             }
69         }
70     }
71 
72     /**
73      * Builds the internal lookup structures based on a given table.
74      * @param table the table ([code point, unicode scalar value]+) with the mapping
75      */
buildFromTable(int[] table)76     protected void buildFromTable(int[] table) {
77         int nonLatin1 = 0;
78         latin1Map = new char[256];
79         unicodeMap = new char[256];
80         Arrays.fill(unicodeMap, CharUtilities.NOT_A_CHARACTER);
81         for (int i = 0; i < table.length; i += 2) {
82             char unicode = (char)table[i + 1];
83             if (unicode < 256) {
84                 if (latin1Map[unicode] == 0) {
85                     latin1Map[unicode] = (char) table[i];
86                 }
87             } else {
88                 ++nonLatin1;
89             }
90             if (unicodeMap[table[i]] == CharUtilities.NOT_A_CHARACTER) {
91                 unicodeMap[table[i]] = unicode;
92             }
93         }
94         characters = new char[nonLatin1];
95         codepoints = new char[nonLatin1];
96         int top = 0;
97         for (int i = 0; i < table.length; i += 2) {
98             char c = (char) table[i + 1];
99             if (c >= 256) {
100                ++top;
101                for (int j = top - 1; j >= 0; --j) {
102                    if (j > 0 && characters[j - 1] >= c) {
103                        characters[j] = characters[j - 1];
104                        codepoints[j] = codepoints[j - 1];
105                    } else {
106                        characters[j] = c;
107                        codepoints[j] = (char) table[i];
108                        break;
109                    }
110                }
111             }
112         }
113     }
114 
115     /** {@inheritDoc} */
getName()116     public String getName() {
117         return this.name;
118     }
119 
120     /** {@inheritDoc} */
mapChar(char c)121     public final char mapChar(char c) {
122         if (c < 256) {
123             char latin1 = latin1Map[c];
124             if (latin1 > 0) {
125                 return latin1;
126             }
127         }
128         int bot = 0;
129         int top = characters.length - 1;
130         while (top >= bot) {
131             assert bot < 65536;
132             assert top < 65536;
133             int mid = (bot + top) >>> 1;
134             char mc = characters[mid];
135 
136             if (c == mc) {
137                 return codepoints[mid];
138             } else if (c < mc) {
139                 top = mid - 1;
140             } else {
141                 bot = mid + 1;
142             }
143         }
144         return NOT_FOUND_CODE_POINT;
145     }
146 
147     /**
148      * Returns the main Unicode value that is associated with the given code point in the encoding.
149      * Note that multiple Unicode values can theoretically be mapped to one code point in the
150      * encoding.
151      * @param idx the code point in the encoding
152      * @return the Unicode value (or \uFFFF (NOT A CHARACTER) if no Unicode value is at that point)
153      */
getUnicodeForIndex(int idx)154     public final char getUnicodeForIndex(int idx) {
155         return this.unicodeMap[idx];
156     }
157 
158     /** {@inheritDoc} */
getUnicodeCharMap()159     public final char[] getUnicodeCharMap() {
160         char[] copy = new char[this.unicodeMap.length];
161         System.arraycopy(this.unicodeMap, 0, copy, 0, this.unicodeMap.length);
162         return copy;
163     }
164 
165     /**
166      * Returns the index of a character/glyph with the given name. Note that this
167      * method is relatively slow and should only be used for fallback operations.
168      * @param charName the character name
169      * @return the index of the character in the encoding or -1 if it doesn't exist
170      */
getCodePointForGlyph(String charName)171     public short getCodePointForGlyph(String charName) {
172         String[] names = this.charNameMap;
173         if (names == null) {
174             names = getCharNameMap();
175         }
176         for (short i = 0, c = (short)names.length; i < c; i++) {
177             if (names[i].equals(charName)) {
178                 return i;
179             }
180         }
181         return -1;
182     }
183 
getNameFromCodePoint(int idx)184     public String getNameFromCodePoint(int idx) {
185         return getCharNameMap()[idx];
186     }
187 
188     /** {@inheritDoc} */
getCharNameMap()189     public String[] getCharNameMap() {
190         if (this.charNameMap != null) {
191             String[] copy = new String[this.charNameMap.length];
192             System.arraycopy(this.charNameMap, 0, copy, 0, this.charNameMap.length);
193             return copy;
194         } else {
195             //Note: this is suboptimal but will probably never be used.
196             String[] derived = new String[256];
197             Arrays.fill(derived, Glyphs.NOTDEF);
198             for (int i = 0; i < 256; i++) {
199                 char c = getUnicodeForIndex(i);
200                 if (c != CharUtilities.NOT_A_CHARACTER) {
201                     String charName = Glyphs.charToGlyphName(c);
202                     if (charName.length() > 0) {
203                         derived[i] = charName;
204                     }
205                 }
206             }
207             return derived;
208         }
209     }
210 
211     /** {@inheritDoc} */
212     @Override
toString()213     public String toString() {
214         return getName();
215     }
216 }
217