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