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