1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /* $Id$ */
19 
20 package org.apache.fop.complexscripts.fonts;
21 
22 import java.lang.ref.WeakReference;
23 import java.util.List;
24 import java.util.Map;
25 
26 // CSOFF: LineLengthCheck
27 
28 /**
29  * <p>The <code>GlyphSubtable</code> implements an abstract glyph subtable that
30  * encapsulates identification, type, format, and coverage information.</p>
31  *
32  * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
33  */
34 public abstract class GlyphSubtable implements Comparable {
35 
36     /** lookup flag - right to left */
37     public static final int LF_RIGHT_TO_LEFT = 0x0001;
38     /** lookup flag - ignore base glyphs */
39     public static final int LF_IGNORE_BASE = 0x0002;
40     /** lookup flag - ignore ligatures */
41     public static final int LF_IGNORE_LIGATURE = 0x0004;
42     /** lookup flag - ignore marks */
43     public static final int LF_IGNORE_MARK = 0x0008;
44     /** lookup flag - use mark filtering set */
45     public static final int LF_USE_MARK_FILTERING_SET = 0x0010;
46     /** lookup flag - reserved */
47     public static final int LF_RESERVED = 0x0E00;
48     /** lookup flag - mark attachment type */
49     public static final int LF_MARK_ATTACHMENT_TYPE = 0xFF00;
50     /** internal flag - use reverse scan */
51     public static final int LF_INTERNAL_USE_REVERSE_SCAN = 0x10000;
52 
53     /** lookup identifier, having form of "lu%d" where %d is index of lookup in lookup list; shared by multiple subtables in a single lookup  */
54     private String lookupId;
55     /** subtable sequence (index) number in lookup, zero based */
56     private int sequence;
57     /** subtable flags */
58     private int flags;
59     /** subtable format */
60     private int format;
61     /** subtable mapping table */
62     private GlyphMappingTable mapping;
63     /** weak reference to parent (gsub or gpos) table */
64     private WeakReference table;
65 
66     /**
67      * Instantiate this glyph subtable.
68      * @param lookupId lookup identifier, having form of "lu%d" where %d is index of lookup in lookup list
69      * @param sequence subtable sequence (within lookup), starting with zero
70      * @param flags subtable flags
71      * @param format subtable format
72      * @param mapping subtable mapping table
73      */
GlyphSubtable(String lookupId, int sequence, int flags, int format, GlyphMappingTable mapping)74     protected GlyphSubtable(String lookupId, int sequence, int flags, int format, GlyphMappingTable mapping)
75     {
76         if ((lookupId == null) || (lookupId.length() == 0)) {
77             throw new AdvancedTypographicTableFormatException("invalid lookup identifier, must be non-empty string");
78         } else if (mapping == null) {
79             throw new AdvancedTypographicTableFormatException("invalid mapping table, must not be null");
80         } else {
81             this.lookupId = lookupId;
82             this.sequence = sequence;
83             this.flags = flags;
84             this.format = format;
85             this.mapping = mapping;
86         }
87     }
88 
89     /** @return this subtable's lookup identifer */
getLookupId()90     public String getLookupId() {
91         return lookupId;
92     }
93 
94     /** @return this subtable's table type */
getTableType()95     public abstract int getTableType();
96 
97     /** @return this subtable's type */
getType()98     public abstract int getType();
99 
100     /** @return this subtable's type name */
getTypeName()101     public abstract String getTypeName();
102 
103     /**
104      * Determine if a glyph subtable is compatible with this glyph subtable. Two glyph subtables are
105      * compatible if the both may appear in a single lookup table.
106      * @param subtable a glyph subtable to determine compatibility
107      * @return true if specified subtable is compatible with this glyph subtable, where by compatible
108      * is meant that they share the same lookup type
109      */
isCompatible(GlyphSubtable subtable)110     public abstract boolean isCompatible(GlyphSubtable subtable);
111 
112     /** @return true if subtable uses reverse scanning of glyph sequence, meaning from the last glyph
113      * in a glyph sequence to the first glyph
114      */
usesReverseScan()115     public abstract boolean usesReverseScan();
116 
117     /** @return this subtable's sequence (index) within lookup */
getSequence()118     public int getSequence() {
119         return sequence;
120     }
121 
122     /** @return this subtable's flags */
getFlags()123     public int getFlags() {
124         return flags;
125     }
126 
127     /** @return this subtable's format */
getFormat()128     public int getFormat() {
129         return format;
130     }
131 
132     /** @return this subtable's governing glyph definition table or null if none available */
getGDEF()133     public GlyphDefinitionTable getGDEF() {
134         GlyphTable gt = getTable();
135         if (gt != null) {
136             return gt.getGlyphDefinitions();
137         } else {
138             return null;
139         }
140     }
141 
142     /** @return this subtable's coverage mapping or null if mapping is not a coverage mapping */
getCoverage()143     public GlyphCoverageMapping getCoverage() {
144         if (mapping instanceof GlyphCoverageMapping) {
145             return (GlyphCoverageMapping) mapping;
146         } else {
147             return null;
148         }
149     }
150 
151     /** @return this subtable's class mapping or null if mapping is not a class mapping */
getClasses()152     public GlyphClassMapping getClasses() {
153         if (mapping instanceof GlyphClassMapping) {
154             return (GlyphClassMapping) mapping;
155         } else {
156             return null;
157         }
158     }
159 
160     /** @return this subtable's lookup entries */
getEntries()161     public abstract List getEntries();
162 
163     /** @return this subtable's parent table (or null if undefined) */
getTable()164     public synchronized GlyphTable getTable() {
165         WeakReference r = this.table;
166         return (r != null) ? (GlyphTable) r.get() : null;
167     }
168 
169     /**
170      * Establish a weak reference from this subtable to its parent
171      * table. If table parameter is specified as <code>null</code>, then
172      * clear and remove weak reference.
173      * @param table the table or null
174      * @throws IllegalStateException if table is already set to non-null
175      */
setTable(GlyphTable table)176     public synchronized void setTable(GlyphTable table) throws IllegalStateException {
177         WeakReference r = this.table;
178         if (table == null) {
179             this.table = null;
180             if (r != null) {
181                 r.clear();
182             }
183         } else if (r == null) {
184             this.table = new WeakReference(table);
185         } else {
186             throw new IllegalStateException("table already set");
187         }
188     }
189 
190     /**
191      * Resolve references to lookup tables, e.g., in RuleLookup, to the lookup tables themselves.
192      * @param lookupTables map from lookup table identifers, e.g. "lu4", to lookup tables
193      */
resolveLookupReferences(Map<String, GlyphTable.LookupTable> lookupTables)194     public void resolveLookupReferences(Map<String, GlyphTable.LookupTable> lookupTables) {
195     }
196 
197     /**
198      * Map glyph id to coverage index.
199      * @param gid glyph id
200      * @return the corresponding coverage index of the specified glyph id
201      */
getCoverageIndex(int gid)202     public int getCoverageIndex(int gid) {
203         if (mapping instanceof GlyphCoverageMapping) {
204             return ((GlyphCoverageMapping) mapping) .getCoverageIndex(gid);
205         } else {
206             return -1;
207         }
208     }
209 
210     /**
211      * Map glyph id to coverage index.
212      * @return the corresponding coverage index of the specified glyph id
213      */
getCoverageSize()214     public int getCoverageSize() {
215         if (mapping instanceof GlyphCoverageMapping) {
216             return ((GlyphCoverageMapping) mapping) .getCoverageSize();
217         } else {
218             return 0;
219         }
220     }
221 
222     /** {@inheritDoc} */
hashCode()223     public int hashCode() {
224         int hc = sequence;
225         hc = (hc * 3) + (lookupId.hashCode() ^ hc);
226         return hc;
227     }
228 
229     /**
230      * {@inheritDoc}
231      * @return true if the lookup identifier and the sequence number of the specified subtable is the same
232      * as the lookup identifier and sequence number of this subtable
233      */
equals(Object o)234     public boolean equals(Object o) {
235         if (o instanceof GlyphSubtable) {
236             GlyphSubtable st = (GlyphSubtable) o;
237             return lookupId.equals(st.lookupId) && (sequence == st.sequence);
238         } else {
239             return false;
240         }
241     }
242 
243     /**
244      * {@inheritDoc}
245      * @return the result of comparing the lookup identifier and the sequence number of the specified subtable with
246      * the lookup identifier and sequence number of this subtable
247      */
compareTo(Object o)248     public int compareTo(Object o) {
249         int d;
250         if (o instanceof GlyphSubtable) {
251             GlyphSubtable st = (GlyphSubtable) o;
252             if ((d = lookupId.compareTo(st.lookupId)) == 0) {
253                 if (sequence < st.sequence) {
254                     d = -1;
255                 } else if (sequence > st.sequence) {
256                     d = 1;
257                 }
258             }
259         } else {
260             d = -1;
261         }
262         return d;
263     }
264 
265     /**
266      * Determine if any of the specified subtables uses reverse scanning.
267      * @param subtables array of glyph subtables
268      * @return true if any of the specified subtables uses reverse scanning.
269      */
usesReverseScan(GlyphSubtable[] subtables)270     public static boolean usesReverseScan(GlyphSubtable[] subtables) {
271         if ((subtables == null) || (subtables.length == 0)) {
272             return false;
273         } else {
274             for (GlyphSubtable subtable : subtables) {
275                 if (subtable.usesReverseScan()) {
276                     return true;
277                 }
278             }
279             return false;
280         }
281     }
282 
283     /**
284      * Determine consistent flags for a set of subtables.
285      * @param subtables array of glyph subtables
286      * @return consistent flags
287      * @throws IllegalStateException if inconsistent flags
288      */
getFlags(GlyphSubtable[] subtables)289     public static int getFlags(GlyphSubtable[] subtables) throws IllegalStateException {
290         if ((subtables == null) || (subtables.length == 0)) {
291             return 0;
292         } else {
293             int flags = 0;
294             // obtain first non-zero value of flags in array of subtables
295             for (GlyphSubtable subtable1 : subtables) {
296                 int f = subtable1.getFlags();
297                 if (flags == 0) {
298                     flags = f;
299                     break;
300                 }
301             }
302             // enforce flag consistency
303             for (GlyphSubtable subtable : subtables) {
304                 int f = subtable.getFlags();
305                 if (f != flags) {
306                     throw new IllegalStateException("inconsistent lookup flags " + f + ", expected " + flags);
307                 }
308             }
309             return flags | (usesReverseScan(subtables) ? LF_INTERNAL_USE_REVERSE_SCAN : 0);
310         }
311     }
312 
313 }
314