/* * 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 (GlyphSubtable subtable : subtables) { if (subtable.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 (GlyphSubtable subtable1 : subtables) { int f = subtable1.getFlags(); if (flags == 0) { flags = f; break; } } // enforce flag consistency for (GlyphSubtable subtable : subtables) { int f = subtable.getFlags(); if (f != flags) { throw new IllegalStateException("inconsistent lookup flags " + f + ", expected " + flags); } } return flags | (usesReverseScan(subtables) ? LF_INTERNAL_USE_REVERSE_SCAN : 0); } } }