/*
* 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.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
/**
* The GlyphSubtable
implements an abstract glyph subtable that
* encapsulates identification, type, format, and coverage information.
* @author Glenn Adams
*/
public abstract class GlyphSubtable implements Comparable {
/** lookup flag - right to left */
public static final int LF_RIGHT_TO_LEFT = 0x0001;
/** lookup flag - ignore base glyphs */
public static final int LF_IGNORE_BASE = 0x0002;
/** lookup flag - ignore ligatures */
public static final int LF_IGNORE_LIGATURE = 0x0004;
/** lookup flag - ignore marks */
public static final int LF_IGNORE_MARK = 0x0008;
/** lookup flag - use mark filtering set */
public static final int LF_USE_MARK_FILTERING_SET = 0x0010;
/** lookup flag - reserved */
public static final int LF_RESERVED = 0x0E00;
/** lookup flag - mark attachment type */
public static final int LF_MARK_ATTACHMENT_TYPE = 0xFF00;
/** internal flag - use reverse scan */
public static final int LF_INTERNAL_USE_REVERSE_SCAN = 0x10000;
/** lookup identifier, having form of "lu%d" where %d is index of lookup in lookup list; shared by multiple subtables in a single lookup */
private String lookupId;
/** subtable sequence (index) number in lookup, zero based */
private int sequence;
/** subtable flags */
private int flags;
/** subtable format */
private int format;
/** subtable mapping table */
private GlyphMappingTable mapping;
/** weak reference to parent (gsub or gpos) table */
private WeakReference table;
/**
* Instantiate this glyph subtable.
* @param lookupId lookup identifier, having form of "lu%d" where %d is index of lookup in lookup list
* @param sequence subtable sequence (within lookup), starting with zero
* @param flags subtable flags
* @param format subtable format
* @param mapping subtable mapping table
*/
protected GlyphSubtable ( String lookupId, int sequence, int flags, int format, GlyphMappingTable mapping )
{
if ( ( lookupId == null ) || ( lookupId.length() == 0 ) ) {
throw new AdvancedTypographicTableFormatException ( "invalid lookup identifier, must be non-empty string" );
} else if ( mapping == null ) {
throw new AdvancedTypographicTableFormatException ( "invalid mapping table, must not be null" );
} else {
this.lookupId = lookupId;
this.sequence = sequence;
this.flags = flags;
this.format = format;
this.mapping = mapping;
}
}
/** @return this subtable's lookup identifer */
public String getLookupId() {
return lookupId;
}
/** @return this subtable's table type */
public abstract int getTableType();
/** @return this subtable's type */
public abstract int getType();
/** @return this subtable's type name */
public abstract String getTypeName();
/**
* Determine if a glyph subtable is compatible with this glyph subtable. Two glyph subtables are
* compatible if the both may appear in a single lookup table.
* @param subtable a glyph subtable to determine compatibility
* @return true if specified subtable is compatible with this glyph subtable, where by compatible
* is meant that they share the same lookup type
*/
public abstract boolean isCompatible ( GlyphSubtable subtable );
/** @return true if subtable uses reverse scanning of glyph sequence, meaning from the last glyph
* in a glyph sequence to the first glyph
*/
public abstract boolean usesReverseScan();
/** @return this subtable's sequence (index) within lookup */
public int getSequence() {
return sequence;
}
/** @return this subtable's flags */
public int getFlags() {
return flags;
}
/** @return this subtable's format */
public int getFormat() {
return format;
}
/** @return this subtable's governing glyph definition table or null if none available */
public GlyphDefinitionTable getGDEF() {
GlyphTable gt = getTable();
if ( gt != null ) {
return gt.getGlyphDefinitions();
} else {
return null;
}
}
/** @return this subtable's coverage mapping or null if mapping is not a coverage mapping */
public GlyphCoverageMapping getCoverage() {
if ( mapping instanceof GlyphCoverageMapping ) {
return (GlyphCoverageMapping) mapping;
} else {
return null;
}
}
/** @return this subtable's class mapping or null if mapping is not a class mapping */
public GlyphClassMapping getClasses() {
if ( mapping instanceof GlyphClassMapping ) {
return (GlyphClassMapping) mapping;
} else {
return null;
}
}
/** @return this subtable's lookup entries */
public abstract List getEntries();
/** @return this subtable's parent table (or null if undefined) */
public synchronized GlyphTable getTable() {
WeakReference r = this.table;
return ( r != null ) ? (GlyphTable) r.get() : null;
}
/**
* Establish a weak reference from this subtable to its parent
* table. If table parameter is specified as null
, then
* clear and remove weak reference.
* @param table the table or null
* @throws IllegalStateException if table is already set to non-null
*/
public synchronized void setTable ( GlyphTable table ) throws IllegalStateException {
WeakReference r = this.table;
if ( table == null ) {
this.table = null;
if ( r != null ) {
r.clear();
}
} else if ( r == null ) {
this.table = new WeakReference ( table );
} else {
throw new IllegalStateException ( "table already set" );
}
}
/**
* Resolve references to lookup tables, e.g., in RuleLookup, to the lookup tables themselves.
* @param lookupTables map from lookup table identifers, e.g. "lu4", to lookup tables
*/
public void resolveLookupReferences ( Map/**/ lookupTables ) {
}
/**
* Map glyph id to coverage index.
* @param gid glyph id
* @return the corresponding coverage index of the specified glyph id
*/
public int getCoverageIndex ( int gid ) {
if ( mapping instanceof GlyphCoverageMapping ) {
return ( (GlyphCoverageMapping) mapping ) .getCoverageIndex ( gid );
} else {
return -1;
}
}
/**
* Map glyph id to coverage index.
* @return the corresponding coverage index of the specified glyph id
*/
public int getCoverageSize() {
if ( mapping instanceof GlyphCoverageMapping ) {
return ( (GlyphCoverageMapping) mapping ) .getCoverageSize();
} else {
return 0;
}
}
/** {@inheritDoc} */
public int hashCode() {
int hc = sequence;
hc = ( hc * 3 ) + ( lookupId.hashCode() ^ hc );
return hc;
}
/**
* {@inheritDoc}
* @return true if the lookup identifier and the sequence number of the specified subtable is the same
* as the lookup identifier and sequence number of this subtable
*/
public boolean equals ( Object o ) {
if ( o instanceof GlyphSubtable ) {
GlyphSubtable st = (GlyphSubtable) o;
return lookupId.equals ( st.lookupId ) && ( sequence == st.sequence );
} else {
return false;
}
}
/**
* {@inheritDoc}
* @return the result of comparing the lookup identifier and the sequence number of the specified subtable with
* the lookup identifier and sequence number of this subtable
*/
public int compareTo ( Object o ) {
int d;
if ( o instanceof GlyphSubtable ) {
GlyphSubtable st = (GlyphSubtable) o;
if ( ( d = lookupId.compareTo ( st.lookupId ) ) == 0 ) {
if ( sequence < st.sequence ) {
d = -1;
} else if ( sequence > st.sequence ) {
d = 1;
}
}
} else {
d = -1;
}
return d;
}
/**
* Determine if any of the specified subtables uses reverse scanning.
* @param subtables array of glyph subtables
* @return true if any of the specified subtables uses reverse scanning.
*/
public static boolean usesReverseScan ( GlyphSubtable[] subtables ) {
if ( ( subtables == null ) || ( subtables.length == 0 ) ) {
return false;
} else {
for ( int i = 0, n = subtables.length; i < n; i++ ) {
if ( subtables[i].usesReverseScan() ) {
return true;
}
}
return false;
}
}
/**
* Determine consistent flags for a set of subtables.
* @param subtables array of glyph subtables
* @return consistent flags
* @throws IllegalStateException if inconsistent flags
*/
public static int getFlags ( GlyphSubtable[] subtables ) throws IllegalStateException {
if ( ( subtables == null ) || ( subtables.length == 0 ) ) {
return 0;
} else {
int flags = 0;
// obtain first non-zero value of flags in array of subtables
for ( int i = 0, n = subtables.length; i < n; i++ ) {
int f = subtables[i].getFlags();
if ( flags == 0 ) {
flags = f;
break;
}
}
// enforce flag consistency
for ( int i = 0, n = subtables.length; i < n; i++ ) {
int f = subtables[i].getFlags();
if ( f != flags ) {
throw new IllegalStateException ( "inconsistent lookup flags " + f + ", expected " + flags );
}
}
return flags | ( usesReverseScan ( subtables ) ? LF_INTERNAL_USE_REVERSE_SCAN : 0 );
}
}
}