blob: e5af471af1903a0bb732050667dbcdf2eaf97090 [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.complexscripts.fonts;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
// CSOFF: LineLengthCheck
/**
* <p>The <code>GlyphSubtable</code> implements an abstract glyph subtable that
* encapsulates identification, type, format, and coverage information.</p>
*
* <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
*/
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 <code>null</code>, 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/*<String,GlyphTable.LookupTable>*/ 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);
}
}
}