blob: ca0c52d5e44bb03b424905afe3d1c2a700e5c24d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.fonts;
import java.util.Arrays;
import org.apache.xmlgraphics.fonts.Glyphs;
import org.apache.fop.util.CharUtilities;
/**
* Abstract base class for code point mapping classes (1-byte character encodings).
*/
public class AbstractCodePointMapping implements SingleByteEncoding {
private final String name;
private char[] latin1Map;
private char[] characters;
private char[] codepoints;
private char[] unicodeMap; //code point to Unicode char
private String[] charNameMap; //all character names in the encoding
/**
* Main constructor.
* @param name the name of the encoding
* @param table the table ([code point, unicode scalar value]+) with the mapping
*/
public AbstractCodePointMapping(String name, int[] table) {
this(name, table, null);
}
/**
* Extended constructor.
* @param name the name of the encoding
* @param table the table ([code point, unicode scalar value]+) with the mapping
* @param charNameMap all character names in the encoding (a value of null will be converted
* to ".notdef")
*/
public AbstractCodePointMapping(String name, int[] table, String[] charNameMap) {
this.name = name;
buildFromTable(table);
if (charNameMap != null) {
this.charNameMap = new String[256];
for (int i = 0; i < 256; i++) {
String charName = charNameMap[i];
if (charName == null) {
this.charNameMap[i] = Glyphs.NOTDEF;
} else {
this.charNameMap[i] = charName;
}
}
}
}
/**
* Builds the internal lookup structures based on a given table.
* @param table the table ([code point, unicode scalar value]+) with the mapping
*/
protected void buildFromTable(int[] table) {
int nonLatin1 = 0;
latin1Map = new char[256];
unicodeMap = new char[256];
Arrays.fill(unicodeMap, CharUtilities.NOT_A_CHARACTER);
for (int i = 0; i < table.length; i += 2) {
char unicode = (char)table[i + 1];
if (unicode < 256) {
if (latin1Map[unicode] == 0) {
latin1Map[unicode] = (char) table[i];
}
} else {
++nonLatin1;
}
if (unicodeMap[table[i]] == CharUtilities.NOT_A_CHARACTER) {
unicodeMap[table[i]] = unicode;
}
}
characters = new char[nonLatin1];
codepoints = new char[nonLatin1];
int top = 0;
for (int i = 0; i < table.length; i += 2) {
char c = (char) table[i + 1];
if (c >= 256) {
++top;
for (int j = top - 1; j >= 0; --j) {
if (j > 0 && characters[j - 1] >= c) {
characters[j] = characters[j - 1];
codepoints[j] = codepoints[j - 1];
} else {
characters[j] = c;
codepoints[j] = (char) table[i];
break;
}
}
}
}
}
/** {@inheritDoc} */
public String getName() {
return this.name;
}
/** {@inheritDoc} */
public final char mapChar(char c) {
if (c < 256) {
char latin1 = latin1Map[c];
if (latin1 > 0) {
return latin1;
}
}
int bot = 0;
int top = characters.length - 1;
while (top >= bot) {
assert bot < 65536;
assert top < 65536;
int mid = (bot + top) >>> 1;
char mc = characters[mid];
if (c == mc) {
return codepoints[mid];
} else if (c < mc) {
top = mid - 1;
} else {
bot = mid + 1;
}
}
return NOT_FOUND_CODE_POINT;
}
/**
* Returns the main Unicode value that is associated with the given code point in the encoding.
* Note that multiple Unicode values can theoretically be mapped to one code point in the
* encoding.
* @param idx the code point in the encoding
* @return the Unicode value (or \uFFFF (NOT A CHARACTER) if no Unicode value is at that point)
*/
public final char getUnicodeForIndex(int idx) {
return this.unicodeMap[idx];
}
/** {@inheritDoc} */
public final char[] getUnicodeCharMap() {
char[] copy = new char[this.unicodeMap.length];
System.arraycopy(this.unicodeMap, 0, copy, 0, this.unicodeMap.length);
return copy;
}
/**
* Returns the index of a character/glyph with the given name. Note that this
* method is relatively slow and should only be used for fallback operations.
* @param charName the character name
* @return the index of the character in the encoding or -1 if it doesn't exist
*/
public short getCodePointForGlyph(String charName) {
String[] names = this.charNameMap;
if (names == null) {
names = getCharNameMap();
}
for (short i = 0, c = (short)names.length; i < c; i++) {
if (names[i].equals(charName)) {
return i;
}
}
return -1;
}
public String getNameFromCodePoint(int idx) {
return getCharNameMap()[idx];
}
/** {@inheritDoc} */
public String[] getCharNameMap() {
if (this.charNameMap != null) {
String[] copy = new String[this.charNameMap.length];
System.arraycopy(this.charNameMap, 0, copy, 0, this.charNameMap.length);
return copy;
} else {
//Note: this is suboptimal but will probably never be used.
String[] derived = new String[256];
Arrays.fill(derived, Glyphs.NOTDEF);
for (int i = 0; i < 256; i++) {
char c = getUnicodeForIndex(i);
if (c != CharUtilities.NOT_A_CHARACTER) {
String charName = Glyphs.charToGlyphName(c);
if (charName.length() > 0) {
derived[i] = charName;
}
}
}
return derived;
}
}
/** {@inheritDoc} */
@Override
public String toString() {
return getName();
}
}