Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
What Is fop.jar in fop-2.7-bin.zip
What Is fop.jar? I got it from the fop-2.7-bin.zip.
✍: FYIcenter.com
fop.jar in fop-2.7-bin.zip is the JAR file for FOP 2.7, which is a print formatter driven by XSL formatting objects (XSL-FO). You can obtain fop.jar from the build folder of the fop-2.7-bin.zip file.
Below is the information about the fop.jar (2.2) file:
JAR File Size and Download Location:
JAR name: fop.jar, fop-2.7.jar Target JDK version: 1.7 File name: fop.jar File size: 4442817 bytes Release date: 20-Jan-2022 Download: Apache FOP Website
Java source code files for fop.jar:
⏎ org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
/* * 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.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.complexscripts.scripts.ScriptProcessor; import org.apache.fop.fonts.truetype.FontFileReader; import org.apache.fop.fonts.truetype.OFDirTabEntry; import org.apache.fop.fonts.truetype.OFTableName; import org.apache.fop.fonts.truetype.OpenFont; // CSOFF: LineLengthCheck /** * <p>OpenType Font (OTF) advanced typographic table reader. Used by @{Link org.apache.fop.fonts.truetype.TTFFile} * to read advanced typographic tables (GDEF, GSUB, GPOS).</p> * * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p> */ public final class OTFAdvancedTypographicTableReader { // logging state private static Log log = LogFactory.getLog(OTFAdvancedTypographicTableReader.class); // instance state private OpenFont otf; // parent font file reader private FontFileReader in; // input reader private GlyphDefinitionTable gdef; // glyph definition table private GlyphSubstitutionTable gsub; // glyph substitution table private GlyphPositioningTable gpos; // glyph positioning table // transient parsing state private transient Map<String, Object> seScripts; // script-tag => Object[3] : { default-language-tag, List(language-tag), seLanguages } private transient Map<String, Object> seLanguages; // language-tag => Object[2] : { "f<required-feature-index>", List("f<feature-index>") private transient Map<String, Object> seFeatures; // "f<feature-index>" => Object[2] : { feature-tag, List("lu<lookup-index>") } private transient GlyphMappingTable seMapping; // subtable entry mappings private transient List seEntries; // subtable entry entries private transient List seSubtables; // subtable entry subtables private Map<String, ScriptProcessor> processors = new HashMap<String, ScriptProcessor>(); /** * Construct an <code>OTFAdvancedTypographicTableReader</code> instance. * @param otf parent font file reader (must be non-null) * @param in font file reader (must be non-null) */ public OTFAdvancedTypographicTableReader(OpenFont otf, FontFileReader in) { assert otf != null; assert in != null; this.otf = otf; this.in = in; } /** * Read all advanced typographic tables. * @throws AdvancedTypographicTableFormatException if ATT table has invalid format */ public void readAll() throws AdvancedTypographicTableFormatException { try { readGDEF(); readGSUB(); readGPOS(); } catch (AdvancedTypographicTableFormatException e) { resetATStateAll(); throw e; } catch (IOException e) { resetATStateAll(); throw new AdvancedTypographicTableFormatException(e.getMessage(), e); } finally { resetATState(); } } /** * Determine if advanced (typographic) table is present. * @return true if advanced (typographic) table is present */ public boolean hasAdvancedTable() { return (gdef != null) || (gsub != null) || (gpos != null); } /** * Returns the GDEF table or null if none present. * @return the GDEF table */ public GlyphDefinitionTable getGDEF() { return gdef; } /** * Returns the GSUB table or null if none present. * @return the GSUB table */ public GlyphSubstitutionTable getGSUB() { return gsub; } /** * Returns the GPOS table or null if none present. * @return the GPOS table */ public GlyphPositioningTable getGPOS() { return gpos; } private void readLangSysTable(OFTableName tableTag, long langSysTable, String langSysTag) throws IOException { in.seekSet(langSysTable); if (log.isDebugEnabled()) { log.debug(tableTag + " lang sys table: " + langSysTag); } // read lookup order (reorder) table offset int lo = in.readTTFUShort(); // read required feature index int rf = in.readTTFUShort(); String rfi; if (rf != 65535) { rfi = "f" + rf; } else { rfi = null; } // read (non-required) feature count int nf = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " lang sys table reorder table: " + lo); log.debug(tableTag + " lang sys table required feature index: " + rf); log.debug(tableTag + " lang sys table non-required feature count: " + nf); } // read (non-required) feature indices List fl = new java.util.ArrayList(); for (int i = 0; i < nf; i++) { int fi = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " lang sys table non-required feature index: " + fi); } fl.add("f" + fi); } if (seLanguages == null) { seLanguages = new java.util.LinkedHashMap(); } seLanguages.put(langSysTag, new Object[] { rfi, fl }); } private static String defaultTag = "dflt"; private void readScriptTable(OFTableName tableTag, long scriptTable, String scriptTag) throws IOException { in.seekSet(scriptTable); if (log.isDebugEnabled()) { log.debug(tableTag + " script table: " + scriptTag); } // read default language system table offset int dl = in.readTTFUShort(); String dt = defaultTag; if (dl > 0) { if (log.isDebugEnabled()) { log.debug(tableTag + " default lang sys tag: " + dt); log.debug(tableTag + " default lang sys table offset: " + dl); } } // read language system record count int nl = in.readTTFUShort(); List ll = new java.util.ArrayList(); if (nl > 0) { String[] lta = new String[nl]; int[] loa = new int[nl]; // read language system records for (int i = 0, n = nl; i < n; i++) { String lt = in.readTTFString(4); int lo = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " lang sys tag: " + lt); log.debug(tableTag + " lang sys table offset: " + lo); } lta[i] = lt; loa[i] = lo; if (dl == lo) { dl = 0; dt = lt; } ll.add(lt); } // read non-default language system tables for (int i = 0, n = nl; i < n; i++) { readLangSysTable(tableTag, scriptTable + loa [ i ], lta [ i ]); } } // read default language system table (if specified) if (dl > 0) { readLangSysTable(tableTag, scriptTable + dl, dt); } else if (dt != null) { if (log.isDebugEnabled()) { log.debug(tableTag + " lang sys default: " + dt); } } if (seLanguages != null) { seScripts.put(scriptTag, new Object[]{dt, ll, seLanguages}); } seLanguages = null; } private void readScriptList(OFTableName tableTag, long scriptList) throws IOException { in.seekSet(scriptList); // read script record count int ns = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " script list record count: " + ns); } if (ns > 0) { String[] sta = new String[ns]; int[] soa = new int[ns]; // read script records for (int i = 0, n = ns; i < n; i++) { String st = in.readTTFString(4); int so = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " script tag: " + st); log.debug(tableTag + " script table offset: " + so); } sta[i] = st; soa[i] = so; } // read script tables for (int i = 0, n = ns; i < n; i++) { seLanguages = null; readScriptTable(tableTag, scriptList + soa [ i ], sta [ i ]); } } } private void readFeatureTable(OFTableName tableTag, long featureTable, String featureTag, int featureIndex) throws IOException { in.seekSet(featureTable); if (log.isDebugEnabled()) { log.debug(tableTag + " feature table: " + featureTag); } // read feature params offset int po = in.readTTFUShort(); // read lookup list indices count int nl = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " feature table parameters offset: " + po); log.debug(tableTag + " feature table lookup list index count: " + nl); } // read lookup table indices List lul = new java.util.ArrayList(); for (int i = 0; i < nl; i++) { int li = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " feature table lookup index: " + li); } lul.add("lu" + li); } seFeatures.put("f" + featureIndex, new Object[] { featureTag, lul }); } private void readFeatureList(OFTableName tableTag, long featureList) throws IOException { in.seekSet(featureList); // read feature record count int nf = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " feature list record count: " + nf); } if (nf > 0) { String[] fta = new String[nf]; int[] foa = new int[nf]; // read feature records for (int i = 0, n = nf; i < n; i++) { String ft = in.readTTFString(4); int fo = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " feature tag: " + ft); log.debug(tableTag + " feature table offset: " + fo); } fta[i] = ft; foa[i] = fo; } // read feature tables for (int i = 0, n = nf; i < n; i++) { if (log.isDebugEnabled()) { log.debug(tableTag + " feature index: " + i); } readFeatureTable(tableTag, featureList + foa [ i ], fta [ i ], i); } } } static final class GDEFLookupType { static final int GLYPH_CLASS = 1; static final int ATTACHMENT_POINT = 2; static final int LIGATURE_CARET = 3; static final int MARK_ATTACHMENT = 4; private GDEFLookupType() { } public static int getSubtableType(int lt) { int st; switch (lt) { case GDEFLookupType.GLYPH_CLASS: st = GlyphDefinitionTable.GDEF_LOOKUP_TYPE_GLYPH_CLASS; break; case GDEFLookupType.ATTACHMENT_POINT: st = GlyphDefinitionTable.GDEF_LOOKUP_TYPE_ATTACHMENT_POINT; break; case GDEFLookupType.LIGATURE_CARET: st = GlyphDefinitionTable.GDEF_LOOKUP_TYPE_LIGATURE_CARET; break; case GDEFLookupType.MARK_ATTACHMENT: st = GlyphDefinitionTable.GDEF_LOOKUP_TYPE_MARK_ATTACHMENT; break; default: st = -1; break; } return st; } public static String toString(int type) { String s; switch (type) { case GLYPH_CLASS: s = "GlyphClass"; break; case ATTACHMENT_POINT: s = "AttachmentPoint"; break; case LIGATURE_CARET: s = "LigatureCaret"; break; case MARK_ATTACHMENT: s = "MarkAttachment"; break; default: s = "?"; break; } return s; } } static final class GSUBLookupType { static final int SINGLE = 1; static final int MULTIPLE = 2; static final int ALTERNATE = 3; static final int LIGATURE = 4; static final int CONTEXTUAL = 5; static final int CHAINED_CONTEXTUAL = 6; static final int EXTENSION = 7; static final int REVERSE_CHAINED_SINGLE = 8; private GSUBLookupType() { } public static int getSubtableType(int lt) { int st; switch (lt) { case GSUBLookupType.SINGLE: st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_SINGLE; break; case GSUBLookupType.MULTIPLE: st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_MULTIPLE; break; case GSUBLookupType.ALTERNATE: st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_ALTERNATE; break; case GSUBLookupType.LIGATURE: st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_LIGATURE; break; case GSUBLookupType.CONTEXTUAL: st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_CONTEXTUAL; break; case GSUBLookupType.CHAINED_CONTEXTUAL: st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_CHAINED_CONTEXTUAL; break; case GSUBLookupType.EXTENSION: st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_EXTENSION_SUBSTITUTION; break; case GSUBLookupType.REVERSE_CHAINED_SINGLE: st = GlyphSubstitutionTable.GSUB_LOOKUP_TYPE_REVERSE_CHAINED_SINGLE; break; default: st = -1; break; } return st; } public static String toString(int type) { String s; switch (type) { case SINGLE: s = "Single"; break; case MULTIPLE: s = "Multiple"; break; case ALTERNATE: s = "Alternate"; break; case LIGATURE: s = "Ligature"; break; case CONTEXTUAL: s = "Contextual"; break; case CHAINED_CONTEXTUAL: s = "ChainedContextual"; break; case EXTENSION: s = "Extension"; break; case REVERSE_CHAINED_SINGLE: s = "ReverseChainedSingle"; break; default: s = "?"; break; } return s; } } static final class GPOSLookupType { static final int SINGLE = 1; static final int PAIR = 2; static final int CURSIVE = 3; static final int MARK_TO_BASE = 4; static final int MARK_TO_LIGATURE = 5; static final int MARK_TO_MARK = 6; static final int CONTEXTUAL = 7; static final int CHAINED_CONTEXTUAL = 8; static final int EXTENSION = 9; private GPOSLookupType() { } public static String toString(int type) { String s; switch (type) { case SINGLE: s = "Single"; break; case PAIR: s = "Pair"; break; case CURSIVE: s = "Cursive"; break; case MARK_TO_BASE: s = "MarkToBase"; break; case MARK_TO_LIGATURE: s = "MarkToLigature"; break; case MARK_TO_MARK: s = "MarkToMark"; break; case CONTEXTUAL: s = "Contextual"; break; case CHAINED_CONTEXTUAL: s = "ChainedContextual"; break; case EXTENSION: s = "Extension"; break; default: s = "?"; break; } return s; } } static final class LookupFlag { static final int RIGHT_TO_LEFT = 0x0001; static final int IGNORE_BASE_GLYPHS = 0x0002; static final int IGNORE_LIGATURE = 0x0004; static final int IGNORE_MARKS = 0x0008; static final int USE_MARK_FILTERING_SET = 0x0010; static final int MARK_ATTACHMENT_TYPE = 0xFF00; private LookupFlag() { } public static String toString(int flags) { StringBuffer sb = new StringBuffer(); boolean first = true; if ((flags & RIGHT_TO_LEFT) != 0) { if (first) { first = false; } else { sb.append('|'); } sb.append("RightToLeft"); } if ((flags & IGNORE_BASE_GLYPHS) != 0) { if (first) { first = false; } else { sb.append('|'); } sb.append("IgnoreBaseGlyphs"); } if ((flags & IGNORE_LIGATURE) != 0) { if (first) { first = false; } else { sb.append('|'); } sb.append("IgnoreLigature"); } if ((flags & IGNORE_MARKS) != 0) { if (first) { first = false; } else { sb.append('|'); } sb.append("IgnoreMarks"); } if ((flags & USE_MARK_FILTERING_SET) != 0) { if (first) { first = false; } else { sb.append('|'); } sb.append("UseMarkFilteringSet"); } if (sb.length() == 0) { sb.append('-'); } return sb.toString(); } } private GlyphCoverageTable readCoverageTableFormat1(String label, long tableOffset, int coverageFormat) throws IOException { List entries = new java.util.ArrayList(); in.seekSet(tableOffset); // skip over format (already known) in.skip(2); // read glyph count int ng = in.readTTFUShort(); int[] ga = new int[ng]; for (int i = 0, n = ng; i < n; i++) { int g = in.readTTFUShort(); ga[i] = g; entries.add(g); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(label + " glyphs: " + toString(ga)); } return GlyphCoverageTable.createCoverageTable(entries); } private GlyphCoverageTable readCoverageTableFormat2(String label, long tableOffset, int coverageFormat) throws IOException { List entries = new java.util.ArrayList(); in.seekSet(tableOffset); // skip over format (already known) in.skip(2); // read range record count int nr = in.readTTFUShort(); for (int i = 0, n = nr; i < n; i++) { // read range start int s = in.readTTFUShort(); // read range end int e = in.readTTFUShort(); // read range coverage (mapping) index int m = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(label + " range[" + i + "]: [" + s + "," + e + "]: " + m); } entries.add(new GlyphCoverageTable.MappingRange(s, e, m)); } return GlyphCoverageTable.createCoverageTable(entries); } private GlyphCoverageTable readCoverageTable(String label, long tableOffset) throws IOException { GlyphCoverageTable gct; long cp = in.getCurrentPos(); in.seekSet(tableOffset); // read coverage table format int cf = in.readTTFUShort(); if (cf == 1) { gct = readCoverageTableFormat1(label, tableOffset, cf); } else if (cf == 2) { gct = readCoverageTableFormat2(label, tableOffset, cf); } else { throw new AdvancedTypographicTableFormatException("unsupported coverage table format: " + cf); } in.seekSet(cp); return gct; } private GlyphClassTable readClassDefTableFormat1(String label, long tableOffset, int classFormat) throws IOException { List entries = new java.util.ArrayList(); in.seekSet(tableOffset); // skip over format (already known) in.skip(2); // read start glyph int sg = in.readTTFUShort(); entries.add(sg); // read glyph count int ng = in.readTTFUShort(); // read glyph classes int[] ca = new int[ng]; for (int i = 0, n = ng; i < n; i++) { int gc = in.readTTFUShort(); ca[i] = gc; entries.add(gc); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(label + " glyph classes: " + toString(ca)); } return GlyphClassTable.createClassTable(entries); } private GlyphClassTable readClassDefTableFormat2(String label, long tableOffset, int classFormat) throws IOException { List entries = new java.util.ArrayList(); in.seekSet(tableOffset); // skip over format (already known) in.skip(2); // read range record count int nr = in.readTTFUShort(); for (int i = 0, n = nr; i < n; i++) { // read range start int s = in.readTTFUShort(); // read range end int e = in.readTTFUShort(); // read range glyph class (mapping) index int m = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(label + " range[" + i + "]: [" + s + "," + e + "]: " + m); } entries.add(new GlyphClassTable.MappingRange(s, e, m)); } return GlyphClassTable.createClassTable(entries); } private GlyphClassTable readClassDefTable(String label, long tableOffset) throws IOException { GlyphClassTable gct; long cp = in.getCurrentPos(); in.seekSet(tableOffset); // read class table format int cf = in.readTTFUShort(); if (cf == 1) { gct = readClassDefTableFormat1(label, tableOffset, cf); } else if (cf == 2) { gct = readClassDefTableFormat2(label, tableOffset, cf); } else { throw new AdvancedTypographicTableFormatException("unsupported class definition table format: " + cf); } in.seekSet(cp); return gct; } private void readSingleSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read delta glyph int dg = in.readTTFShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " single substitution subtable format: " + subtableFormat + " (delta)"); log.debug(tableTag + " single substitution coverage table offset: " + co); log.debug(tableTag + " single substitution delta: " + dg); } // read coverage table seMapping = readCoverageTable(tableTag + " single substitution coverage", subtableOffset + co); seEntries.add(dg); } private void readSingleSubTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read glyph count int ng = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " single substitution subtable format: " + subtableFormat + " (mapped)"); log.debug(tableTag + " single substitution coverage table offset: " + co); log.debug(tableTag + " single substitution glyph count: " + ng); } // read coverage table seMapping = readCoverageTable(tableTag + " single substitution coverage", subtableOffset + co); // read glyph substitutions for (int i = 0, n = ng; i < n; i++) { int gs = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " single substitution glyph[" + i + "]: " + gs); } seEntries.add(gs); } } private int readSingleSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read substitution subtable format int sf = in.readTTFUShort(); if (sf == 1) { readSingleSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 2) { readSingleSubTableFormat2(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported single substitution subtable format: " + sf); } return sf; } private void readMultipleSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read sequence count int ns = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " multiple substitution subtable format: " + subtableFormat + " (mapped)"); log.debug(tableTag + " multiple substitution coverage table offset: " + co); log.debug(tableTag + " multiple substitution sequence count: " + ns); } // read coverage table seMapping = readCoverageTable(tableTag + " multiple substitution coverage", subtableOffset + co); // read sequence table offsets int[] soa = new int[ns]; for (int i = 0, n = ns; i < n; i++) { soa[i] = in.readTTFUShort(); } // read sequence tables int[][] gsa = new int [ ns ] []; for (int i = 0, n = ns; i < n; i++) { int so = soa[i]; int[] ga; if (so > 0) { in.seekSet(subtableOffset + so); // read glyph count int ng = in.readTTFUShort(); ga = new int[ng]; for (int j = 0; j < ng; j++) { ga[j] = in.readTTFUShort(); } } else { ga = null; } if (log.isDebugEnabled()) { log.debug(tableTag + " multiple substitution sequence[" + i + "]: " + toString(ga)); } gsa [ i ] = ga; } seEntries.add(gsa); } private int readMultipleSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read substitution subtable format int sf = in.readTTFUShort(); if (sf == 1) { readMultipleSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported multiple substitution subtable format: " + sf); } return sf; } private void readAlternateSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read alternate set count int ns = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " alternate substitution subtable format: " + subtableFormat + " (mapped)"); log.debug(tableTag + " alternate substitution coverage table offset: " + co); log.debug(tableTag + " alternate substitution alternate set count: " + ns); } // read coverage table seMapping = readCoverageTable(tableTag + " alternate substitution coverage", subtableOffset + co); // read alternate set table offsets int[] soa = new int[ns]; for (int i = 0, n = ns; i < n; i++) { soa[i] = in.readTTFUShort(); } // read alternate set tables for (int i = 0, n = ns; i < n; i++) { int so = soa[i]; in.seekSet(subtableOffset + so); // read glyph count int ng = in.readTTFUShort(); int[] ga = new int[ng]; for (int j = 0; j < ng; j++) { int gs = in.readTTFUShort(); ga[j] = gs; } if (log.isDebugEnabled()) { log.debug(tableTag + " alternate substitution alternate set[" + i + "]: " + toString(ga)); } seEntries.add(ga); } } private int readAlternateSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read substitution subtable format int sf = in.readTTFUShort(); if (sf == 1) { readAlternateSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported alternate substitution subtable format: " + sf); } return sf; } private void readLigatureSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read ligature set count int ns = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " ligature substitution subtable format: " + subtableFormat + " (mapped)"); log.debug(tableTag + " ligature substitution coverage table offset: " + co); log.debug(tableTag + " ligature substitution ligature set count: " + ns); } // read coverage table seMapping = readCoverageTable(tableTag + " ligature substitution coverage", subtableOffset + co); // read ligature set table offsets int[] soa = new int[ns]; for (int i = 0, n = ns; i < n; i++) { soa[i] = in.readTTFUShort(); } // read ligature set tables for (int i = 0, n = ns; i < n; i++) { int so = soa[i]; in.seekSet(subtableOffset + so); // read ligature table count int nl = in.readTTFUShort(); int[] loa = new int[nl]; for (int j = 0; j < nl; j++) { loa[j] = in.readTTFUShort(); } List ligs = new java.util.ArrayList(); for (int j = 0; j < nl; j++) { int lo = loa[j]; in.seekSet(subtableOffset + so + lo); // read ligature glyph id int lg = in.readTTFUShort(); // read ligature (input) component count int nc = in.readTTFUShort(); int[] ca = new int [ nc - 1 ]; // read ligature (input) component glyph ids for (int k = 0; k < nc - 1; k++) { ca[k] = in.readTTFUShort(); } if (log.isDebugEnabled()) { log.debug(tableTag + " ligature substitution ligature set[" + i + "]: ligature(" + lg + "), components: " + toString(ca)); } ligs.add(new GlyphSubstitutionTable.Ligature(lg, ca)); } seEntries.add(new GlyphSubstitutionTable.LigatureSet(ligs)); } } private int readLigatureSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read substitution subtable format int sf = in.readTTFUShort(); if (sf == 1) { readLigatureSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported ligature substitution subtable format: " + sf); } return sf; } private GlyphTable.RuleLookup[] readRuleLookups(int numLookups, String header) throws IOException { GlyphTable.RuleLookup[] la = new GlyphTable.RuleLookup [ numLookups ]; for (int i = 0, n = numLookups; i < n; i++) { int sequenceIndex = in.readTTFUShort(); int lookupIndex = in.readTTFUShort(); la [ i ] = new GlyphTable.RuleLookup(sequenceIndex, lookupIndex); // dump info if debugging and header is non-null if (log.isDebugEnabled() && (header != null)) { log.debug(header + "lookup[" + i + "]: " + la[i]); } } return la; } private void readContextualSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read rule set count int nrs = in.readTTFUShort(); // read rule set offsets int[] rsoa = new int [ nrs ]; for (int i = 0; i < nrs; i++) { rsoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " contextual substitution format: " + subtableFormat + " (glyphs)"); log.debug(tableTag + " contextual substitution coverage table offset: " + co); log.debug(tableTag + " contextual substitution rule set count: " + nrs); for (int i = 0; i < nrs; i++) { log.debug(tableTag + " contextual substitution rule set offset[" + i + "]: " + rsoa[i]); } } // read coverage table GlyphCoverageTable ct; if (co > 0) { ct = readCoverageTable(tableTag + " contextual substitution coverage", subtableOffset + co); } else { ct = null; } // read rule sets GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ nrs ]; String header = null; for (int i = 0; i < nrs; i++) { GlyphTable.RuleSet rs; int rso = rsoa [ i ]; if (rso > 0) { // seek to rule set [ i ] in.seekSet(subtableOffset + rso); // read rule count int nr = in.readTTFUShort(); // read rule offsets int[] roa = new int [ nr ]; GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ]; for (int j = 0; j < nr; j++) { roa [ j ] = in.readTTFUShort(); } // read glyph sequence rules for (int j = 0; j < nr; j++) { GlyphTable.GlyphSequenceRule r; int ro = roa [ j ]; if (ro > 0) { // seek to rule [ j ] in.seekSet(subtableOffset + rso + ro); // read glyph count int ng = in.readTTFUShort(); // read rule lookup count int nl = in.readTTFUShort(); // read glyphs int[] glyphs = new int [ ng - 1 ]; for (int k = 0, nk = glyphs.length; k < nk; k++) { glyphs [ k ] = in.readTTFUShort(); } // read rule lookups if (log.isDebugEnabled()) { header = tableTag + " contextual substitution lookups @rule[" + i + "][" + j + "]: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); r = new GlyphTable.GlyphSequenceRule(lookups, ng, glyphs); } else { r = null; } ra [ j ] = r; } rs = new GlyphTable.HomogeneousRuleSet(ra); } else { rs = null; } rsa [ i ] = rs; } // store results seMapping = ct; seEntries.add(rsa); } private void readContextualSubTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read class def table offset int cdo = in.readTTFUShort(); // read class rule set count int ngc = in.readTTFUShort(); // read class rule set offsets int[] csoa = new int [ ngc ]; for (int i = 0; i < ngc; i++) { csoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " contextual substitution format: " + subtableFormat + " (glyph classes)"); log.debug(tableTag + " contextual substitution coverage table offset: " + co); log.debug(tableTag + " contextual substitution class set count: " + ngc); for (int i = 0; i < ngc; i++) { log.debug(tableTag + " contextual substitution class set offset[" + i + "]: " + csoa[i]); } } // read coverage table GlyphCoverageTable ct; if (co > 0) { ct = readCoverageTable(tableTag + " contextual substitution coverage", subtableOffset + co); } else { ct = null; } // read class definition table GlyphClassTable cdt; if (cdo > 0) { cdt = readClassDefTable(tableTag + " contextual substitution class definition", subtableOffset + cdo); } else { cdt = null; } // read rule sets GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ ngc ]; String header = null; for (int i = 0; i < ngc; i++) { int cso = csoa [ i ]; GlyphTable.RuleSet rs; if (cso > 0) { // seek to rule set [ i ] in.seekSet(subtableOffset + cso); // read rule count int nr = in.readTTFUShort(); // read rule offsets int[] roa = new int [ nr ]; GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ]; for (int j = 0; j < nr; j++) { roa [ j ] = in.readTTFUShort(); } // read glyph sequence rules for (int j = 0; j < nr; j++) { int ro = roa [ j ]; GlyphTable.ClassSequenceRule r; if (ro > 0) { // seek to rule [ j ] in.seekSet(subtableOffset + cso + ro); // read glyph count int ng = in.readTTFUShort(); // read rule lookup count int nl = in.readTTFUShort(); // read classes int[] classes = new int [ ng - 1 ]; for (int k = 0, nk = classes.length; k < nk; k++) { classes [ k ] = in.readTTFUShort(); } // read rule lookups if (log.isDebugEnabled()) { header = tableTag + " contextual substitution lookups @rule[" + i + "][" + j + "]: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); r = new GlyphTable.ClassSequenceRule(lookups, ng, classes); } else { assert ro > 0 : "unexpected null subclass rule offset"; r = null; } ra [ j ] = r; } rs = new GlyphTable.HomogeneousRuleSet(ra); } else { rs = null; } rsa [ i ] = rs; } // store results seMapping = ct; seEntries.add(cdt); seEntries.add(ngc); seEntries.add(rsa); } private void readContextualSubTableFormat3(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read glyph (input sequence length) count int ng = in.readTTFUShort(); // read substitution lookup count int nl = in.readTTFUShort(); // read glyph coverage offsets, one per glyph input sequence length count int[] gcoa = new int [ ng ]; for (int i = 0; i < ng; i++) { gcoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " contextual substitution format: " + subtableFormat + " (glyph sets)"); log.debug(tableTag + " contextual substitution glyph input sequence length count: " + ng); log.debug(tableTag + " contextual substitution lookup count: " + nl); for (int i = 0; i < ng; i++) { log.debug(tableTag + " contextual substitution coverage table offset[" + i + "]: " + gcoa[i]); } } // read coverage tables GlyphCoverageTable[] gca = new GlyphCoverageTable [ ng ]; for (int i = 0; i < ng; i++) { int gco = gcoa [ i ]; GlyphCoverageTable gct; if (gco > 0) { gct = readCoverageTable(tableTag + " contextual substitution coverage[" + i + "]", subtableOffset + gco); } else { gct = null; } gca [ i ] = gct; } // read rule lookups String header = null; if (log.isDebugEnabled()) { header = tableTag + " contextual substitution lookups: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); // construct rule, rule set, and rule set array GlyphTable.Rule r = new GlyphTable.CoverageSequenceRule(lookups, ng, gca); GlyphTable.RuleSet rs = new GlyphTable.HomogeneousRuleSet(new GlyphTable.Rule[] {r}); GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet[] {rs}; // store results assert (gca != null) && (gca.length > 0); seMapping = gca[0]; seEntries.add(rsa); } private int readContextualSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read substitution subtable format int sf = in.readTTFUShort(); if (sf == 1) { readContextualSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 2) { readContextualSubTableFormat2(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 3) { readContextualSubTableFormat3(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported contextual substitution subtable format: " + sf); } return sf; } private void readChainedContextualSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read rule set count int nrs = in.readTTFUShort(); // read rule set offsets int[] rsoa = new int [ nrs ]; for (int i = 0; i < nrs; i++) { rsoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " chained contextual substitution format: " + subtableFormat + " (glyphs)"); log.debug(tableTag + " chained contextual substitution coverage table offset: " + co); log.debug(tableTag + " chained contextual substitution rule set count: " + nrs); for (int i = 0; i < nrs; i++) { log.debug(tableTag + " chained contextual substitution rule set offset[" + i + "]: " + rsoa[i]); } } // read coverage table GlyphCoverageTable ct; if (co > 0) { ct = readCoverageTable(tableTag + " chained contextual substitution coverage", subtableOffset + co); } else { ct = null; } // read rule sets GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ nrs ]; String header = null; for (int i = 0; i < nrs; i++) { GlyphTable.RuleSet rs; int rso = rsoa [ i ]; if (rso > 0) { // seek to rule set [ i ] in.seekSet(subtableOffset + rso); // read rule count int nr = in.readTTFUShort(); // read rule offsets int[] roa = new int [ nr ]; GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ]; for (int j = 0; j < nr; j++) { roa [ j ] = in.readTTFUShort(); } // read glyph sequence rules for (int j = 0; j < nr; j++) { GlyphTable.ChainedGlyphSequenceRule r; int ro = roa [ j ]; if (ro > 0) { // seek to rule [ j ] in.seekSet(subtableOffset + rso + ro); // read backtrack glyph count int nbg = in.readTTFUShort(); // read backtrack glyphs int[] backtrackGlyphs = new int [ nbg ]; for (int k = 0, nk = backtrackGlyphs.length; k < nk; k++) { backtrackGlyphs [ k ] = in.readTTFUShort(); } // read input glyph count int nig = in.readTTFUShort(); // read glyphs int[] glyphs = new int [ nig - 1 ]; for (int k = 0, nk = glyphs.length; k < nk; k++) { glyphs [ k ] = in.readTTFUShort(); } // read lookahead glyph count int nlg = in.readTTFUShort(); // read lookahead glyphs int[] lookaheadGlyphs = new int [ nlg ]; for (int k = 0, nk = lookaheadGlyphs.length; k < nk; k++) { lookaheadGlyphs [ k ] = in.readTTFUShort(); } // read rule lookup count int nl = in.readTTFUShort(); // read rule lookups if (log.isDebugEnabled()) { header = tableTag + " contextual substitution lookups @rule[" + i + "][" + j + "]: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); r = new GlyphTable.ChainedGlyphSequenceRule(lookups, nig, glyphs, backtrackGlyphs, lookaheadGlyphs); } else { r = null; } ra [ j ] = r; } rs = new GlyphTable.HomogeneousRuleSet(ra); } else { rs = null; } rsa [ i ] = rs; } // store results seMapping = ct; seEntries.add(rsa); } private void readChainedContextualSubTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read backtrack class def table offset int bcdo = in.readTTFUShort(); // read input class def table offset int icdo = in.readTTFUShort(); // read lookahead class def table offset int lcdo = in.readTTFUShort(); // read class set count int ngc = in.readTTFUShort(); // read class set offsets int[] csoa = new int [ ngc ]; for (int i = 0; i < ngc; i++) { csoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " chained contextual substitution format: " + subtableFormat + " (glyph classes)"); log.debug(tableTag + " chained contextual substitution coverage table offset: " + co); log.debug(tableTag + " chained contextual substitution class set count: " + ngc); for (int i = 0; i < ngc; i++) { log.debug(tableTag + " chained contextual substitution class set offset[" + i + "]: " + csoa[i]); } } // read coverage table GlyphCoverageTable ct; if (co > 0) { ct = readCoverageTable(tableTag + " chained contextual substitution coverage", subtableOffset + co); } else { ct = null; } // read backtrack class definition table GlyphClassTable bcdt; if (bcdo > 0) { bcdt = readClassDefTable(tableTag + " contextual substitution backtrack class definition", subtableOffset + bcdo); } else { bcdt = null; } // read input class definition table GlyphClassTable icdt; if (icdo > 0) { icdt = readClassDefTable(tableTag + " contextual substitution input class definition", subtableOffset + icdo); } else { icdt = null; } // read lookahead class definition table GlyphClassTable lcdt; if (lcdo > 0) { lcdt = readClassDefTable(tableTag + " contextual substitution lookahead class definition", subtableOffset + lcdo); } else { lcdt = null; } // read rule sets GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ ngc ]; String header = null; for (int i = 0; i < ngc; i++) { int cso = csoa [ i ]; GlyphTable.RuleSet rs; if (cso > 0) { // seek to rule set [ i ] in.seekSet(subtableOffset + cso); // read rule count int nr = in.readTTFUShort(); // read rule offsets int[] roa = new int [ nr ]; GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ]; for (int j = 0; j < nr; j++) { roa [ j ] = in.readTTFUShort(); } // read glyph sequence rules for (int j = 0; j < nr; j++) { int ro = roa [ j ]; GlyphTable.ChainedClassSequenceRule r; if (ro > 0) { // seek to rule [ j ] in.seekSet(subtableOffset + cso + ro); // read backtrack glyph class count int nbc = in.readTTFUShort(); // read backtrack glyph classes int[] backtrackClasses = new int [ nbc ]; for (int k = 0, nk = backtrackClasses.length; k < nk; k++) { backtrackClasses [ k ] = in.readTTFUShort(); } // read input glyph class count int nic = in.readTTFUShort(); // read input glyph classes int[] classes = new int [ nic - 1 ]; for (int k = 0, nk = classes.length; k < nk; k++) { classes [ k ] = in.readTTFUShort(); } // read lookahead glyph class count int nlc = in.readTTFUShort(); // read lookahead glyph classes int[] lookaheadClasses = new int [ nlc ]; for (int k = 0, nk = lookaheadClasses.length; k < nk; k++) { lookaheadClasses [ k ] = in.readTTFUShort(); } // read rule lookup count int nl = in.readTTFUShort(); // read rule lookups if (log.isDebugEnabled()) { header = tableTag + " contextual substitution lookups @rule[" + i + "][" + j + "]: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); r = new GlyphTable.ChainedClassSequenceRule(lookups, nic, classes, backtrackClasses, lookaheadClasses); } else { r = null; } ra [ j ] = r; } rs = new GlyphTable.HomogeneousRuleSet(ra); } else { rs = null; } rsa [ i ] = rs; } // store results seMapping = ct; seEntries.add(icdt); seEntries.add(bcdt); seEntries.add(lcdt); seEntries.add(ngc); seEntries.add(rsa); } private void readChainedContextualSubTableFormat3(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read backtrack glyph count int nbg = in.readTTFUShort(); // read backtrack glyph coverage offsets int[] bgcoa = new int [ nbg ]; for (int i = 0; i < nbg; i++) { bgcoa [ i ] = in.readTTFUShort(); } // read input glyph count int nig = in.readTTFUShort(); // read input glyph coverage offsets int[] igcoa = new int [ nig ]; for (int i = 0; i < nig; i++) { igcoa [ i ] = in.readTTFUShort(); } // read lookahead glyph count int nlg = in.readTTFUShort(); // read lookahead glyph coverage offsets int[] lgcoa = new int [ nlg ]; for (int i = 0; i < nlg; i++) { lgcoa [ i ] = in.readTTFUShort(); } // read substitution lookup count int nl = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " chained contextual substitution format: " + subtableFormat + " (glyph sets)"); log.debug(tableTag + " chained contextual substitution backtrack glyph count: " + nbg); for (int i = 0; i < nbg; i++) { log.debug(tableTag + " chained contextual substitution backtrack coverage table offset[" + i + "]: " + bgcoa[i]); } log.debug(tableTag + " chained contextual substitution input glyph count: " + nig); for (int i = 0; i < nig; i++) { log.debug(tableTag + " chained contextual substitution input coverage table offset[" + i + "]: " + igcoa[i]); } log.debug(tableTag + " chained contextual substitution lookahead glyph count: " + nlg); for (int i = 0; i < nlg; i++) { log.debug(tableTag + " chained contextual substitution lookahead coverage table offset[" + i + "]: " + lgcoa[i]); } log.debug(tableTag + " chained contextual substitution lookup count: " + nl); } // read backtrack coverage tables GlyphCoverageTable[] bgca = new GlyphCoverageTable[nbg]; for (int i = 0; i < nbg; i++) { int bgco = bgcoa [ i ]; GlyphCoverageTable bgct; if (bgco > 0) { bgct = readCoverageTable(tableTag + " chained contextual substitution backtrack coverage[" + i + "]", subtableOffset + bgco); } else { bgct = null; } bgca[i] = bgct; } // read input coverage tables GlyphCoverageTable[] igca = new GlyphCoverageTable[nig]; for (int i = 0; i < nig; i++) { int igco = igcoa [ i ]; GlyphCoverageTable igct; if (igco > 0) { igct = readCoverageTable(tableTag + " chained contextual substitution input coverage[" + i + "]", subtableOffset + igco); } else { igct = null; } igca[i] = igct; } // read lookahead coverage tables GlyphCoverageTable[] lgca = new GlyphCoverageTable[nlg]; for (int i = 0; i < nlg; i++) { int lgco = lgcoa [ i ]; GlyphCoverageTable lgct; if (lgco > 0) { lgct = readCoverageTable(tableTag + " chained contextual substitution lookahead coverage[" + i + "]", subtableOffset + lgco); } else { lgct = null; } lgca[i] = lgct; } // read rule lookups String header = null; if (log.isDebugEnabled()) { header = tableTag + " chained contextual substitution lookups: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); // construct rule, rule set, and rule set array GlyphTable.Rule r = new GlyphTable.ChainedCoverageSequenceRule(lookups, nig, igca, bgca, lgca); GlyphTable.RuleSet rs = new GlyphTable.HomogeneousRuleSet(new GlyphTable.Rule[] {r}); GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet[] {rs}; // store results assert (igca != null) && (igca.length > 0); seMapping = igca[0]; seEntries.add(rsa); } private int readChainedContextualSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read substitution subtable format int sf = in.readTTFUShort(); if (sf == 1) { readChainedContextualSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 2) { readChainedContextualSubTableFormat2(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 3) { readChainedContextualSubTableFormat3(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported chained contextual substitution subtable format: " + sf); } return sf; } private void readExtensionSubTableFormat1(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read extension lookup type int lt = in.readTTFUShort(); // read extension offset long eo = in.readTTFULong(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " extension substitution subtable format: " + subtableFormat); log.debug(tableTag + " extension substitution lookup type: " + lt); log.debug(tableTag + " extension substitution lookup table offset: " + eo); } // read referenced subtable from extended offset readGSUBSubtable(lt, lookupFlags, lookupSequence, subtableSequence, subtableOffset + eo); } private int readExtensionSubTable(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read substitution subtable format int sf = in.readTTFUShort(); if (sf == 1) { readExtensionSubTableFormat1(lookupType, lookupFlags, lookupSequence, subtableSequence, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported extension substitution subtable format: " + sf); } return sf; } private void readReverseChainedSingleSubTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GSUB"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read backtrack glyph count int nbg = in.readTTFUShort(); // read backtrack glyph coverage offsets int[] bgcoa = new int [ nbg ]; for (int i = 0; i < nbg; i++) { bgcoa [ i ] = in.readTTFUShort(); } // read lookahead glyph count int nlg = in.readTTFUShort(); // read backtrack glyph coverage offsets int[] lgcoa = new int [ nlg ]; for (int i = 0; i < nlg; i++) { lgcoa [ i ] = in.readTTFUShort(); } // read substitution (output) glyph count int ng = in.readTTFUShort(); // read substitution (output) glyphs int[] glyphs = new int [ ng ]; for (int i = 0, n = ng; i < n; i++) { glyphs [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " reverse chained contextual substitution format: " + subtableFormat); log.debug(tableTag + " reverse chained contextual substitution coverage table offset: " + co); log.debug(tableTag + " reverse chained contextual substitution backtrack glyph count: " + nbg); for (int i = 0; i < nbg; i++) { log.debug(tableTag + " reverse chained contextual substitution backtrack coverage table offset[" + i + "]: " + bgcoa[i]); } log.debug(tableTag + " reverse chained contextual substitution lookahead glyph count: " + nlg); for (int i = 0; i < nlg; i++) { log.debug(tableTag + " reverse chained contextual substitution lookahead coverage table offset[" + i + "]: " + lgcoa[i]); } log.debug(tableTag + " reverse chained contextual substitution glyphs: " + toString(glyphs)); } // read coverage table GlyphCoverageTable ct = readCoverageTable(tableTag + " reverse chained contextual substitution coverage", subtableOffset + co); // read backtrack coverage tables GlyphCoverageTable[] bgca = new GlyphCoverageTable[nbg]; for (int i = 0; i < nbg; i++) { int bgco = bgcoa[i]; GlyphCoverageTable bgct; if (bgco > 0) { bgct = readCoverageTable(tableTag + " reverse chained contextual substitution backtrack coverage[" + i + "]", subtableOffset + bgco); } else { bgct = null; } bgca[i] = bgct; } // read lookahead coverage tables GlyphCoverageTable[] lgca = new GlyphCoverageTable[nlg]; for (int i = 0; i < nlg; i++) { int lgco = lgcoa[i]; GlyphCoverageTable lgct; if (lgco > 0) { lgct = readCoverageTable(tableTag + " reverse chained contextual substitution lookahead coverage[" + i + "]", subtableOffset + lgco); } else { lgct = null; } lgca[i] = lgct; } // store results seMapping = ct; seEntries.add(bgca); seEntries.add(lgca); seEntries.add(glyphs); } private int readReverseChainedSingleSubTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read substitution subtable format int sf = in.readTTFUShort(); if (sf == 1) { readReverseChainedSingleSubTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported reverse chained single substitution subtable format: " + sf); } return sf; } private void readGSUBSubtable(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException { initATSubState(); int subtableFormat = -1; switch (lookupType) { case GSUBLookupType.SINGLE: subtableFormat = readSingleSubTable(lookupType, lookupFlags, subtableOffset); break; case GSUBLookupType.MULTIPLE: subtableFormat = readMultipleSubTable(lookupType, lookupFlags, subtableOffset); break; case GSUBLookupType.ALTERNATE: subtableFormat = readAlternateSubTable(lookupType, lookupFlags, subtableOffset); break; case GSUBLookupType.LIGATURE: subtableFormat = readLigatureSubTable(lookupType, lookupFlags, subtableOffset); break; case GSUBLookupType.CONTEXTUAL: subtableFormat = readContextualSubTable(lookupType, lookupFlags, subtableOffset); break; case GSUBLookupType.CHAINED_CONTEXTUAL: subtableFormat = readChainedContextualSubTable(lookupType, lookupFlags, subtableOffset); break; case GSUBLookupType.REVERSE_CHAINED_SINGLE: subtableFormat = readReverseChainedSingleSubTable(lookupType, lookupFlags, subtableOffset); break; case GSUBLookupType.EXTENSION: subtableFormat = readExtensionSubTable(lookupType, lookupFlags, lookupSequence, subtableSequence, subtableOffset); break; default: break; } extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_SUBSTITUTION, lookupType, lookupFlags, lookupSequence, subtableSequence, subtableFormat); resetATSubState(); } private GlyphPositioningTable.DeviceTable readPosDeviceTable(long subtableOffset, long deviceTableOffset) throws IOException { long cp = in.getCurrentPos(); in.seekSet(subtableOffset + deviceTableOffset); // read start size int ss = in.readTTFUShort(); // read end size int es = in.readTTFUShort(); // read delta format int df = in.readTTFUShort(); int s1; int m1; int dm; int dd; int s2; if (df == 1) { s1 = 14; m1 = 0x3; dm = 1; dd = 4; s2 = 2; } else if (df == 2) { s1 = 12; m1 = 0xF; dm = 7; dd = 16; s2 = 4; } else if (df == 3) { s1 = 8; m1 = 0xFF; dm = 127; dd = 256; s2 = 8; } else { log.debug("unsupported device table delta format: " + df + ", ignoring device table"); return null; } // read deltas int n = (es - ss) + 1; if (n < 0) { log.debug("invalid device table delta count: " + n + ", ignoring device table"); return null; } int[] da = new int [ n ]; for (int i = 0; (i < n) && (s2 > 0);) { int p = in.readTTFUShort(); for (int j = 0, k = 16 / s2; j < k; j++) { int d = (p >> s1) & m1; if (d > dm) { d -= dd; } if (i < n) { da [ i++ ] = d; } else { break; } p <<= s2; } } in.seekSet(cp); return new GlyphPositioningTable.DeviceTable(ss, es, da); } private GlyphPositioningTable.Value readPosValue(long subtableOffset, int valueFormat) throws IOException { // XPlacement int xp; if ((valueFormat & GlyphPositioningTable.Value.X_PLACEMENT) != 0) { xp = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); } else { xp = 0; } // YPlacement int yp; if ((valueFormat & GlyphPositioningTable.Value.Y_PLACEMENT) != 0) { yp = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); } else { yp = 0; } // XAdvance int xa; if ((valueFormat & GlyphPositioningTable.Value.X_ADVANCE) != 0) { xa = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); } else { xa = 0; } // YAdvance int ya; if ((valueFormat & GlyphPositioningTable.Value.Y_ADVANCE) != 0) { ya = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); } else { ya = 0; } // XPlaDevice GlyphPositioningTable.DeviceTable xpd; if ((valueFormat & GlyphPositioningTable.Value.X_PLACEMENT_DEVICE) != 0) { int xpdo = in.readTTFUShort(); xpd = readPosDeviceTable(subtableOffset, xpdo); } else { xpd = null; } // YPlaDevice GlyphPositioningTable.DeviceTable ypd; if ((valueFormat & GlyphPositioningTable.Value.Y_PLACEMENT_DEVICE) != 0) { int ypdo = in.readTTFUShort(); ypd = readPosDeviceTable(subtableOffset, ypdo); } else { ypd = null; } // XAdvDevice GlyphPositioningTable.DeviceTable xad; if ((valueFormat & GlyphPositioningTable.Value.X_ADVANCE_DEVICE) != 0) { int xado = in.readTTFUShort(); xad = readPosDeviceTable(subtableOffset, xado); } else { xad = null; } // YAdvDevice GlyphPositioningTable.DeviceTable yad; if ((valueFormat & GlyphPositioningTable.Value.Y_ADVANCE_DEVICE) != 0) { int yado = in.readTTFUShort(); yad = readPosDeviceTable(subtableOffset, yado); } else { yad = null; } return new GlyphPositioningTable.Value(xp, yp, xa, ya, xpd, ypd, xad, yad); } private void readSinglePosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read value format int vf = in.readTTFUShort(); // read value GlyphPositioningTable.Value v = readPosValue(subtableOffset, vf); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " single positioning subtable format: " + subtableFormat + " (delta)"); log.debug(tableTag + " single positioning coverage table offset: " + co); log.debug(tableTag + " single positioning value: " + v); } // read coverage table GlyphCoverageTable ct = readCoverageTable(tableTag + " single positioning coverage", subtableOffset + co); // store results seMapping = ct; seEntries.add(v); } private void readSinglePosTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read value format int vf = in.readTTFUShort(); // read value count int nv = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " single positioning subtable format: " + subtableFormat + " (mapped)"); log.debug(tableTag + " single positioning coverage table offset: " + co); log.debug(tableTag + " single positioning value count: " + nv); } // read coverage table GlyphCoverageTable ct = readCoverageTable(tableTag + " single positioning coverage", subtableOffset + co); // read positioning values GlyphPositioningTable.Value[] pva = new GlyphPositioningTable.Value[nv]; for (int i = 0, n = nv; i < n; i++) { GlyphPositioningTable.Value pv = readPosValue(subtableOffset, vf); if (log.isDebugEnabled()) { log.debug(tableTag + " single positioning value[" + i + "]: " + pv); } pva[i] = pv; } // store results seMapping = ct; seEntries.add(pva); } private int readSinglePosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positionining subtable format int sf = in.readTTFUShort(); if (sf == 1) { readSinglePosTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 2) { readSinglePosTableFormat2(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported single positioning subtable format: " + sf); } return sf; } private GlyphPositioningTable.PairValues readPosPairValues(long subtableOffset, boolean hasGlyph, int vf1, int vf2) throws IOException { // read glyph (if present) int glyph; if (hasGlyph) { glyph = in.readTTFUShort(); } else { glyph = 0; } // read first value (if present) GlyphPositioningTable.Value v1; if (vf1 != 0) { v1 = readPosValue(subtableOffset, vf1); } else { v1 = null; } // read second value (if present) GlyphPositioningTable.Value v2; if (vf2 != 0) { v2 = readPosValue(subtableOffset, vf2); } else { v2 = null; } return new GlyphPositioningTable.PairValues(glyph, v1, v2); } private GlyphPositioningTable.PairValues[] readPosPairSetTable(long subtableOffset, int pairSetTableOffset, int vf1, int vf2) throws IOException { String tableTag = "GPOS"; long cp = in.getCurrentPos(); in.seekSet(subtableOffset + pairSetTableOffset); // read pair values count int npv = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " pair set table offset: " + pairSetTableOffset); log.debug(tableTag + " pair set table values count: " + npv); } // read pair values GlyphPositioningTable.PairValues[] pva = new GlyphPositioningTable.PairValues [ npv ]; for (int i = 0, n = npv; i < n; i++) { GlyphPositioningTable.PairValues pv = readPosPairValues(subtableOffset, true, vf1, vf2); pva [ i ] = pv; if (log.isDebugEnabled()) { log.debug(tableTag + " pair set table value[" + i + "]: " + pv); } } in.seekSet(cp); return pva; } private void readPairPosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read value format for first glyph int vf1 = in.readTTFUShort(); // read value format for second glyph int vf2 = in.readTTFUShort(); // read number (count) of pair sets int nps = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " pair positioning subtable format: " + subtableFormat + " (glyphs)"); log.debug(tableTag + " pair positioning coverage table offset: " + co); log.debug(tableTag + " pair positioning value format #1: " + vf1); log.debug(tableTag + " pair positioning value format #2: " + vf2); } // read coverage table GlyphCoverageTable ct = readCoverageTable(tableTag + " pair positioning coverage", subtableOffset + co); // read pair value matrix GlyphPositioningTable.PairValues[][] pvm = new GlyphPositioningTable.PairValues [ nps ][]; for (int i = 0, n = nps; i < n; i++) { // read pair set offset int pso = in.readTTFUShort(); // read pair set table at offset pvm [ i ] = readPosPairSetTable(subtableOffset, pso, vf1, vf2); } // store results seMapping = ct; seEntries.add(pvm); } private void readPairPosTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read value format for first glyph int vf1 = in.readTTFUShort(); // read value format for second glyph int vf2 = in.readTTFUShort(); // read class def 1 offset int cd1o = in.readTTFUShort(); // read class def 2 offset int cd2o = in.readTTFUShort(); // read number (count) of classes in class def 1 table int nc1 = in.readTTFUShort(); // read number (count) of classes in class def 2 table int nc2 = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " pair positioning subtable format: " + subtableFormat + " (glyph classes)"); log.debug(tableTag + " pair positioning coverage table offset: " + co); log.debug(tableTag + " pair positioning value format #1: " + vf1); log.debug(tableTag + " pair positioning value format #2: " + vf2); log.debug(tableTag + " pair positioning class def table #1 offset: " + cd1o); log.debug(tableTag + " pair positioning class def table #2 offset: " + cd2o); log.debug(tableTag + " pair positioning class #1 count: " + nc1); log.debug(tableTag + " pair positioning class #2 count: " + nc2); } // read coverage table GlyphCoverageTable ct = readCoverageTable(tableTag + " pair positioning coverage", subtableOffset + co); // read class definition table #1 GlyphClassTable cdt1 = readClassDefTable(tableTag + " pair positioning class definition #1", subtableOffset + cd1o); // read class definition table #2 GlyphClassTable cdt2 = readClassDefTable(tableTag + " pair positioning class definition #2", subtableOffset + cd2o); // read pair value matrix GlyphPositioningTable.PairValues[][] pvm = new GlyphPositioningTable.PairValues [ nc1 ] [ nc2 ]; for (int i = 0; i < nc1; i++) { for (int j = 0; j < nc2; j++) { GlyphPositioningTable.PairValues pv = readPosPairValues(subtableOffset, false, vf1, vf2); pvm [ i ] [ j ] = pv; if (log.isDebugEnabled()) { log.debug(tableTag + " pair set table value[" + i + "][" + j + "]: " + pv); } } } // store results seMapping = ct; seEntries.add(cdt1); seEntries.add(cdt2); seEntries.add(nc1); seEntries.add(nc2); seEntries.add(pvm); } private int readPairPosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positioning subtable format int sf = in.readTTFUShort(); if (sf == 1) { readPairPosTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 2) { readPairPosTableFormat2(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported pair positioning subtable format: " + sf); } return sf; } private GlyphPositioningTable.Anchor readPosAnchor(long anchorTableOffset) throws IOException { GlyphPositioningTable.Anchor a; long cp = in.getCurrentPos(); in.seekSet(anchorTableOffset); // read anchor table format int af = in.readTTFUShort(); if (af == 1) { // read x coordinate int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read y coordinate int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); a = new GlyphPositioningTable.Anchor(x, y); } else if (af == 2) { // read x coordinate int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read y coordinate int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read anchor point index int ap = in.readTTFUShort(); a = new GlyphPositioningTable.Anchor(x, y, ap); } else if (af == 3) { // read x coordinate int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read y coordinate int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort()); // read x device table offset int xdo = in.readTTFUShort(); // read y device table offset int ydo = in.readTTFUShort(); // read x device table (if present) GlyphPositioningTable.DeviceTable xd; if (xdo != 0) { xd = readPosDeviceTable(cp, xdo); } else { xd = null; } // read y device table (if present) GlyphPositioningTable.DeviceTable yd; if (ydo != 0) { yd = readPosDeviceTable(cp, ydo); } else { yd = null; } a = new GlyphPositioningTable.Anchor(x, y, xd, yd); } else { throw new AdvancedTypographicTableFormatException("unsupported positioning anchor format: " + af); } in.seekSet(cp); return a; } private void readCursivePosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read entry/exit count int ec = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " cursive positioning subtable format: " + subtableFormat); log.debug(tableTag + " cursive positioning coverage table offset: " + co); log.debug(tableTag + " cursive positioning entry/exit count: " + ec); } // read coverage table GlyphCoverageTable ct = readCoverageTable(tableTag + " cursive positioning coverage", subtableOffset + co); // read entry/exit records GlyphPositioningTable.Anchor[] aa = new GlyphPositioningTable.Anchor [ ec * 2 ]; for (int i = 0, n = ec; i < n; i++) { // read entry anchor offset int eno = in.readTTFUShort(); // read exit anchor offset int exo = in.readTTFUShort(); // read entry anchor GlyphPositioningTable.Anchor ena; if (eno > 0) { ena = readPosAnchor(subtableOffset + eno); } else { ena = null; } // read exit anchor GlyphPositioningTable.Anchor exa; if (exo > 0) { exa = readPosAnchor(subtableOffset + exo); } else { exa = null; } aa [ (i * 2) + 0 ] = ena; aa [ (i * 2) + 1 ] = exa; if (log.isDebugEnabled()) { if (ena != null) { log.debug(tableTag + " cursive entry anchor [" + i + "]: " + ena); } if (exa != null) { log.debug(tableTag + " cursive exit anchor [" + i + "]: " + exa); } } } // store results seMapping = ct; seEntries.add(aa); } private int readCursivePosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positioning subtable format int sf = in.readTTFUShort(); if (sf == 1) { readCursivePosTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported cursive positioning subtable format: " + sf); } return sf; } private void readMarkToBasePosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read mark coverage offset int mco = in.readTTFUShort(); // read base coverage offset int bco = in.readTTFUShort(); // read mark class count int nmc = in.readTTFUShort(); // read mark array offset int mao = in.readTTFUShort(); // read base array offset int bao = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-base positioning subtable format: " + subtableFormat); log.debug(tableTag + " mark-to-base positioning mark coverage table offset: " + mco); log.debug(tableTag + " mark-to-base positioning base coverage table offset: " + bco); log.debug(tableTag + " mark-to-base positioning mark class count: " + nmc); log.debug(tableTag + " mark-to-base positioning mark array offset: " + mao); log.debug(tableTag + " mark-to-base positioning base array offset: " + bao); } // read mark coverage table GlyphCoverageTable mct = readCoverageTable(tableTag + " mark-to-base positioning mark coverage", subtableOffset + mco); // read base coverage table GlyphCoverageTable bct = readCoverageTable(tableTag + " mark-to-base positioning base coverage", subtableOffset + bco); // read mark anchor array // seek to mark array in.seekSet(subtableOffset + mao); // read mark count int nm = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-base positioning mark count: " + nm); } // read mark anchor array, where i:{0...markCount} GlyphPositioningTable.MarkAnchor[] maa = new GlyphPositioningTable.MarkAnchor [ nm ]; for (int i = 0; i < nm; i++) { // read mark class int mc = in.readTTFUShort(); // read mark anchor offset int ao = in.readTTFUShort(); GlyphPositioningTable.Anchor a; if (ao > 0) { a = readPosAnchor(subtableOffset + mao + ao); } else { a = null; } GlyphPositioningTable.MarkAnchor ma; if (a != null) { ma = new GlyphPositioningTable.MarkAnchor(mc, a); } else { ma = null; } maa [ i ] = ma; if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-base positioning mark anchor[" + i + "]: " + ma); } } // read base anchor matrix // seek to base array in.seekSet(subtableOffset + bao); // read base count int nb = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-base positioning base count: " + nb); } // read anchor matrix, where i:{0...baseCount - 1}, j:{0...markClassCount - 1} GlyphPositioningTable.Anchor[][] bam = new GlyphPositioningTable.Anchor [ nb ] [ nmc ]; for (int i = 0; i < nb; i++) { for (int j = 0; j < nmc; j++) { // read base anchor offset int ao = in.readTTFUShort(); GlyphPositioningTable.Anchor a; if (ao > 0) { a = readPosAnchor(subtableOffset + bao + ao); } else { a = null; } bam [ i ] [ j ] = a; if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-base positioning base anchor[" + i + "][" + j + "]: " + a); } } } // store results seMapping = mct; seEntries.add(bct); seEntries.add(nmc); seEntries.add(maa); seEntries.add(bam); } private int readMarkToBasePosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positioning subtable format int sf = in.readTTFUShort(); if (sf == 1) { readMarkToBasePosTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported mark-to-base positioning subtable format: " + sf); } return sf; } private void readMarkToLigaturePosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read mark coverage offset int mco = in.readTTFUShort(); // read ligature coverage offset int lco = in.readTTFUShort(); // read mark class count int nmc = in.readTTFUShort(); // read mark array offset int mao = in.readTTFUShort(); // read ligature array offset int lao = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-ligature positioning subtable format: " + subtableFormat); log.debug(tableTag + " mark-to-ligature positioning mark coverage table offset: " + mco); log.debug(tableTag + " mark-to-ligature positioning ligature coverage table offset: " + lco); log.debug(tableTag + " mark-to-ligature positioning mark class count: " + nmc); log.debug(tableTag + " mark-to-ligature positioning mark array offset: " + mao); log.debug(tableTag + " mark-to-ligature positioning ligature array offset: " + lao); } // read mark coverage table GlyphCoverageTable mct = readCoverageTable(tableTag + " mark-to-ligature positioning mark coverage", subtableOffset + mco); // read ligature coverage table GlyphCoverageTable lct = readCoverageTable(tableTag + " mark-to-ligature positioning ligature coverage", subtableOffset + lco); // read mark anchor array // seek to mark array in.seekSet(subtableOffset + mao); // read mark count int nm = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-ligature positioning mark count: " + nm); } // read mark anchor array, where i:{0...markCount} GlyphPositioningTable.MarkAnchor[] maa = new GlyphPositioningTable.MarkAnchor [ nm ]; for (int i = 0; i < nm; i++) { // read mark class int mc = in.readTTFUShort(); // read mark anchor offset int ao = in.readTTFUShort(); GlyphPositioningTable.Anchor a; if (ao > 0) { a = readPosAnchor(subtableOffset + mao + ao); } else { a = null; } GlyphPositioningTable.MarkAnchor ma; if (a != null) { ma = new GlyphPositioningTable.MarkAnchor(mc, a); } else { ma = null; } maa [ i ] = ma; if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-ligature positioning mark anchor[" + i + "]: " + ma); } } // read ligature anchor matrix // seek to ligature array in.seekSet(subtableOffset + lao); // read ligature count int nl = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-ligature positioning ligature count: " + nl); } // read ligature attach table offsets int[] laoa = new int [ nl ]; for (int i = 0; i < nl; i++) { laoa [ i ] = in.readTTFUShort(); } // iterate over ligature attach tables, recording maximum component count int mxc = 0; for (int i = 0; i < nl; i++) { int lato = laoa [ i ]; in.seekSet(subtableOffset + lao + lato); // read component count int cc = in.readTTFUShort(); if (cc > mxc) { mxc = cc; } } if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-ligature positioning maximum component count: " + mxc); } // read anchor matrix, where i:{0...ligatureCount - 1}, j:{0...maxComponentCount - 1}, k:{0...markClassCount - 1} GlyphPositioningTable.Anchor[][][] lam = new GlyphPositioningTable.Anchor [ nl ][][]; for (int i = 0; i < nl; i++) { int lato = laoa [ i ]; // seek to ligature attach table for ligature[i] in.seekSet(subtableOffset + lao + lato); // read component count int cc = in.readTTFUShort(); GlyphPositioningTable.Anchor[][] lcm = new GlyphPositioningTable.Anchor [ cc ] [ nmc ]; for (int j = 0; j < cc; j++) { for (int k = 0; k < nmc; k++) { // read ligature anchor offset int ao = in.readTTFUShort(); GlyphPositioningTable.Anchor a; if (ao > 0) { a = readPosAnchor(subtableOffset + lao + lato + ao); } else { a = null; } lcm [ j ] [ k ] = a; if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-ligature positioning ligature anchor[" + i + "][" + j + "][" + k + "]: " + a); } } } lam [ i ] = lcm; } // store results seMapping = mct; seEntries.add(lct); seEntries.add(nmc); seEntries.add(mxc); seEntries.add(maa); seEntries.add(lam); } private int readMarkToLigaturePosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positioning subtable format int sf = in.readTTFUShort(); if (sf == 1) { readMarkToLigaturePosTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported mark-to-ligature positioning subtable format: " + sf); } return sf; } private void readMarkToMarkPosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read mark #1 coverage offset int m1co = in.readTTFUShort(); // read mark #2 coverage offset int m2co = in.readTTFUShort(); // read mark class count int nmc = in.readTTFUShort(); // read mark #1 array offset int m1ao = in.readTTFUShort(); // read mark #2 array offset int m2ao = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-mark positioning subtable format: " + subtableFormat); log.debug(tableTag + " mark-to-mark positioning mark #1 coverage table offset: " + m1co); log.debug(tableTag + " mark-to-mark positioning mark #2 coverage table offset: " + m2co); log.debug(tableTag + " mark-to-mark positioning mark class count: " + nmc); log.debug(tableTag + " mark-to-mark positioning mark #1 array offset: " + m1ao); log.debug(tableTag + " mark-to-mark positioning mark #2 array offset: " + m2ao); } // read mark #1 coverage table GlyphCoverageTable mct1 = readCoverageTable(tableTag + " mark-to-mark positioning mark #1 coverage", subtableOffset + m1co); // read mark #2 coverage table GlyphCoverageTable mct2 = readCoverageTable(tableTag + " mark-to-mark positioning mark #2 coverage", subtableOffset + m2co); // read mark #1 anchor array // seek to mark array in.seekSet(subtableOffset + m1ao); // read mark count int nm1 = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-mark positioning mark #1 count: " + nm1); } // read mark anchor array, where i:{0...mark1Count} GlyphPositioningTable.MarkAnchor[] maa = new GlyphPositioningTable.MarkAnchor [ nm1 ]; for (int i = 0; i < nm1; i++) { // read mark class int mc = in.readTTFUShort(); // read mark anchor offset int ao = in.readTTFUShort(); GlyphPositioningTable.Anchor a; if (ao > 0) { a = readPosAnchor(subtableOffset + m1ao + ao); } else { a = null; } GlyphPositioningTable.MarkAnchor ma; if (a != null) { ma = new GlyphPositioningTable.MarkAnchor(mc, a); } else { ma = null; } maa [ i ] = ma; if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-mark positioning mark #1 anchor[" + i + "]: " + ma); } } // read mark #2 anchor matrix // seek to mark #2 array in.seekSet(subtableOffset + m2ao); // read mark #2 count int nm2 = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-mark positioning mark #2 count: " + nm2); } // read anchor matrix, where i:{0...mark2Count - 1}, j:{0...markClassCount - 1} GlyphPositioningTable.Anchor[][] mam = new GlyphPositioningTable.Anchor [ nm2 ] [ nmc ]; for (int i = 0; i < nm2; i++) { for (int j = 0; j < nmc; j++) { // read mark anchor offset int ao = in.readTTFUShort(); GlyphPositioningTable.Anchor a; if (ao > 0) { a = readPosAnchor(subtableOffset + m2ao + ao); } else { a = null; } mam [ i ] [ j ] = a; if (log.isDebugEnabled()) { log.debug(tableTag + " mark-to-mark positioning mark #2 anchor[" + i + "][" + j + "]: " + a); } } } // store results seMapping = mct1; seEntries.add(mct2); seEntries.add(nmc); seEntries.add(maa); seEntries.add(mam); } private int readMarkToMarkPosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positioning subtable format int sf = in.readTTFUShort(); if (sf == 1) { readMarkToMarkPosTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported mark-to-mark positioning subtable format: " + sf); } return sf; } private void readContextualPosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read rule set count int nrs = in.readTTFUShort(); // read rule set offsets int[] rsoa = new int [ nrs ]; for (int i = 0; i < nrs; i++) { rsoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " contextual positioning subtable format: " + subtableFormat + " (glyphs)"); log.debug(tableTag + " contextual positioning coverage table offset: " + co); log.debug(tableTag + " contextual positioning rule set count: " + nrs); for (int i = 0; i < nrs; i++) { log.debug(tableTag + " contextual positioning rule set offset[" + i + "]: " + rsoa[i]); } } // read coverage table GlyphCoverageTable ct; if (co > 0) { ct = readCoverageTable(tableTag + " contextual positioning coverage", subtableOffset + co); } else { ct = null; } // read rule sets GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ nrs ]; String header = null; for (int i = 0; i < nrs; i++) { GlyphTable.RuleSet rs; int rso = rsoa [ i ]; if (rso > 0) { // seek to rule set [ i ] in.seekSet(subtableOffset + rso); // read rule count int nr = in.readTTFUShort(); // read rule offsets int[] roa = new int [ nr ]; GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ]; for (int j = 0; j < nr; j++) { roa [ j ] = in.readTTFUShort(); } // read glyph sequence rules for (int j = 0; j < nr; j++) { GlyphTable.GlyphSequenceRule r; int ro = roa [ j ]; if (ro > 0) { // seek to rule [ j ] in.seekSet(subtableOffset + rso + ro); // read glyph count int ng = in.readTTFUShort(); // read rule lookup count int nl = in.readTTFUShort(); // read glyphs int[] glyphs = new int [ ng - 1 ]; for (int k = 0, nk = glyphs.length; k < nk; k++) { glyphs [ k ] = in.readTTFUShort(); } // read rule lookups if (log.isDebugEnabled()) { header = tableTag + " contextual positioning lookups @rule[" + i + "][" + j + "]: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); r = new GlyphTable.GlyphSequenceRule(lookups, ng, glyphs); } else { r = null; } ra [ j ] = r; } rs = new GlyphTable.HomogeneousRuleSet(ra); } else { rs = null; } rsa [ i ] = rs; } // store results seMapping = ct; seEntries.add(rsa); } private void readContextualPosTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read class def table offset int cdo = in.readTTFUShort(); // read class rule set count int ngc = in.readTTFUShort(); // read class rule set offsets int[] csoa = new int [ ngc ]; for (int i = 0; i < ngc; i++) { csoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " contextual positioning subtable format: " + subtableFormat + " (glyph classes)"); log.debug(tableTag + " contextual positioning coverage table offset: " + co); log.debug(tableTag + " contextual positioning class set count: " + ngc); for (int i = 0; i < ngc; i++) { log.debug(tableTag + " contextual positioning class set offset[" + i + "]: " + csoa[i]); } } // read coverage table GlyphCoverageTable ct; if (co > 0) { ct = readCoverageTable(tableTag + " contextual positioning coverage", subtableOffset + co); } else { ct = null; } // read class definition table GlyphClassTable cdt; if (cdo > 0) { cdt = readClassDefTable(tableTag + " contextual positioning class definition", subtableOffset + cdo); } else { cdt = null; } // read rule sets GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ ngc ]; String header = null; for (int i = 0; i < ngc; i++) { int cso = csoa [ i ]; GlyphTable.RuleSet rs; if (cso > 0) { // seek to rule set [ i ] in.seekSet(subtableOffset + cso); // read rule count int nr = in.readTTFUShort(); // read rule offsets int[] roa = new int [ nr ]; GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ]; for (int j = 0; j < nr; j++) { roa [ j ] = in.readTTFUShort(); } // read glyph sequence rules for (int j = 0; j < nr; j++) { int ro = roa [ j ]; GlyphTable.ClassSequenceRule r; if (ro > 0) { // seek to rule [ j ] in.seekSet(subtableOffset + cso + ro); // read glyph count int ng = in.readTTFUShort(); // read rule lookup count int nl = in.readTTFUShort(); // read classes int[] classes = new int [ ng - 1 ]; for (int k = 0, nk = classes.length; k < nk; k++) { classes [ k ] = in.readTTFUShort(); } // read rule lookups if (log.isDebugEnabled()) { header = tableTag + " contextual positioning lookups @rule[" + i + "][" + j + "]: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); r = new GlyphTable.ClassSequenceRule(lookups, ng, classes); } else { r = null; } ra [ j ] = r; } rs = new GlyphTable.HomogeneousRuleSet(ra); } else { rs = null; } rsa [ i ] = rs; } // store results seMapping = ct; seEntries.add(cdt); seEntries.add(ngc); seEntries.add(rsa); } private void readContextualPosTableFormat3(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read glyph (input sequence length) count int ng = in.readTTFUShort(); // read positioning lookup count int nl = in.readTTFUShort(); // read glyph coverage offsets, one per glyph input sequence length count int[] gcoa = new int [ ng ]; for (int i = 0; i < ng; i++) { gcoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " contextual positioning subtable format: " + subtableFormat + " (glyph sets)"); log.debug(tableTag + " contextual positioning glyph input sequence length count: " + ng); log.debug(tableTag + " contextual positioning lookup count: " + nl); for (int i = 0; i < ng; i++) { log.debug(tableTag + " contextual positioning coverage table offset[" + i + "]: " + gcoa[i]); } } // read coverage tables GlyphCoverageTable[] gca = new GlyphCoverageTable [ ng ]; for (int i = 0; i < ng; i++) { int gco = gcoa [ i ]; GlyphCoverageTable gct; if (gco > 0) { gct = readCoverageTable(tableTag + " contextual positioning coverage[" + i + "]", subtableOffset + gcoa[i]); } else { gct = null; } gca [ i ] = gct; } // read rule lookups String header = null; if (log.isDebugEnabled()) { header = tableTag + " contextual positioning lookups: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); // construct rule, rule set, and rule set array GlyphTable.Rule r = new GlyphTable.CoverageSequenceRule(lookups, ng, gca); GlyphTable.RuleSet rs = new GlyphTable.HomogeneousRuleSet(new GlyphTable.Rule[] {r}); GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet[] {rs}; // store results assert (gca != null) && (gca.length > 0); seMapping = gca[0]; seEntries.add(rsa); } private int readContextualPosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positioning subtable format int sf = in.readTTFUShort(); if (sf == 1) { readContextualPosTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 2) { readContextualPosTableFormat2(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 3) { readContextualPosTableFormat3(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported contextual positioning subtable format: " + sf); } return sf; } private void readChainedContextualPosTableFormat1(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read rule set count int nrs = in.readTTFUShort(); // read rule set offsets int[] rsoa = new int [ nrs ]; for (int i = 0; i < nrs; i++) { rsoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " chained contextual positioning subtable format: " + subtableFormat + " (glyphs)"); log.debug(tableTag + " chained contextual positioning coverage table offset: " + co); log.debug(tableTag + " chained contextual positioning rule set count: " + nrs); for (int i = 0; i < nrs; i++) { log.debug(tableTag + " chained contextual positioning rule set offset[" + i + "]: " + rsoa[i]); } } // read coverage table GlyphCoverageTable ct; if (co > 0) { ct = readCoverageTable(tableTag + " chained contextual positioning coverage", subtableOffset + co); } else { ct = null; } // read rule sets GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ nrs ]; String header = null; for (int i = 0; i < nrs; i++) { GlyphTable.RuleSet rs; int rso = rsoa [ i ]; if (rso > 0) { // seek to rule set [ i ] in.seekSet(subtableOffset + rso); // read rule count int nr = in.readTTFUShort(); // read rule offsets int[] roa = new int [ nr ]; GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ]; for (int j = 0; j < nr; j++) { roa [ j ] = in.readTTFUShort(); } // read glyph sequence rules for (int j = 0; j < nr; j++) { GlyphTable.ChainedGlyphSequenceRule r; int ro = roa [ j ]; if (ro > 0) { // seek to rule [ j ] in.seekSet(subtableOffset + rso + ro); // read backtrack glyph count int nbg = in.readTTFUShort(); // read backtrack glyphs int[] backtrackGlyphs = new int [ nbg ]; for (int k = 0, nk = backtrackGlyphs.length; k < nk; k++) { backtrackGlyphs [ k ] = in.readTTFUShort(); } // read input glyph count int nig = in.readTTFUShort(); // read glyphs int[] glyphs = new int [ nig - 1 ]; for (int k = 0, nk = glyphs.length; k < nk; k++) { glyphs [ k ] = in.readTTFUShort(); } // read lookahead glyph count int nlg = in.readTTFUShort(); // read lookahead glyphs int[] lookaheadGlyphs = new int [ nlg ]; for (int k = 0, nk = lookaheadGlyphs.length; k < nk; k++) { lookaheadGlyphs [ k ] = in.readTTFUShort(); } // read rule lookup count int nl = in.readTTFUShort(); // read rule lookups if (log.isDebugEnabled()) { header = tableTag + " contextual positioning lookups @rule[" + i + "][" + j + "]: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); r = new GlyphTable.ChainedGlyphSequenceRule(lookups, nig, glyphs, backtrackGlyphs, lookaheadGlyphs); } else { r = null; } ra [ j ] = r; } rs = new GlyphTable.HomogeneousRuleSet(ra); } else { rs = null; } rsa [ i ] = rs; } // store results seMapping = ct; seEntries.add(rsa); } private void readChainedContextualPosTableFormat2(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read coverage offset int co = in.readTTFUShort(); // read backtrack class def table offset int bcdo = in.readTTFUShort(); // read input class def table offset int icdo = in.readTTFUShort(); // read lookahead class def table offset int lcdo = in.readTTFUShort(); // read class set count int ngc = in.readTTFUShort(); // read class set offsets int[] csoa = new int [ ngc ]; for (int i = 0; i < ngc; i++) { csoa [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " chained contextual positioning subtable format: " + subtableFormat + " (glyph classes)"); log.debug(tableTag + " chained contextual positioning coverage table offset: " + co); log.debug(tableTag + " chained contextual positioning class set count: " + ngc); for (int i = 0; i < ngc; i++) { log.debug(tableTag + " chained contextual positioning class set offset[" + i + "]: " + csoa[i]); } } // read coverage table GlyphCoverageTable ct; if (co > 0) { ct = readCoverageTable(tableTag + " chained contextual positioning coverage", subtableOffset + co); } else { ct = null; } // read backtrack class definition table GlyphClassTable bcdt; if (bcdo > 0) { bcdt = readClassDefTable(tableTag + " contextual positioning backtrack class definition", subtableOffset + bcdo); } else { bcdt = null; } // read input class definition table GlyphClassTable icdt; if (icdo > 0) { icdt = readClassDefTable(tableTag + " contextual positioning input class definition", subtableOffset + icdo); } else { icdt = null; } // read lookahead class definition table GlyphClassTable lcdt; if (lcdo > 0) { lcdt = readClassDefTable(tableTag + " contextual positioning lookahead class definition", subtableOffset + lcdo); } else { lcdt = null; } // read rule sets GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet [ ngc ]; String header = null; for (int i = 0; i < ngc; i++) { int cso = csoa [ i ]; GlyphTable.RuleSet rs; if (cso > 0) { // seek to rule set [ i ] in.seekSet(subtableOffset + cso); // read rule count int nr = in.readTTFUShort(); // read rule offsets int[] roa = new int [ nr ]; GlyphTable.Rule[] ra = new GlyphTable.Rule [ nr ]; for (int j = 0; j < nr; j++) { roa [ j ] = in.readTTFUShort(); } // read glyph sequence rules for (int j = 0; j < nr; j++) { GlyphTable.ChainedClassSequenceRule r; int ro = roa [ j ]; if (ro > 0) { // seek to rule [ j ] in.seekSet(subtableOffset + cso + ro); // read backtrack glyph class count int nbc = in.readTTFUShort(); // read backtrack glyph classes int[] backtrackClasses = new int [ nbc ]; for (int k = 0, nk = backtrackClasses.length; k < nk; k++) { backtrackClasses [ k ] = in.readTTFUShort(); } // read input glyph class count int nic = in.readTTFUShort(); // read input glyph classes int[] classes = new int [ nic - 1 ]; for (int k = 0, nk = classes.length; k < nk; k++) { classes [ k ] = in.readTTFUShort(); } // read lookahead glyph class count int nlc = in.readTTFUShort(); // read lookahead glyph classes int[] lookaheadClasses = new int [ nlc ]; for (int k = 0, nk = lookaheadClasses.length; k < nk; k++) { lookaheadClasses [ k ] = in.readTTFUShort(); } // read rule lookup count int nl = in.readTTFUShort(); // read rule lookups if (log.isDebugEnabled()) { header = tableTag + " contextual positioning lookups @rule[" + i + "][" + j + "]: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); r = new GlyphTable.ChainedClassSequenceRule(lookups, nic, classes, backtrackClasses, lookaheadClasses); } else { r = null; } ra [ j ] = r; } rs = new GlyphTable.HomogeneousRuleSet(ra); } else { rs = null; } rsa [ i ] = rs; } // store results seMapping = ct; seEntries.add(icdt); seEntries.add(bcdt); seEntries.add(lcdt); seEntries.add(ngc); seEntries.add(rsa); } private void readChainedContextualPosTableFormat3(int lookupType, int lookupFlags, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read backtrack glyph count int nbg = in.readTTFUShort(); // read backtrack glyph coverage offsets int[] bgcoa = new int [ nbg ]; for (int i = 0; i < nbg; i++) { bgcoa [ i ] = in.readTTFUShort(); } // read input glyph count int nig = in.readTTFUShort(); // read backtrack glyph coverage offsets int[] igcoa = new int [ nig ]; for (int i = 0; i < nig; i++) { igcoa [ i ] = in.readTTFUShort(); } // read lookahead glyph count int nlg = in.readTTFUShort(); // read backtrack glyph coverage offsets int[] lgcoa = new int [ nlg ]; for (int i = 0; i < nlg; i++) { lgcoa [ i ] = in.readTTFUShort(); } // read positioning lookup count int nl = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " chained contextual positioning subtable format: " + subtableFormat + " (glyph sets)"); log.debug(tableTag + " chained contextual positioning backtrack glyph count: " + nbg); for (int i = 0; i < nbg; i++) { log.debug(tableTag + " chained contextual positioning backtrack coverage table offset[" + i + "]: " + bgcoa[i]); } log.debug(tableTag + " chained contextual positioning input glyph count: " + nig); for (int i = 0; i < nig; i++) { log.debug(tableTag + " chained contextual positioning input coverage table offset[" + i + "]: " + igcoa[i]); } log.debug(tableTag + " chained contextual positioning lookahead glyph count: " + nlg); for (int i = 0; i < nlg; i++) { log.debug(tableTag + " chained contextual positioning lookahead coverage table offset[" + i + "]: " + lgcoa[i]); } log.debug(tableTag + " chained contextual positioning lookup count: " + nl); } // read backtrack coverage tables GlyphCoverageTable[] bgca = new GlyphCoverageTable[nbg]; for (int i = 0; i < nbg; i++) { int bgco = bgcoa [ i ]; GlyphCoverageTable bgct; if (bgco > 0) { bgct = readCoverageTable(tableTag + " chained contextual positioning backtrack coverage[" + i + "]", subtableOffset + bgco); } else { bgct = null; } bgca[i] = bgct; } // read input coverage tables GlyphCoverageTable[] igca = new GlyphCoverageTable[nig]; for (int i = 0; i < nig; i++) { int igco = igcoa [ i ]; GlyphCoverageTable igct; if (igco > 0) { igct = readCoverageTable(tableTag + " chained contextual positioning input coverage[" + i + "]", subtableOffset + igco); } else { igct = null; } igca[i] = igct; } // read lookahead coverage tables GlyphCoverageTable[] lgca = new GlyphCoverageTable[nlg]; for (int i = 0; i < nlg; i++) { int lgco = lgcoa [ i ]; GlyphCoverageTable lgct; if (lgco > 0) { lgct = readCoverageTable(tableTag + " chained contextual positioning lookahead coverage[" + i + "]", subtableOffset + lgco); } else { lgct = null; } lgca[i] = lgct; } // read rule lookups String header = null; if (log.isDebugEnabled()) { header = tableTag + " chained contextual positioning lookups: "; } GlyphTable.RuleLookup[] lookups = readRuleLookups(nl, header); // construct rule, rule set, and rule set array GlyphTable.Rule r = new GlyphTable.ChainedCoverageSequenceRule(lookups, nig, igca, bgca, lgca); GlyphTable.RuleSet rs = new GlyphTable.HomogeneousRuleSet(new GlyphTable.Rule[] {r}); GlyphTable.RuleSet[] rsa = new GlyphTable.RuleSet[] {rs}; // store results assert (igca != null) && (igca.length > 0); seMapping = igca[0]; seEntries.add(rsa); } private int readChainedContextualPosTable(int lookupType, int lookupFlags, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positioning subtable format int sf = in.readTTFUShort(); if (sf == 1) { readChainedContextualPosTableFormat1(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 2) { readChainedContextualPosTableFormat2(lookupType, lookupFlags, subtableOffset, sf); } else if (sf == 3) { readChainedContextualPosTableFormat3(lookupType, lookupFlags, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported chained contextual positioning subtable format: " + sf); } return sf; } private void readExtensionPosTableFormat1(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset, int subtableFormat) throws IOException { String tableTag = "GPOS"; in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read extension lookup type int lt = in.readTTFUShort(); // read extension offset long eo = in.readTTFULong(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " extension positioning subtable format: " + subtableFormat); log.debug(tableTag + " extension positioning lookup type: " + lt); log.debug(tableTag + " extension positioning lookup table offset: " + eo); } // read referenced subtable from extended offset readGPOSSubtable(lt, lookupFlags, lookupSequence, subtableSequence, subtableOffset + eo); } private int readExtensionPosTable(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read positioning subtable format int sf = in.readTTFUShort(); if (sf == 1) { readExtensionPosTableFormat1(lookupType, lookupFlags, lookupSequence, subtableSequence, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported extension positioning subtable format: " + sf); } return sf; } private void readGPOSSubtable(int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, long subtableOffset) throws IOException { initATSubState(); int subtableFormat = -1; switch (lookupType) { case GPOSLookupType.SINGLE: subtableFormat = readSinglePosTable(lookupType, lookupFlags, subtableOffset); break; case GPOSLookupType.PAIR: subtableFormat = readPairPosTable(lookupType, lookupFlags, subtableOffset); break; case GPOSLookupType.CURSIVE: subtableFormat = readCursivePosTable(lookupType, lookupFlags, subtableOffset); break; case GPOSLookupType.MARK_TO_BASE: subtableFormat = readMarkToBasePosTable(lookupType, lookupFlags, subtableOffset); break; case GPOSLookupType.MARK_TO_LIGATURE: subtableFormat = readMarkToLigaturePosTable(lookupType, lookupFlags, subtableOffset); break; case GPOSLookupType.MARK_TO_MARK: subtableFormat = readMarkToMarkPosTable(lookupType, lookupFlags, subtableOffset); break; case GPOSLookupType.CONTEXTUAL: subtableFormat = readContextualPosTable(lookupType, lookupFlags, subtableOffset); break; case GPOSLookupType.CHAINED_CONTEXTUAL: subtableFormat = readChainedContextualPosTable(lookupType, lookupFlags, subtableOffset); break; case GPOSLookupType.EXTENSION: subtableFormat = readExtensionPosTable(lookupType, lookupFlags, lookupSequence, subtableSequence, subtableOffset); break; default: break; } extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_POSITIONING, lookupType, lookupFlags, lookupSequence, subtableSequence, subtableFormat); resetATSubState(); } private void readLookupTable(OFTableName tableTag, int lookupSequence, long lookupTable) throws IOException { boolean isGSUB = tableTag.equals(OFTableName.GSUB); boolean isGPOS = tableTag.equals(OFTableName.GPOS); in.seekSet(lookupTable); // read lookup type int lt = in.readTTFUShort(); // read lookup flags int lf = in.readTTFUShort(); // read sub-table count int ns = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { String lts; if (isGSUB) { lts = GSUBLookupType.toString(lt); } else if (isGPOS) { lts = GPOSLookupType.toString(lt); } else { lts = "?"; } log.debug(tableTag + " lookup table type: " + lt + " (" + lts + ")"); log.debug(tableTag + " lookup table flags: " + lf + " (" + LookupFlag.toString(lf) + ")"); log.debug(tableTag + " lookup table subtable count: " + ns); } // read subtable offsets int[] soa = new int[ns]; for (int i = 0; i < ns; i++) { int so = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " lookup table subtable offset: " + so); } soa[i] = so; } // read mark filtering set if ((lf & LookupFlag.USE_MARK_FILTERING_SET) != 0) { // read mark filtering set int fs = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " lookup table mark filter set: " + fs); } } // read subtables for (int i = 0; i < ns; i++) { int so = soa[i]; if (isGSUB) { readGSUBSubtable(lt, lf, lookupSequence, i, lookupTable + so); } else if (isGPOS) { readGPOSSubtable(lt, lf, lookupSequence, i, lookupTable + so); } } } private void readLookupList(OFTableName tableTag, long lookupList) throws IOException { in.seekSet(lookupList); // read lookup record count int nl = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " lookup list record count: " + nl); } if (nl > 0) { int[] loa = new int[nl]; // read lookup records for (int i = 0, n = nl; i < n; i++) { int lo = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " lookup table offset: " + lo); } loa[i] = lo; } // read lookup tables for (int i = 0, n = nl; i < n; i++) { if (log.isDebugEnabled()) { log.debug(tableTag + " lookup index: " + i); } readLookupTable(tableTag, i, lookupList + loa [ i ]); } } } /** * Read the common layout tables (used by GSUB and GPOS). * @param tableTag tag of table being read * @param scriptList offset to script list from beginning of font file * @param featureList offset to feature list from beginning of font file * @param lookupList offset to lookup list from beginning of font file * @throws IOException In case of a I/O problem */ private void readCommonLayoutTables(OFTableName tableTag, long scriptList, long featureList, long lookupList) throws IOException { if (scriptList > 0) { readScriptList(tableTag, scriptList); } if (featureList > 0) { readFeatureList(tableTag, featureList); } if (lookupList > 0) { readLookupList(tableTag, lookupList); } } private void readGDEFClassDefTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { initATSubState(); in.seekSet(subtableOffset); // subtable is a bare class definition table GlyphClassTable ct = readClassDefTable(tableTag + " glyph class definition table", subtableOffset); // store results seMapping = ct; // extract subtable extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.GLYPH_CLASS, 0, lookupSequence, 0, 1); resetATSubState(); } private void readGDEFAttachmentTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { initATSubState(); in.seekSet(subtableOffset); // read coverage offset int co = in.readTTFUShort(); // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " attachment point coverage table offset: " + co); } // read coverage table GlyphCoverageTable ct = readCoverageTable(tableTag + " attachment point coverage", subtableOffset + co); // store results seMapping = ct; // extract subtable extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.ATTACHMENT_POINT, 0, lookupSequence, 0, 1); resetATSubState(); } private void readGDEFLigatureCaretTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { initATSubState(); in.seekSet(subtableOffset); // read coverage offset int co = in.readTTFUShort(); // read ligature glyph count int nl = in.readTTFUShort(); // read ligature glyph table offsets int[] lgto = new int [ nl ]; for (int i = 0; i < nl; i++) { lgto [ i ] = in.readTTFUShort(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " ligature caret coverage table offset: " + co); log.debug(tableTag + " ligature caret ligature glyph count: " + nl); for (int i = 0; i < nl; i++) { log.debug(tableTag + " ligature glyph table offset[" + i + "]: " + lgto[i]); } } // read coverage table GlyphCoverageTable ct = readCoverageTable(tableTag + " ligature caret coverage", subtableOffset + co); // store results seMapping = ct; // extract subtable extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.LIGATURE_CARET, 0, lookupSequence, 0, 1); resetATSubState(); } private void readGDEFMarkAttachmentTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { initATSubState(); in.seekSet(subtableOffset); // subtable is a bare class definition table GlyphClassTable ct = readClassDefTable(tableTag + " glyph class definition table", subtableOffset); // store results seMapping = ct; // extract subtable extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.MARK_ATTACHMENT, 0, lookupSequence, 0, 1); resetATSubState(); } private void readGDEFMarkGlyphsTableFormat1(OFTableName tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException { initATSubState(); in.seekSet(subtableOffset); // skip over format (already known) in.skip(2); // read mark set class count int nmc = in.readTTFUShort(); long[] mso = new long [ nmc ]; // read mark set coverage offsets for (int i = 0; i < nmc; i++) { mso [ i ] = in.readTTFULong(); } // dump info if debugging if (log.isDebugEnabled()) { log.debug(tableTag + " mark set subtable format: " + subtableFormat + " (glyph sets)"); log.debug(tableTag + " mark set class count: " + nmc); for (int i = 0; i < nmc; i++) { log.debug(tableTag + " mark set coverage table offset[" + i + "]: " + mso[i]); } } // read mark set coverage tables, one per class GlyphCoverageTable[] msca = new GlyphCoverageTable[nmc]; for (int i = 0; i < nmc; i++) { msca[i] = readCoverageTable(tableTag + " mark set coverage[" + i + "]", subtableOffset + mso[i]); } // create combined class table from per-class coverage tables GlyphClassTable ct = GlyphClassTable.createClassTable(Arrays.asList(msca)); // store results seMapping = ct; // extract subtable extractSESubState(GlyphTable.GLYPH_TABLE_TYPE_DEFINITION, GDEFLookupType.MARK_ATTACHMENT, 0, lookupSequence, 0, 1); resetATSubState(); } private void readGDEFMarkGlyphsTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException { in.seekSet(subtableOffset); // read mark set subtable format int sf = in.readTTFUShort(); if (sf == 1) { readGDEFMarkGlyphsTableFormat1(tableTag, lookupSequence, subtableOffset, sf); } else { throw new AdvancedTypographicTableFormatException("unsupported mark glyph sets subtable format: " + sf); } } /** * Read the GDEF table. * @throws IOException In case of a I/O problem */ private void readGDEF() throws IOException { OFTableName tableTag = OFTableName.GDEF; // Initialize temporary state initATState(); // Read glyph definition (GDEF) table OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag); if (gdef != null) { if (log.isDebugEnabled()) { log.debug(tableTag + ": ignoring duplicate table"); } } else if (dirTab != null) { otf.seekTab(in, tableTag, 0); long version = in.readTTFULong(); if (log.isDebugEnabled()) { log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536)); } // glyph class definition table offset (may be null) int cdo = in.readTTFUShort(); // attach point list offset (may be null) int apo = in.readTTFUShort(); // ligature caret list offset (may be null) int lco = in.readTTFUShort(); // mark attach class definition table offset (may be null) int mao = in.readTTFUShort(); // mark glyph sets definition table offset (may be null) int mgo; if (version >= 0x00010002) { mgo = in.readTTFUShort(); } else { mgo = 0; } if (log.isDebugEnabled()) { log.debug(tableTag + " glyph class definition table offset: " + cdo); log.debug(tableTag + " attachment point list offset: " + apo); log.debug(tableTag + " ligature caret list offset: " + lco); log.debug(tableTag + " mark attachment class definition table offset: " + mao); log.debug(tableTag + " mark glyph set definitions table offset: " + mgo); } // initialize subtable sequence number int seqno = 0; // obtain offset to start of gdef table long to = dirTab.getOffset(); // (optionally) read glyph class definition subtable if (cdo != 0) { readGDEFClassDefTable(tableTag, seqno++, to + cdo); } // (optionally) read glyph attachment point subtable if (apo != 0) { readGDEFAttachmentTable(tableTag, seqno++, to + apo); } // (optionally) read ligature caret subtable if (lco != 0) { readGDEFLigatureCaretTable(tableTag, seqno++, to + lco); } // (optionally) read mark attachment class subtable if (mao != 0) { readGDEFMarkAttachmentTable(tableTag, seqno++, to + mao); } // (optionally) read mark glyph sets subtable if (mgo != 0) { readGDEFMarkGlyphsTable(tableTag, seqno++, to + mgo); } GlyphDefinitionTable gdef; if ((gdef = constructGDEF()) != null) { this.gdef = gdef; } } } /** * Read the GSUB table. * @throws IOException In case of a I/O problem */ private void readGSUB() throws IOException { OFTableName tableTag = OFTableName.GSUB; // Initialize temporary state initATState(); // Read glyph substitution (GSUB) table OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag); if (gpos != null) { if (log.isDebugEnabled()) { log.debug(tableTag + ": ignoring duplicate table"); } } else if (dirTab != null) { otf.seekTab(in, tableTag, 0); int version = in.readTTFLong(); if (log.isDebugEnabled()) { log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536)); } int slo = in.readTTFUShort(); int flo = in.readTTFUShort(); int llo = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " script list offset: " + slo); log.debug(tableTag + " feature list offset: " + flo); log.debug(tableTag + " lookup list offset: " + llo); } long to = dirTab.getOffset(); readCommonLayoutTables(tableTag, to + slo, to + flo, to + llo); GlyphSubstitutionTable gsub; if ((gsub = constructGSUB()) != null) { this.gsub = gsub; } } } /** * Read the GPOS table. * @throws IOException In case of a I/O problem */ private void readGPOS() throws IOException { OFTableName tableTag = OFTableName.GPOS; // Initialize temporary state initATState(); // Read glyph positioning (GPOS) table OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag); if (gpos != null) { if (log.isDebugEnabled()) { log.debug(tableTag + ": ignoring duplicate table"); } } else if (dirTab != null) { otf.seekTab(in, tableTag, 0); int version = in.readTTFLong(); if (log.isDebugEnabled()) { log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536)); } int slo = in.readTTFUShort(); int flo = in.readTTFUShort(); int llo = in.readTTFUShort(); if (log.isDebugEnabled()) { log.debug(tableTag + " script list offset: " + slo); log.debug(tableTag + " feature list offset: " + flo); log.debug(tableTag + " lookup list offset: " + llo); } long to = dirTab.getOffset(); readCommonLayoutTables(tableTag, to + slo, to + flo, to + llo); GlyphPositioningTable gpos; if ((gpos = constructGPOS()) != null) { this.gpos = gpos; } } } /** * Construct the (internal representation of the) GDEF table based on previously * parsed state. * @returns glyph definition table or null if insufficient or invalid state */ private GlyphDefinitionTable constructGDEF() { GlyphDefinitionTable gdef = null; List subtables; if ((subtables = constructGDEFSubtables()) != null) { if (subtables.size() > 0) { gdef = new GlyphDefinitionTable(subtables, processors); } } resetATState(); return gdef; } /** * Construct the (internal representation of the) GSUB table based on previously * parsed state. * @returns glyph substitution table or null if insufficient or invalid state */ private GlyphSubstitutionTable constructGSUB() { GlyphSubstitutionTable gsub = null; Map lookups; if ((lookups = constructLookups()) != null) { List subtables; if ((subtables = constructGSUBSubtables()) != null) { if ((lookups.size() > 0) && (subtables.size() > 0)) { gsub = new GlyphSubstitutionTable(gdef, lookups, subtables, processors); } } } resetATState(); return gsub; } /** * Construct the (internal representation of the) GPOS table based on previously * parsed state. * @returns glyph positioning table or null if insufficient or invalid state */ private GlyphPositioningTable constructGPOS() { GlyphPositioningTable gpos = null; Map lookups; if ((lookups = constructLookups()) != null) { List subtables; if ((subtables = constructGPOSSubtables()) != null) { if ((lookups.size() > 0) && (subtables.size() > 0)) { gpos = new GlyphPositioningTable(gdef, lookups, subtables, processors); } } } resetATState(); return gpos; } private void constructLookupsFeature(Map lookups, String st, String lt, String fid) { Object[] fp = (Object[]) seFeatures.get(fid); if (fp != null) { assert fp.length == 2; String ft = (String) fp[0]; // feature tag List<String> lul = (List) fp[1]; // list of lookup table ids if ((ft != null) && (lul != null) && (lul.size() > 0)) { GlyphTable.LookupSpec ls = new GlyphTable.LookupSpec(st, lt, ft); lookups.put(ls, lul); } } } private void constructLookupsFeatures(Map lookups, String st, String lt, List<String> fids) { for (Object fid1 : fids) { String fid = (String) fid1; constructLookupsFeature(lookups, st, lt, fid); } } private void constructLookupsLanguage(Map lookups, String st, String lt, Map<String, Object> languages) { Object[] lp = (Object[]) languages.get(lt); if (lp != null) { assert lp.length == 2; if (lp[0] != null) { // required feature id constructLookupsFeature(lookups, st, lt, (String) lp[0]); } if (lp[1] != null) { // non-required features ids constructLookupsFeatures(lookups, st, lt, (List) lp[1]); } } } private void constructLookupsLanguages(Map lookups, String st, List<String> ll, Map<String, Object> languages) { for (Object aLl : ll) { String lt = (String) aLl; constructLookupsLanguage(lookups, st, lt, languages); } } private Map constructLookups() { Map<GlyphTable.LookupSpec, List<String>> lookups = new java.util.LinkedHashMap(); for (Object o : seScripts.keySet()) { String st = (String) o; Object[] sp = (Object[]) seScripts.get(st); if (sp != null) { assert sp.length == 3; Map<String, Object> languages = (Map) sp[2]; if (sp[0] != null) { // default language constructLookupsLanguage(lookups, st, (String) sp[0], languages); } if (sp[1] != null) { // non-default languages constructLookupsLanguages(lookups, st, (List) sp[1], languages); } } } return lookups; } private List constructGDEFSubtables() { List<GlyphSubtable> subtables = new java.util.ArrayList(); if (seSubtables != null) { for (Object seSubtable : seSubtables) { Object[] stp = (Object[]) seSubtable; GlyphSubtable st; if ((st = constructGDEFSubtable(stp)) != null) { subtables.add(st); } } } return subtables; } private GlyphSubtable constructGDEFSubtable(Object[] stp) { GlyphSubtable st = null; assert (stp != null) && (stp.length == 8); Integer tt = (Integer) stp[0]; // table type Integer lt = (Integer) stp[1]; // lookup type Integer ln = (Integer) stp[2]; // lookup sequence number Integer lf = (Integer) stp[3]; // lookup flags Integer sn = (Integer) stp[4]; // subtable sequence number Integer sf = (Integer) stp[5]; // subtable format GlyphMappingTable mapping = (GlyphMappingTable) stp[6]; List entries = (List) stp[7]; if (tt == GlyphTable.GLYPH_TABLE_TYPE_DEFINITION) { int type = GDEFLookupType.getSubtableType(lt); String lid = "lu" + ln; int sequence = sn; int flags = lf; int format = sf; st = GlyphDefinitionTable.createSubtable(type, lid, sequence, flags, format, mapping, entries); } return st; } private List constructGSUBSubtables() { List<GlyphSubtable> subtables = new java.util.ArrayList(); if (seSubtables != null) { for (Object seSubtable : seSubtables) { Object[] stp = (Object[]) seSubtable; GlyphSubtable st; if ((st = constructGSUBSubtable(stp)) != null) { subtables.add(st); } } } return subtables; } private GlyphSubtable constructGSUBSubtable(Object[] stp) { GlyphSubtable st = null; assert (stp != null) && (stp.length == 8); Integer tt = (Integer) stp[0]; // table type Integer lt = (Integer) stp[1]; // lookup type Integer ln = (Integer) stp[2]; // lookup sequence number Integer lf = (Integer) stp[3]; // lookup flags Integer sn = (Integer) stp[4]; // subtable sequence number Integer sf = (Integer) stp[5]; // subtable format GlyphCoverageTable coverage = (GlyphCoverageTable) stp[6]; List entries = (List) stp[7]; if (tt == GlyphTable.GLYPH_TABLE_TYPE_SUBSTITUTION) { int type = GSUBLookupType.getSubtableType(lt); String lid = "lu" + ln; int sequence = sn; int flags = lf; int format = sf; st = GlyphSubstitutionTable.createSubtable(type, lid, sequence, flags, format, coverage, entries); } return st; } private List constructGPOSSubtables() { List<GlyphSubtable> subtables = new java.util.ArrayList(); if (seSubtables != null) { for (Object seSubtable : seSubtables) { Object[] stp = (Object[]) seSubtable; GlyphSubtable st; if ((st = constructGPOSSubtable(stp)) != null) { subtables.add(st); } } } return subtables; } private GlyphSubtable constructGPOSSubtable(Object[] stp) { GlyphSubtable st = null; assert (stp != null) && (stp.length == 8); Integer tt = (Integer) stp[0]; // table type Integer lt = (Integer) stp[1]; // lookup type Integer ln = (Integer) stp[2]; // lookup sequence number Integer lf = (Integer) stp[3]; // lookup flags Integer sn = (Integer) stp[4]; // subtable sequence number Integer sf = (Integer) stp[5]; // subtable format GlyphCoverageTable coverage = (GlyphCoverageTable) stp[6]; List entries = (List) stp[7]; if (tt == GlyphTable.GLYPH_TABLE_TYPE_POSITIONING) { int type = GSUBLookupType.getSubtableType(lt); String lid = "lu" + ln; int sequence = sn; int flags = lf; int format = sf; st = GlyphPositioningTable.createSubtable(type, lid, sequence, flags, format, coverage, entries); } return st; } private void initATState() { seScripts = new java.util.LinkedHashMap(); seLanguages = new java.util.LinkedHashMap(); seFeatures = new java.util.LinkedHashMap(); seSubtables = new java.util.ArrayList(); resetATSubState(); } private void resetATState() { seScripts = null; seLanguages = null; seFeatures = null; seSubtables = null; resetATSubState(); } private void initATSubState() { seMapping = null; seEntries = new java.util.ArrayList(); } private void extractSESubState(int tableType, int lookupType, int lookupFlags, int lookupSequence, int subtableSequence, int subtableFormat) { if (seEntries != null) { if ((tableType == GlyphTable.GLYPH_TABLE_TYPE_DEFINITION) || (seEntries.size() > 0)) { if (seSubtables != null) { Integer tt = tableType; Integer lt = lookupType; Integer ln = lookupSequence; Integer lf = lookupFlags; Integer sn = subtableSequence; Integer sf = subtableFormat; seSubtables.add(new Object[] { tt, lt, ln, lf, sn, sf, seMapping, seEntries }); } } } } private void resetATSubState() { seMapping = null; seEntries = null; } private void resetATStateAll() { resetATState(); gdef = null; gsub = null; gpos = null; } /** helper method for formatting an integer array for output */ private String toString(int[] ia) { StringBuffer sb = new StringBuffer(); if ((ia == null) || (ia.length == 0)) { sb.append('-'); } else { boolean first = true; for (int anIa : ia) { if (!first) { sb.append(' '); } else { first = false; } sb.append(anIa); } } return sb.toString(); } }
⏎ org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
Or download all of them as a single archive file:
File name: fop-2.7-src.zip File size: 3401312 bytes Release date: 2022-01-20 Download
⇒ "fop" Command in fop-2.7-bin.zip
2016-07-07, 61124👍, 0💬
Popular Posts:
maven-core-3.8.6.jar is the JAR file for Apache Maven 3.8.6 Core module. Apache Maven is a software ...
JRE 8 rt.jar is the JAR file for JRE 8 RT (Runtime) libraries. JRE (Java Runtime) 8 is the runtime e...
JDK 17 java.desktop.jmod is the JMOD file for JDK 17 Desktop module. JDK 17 Desktop module compiled ...
How to read XML document from socket connections with the socket\DelayedInput.java provided in the A...
How to perform XML Schema validation with sax\Writer.java provided in the Apache Xerces package? You...