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