blob: 9ba8de16c07d123d1106a24389a098284a603371 [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 java.util.List;
import java.util.Iterator;
// CSOFF: NoWhitespaceAfterCheck
// CSOFF: InnerAssignmentCheck
// 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.
* @author Glenn Adams
*/
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 = null; // array of range (inclusive) starts
private int[] ea = null; // array of range (inclusive) ends
private int[] ma = null; // 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, 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, n = entries.size(), gidMax = -1, miMax = -1;
int[] sa = new int [ n ];
int[] ea = new int [ n ];
int[] ma = new int [ n ];
for ( Iterator it = entries.iterator(); it.hasNext();) {
Object o = it.next();
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 <code>MappingRange</code> 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;
}
}
}