| /* |
| * 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; |
| } |
| |
| } |
| |
| } |