org.apache.fop.complexscripts.fonts.GlyphMappingTable Maven / Gradle / Ivy
/*
* 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.complexscripts.fonts;
import java.util.Arrays;
import java.util.List;
// CSOFF: LineLengthCheck
/**
* Base class implementation of glyph mapping table. This base
* class maps glyph indices to arbitrary integers (mappping indices), and
* is used to implement both glyph coverage and glyph class maps.
*
* This work was originally authored by Glenn Adams ([email protected]).
*/
public class GlyphMappingTable {
/** empty mapping table */
public static final int GLYPH_MAPPING_TYPE_EMPTY = 0;
/** mapped mapping table */
public static final int GLYPH_MAPPING_TYPE_MAPPED = 1;
/** range based mapping table */
public static final int GLYPH_MAPPING_TYPE_RANGE = 2;
/**
* Obtain mapping type.
* @return mapping format type
*/
public int getType() {
return -1;
}
/**
* Obtain mapping entries.
* @return list of mapping entries
*/
public List getEntries() {
return null;
}
/**
* Obtain size of mapping table, i.e., ciMax + 1, where ciMax is the maximum
* mapping index.
* @return size of mapping table
*/
public int getMappingSize() {
return 0;
}
/**
* Map glyph identifier (code) to coverge index. Returns -1 if glyph identifier is not in the domain of
* the mapping table.
* @param gid glyph identifier (code)
* @return non-negative glyph mapping index or -1 if glyph identifiers is not mapped by table
*/
public int getMappedIndex(int gid) {
return -1;
}
/** empty mapping table base class */
protected static class EmptyMappingTable extends GlyphMappingTable {
/**
* Construct empty mapping table.
*/
public EmptyMappingTable() {
this ((List) null);
}
/**
* Construct empty mapping table with entries (ignored).
* @param entries list of entries (ignored)
*/
public EmptyMappingTable(List entries) {
}
/** {@inheritDoc} */
public int getType() {
return GLYPH_MAPPING_TYPE_EMPTY;
}
/** {@inheritDoc} */
public List getEntries() {
return new java.util.ArrayList();
}
/** {@inheritDoc} */
public int getMappingSize() {
return 0;
}
/** {@inheritDoc} */
public int getMappedIndex(int gid) {
return -1;
}
}
/** mapped mapping table base class */
protected static class MappedMappingTable extends GlyphMappingTable {
/**
* Construct mapped mapping table.
*/
public MappedMappingTable() {
}
/** {@inheritDoc} */
public int getType() {
return GLYPH_MAPPING_TYPE_MAPPED;
}
}
/** range mapping table base class */
protected abstract static class RangeMappingTable extends GlyphMappingTable {
private int[] sa; // array of range (inclusive) starts
private int[] ea; // array of range (inclusive) ends
private int[] ma; // array of range mapped values
private int miMax = -1;
/**
* Construct range mapping table.
* @param entries of mapping ranges
*/
public RangeMappingTable(List entries) {
populate(entries);
}
/** {@inheritDoc} */
public int getType() {
return GLYPH_MAPPING_TYPE_RANGE;
}
/** {@inheritDoc} */
public List getEntries() {
List entries = new java.util.ArrayList();
if (sa != null) {
for (int i = 0, n = sa.length; i < n; i++) {
entries.add(new MappingRange(sa [ i ], ea [ i ], ma [ i ]));
}
}
return entries;
}
/** {@inheritDoc} */
public int getMappingSize() {
return miMax + 1;
}
/** {@inheritDoc} */
public int getMappedIndex(int gid) {
int i;
int mi;
if ((i = Arrays.binarySearch(sa, gid)) >= 0) {
mi = getMappedIndex(gid, sa [ i ], ma [ i ]); // matches start of (some) range
} else if ((i = -(i + 1)) == 0) {
mi = -1; // precedes first range
} else if (gid > ea [ --i ]) {
mi = -1; // follows preceding (or last) range
} else {
mi = getMappedIndex(gid, sa [ i ], ma [ i ]); // intersects (some) range
}
return mi;
}
/**
* Map glyph identifier (code) to coverge index. Returns -1 if glyph identifier is not in the domain of
* the mapping table.
* @param gid glyph identifier (code)
* @param s start of range
* @param m mapping value
* @return non-negative glyph mapping index or -1 if glyph identifiers is not mapped by table
*/
public abstract int getMappedIndex(int gid, int s, int m);
private void populate(List entries) {
int i = 0;
int n = entries.size();
int gidMax = -1;
int miMax = -1;
int[] sa = new int [ n ];
int[] ea = new int [ n ];
int[] ma = new int [ n ];
for (Object o : entries) {
if (o instanceof MappingRange) {
MappingRange r = (MappingRange) o;
int gs = r.getStart();
int ge = r.getEnd();
int mi = r.getIndex();
if ((gs < 0) || (gs > 65535)) {
throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: bad start index");
} else if ((ge < 0) || (ge > 65535)) {
throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: bad end index");
} else if (gs > ge) {
throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: start index exceeds end index");
} else if (gs < gidMax) {
throw new AdvancedTypographicTableFormatException("out of order glyph range: [" + gs + "," + ge + "]");
} else if (mi < 0) {
throw new AdvancedTypographicTableFormatException("illegal mapping index: " + mi);
} else {
int miLast;
sa[i] = gs;
ea[i] = gidMax = ge;
ma[i] = mi;
if ((miLast = mi + (ge - gs)) > miMax) {
miMax = miLast;
}
i++;
}
} else {
throw new AdvancedTypographicTableFormatException("illegal mapping entry, must be Integer: " + o);
}
}
assert i == n;
assert this.sa == null;
assert this.ea == null;
assert this.ma == null;
this.sa = sa;
this.ea = ea;
this.ma = ma;
this.miMax = miMax;
}
/** {@inheritDoc} */
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append('{');
for (int i = 0, n = sa.length; i < n; i++) {
if (i > 0) {
sb.append(',');
}
sb.append('[');
sb.append(Integer.toString(sa [ i ]));
sb.append(Integer.toString(ea [ i ]));
sb.append("]:");
sb.append(Integer.toString(ma [ i ]));
}
sb.append('}');
return sb.toString();
}
}
/**
* The MappingRange
class encapsulates a glyph [start,end] range and
* a mapping index.
*/
public static class MappingRange {
private final int gidStart; // first glyph in range (inclusive)
private final int gidEnd; // last glyph in range (inclusive)
private final int index; // mapping index;
/**
* Instantiate a mapping range.
*/
public MappingRange() {
this (0, 0, 0);
}
/**
* Instantiate a specific mapping range.
* @param gidStart start of range
* @param gidEnd end of range
* @param index mapping index
*/
public MappingRange(int gidStart, int gidEnd, int index) {
if ((gidStart < 0) || (gidEnd < 0) || (index < 0)) {
throw new AdvancedTypographicTableFormatException();
} else if (gidStart > gidEnd) {
throw new AdvancedTypographicTableFormatException();
} else {
this.gidStart = gidStart;
this.gidEnd = gidEnd;
this.index = index;
}
}
/** @return start of range */
public int getStart() {
return gidStart;
}
/** @return end of range */
public int getEnd() {
return gidEnd;
}
/** @return mapping index */
public int getIndex() {
return index;
}
/** @return interval as a pair of integers */
public int[] getInterval() {
return new int[] { gidStart, gidEnd };
}
/**
* Obtain interval, filled into first two elements of specified array, or returning new array.
* @param interval an array of length two or greater or null
* @return interval as a pair of integers, filled into specified array
*/
public int[] getInterval(int[] interval) {
if ((interval == null) || (interval.length != 2)) {
throw new IllegalArgumentException();
} else {
interval[0] = gidStart;
interval[1] = gidEnd;
}
return interval;
}
/** @return length of interval */
public int getLength() {
return gidStart - gidEnd;
}
}
}