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/GlyphPositioningTable.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.util.ArrayList; import java.util.Arrays; import java.util.Collections; 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.complexscripts.util.GlyphSequence; import org.apache.fop.complexscripts.util.GlyphTester; // CSOFF: LineLengthCheck /** * <p>The <code>GlyphPositioningTable</code> class is a glyph table that implements * <code>GlyphPositioning</code> functionality.</p> * * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p> */ public class GlyphPositioningTable extends GlyphTable { /** logging instance */ private static final Log log = LogFactory.getLog(GlyphPositioningTable.class); /** single positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_SINGLE = 1; /** multiple positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_PAIR = 2; /** cursive positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_CURSIVE = 3; /** mark to base positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_MARK_TO_BASE = 4; /** mark to ligature positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_MARK_TO_LIGATURE = 5; /** mark to mark positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_MARK_TO_MARK = 6; /** contextual positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_CONTEXTUAL = 7; /** chained contextual positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_CHAINED_CONTEXTUAL = 8; /** extension positioning subtable type */ public static final int GPOS_LOOKUP_TYPE_EXTENSION_POSITIONING = 9; /** * Instantiate a <code>GlyphPositioningTable</code> object using the specified lookups * and subtables. * @param gdef glyph definition table that applies * @param lookups a map of lookup specifications to subtable identifier strings * @param subtables a list of identified subtables */ public GlyphPositioningTable(GlyphDefinitionTable gdef, Map lookups, List subtables, Map<String, ScriptProcessor> processors) { super(gdef, lookups, processors); if ((subtables == null) || (subtables.size() == 0)) { throw new AdvancedTypographicTableFormatException("subtables must be non-empty"); } else { for (Object o : subtables) { if (o instanceof GlyphPositioningSubtable) { addSubtable((GlyphSubtable) o); } else { throw new AdvancedTypographicTableFormatException("subtable must be a glyph positioning subtable"); } } freezeSubtables(); } } /** * Map a lookup type name to its constant (integer) value. * @param name lookup type name * @return lookup type */ public static int getLookupTypeFromName(String name) { int t; String s = name.toLowerCase(); if ("single".equals(s)) { t = GPOS_LOOKUP_TYPE_SINGLE; } else if ("pair".equals(s)) { t = GPOS_LOOKUP_TYPE_PAIR; } else if ("cursive".equals(s)) { t = GPOS_LOOKUP_TYPE_CURSIVE; } else if ("marktobase".equals(s)) { t = GPOS_LOOKUP_TYPE_MARK_TO_BASE; } else if ("marktoligature".equals(s)) { t = GPOS_LOOKUP_TYPE_MARK_TO_LIGATURE; } else if ("marktomark".equals(s)) { t = GPOS_LOOKUP_TYPE_MARK_TO_MARK; } else if ("contextual".equals(s)) { t = GPOS_LOOKUP_TYPE_CONTEXTUAL; } else if ("chainedcontextual".equals(s)) { t = GPOS_LOOKUP_TYPE_CHAINED_CONTEXTUAL; } else if ("extensionpositioning".equals(s)) { t = GPOS_LOOKUP_TYPE_EXTENSION_POSITIONING; } else { t = -1; } return t; } /** * Map a lookup type constant (integer) value to its name. * @param type lookup type * @return lookup type name */ public static String getLookupTypeName(int type) { String tn; switch (type) { case GPOS_LOOKUP_TYPE_SINGLE: tn = "single"; break; case GPOS_LOOKUP_TYPE_PAIR: tn = "pair"; break; case GPOS_LOOKUP_TYPE_CURSIVE: tn = "cursive"; break; case GPOS_LOOKUP_TYPE_MARK_TO_BASE: tn = "marktobase"; break; case GPOS_LOOKUP_TYPE_MARK_TO_LIGATURE: tn = "marktoligature"; break; case GPOS_LOOKUP_TYPE_MARK_TO_MARK: tn = "marktomark"; break; case GPOS_LOOKUP_TYPE_CONTEXTUAL: tn = "contextual"; break; case GPOS_LOOKUP_TYPE_CHAINED_CONTEXTUAL: tn = "chainedcontextual"; break; case GPOS_LOOKUP_TYPE_EXTENSION_POSITIONING: tn = "extensionpositioning"; break; default: tn = "unknown"; break; } return tn; } /** * Create a positioning subtable according to the specified arguments. * @param type subtable type * @param id subtable identifier * @param sequence subtable sequence * @param flags subtable flags * @param format subtable format * @param coverage subtable coverage table * @param entries subtable entries * @return a glyph subtable instance */ public static GlyphSubtable createSubtable(int type, String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { GlyphSubtable st = null; switch (type) { case GPOS_LOOKUP_TYPE_SINGLE: st = SingleSubtable.create(id, sequence, flags, format, coverage, entries); break; case GPOS_LOOKUP_TYPE_PAIR: st = PairSubtable.create(id, sequence, flags, format, coverage, entries); break; case GPOS_LOOKUP_TYPE_CURSIVE: st = CursiveSubtable.create(id, sequence, flags, format, coverage, entries); break; case GPOS_LOOKUP_TYPE_MARK_TO_BASE: st = MarkToBaseSubtable.create(id, sequence, flags, format, coverage, entries); break; case GPOS_LOOKUP_TYPE_MARK_TO_LIGATURE: st = MarkToLigatureSubtable.create(id, sequence, flags, format, coverage, entries); break; case GPOS_LOOKUP_TYPE_MARK_TO_MARK: st = MarkToMarkSubtable.create(id, sequence, flags, format, coverage, entries); break; case GPOS_LOOKUP_TYPE_CONTEXTUAL: st = ContextualSubtable.create(id, sequence, flags, format, coverage, entries); break; case GPOS_LOOKUP_TYPE_CHAINED_CONTEXTUAL: st = ChainedContextualSubtable.create(id, sequence, flags, format, coverage, entries); break; default: break; } return st; } /** * Create a positioning subtable according to the specified arguments. * @param type subtable type * @param id subtable identifier * @param sequence subtable sequence * @param flags subtable flags * @param format subtable format * @param coverage list of coverage table entries * @param entries subtable entries * @return a glyph subtable instance */ public static GlyphSubtable createSubtable(int type, String id, int sequence, int flags, int format, List coverage, List entries) { return createSubtable(type, id, sequence, flags, format, GlyphCoverageTable.createCoverageTable(coverage), entries); } /** * Perform positioning processing using all matching lookups. * @param gs an input glyph sequence * @param script a script identifier * @param language a language identifier * @param fontSize size in device units * @param widths array of default advancements for each glyph * @param adjustments accumulated adjustments array (sequence) of 4-tuples of placement [PX,PY] and advance [AX,AY] adjustments, in that order, * with one 4-tuple for each element of glyph sequence * @return true if some adjustment is not zero; otherwise, false */ public boolean position(GlyphSequence gs, String script, String language, int fontSize, int[] widths, int[][] adjustments) { Map<LookupSpec, List<LookupTable>> lookups = matchLookups(script, language, "*"); if ((lookups != null) && (lookups.size() > 0)) { ScriptProcessor sp = ScriptProcessor.getInstance(script, processors); return sp.position(this, gs, script, language, fontSize, lookups, widths, adjustments); } else { return false; } } private abstract static class SingleSubtable extends GlyphPositioningSubtable { SingleSubtable(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage); } /** {@inheritDoc} */ public int getType() { return GPOS_LOOKUP_TYPE_SINGLE; } /** {@inheritDoc} */ public boolean isCompatible(GlyphSubtable subtable) { return subtable instanceof SingleSubtable; } /** {@inheritDoc} */ public boolean position(GlyphPositioningState ps) { int gi = ps.getGlyph(); int ci; if ((ci = getCoverageIndex(gi)) < 0) { return false; } else { Value v = getValue(ci, gi); if (v != null) { if (ps.adjust(v)) { ps.setAdjusted(true); } ps.consume(1); } return true; } } /** * Obtain positioning value for coverage index. * @param ci coverage index * @param gi input glyph index * @return positioning value or null if none applies */ public abstract Value getValue(int ci, int gi); static GlyphPositioningSubtable create(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { if (format == 1) { return new SingleSubtableFormat1(id, sequence, flags, format, coverage, entries); } else if (format == 2) { return new SingleSubtableFormat2(id, sequence, flags, format, coverage, entries); } else { throw new UnsupportedOperationException(); } } } private static class SingleSubtableFormat1 extends SingleSubtable { private Value value; private int ciMax; SingleSubtableFormat1(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (value != null) { List entries = new ArrayList(1); entries.add(value); return entries; } else { return null; } } /** {@inheritDoc} */ public Value getValue(int ci, int gi) { if ((value != null) && (ci <= ciMax)) { return value; } else { return null; } } private void populate(List entries) { if ((entries == null) || (entries.size() != 1)) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null and contain exactly one entry"); } else { Value v; Object o = entries.get(0); if (o instanceof Value) { v = (Value) o; } else { throw new AdvancedTypographicTableFormatException("illegal entries entry, must be Value, but is: " + ((o != null) ? o.getClass() : null)); } assert this.value == null; this.value = v; this.ciMax = getCoverageSize() - 1; } } } private static class SingleSubtableFormat2 extends SingleSubtable { private Value[] values; SingleSubtableFormat2(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (values != null) { List entries = new ArrayList(values.length); Collections.addAll(entries, values); return entries; } else { return null; } } /** {@inheritDoc} */ public Value getValue(int ci, int gi) { if ((values != null) && (ci < values.length)) { return values [ ci ]; } else { return null; } } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 1) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof Value[])) { throw new AdvancedTypographicTableFormatException("illegal entries, single entry must be a Value[], but is: " + ((o != null) ? o.getClass() : null)); } else { Value[] va = (Value[]) o; if (va.length != getCoverageSize()) { throw new AdvancedTypographicTableFormatException("illegal values array, " + entries.size() + " values present, but requires " + getCoverageSize() + " values"); } else { assert this.values == null; this.values = va; } } } } } private abstract static class PairSubtable extends GlyphPositioningSubtable { PairSubtable(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage); } /** {@inheritDoc} */ public int getType() { return GPOS_LOOKUP_TYPE_PAIR; } /** {@inheritDoc} */ public boolean isCompatible(GlyphSubtable subtable) { return subtable instanceof PairSubtable; } /** {@inheritDoc} */ public boolean position(GlyphPositioningState ps) { boolean applied = false; int gi = ps.getGlyph(0); int ci; if ((ci = getCoverageIndex(gi)) >= 0) { int[] counts = ps.getGlyphsAvailable(0); int nga = counts[0]; if (nga > 1) { int[] iga = ps.getGlyphs(0, 2, null, counts); if ((iga != null) && (iga.length == 2)) { PairValues pv = getPairValues(ci, iga[0], iga[1]); if (pv != null) { int offset = 0; int offsetLast = counts[0] + counts[1]; // skip any ignored glyphs prior to first non-ignored glyph for ( ; offset < offsetLast; ++offset) { if (!ps.isIgnoredGlyph(offset)) { break; } else { ps.consume(1); } } // adjust first non-ignored glyph if first value isn't null Value v1 = pv.getValue1(); if (v1 != null) { if (ps.adjust(v1, offset)) { ps.setAdjusted(true); } ps.consume(1); // consume first non-ignored glyph ++offset; } // skip any ignored glyphs prior to second non-ignored glyph for ( ; offset < offsetLast; ++offset) { if (!ps.isIgnoredGlyph(offset)) { break; } else { ps.consume(1); } } // adjust second non-ignored glyph if second value isn't null Value v2 = pv.getValue2(); if (v2 != null) { if (ps.adjust(v2, offset)) { ps.setAdjusted(true); } ps.consume(1); // consume second non-ignored glyph ++offset; } applied = true; } } } } return applied; } /** * Obtain associated pair values. * @param ci coverage index * @param gi1 first input glyph index * @param gi2 second input glyph index * @return pair values or null if none applies */ public abstract PairValues getPairValues(int ci, int gi1, int gi2); static GlyphPositioningSubtable create(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { if (format == 1) { return new PairSubtableFormat1(id, sequence, flags, format, coverage, entries); } else if (format == 2) { return new PairSubtableFormat2(id, sequence, flags, format, coverage, entries); } else { throw new UnsupportedOperationException(); } } } private static class PairSubtableFormat1 extends PairSubtable { private PairValues[][] pvm; // pair values matrix PairSubtableFormat1(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (pvm != null) { List entries = new ArrayList(1); entries.add(pvm); return entries; } else { return null; } } /** {@inheritDoc} */ public PairValues getPairValues(int ci, int gi1, int gi2) { if ((pvm != null) && (ci < pvm.length)) { PairValues[] pvt = pvm [ ci ]; for (PairValues pv : pvt) { if (pv != null) { int g = pv.getGlyph(); if (g < gi2) { continue; } else if (g == gi2) { return pv; } else { break; } } } } return null; } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 1) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof PairValues[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, first (and only) entry must be a PairValues[][], but is: " + ((o != null) ? o.getClass() : null)); } else { pvm = (PairValues[][]) o; } } } } private static class PairSubtableFormat2 extends PairSubtable { private GlyphClassTable cdt1; // class def table 1 private GlyphClassTable cdt2; // class def table 2 private int nc1; // class 1 count private int nc2; // class 2 count private PairValues[][] pvm; // pair values matrix PairSubtableFormat2(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (pvm != null) { List entries = new ArrayList(5); entries.add(cdt1); entries.add(cdt2); entries.add(nc1); entries.add(nc2); entries.add(pvm); return entries; } else { return null; } } /** {@inheritDoc} */ public PairValues getPairValues(int ci, int gi1, int gi2) { if (pvm != null) { int c1 = cdt1.getClassIndex(gi1, 0); if ((c1 >= 0) && (c1 < nc1) && (c1 < pvm.length)) { PairValues[] pvt = pvm [ c1 ]; if (pvt != null) { int c2 = cdt2.getClassIndex(gi2, 0); if ((c2 >= 0) && (c2 < nc2) && (c2 < pvt.length)) { return pvt [ c2 ]; } } } } return null; } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 5) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { cdt1 = (GlyphClassTable) o; } if (((o = entries.get(1)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { cdt2 = (GlyphClassTable) o; } if (((o = entries.get(2)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nc1 = (Integer) (o); } if (((o = entries.get(3)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nc2 = (Integer) (o); } if (((o = entries.get(4)) == null) || !(o instanceof PairValues[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be a PairValues[][], but is: " + ((o != null) ? o.getClass() : null)); } else { pvm = (PairValues[][]) o; } } } } private abstract static class CursiveSubtable extends GlyphPositioningSubtable { CursiveSubtable(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage); } /** {@inheritDoc} */ public int getType() { return GPOS_LOOKUP_TYPE_CURSIVE; } /** {@inheritDoc} */ public boolean isCompatible(GlyphSubtable subtable) { return subtable instanceof CursiveSubtable; } /** {@inheritDoc} */ public boolean position(GlyphPositioningState ps) { boolean applied = false; int gi = ps.getGlyph(0); int ci; if ((ci = getCoverageIndex(gi)) >= 0) { int[] counts = ps.getGlyphsAvailable(0); int nga = counts[0]; if (nga > 1) { int[] iga = ps.getGlyphs(0, 2, null, counts); if ((iga != null) && (iga.length == 2)) { // int gi1 = gi; int ci1 = ci; int gi2 = iga [ 1 ]; int ci2 = getCoverageIndex(gi2); Anchor[] aa = getExitEntryAnchors(ci1, ci2); if (aa != null) { Anchor exa = aa [ 0 ]; Anchor ena = aa [ 1 ]; // int exw = ps.getWidth ( gi1 ); int enw = ps.getWidth(gi2); if ((exa != null) && (ena != null)) { Value v = ena.getAlignmentAdjustment(exa); v.adjust(-enw, 0, 0, 0); if (ps.adjust(v)) { ps.setAdjusted(true); } } // consume only first glyph of exit/entry glyph pair ps.consume(1); applied = true; } } } } return applied; } /** * Obtain exit anchor for first glyph with coverage index <code>ci1</code> and entry anchor for second * glyph with coverage index <code>ci2</code>. * @param ci1 coverage index of first glyph (may be negative) * @param ci2 coverage index of second glyph (may be negative) * @return array of two anchors or null if either coverage index is negative or corresponding anchor is * missing, where the first entry is the exit anchor of the first glyph and the second entry is the * entry anchor of the second glyph */ public abstract Anchor[] getExitEntryAnchors(int ci1, int ci2); static GlyphPositioningSubtable create(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { if (format == 1) { return new CursiveSubtableFormat1(id, sequence, flags, format, coverage, entries); } else { throw new UnsupportedOperationException(); } } } private static class CursiveSubtableFormat1 extends CursiveSubtable { private Anchor[] aa; // anchor array, where even entries are entry anchors, and odd entries are exit anchors CursiveSubtableFormat1(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (aa != null) { List entries = new ArrayList(1); entries.add(aa); return entries; } else { return null; } } /** {@inheritDoc} */ public Anchor[] getExitEntryAnchors(int ci1, int ci2) { if ((ci1 >= 0) && (ci2 >= 0)) { int ai1 = (ci1 * 2) + 1; // ci1 denotes glyph with exit anchor int ai2 = (ci2 * 2) + 0; // ci2 denotes glyph with entry anchor if ((aa != null) && (ai1 < aa.length) && (ai2 < aa.length)) { Anchor exa = aa [ ai1 ]; Anchor ena = aa [ ai2 ]; if ((exa != null) && (ena != null)) { return new Anchor[] { exa, ena }; } } } return null; } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 1) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof Anchor[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first (and only) entry must be a Anchor[], but is: " + ((o != null) ? o.getClass() : null)); } else if ((((Anchor[]) o) .length % 2) != 0) { throw new AdvancedTypographicTableFormatException("illegal entries, Anchor[] array must have an even number of entries, but has: " + ((Anchor[]) o) .length); } else { aa = (Anchor[]) o; } } } } private abstract static class MarkToBaseSubtable extends GlyphPositioningSubtable { MarkToBaseSubtable(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage); } /** {@inheritDoc} */ public int getType() { return GPOS_LOOKUP_TYPE_MARK_TO_BASE; } /** {@inheritDoc} */ public boolean isCompatible(GlyphSubtable subtable) { return subtable instanceof MarkToBaseSubtable; } /** {@inheritDoc} */ public boolean position(GlyphPositioningState ps) { boolean applied = false; int giMark = ps.getGlyph(); int ciMark; if ((ciMark = getCoverageIndex(giMark)) >= 0) { MarkAnchor ma = getMarkAnchor(ciMark, giMark); if (ma != null) { for (int i = 0, n = ps.getPosition(); i < n; i++) { int gi = ps.getGlyph(-(i + 1)); int unprocessedGlyph = ps.getUnprocessedGlyph(-(i + 1)); if (ps.isMark(gi) && ps.isMark(unprocessedGlyph)) { continue; } else { Anchor a = getBaseAnchor(gi, ma.getMarkClass()); if (a != null) { Value v = a.getAlignmentAdjustment(ma); // start experimental fix for END OF AYAH in Lateef/Scheherazade int[] aa = ps.getAdjustment(); if (aa[2] == 0) { v.adjust(0, 0, -ps.getWidth(giMark), 0); } // end experimental fix for END OF AYAH in Lateef/Scheherazade if (OTFScript.KHMER.equals(ps.script)) { v.adjust(-ps.getWidth(gi), -v.yPlacement, 0, 0); } if (ps.adjust(v)) { ps.setAdjusted(true); } } ps.consume(1); applied = true; break; } } } } return applied; } /** * Obtain mark anchor associated with mark coverage index. * @param ciMark coverage index * @param giMark input glyph index of mark glyph * @return mark anchor or null if none applies */ public abstract MarkAnchor getMarkAnchor(int ciMark, int giMark); /** * Obtain anchor associated with base glyph index and mark class. * @param giBase input glyph index of base glyph * @param markClass class number of mark glyph * @return anchor or null if none applies */ public abstract Anchor getBaseAnchor(int giBase, int markClass); static GlyphPositioningSubtable create(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { if (format == 1) { return new MarkToBaseSubtableFormat1(id, sequence, flags, format, coverage, entries); } else { throw new UnsupportedOperationException(); } } } private static class MarkToBaseSubtableFormat1 extends MarkToBaseSubtable { private GlyphCoverageTable bct; // base coverage table private int nmc; // mark class count private MarkAnchor[] maa; // mark anchor array, ordered by mark coverage index private Anchor[][] bam; // base anchor matrix, ordered by base coverage index, then by mark class MarkToBaseSubtableFormat1(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if ((bct != null) && (maa != null) && (nmc > 0) && (bam != null)) { List entries = new ArrayList(4); entries.add(bct); entries.add(nmc); entries.add(maa); entries.add(bam); return entries; } else { return null; } } /** {@inheritDoc} */ public MarkAnchor getMarkAnchor(int ciMark, int giMark) { if ((maa != null) && (ciMark < maa.length)) { return maa [ ciMark ]; } else { return null; } } /** {@inheritDoc} */ public Anchor getBaseAnchor(int giBase, int markClass) { int ciBase; if ((bct != null) && ((ciBase = bct.getCoverageIndex(giBase)) >= 0)) { if ((bam != null) && (ciBase < bam.length)) { Anchor[] ba = bam [ ciBase ]; if ((ba != null) && (markClass < ba.length)) { return ba [ markClass ]; } } } return null; } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 4) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 4 entries"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null)); } else { bct = (GlyphCoverageTable) o; } if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nmc = (Integer) (o); } if (((o = entries.get(2)) == null) || !(o instanceof MarkAnchor[])) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null)); } else { maa = (MarkAnchor[]) o; } if (((o = entries.get(3)) == null) || !(o instanceof Anchor[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a Anchor[][], but is: " + ((o != null) ? o.getClass() : null)); } else { bam = (Anchor[][]) o; } } } } private abstract static class MarkToLigatureSubtable extends GlyphPositioningSubtable { MarkToLigatureSubtable(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage); } /** {@inheritDoc} */ public int getType() { return GPOS_LOOKUP_TYPE_MARK_TO_LIGATURE; } /** {@inheritDoc} */ public boolean isCompatible(GlyphSubtable subtable) { return subtable instanceof MarkToLigatureSubtable; } /** {@inheritDoc} */ public boolean position(GlyphPositioningState ps) { boolean applied = false; int giMark = ps.getGlyph(); int ciMark; if ((ciMark = getCoverageIndex(giMark)) >= 0) { MarkAnchor ma = getMarkAnchor(ciMark, giMark); int mxc = getMaxComponentCount(); if (ma != null) { for (int i = 0, n = ps.getPosition(); i < n; i++) { int glyphIndex = ps.getUnprocessedGlyph(-(i + 1)); if (ps.isMark(glyphIndex)) { continue; } else { Anchor anchor = getLigatureAnchor(glyphIndex, mxc, i, ma.getMarkClass()); if (anchor != null) { if (ps.adjust(anchor.getAlignmentAdjustment(ma))) { ps.setAdjusted(true); } } ps.consume(1); applied = true; break; } } } } return applied; } /** * Obtain mark anchor associated with mark coverage index. * @param ciMark coverage index * @param giMark input glyph index of mark glyph * @return mark anchor or null if none applies */ public abstract MarkAnchor getMarkAnchor(int ciMark, int giMark); /** * Obtain maximum component count. * @return maximum component count (>=0) */ public abstract int getMaxComponentCount(); /** * Obtain anchor associated with ligature glyph index and mark class. * @param giLig input glyph index of ligature glyph * @param maxComponents maximum component count * @param component component number (0...maxComponents-1) * @param markClass class number of mark glyph * @return anchor or null if none applies */ public abstract Anchor getLigatureAnchor(int giLig, int maxComponents, int component, int markClass); static GlyphPositioningSubtable create(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { if (format == 1) { return new MarkToLigatureSubtableFormat1(id, sequence, flags, format, coverage, entries); } else { throw new UnsupportedOperationException(); } } } private static class MarkToLigatureSubtableFormat1 extends MarkToLigatureSubtable { private GlyphCoverageTable lct; // ligature coverage table private int nmc; // mark class count private int mxc; // maximum ligature component count private MarkAnchor[] maa; // mark anchor array, ordered by mark coverage index private Anchor[][][] lam; // ligature anchor matrix, ordered by ligature coverage index, then ligature component, then mark class MarkToLigatureSubtableFormat1(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (lam != null) { List entries = new ArrayList(5); entries.add(lct); entries.add(nmc); entries.add(mxc); entries.add(maa); entries.add(lam); return entries; } else { return null; } } /** {@inheritDoc} */ public MarkAnchor getMarkAnchor(int ciMark, int giMark) { if ((maa != null) && (ciMark < maa.length)) { return maa [ ciMark ]; } else { return null; } } /** {@inheritDoc} */ public int getMaxComponentCount() { return mxc; } /** {@inheritDoc} */ public Anchor getLigatureAnchor(int giLig, int maxComponents, int component, int markClass) { int ciLig; if ((lct != null) && ((ciLig = lct.getCoverageIndex(giLig)) >= 0)) { if ((lam != null) && (ciLig < lam.length)) { Anchor[][] lcm = lam [ ciLig ]; if (component < maxComponents) { Anchor[] la = lcm [ component ]; if ((la != null) && (markClass < la.length)) { return la [ markClass ]; } } } } return null; } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 5) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null)); } else { lct = (GlyphCoverageTable) o; } if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nmc = (Integer) (o); } if (((o = entries.get(2)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { mxc = (Integer) (o); } if (((o = entries.get(3)) == null) || !(o instanceof MarkAnchor[])) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null)); } else { maa = (MarkAnchor[]) o; } if (((o = entries.get(4)) == null) || !(o instanceof Anchor[][][])) { throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be a Anchor[][][], but is: " + ((o != null) ? o.getClass() : null)); } else { lam = (Anchor[][][]) o; } } } } private abstract static class MarkToMarkSubtable extends GlyphPositioningSubtable { MarkToMarkSubtable(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage); } /** {@inheritDoc} */ public int getType() { return GPOS_LOOKUP_TYPE_MARK_TO_MARK; } /** {@inheritDoc} */ public boolean isCompatible(GlyphSubtable subtable) { return subtable instanceof MarkToMarkSubtable; } /** {@inheritDoc} */ public boolean position(GlyphPositioningState ps) { boolean applied = false; int giMark1 = ps.getGlyph(); int ciMark1; if ((ciMark1 = getCoverageIndex(giMark1)) >= 0) { MarkAnchor ma = getMark1Anchor(ciMark1, giMark1); if (ma != null) { if (ps.hasPrev()) { Anchor anchor = getMark2Anchor(ps.getUnprocessedGlyph(-1), ma.getMarkClass()); if (anchor != null) { if (ps.adjust(anchor.getAlignmentAdjustment(ma))) { ps.setAdjusted(true); } } ps.consume(1); applied = true; } } } return applied; } /** * Obtain mark 1 anchor associated with mark 1 coverage index. * @param ciMark1 mark 1 coverage index * @param giMark1 input glyph index of mark 1 glyph * @return mark 1 anchor or null if none applies */ public abstract MarkAnchor getMark1Anchor(int ciMark1, int giMark1); /** * Obtain anchor associated with mark 2 glyph index and mark 1 class. * @param giMark2 input glyph index of mark 2 glyph * @param markClass class number of mark 1 glyph * @return anchor or null if none applies */ public abstract Anchor getMark2Anchor(int giBase, int markClass); static GlyphPositioningSubtable create(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { if (format == 1) { return new MarkToMarkSubtableFormat1(id, sequence, flags, format, coverage, entries); } else { throw new UnsupportedOperationException(); } } } private static class MarkToMarkSubtableFormat1 extends MarkToMarkSubtable { private GlyphCoverageTable mct2; // mark 2 coverage table private int nmc; // mark class count private MarkAnchor[] maa; // mark1 anchor array, ordered by mark1 coverage index private Anchor[][] mam; // mark2 anchor matrix, ordered by mark2 coverage index, then by mark1 class MarkToMarkSubtableFormat1(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if ((mct2 != null) && (maa != null) && (nmc > 0) && (mam != null)) { List entries = new ArrayList(4); entries.add(mct2); entries.add(nmc); entries.add(maa); entries.add(mam); return entries; } else { return null; } } /** {@inheritDoc} */ public MarkAnchor getMark1Anchor(int ciMark1, int giMark1) { if ((maa != null) && (ciMark1 < maa.length)) { return maa [ ciMark1 ]; } else { return null; } } /** {@inheritDoc} */ public Anchor getMark2Anchor(int giMark2, int markClass) { int ciMark2; if ((mct2 != null) && ((ciMark2 = mct2.getCoverageIndex(giMark2)) >= 0)) { if ((mam != null) && (ciMark2 < mam.length)) { Anchor[] ma = mam [ ciMark2 ]; if ((ma != null) && (markClass < ma.length)) { return ma [ markClass ]; } } } return null; } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 4) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 4 entries"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null)); } else { mct2 = (GlyphCoverageTable) o; } if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { nmc = (Integer) (o); } if (((o = entries.get(2)) == null) || !(o instanceof MarkAnchor[])) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null)); } else { maa = (MarkAnchor[]) o; } if (((o = entries.get(3)) == null) || !(o instanceof Anchor[][])) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a Anchor[][], but is: " + ((o != null) ? o.getClass() : null)); } else { mam = (Anchor[][]) o; } } } } private abstract static class ContextualSubtable extends GlyphPositioningSubtable { ContextualSubtable(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage); } /** {@inheritDoc} */ public int getType() { return GPOS_LOOKUP_TYPE_CONTEXTUAL; } /** {@inheritDoc} */ public boolean isCompatible(GlyphSubtable subtable) { return subtable instanceof ContextualSubtable; } /** {@inheritDoc} */ public boolean position(GlyphPositioningState ps) { boolean applied = false; int gi = ps.getGlyph(); int ci; if ((ci = getCoverageIndex(gi)) >= 0) { int[] rv = new int[1]; RuleLookup[] la = getLookups(ci, gi, ps, rv); if (la != null) { ps.apply(la, rv[0]); applied = true; } } return applied; } /** * Obtain rule lookups set associated current input glyph context. * @param ci coverage index of glyph at current position * @param gi glyph index of glyph at current position * @param ps glyph positioning state * @param rv array of ints used to receive multiple return values, must be of length 1 or greater, * where the first entry is used to return the input sequence length of the matched rule * @return array of rule lookups or null if none applies */ public abstract RuleLookup[] getLookups(int ci, int gi, GlyphPositioningState ps, int[] rv); static GlyphPositioningSubtable create(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { if (format == 1) { return new ContextualSubtableFormat1(id, sequence, flags, format, coverage, entries); } else if (format == 2) { return new ContextualSubtableFormat2(id, sequence, flags, format, coverage, entries); } else if (format == 3) { return new ContextualSubtableFormat3(id, sequence, flags, format, coverage, entries); } else { throw new UnsupportedOperationException(); } } } private static class ContextualSubtableFormat1 extends ContextualSubtable { private RuleSet[] rsa; // rule set array, ordered by glyph coverage index ContextualSubtableFormat1(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (rsa != null) { List entries = new ArrayList(1); entries.add(rsa); return entries; } else { return null; } } /** {@inheritDoc} */ public void resolveLookupReferences(Map<String, LookupTable> lookupTables) { GlyphTable.resolveLookupReferences(rsa, lookupTables); } /** {@inheritDoc} */ public RuleLookup[] getLookups(int ci, int gi, GlyphPositioningState ps, int[] rv) { assert ps != null; assert (rv != null) && (rv.length > 0); assert rsa != null; if (rsa.length > 0) { RuleSet rs = rsa [ 0 ]; if (rs != null) { Rule[] ra = rs.getRules(); for (Rule r : ra) { if ((r != null) && (r instanceof ChainedGlyphSequenceRule)) { ChainedGlyphSequenceRule cr = (ChainedGlyphSequenceRule) r; int[] iga = cr.getGlyphs(gi); if (matches(ps, iga, 0, rv)) { return r.getLookups(); } } } } } return null; } static boolean matches(GlyphPositioningState ps, int[] glyphs, int offset, int[] rv) { if ((glyphs == null) || (glyphs.length == 0)) { return true; // match null or empty glyph sequence } else { boolean reverse = offset < 0; GlyphTester ignores = ps.getIgnoreDefault(); int[] counts = ps.getGlyphsAvailable(offset, reverse, ignores); int nga = counts[0]; int ngm = glyphs.length; if (nga < ngm) { return false; // insufficient glyphs available to match } else { int[] ga = ps.getGlyphs(offset, ngm, reverse, ignores, null, counts); for (int k = 0; k < ngm; k++) { if (ga [ k ] != glyphs [ k ]) { return false; // match fails at ga [ k ] } } if (rv != null) { rv[0] = counts[0] + counts[1]; } return true; // all glyphs match } } } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 1) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; } } } } private static class ContextualSubtableFormat2 extends ContextualSubtable { private GlyphClassTable cdt; // class def table private int ngc; // class set count private RuleSet[] rsa; // rule set array, ordered by class number [0...ngc - 1] ContextualSubtableFormat2(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (rsa != null) { List entries = new ArrayList(3); entries.add(cdt); entries.add(ngc); entries.add(rsa); return entries; } else { return null; } } /** {@inheritDoc} */ public void resolveLookupReferences(Map<String, LookupTable> lookupTables) { GlyphTable.resolveLookupReferences(rsa, lookupTables); } /** {@inheritDoc} */ public RuleLookup[] getLookups(int ci, int gi, GlyphPositioningState ps, int[] rv) { assert ps != null; assert (rv != null) && (rv.length > 0); assert rsa != null; if (rsa.length > 0) { RuleSet rs = rsa [ 0 ]; if (rs != null) { Rule[] ra = rs.getRules(); for (Rule r : ra) { if ((r != null) && (r instanceof ChainedClassSequenceRule)) { ChainedClassSequenceRule cr = (ChainedClassSequenceRule) r; int[] ca = cr.getClasses(cdt.getClassIndex(gi, ps.getClassMatchSet(gi))); if (matches(ps, cdt, ca, 0, rv)) { return r.getLookups(); } } } } } return null; } static boolean matches(GlyphPositioningState ps, GlyphClassTable cdt, int[] classes, int offset, int[] rv) { if ((cdt == null) || (classes == null) || (classes.length == 0)) { return true; // match null class definitions, null or empty class sequence } else { boolean reverse = offset < 0; GlyphTester ignores = ps.getIgnoreDefault(); int[] counts = ps.getGlyphsAvailable(offset, reverse, ignores); int nga = counts[0]; int ngm = classes.length; if (nga < ngm) { return false; // insufficient glyphs available to match } else { int[] ga = ps.getGlyphs(offset, ngm, reverse, ignores, null, counts); for (int k = 0; k < ngm; k++) { int gi = ga [ k ]; int ms = ps.getClassMatchSet(gi); int gc = cdt.getClassIndex(gi, ms); if ((gc < 0) || (gc >= cdt.getClassSize(ms))) { return false; // none or invalid class fails mat ch } else if (gc != classes [ k ]) { return false; // match fails at ga [ k ] } } if (rv != null) { rv[0] = counts[0] + counts[1]; } return true; // all glyphs match } } } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 3) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 3 entries"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { cdt = (GlyphClassTable) o; } if (((o = entries.get(1)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { ngc = (Integer) (o); } if (((o = entries.get(2)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; if (rsa.length != ngc) { throw new AdvancedTypographicTableFormatException("illegal entries, RuleSet[] length is " + rsa.length + ", but expected " + ngc + " glyph classes"); } } } } } private static class ContextualSubtableFormat3 extends ContextualSubtable { private RuleSet[] rsa; // rule set array, containing a single rule set ContextualSubtableFormat3(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (rsa != null) { List entries = new ArrayList(1); entries.add(rsa); return entries; } else { return null; } } /** {@inheritDoc} */ public void resolveLookupReferences(Map<String, LookupTable> lookupTables) { GlyphTable.resolveLookupReferences(rsa, lookupTables); } /** {@inheritDoc} */ public RuleLookup[] getLookups(int ci, int gi, GlyphPositioningState ps, int[] rv) { assert ps != null; assert (rv != null) && (rv.length > 0); assert rsa != null; if (rsa.length > 0) { RuleSet rs = rsa [ 0 ]; if (rs != null) { Rule[] ra = rs.getRules(); for (Rule r : ra) { if ((r != null) && (r instanceof ChainedCoverageSequenceRule)) { ChainedCoverageSequenceRule cr = (ChainedCoverageSequenceRule) r; GlyphCoverageTable[] gca = cr.getCoverages(); if (matches(ps, gca, 0, rv)) { return r.getLookups(); } } } } } return null; } static boolean matches(GlyphPositioningState ps, GlyphCoverageTable[] gca, int offset, int[] rv) { if ((gca == null) || (gca.length == 0)) { return true; // match null or empty coverage array } else { boolean reverse = offset < 0; GlyphTester ignores = ps.getIgnoreDefault(); int[] counts = ps.getGlyphsAvailable(offset, reverse, ignores); int nga = counts[0]; int ngm = gca.length; if (nga < ngm) { return false; // insufficient glyphs available to match } else { int[] ga = ps.getGlyphs(offset, ngm, reverse, ignores, null, counts); for (int k = 0; k < ngm; k++) { GlyphCoverageTable ct = gca [ k ]; if (ct != null) { if (ct.getCoverageIndex(ga [ k ]) < 0) { return false; // match fails at ga [ k ] } } } if (rv != null) { rv[0] = counts[0] + counts[1]; } return true; // all glyphs match } } } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 1) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; } } } } private abstract static class ChainedContextualSubtable extends GlyphPositioningSubtable { ChainedContextualSubtable(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage); } /** {@inheritDoc} */ public int getType() { return GPOS_LOOKUP_TYPE_CHAINED_CONTEXTUAL; } /** {@inheritDoc} */ public boolean isCompatible(GlyphSubtable subtable) { return subtable instanceof ChainedContextualSubtable; } /** {@inheritDoc} */ public boolean position(GlyphPositioningState ps) { boolean applied = false; int gi = ps.getGlyph(); int ci; if ((ci = getCoverageIndex(gi)) >= 0) { int[] rv = new int[1]; RuleLookup[] la = getLookups(ci, gi, ps, rv); if (la != null) { ps.apply(la, rv[0]); applied = true; } } return applied; } /** * Obtain rule lookups set associated current input glyph context. * @param ci coverage index of glyph at current position * @param gi glyph index of glyph at current position * @param ps glyph positioning state * @param rv array of ints used to receive multiple return values, must be of length 1 or greater, * where the first entry is used to return the input sequence length of the matched rule * @return array of rule lookups or null if none applies */ public abstract RuleLookup[] getLookups(int ci, int gi, GlyphPositioningState ps, int[] rv); static GlyphPositioningSubtable create(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { if (format == 1) { return new ChainedContextualSubtableFormat1(id, sequence, flags, format, coverage, entries); } else if (format == 2) { return new ChainedContextualSubtableFormat2(id, sequence, flags, format, coverage, entries); } else if (format == 3) { return new ChainedContextualSubtableFormat3(id, sequence, flags, format, coverage, entries); } else { throw new UnsupportedOperationException(); } } } private static class ChainedContextualSubtableFormat1 extends ChainedContextualSubtable { private RuleSet[] rsa; // rule set array, ordered by glyph coverage index ChainedContextualSubtableFormat1(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (rsa != null) { List entries = new ArrayList(1); entries.add(rsa); return entries; } else { return null; } } /** {@inheritDoc} */ public void resolveLookupReferences(Map<String, LookupTable> lookupTables) { GlyphTable.resolveLookupReferences(rsa, lookupTables); } /** {@inheritDoc} */ public RuleLookup[] getLookups(int ci, int gi, GlyphPositioningState ps, int[] rv) { assert ps != null; assert (rv != null) && (rv.length > 0); assert rsa != null; if (rsa.length > 0) { RuleSet rs = rsa [ 0 ]; if (rs != null) { Rule[] ra = rs.getRules(); for (Rule r : ra) { if ((r != null) && (r instanceof ChainedGlyphSequenceRule)) { ChainedGlyphSequenceRule cr = (ChainedGlyphSequenceRule) r; int[] iga = cr.getGlyphs(gi); if (matches(ps, iga, 0, rv)) { int[] bga = cr.getBacktrackGlyphs(); if (matches(ps, bga, -1, null)) { int[] lga = cr.getLookaheadGlyphs(); if (matches(ps, lga, rv[0], null)) { return r.getLookups(); } } } } } } } return null; } private boolean matches(GlyphPositioningState ps, int[] glyphs, int offset, int[] rv) { return ContextualSubtableFormat1.matches(ps, glyphs, offset, rv); } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 1) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; } } } } private static class ChainedContextualSubtableFormat2 extends ChainedContextualSubtable { private GlyphClassTable icdt; // input class def table private GlyphClassTable bcdt; // backtrack class def table private GlyphClassTable lcdt; // lookahead class def table private int ngc; // class set count private RuleSet[] rsa; // rule set array, ordered by class number [0...ngc - 1] ChainedContextualSubtableFormat2(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (rsa != null) { List entries = new ArrayList(5); entries.add(icdt); entries.add(bcdt); entries.add(lcdt); entries.add(ngc); entries.add(rsa); return entries; } else { return null; } } /** {@inheritDoc} */ public void resolveLookupReferences(Map<String, LookupTable> lookupTables) { GlyphTable.resolveLookupReferences(rsa, lookupTables); } /** {@inheritDoc} */ public RuleLookup[] getLookups(int ci, int gi, GlyphPositioningState ps, int[] rv) { assert ps != null; assert (rv != null) && (rv.length > 0); assert rsa != null; if (rsa.length > 0) { RuleSet rs = rsa [ 0 ]; if (rs != null) { Rule[] ra = rs.getRules(); for (Rule r : ra) { if ((r != null) && (r instanceof ChainedClassSequenceRule)) { ChainedClassSequenceRule cr = (ChainedClassSequenceRule) r; int[] ica = cr.getClasses(icdt.getClassIndex(gi, ps.getClassMatchSet(gi))); if (matches(ps, icdt, ica, 0, rv)) { int[] bca = cr.getBacktrackClasses(); if (matches(ps, bcdt, bca, -1, null)) { int[] lca = cr.getLookaheadClasses(); if (matches(ps, lcdt, lca, rv[0], null)) { return r.getLookups(); } } } } } } } return null; } private boolean matches(GlyphPositioningState ps, GlyphClassTable cdt, int[] classes, int offset, int[] rv) { return ContextualSubtableFormat2.matches(ps, cdt, classes, offset, rv); } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 5) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null)); } else { icdt = (GlyphClassTable) o; } if (((o = entries.get(1)) != null) && !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an GlyphClassTable, but is: " + o.getClass()); } else { bcdt = (GlyphClassTable) o; } if (((o = entries.get(2)) != null) && !(o instanceof GlyphClassTable)) { throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an GlyphClassTable, but is: " + o.getClass()); } else { lcdt = (GlyphClassTable) o; } if (((o = entries.get(3)) == null) || !(o instanceof Integer)) { throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null)); } else { ngc = (Integer) (o); } if (((o = entries.get(4)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; if (rsa.length != ngc) { throw new AdvancedTypographicTableFormatException("illegal entries, RuleSet[] length is " + rsa.length + ", but expected " + ngc + " glyph classes"); } } } } } private static class ChainedContextualSubtableFormat3 extends ChainedContextualSubtable { private RuleSet[] rsa; // rule set array, containing a single rule set ChainedContextualSubtableFormat3(String id, int sequence, int flags, int format, GlyphCoverageTable coverage, List entries) { super(id, sequence, flags, format, coverage, entries); populate(entries); } /** {@inheritDoc} */ public List getEntries() { if (rsa != null) { List entries = new ArrayList(1); entries.add(rsa); return entries; } else { return null; } } /** {@inheritDoc} */ public void resolveLookupReferences(Map<String, LookupTable> lookupTables) { GlyphTable.resolveLookupReferences(rsa, lookupTables); } /** {@inheritDoc} */ public RuleLookup[] getLookups(int ci, int gi, GlyphPositioningState ps, int[] rv) { assert ps != null; assert (rv != null) && (rv.length > 0); assert rsa != null; if (rsa.length > 0) { RuleSet rs = rsa [ 0 ]; if (rs != null) { Rule[] ra = rs.getRules(); for (Rule r : ra) { if ((r != null) && (r instanceof ChainedCoverageSequenceRule)) { ChainedCoverageSequenceRule cr = (ChainedCoverageSequenceRule) r; GlyphCoverageTable[] igca = cr.getCoverages(); if (matches(ps, igca, 0, rv)) { GlyphCoverageTable[] bgca = cr.getBacktrackCoverages(); if (matches(ps, bgca, -1, null)) { GlyphCoverageTable[] lgca = cr.getLookaheadCoverages(); if (matches(ps, lgca, rv[0], null)) { return r.getLookups(); } } } } } } } return null; } private boolean matches(GlyphPositioningState ps, GlyphCoverageTable[] gca, int offset, int[] rv) { return ContextualSubtableFormat3.matches(ps, gca, offset, rv); } private void populate(List entries) { if (entries == null) { throw new AdvancedTypographicTableFormatException("illegal entries, must be non-null"); } else if (entries.size() != 1) { throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry"); } else { Object o; if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) { throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null)); } else { rsa = (RuleSet[]) o; } } } } /** * The <code>DeviceTable</code> class implements a positioning device table record, comprising * adjustments to be made to scaled design units according to the scaled size. */ public static class DeviceTable { private final int startSize; private final int endSize; private final int[] deltas; /** * Instantiate a DeviceTable. * @param startSize the * @param endSize the ending (scaled) size * @param deltas adjustments for each scaled size */ public DeviceTable(int startSize, int endSize, int[] deltas) { assert startSize >= 0; assert startSize <= endSize; assert deltas != null; assert deltas.length == (endSize - startSize) + 1; this.startSize = startSize; this.endSize = endSize; this.deltas = deltas; } /** @return the start size */ public int getStartSize() { return startSize; } /** @return the end size */ public int getEndSize() { return endSize; } /** @return the deltas */ public int[] getDeltas() { return deltas; } /** * Find device adjustment. asf.todo at present, assumes that 1 device unit equals one point * @param fontSize the font size to search for * @return an adjustment if font size matches an entry */ public int findAdjustment(int fontSize) { // [TODO] at present, assumes that 1 device unit equals one point int fs = fontSize / 1000; if (fs < startSize) { return 0; } else if (fs <= endSize) { return deltas [ fs - startSize ] * 1000; } else { return 0; } } /** {@inheritDoc} */ public String toString() { return "{ start = " + startSize + ", end = " + endSize + ", deltas = " + Arrays.toString(deltas) + "}"; } } /** * The <code>Value</code> class implements a positioning value record, comprising placement * and advancement information in X and Y axes, and optionally including device data used to * perform device (grid-fitted) specific fine grain adjustments. */ public static class Value { /** X_PLACEMENT value format flag */ public static final int X_PLACEMENT = 0x0001; /** Y_PLACEMENT value format flag */ public static final int Y_PLACEMENT = 0x0002; /** X_ADVANCE value format flag */ public static final int X_ADVANCE = 0x0004; /** Y_ADVANCE value format flag */ public static final int Y_ADVANCE = 0x0008; /** X_PLACEMENT_DEVICE value format flag */ public static final int X_PLACEMENT_DEVICE = 0x0010; /** Y_PLACEMENT_DEVICE value format flag */ public static final int Y_PLACEMENT_DEVICE = 0x0020; /** X_ADVANCE_DEVICE value format flag */ public static final int X_ADVANCE_DEVICE = 0x0040; /** Y_ADVANCE_DEVICE value format flag */ public static final int Y_ADVANCE_DEVICE = 0x0080; /** X_PLACEMENT value index (within adjustments arrays) */ public static final int IDX_X_PLACEMENT = 0; /** Y_PLACEMENT value index (within adjustments arrays) */ public static final int IDX_Y_PLACEMENT = 1; /** X_ADVANCE value index (within adjustments arrays) */ public static final int IDX_X_ADVANCE = 2; /** Y_ADVANCE value index (within adjustments arrays) */ public static final int IDX_Y_ADVANCE = 3; private int xPlacement; // x placement private int yPlacement; // y placement private int xAdvance; // x advance private int yAdvance; // y advance private final DeviceTable xPlaDevice; // x placement device table private final DeviceTable yPlaDevice; // y placement device table private final DeviceTable xAdvDevice; // x advance device table private final DeviceTable yAdvDevice; // x advance device table /** * Instantiate a Value. * @param xPlacement the x placement or zero * @param yPlacement the y placement or zero * @param xAdvance the x advance or zero * @param yAdvance the y advance or zero * @param xPlaDevice the x placement device table or null * @param yPlaDevice the y placement device table or null * @param xAdvDevice the x advance device table or null * @param yAdvDevice the y advance device table or null */ public Value(int xPlacement, int yPlacement, int xAdvance, int yAdvance, DeviceTable xPlaDevice, DeviceTable yPlaDevice, DeviceTable xAdvDevice, DeviceTable yAdvDevice) { this.xPlacement = xPlacement; this.yPlacement = yPlacement; this.xAdvance = xAdvance; this.yAdvance = yAdvance; this.xPlaDevice = xPlaDevice; this.yPlaDevice = yPlaDevice; this.xAdvDevice = xAdvDevice; this.yAdvDevice = yAdvDevice; } /** @return the x placement */ public int getXPlacement() { return xPlacement; } /** @return the y placement */ public int getYPlacement() { return yPlacement; } /** @return the x advance */ public int getXAdvance() { return xAdvance; } /** @return the y advance */ public int getYAdvance() { return yAdvance; } /** @return the x placement device table */ public DeviceTable getXPlaDevice() { return xPlaDevice; } /** @return the y placement device table */ public DeviceTable getYPlaDevice() { return yPlaDevice; } /** @return the x advance device table */ public DeviceTable getXAdvDevice() { return xAdvDevice; } /** @return the y advance device table */ public DeviceTable getYAdvDevice() { return yAdvDevice; } /** * Apply value to specific adjustments to without use of device table adjustments. * @param xPlacement the x placement or zero * @param yPlacement the y placement or zero * @param xAdvance the x advance or zero * @param yAdvance the y advance or zero */ public void adjust(int xPlacement, int yPlacement, int xAdvance, int yAdvance) { this.xPlacement += xPlacement; this.yPlacement += yPlacement; this.xAdvance += xAdvance; this.yAdvance += yAdvance; } /** * Apply value to adjustments using font size for device table adjustments. * @param adjustments array of four integers containing X,Y placement and X,Y advance adjustments * @param fontSize font size for device table adjustments * @return true if some adjustment was made */ public boolean adjust(int[] adjustments, int fontSize) { boolean adjust = false; int dv; if ((dv = xPlacement) != 0) { adjustments [ IDX_X_PLACEMENT ] += dv; adjust = true; } if ((dv = yPlacement) != 0) { adjustments [ IDX_Y_PLACEMENT ] += dv; adjust = true; } if ((dv = xAdvance) != 0) { adjustments [ IDX_X_ADVANCE ] += dv; adjust = true; } if ((dv = yAdvance) != 0) { adjustments [ IDX_Y_ADVANCE ] += dv; adjust = true; } if (fontSize != 0) { DeviceTable dt; if ((dt = xPlaDevice) != null) { if ((dv = dt.findAdjustment(fontSize)) != 0) { adjustments [ IDX_X_PLACEMENT ] += dv; adjust = true; } } if ((dt = yPlaDevice) != null) { if ((dv = dt.findAdjustment(fontSize)) != 0) { adjustments [ IDX_Y_PLACEMENT ] += dv; adjust = true; } } if ((dt = xAdvDevice) != null) { if ((dv = dt.findAdjustment(fontSize)) != 0) { adjustments [ IDX_X_ADVANCE ] += dv; adjust = true; } } if ((dt = yAdvDevice) != null) { if ((dv = dt.findAdjustment(fontSize)) != 0) { adjustments [ IDX_Y_ADVANCE ] += dv; adjust = true; } } } return adjust; } /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer(); boolean first = true; sb.append("{ "); if (xPlacement != 0) { if (!first) { sb.append(", "); } else { first = false; } sb.append("xPlacement = " + xPlacement); } if (yPlacement != 0) { if (!first) { sb.append(", "); } else { first = false; } sb.append("yPlacement = " + yPlacement); } if (xAdvance != 0) { if (!first) { sb.append(", "); } else { first = false; } sb.append("xAdvance = " + xAdvance); } if (yAdvance != 0) { if (!first) { sb.append(", "); } else { first = false; } sb.append("yAdvance = " + yAdvance); } if (xPlaDevice != null) { if (!first) { sb.append(", "); } else { first = false; } sb.append("xPlaDevice = " + xPlaDevice); } if (yPlaDevice != null) { if (!first) { sb.append(", "); } else { first = false; } sb.append("xPlaDevice = " + yPlaDevice); } if (xAdvDevice != null) { if (!first) { sb.append(", "); } else { first = false; } sb.append("xAdvDevice = " + xAdvDevice); } if (yAdvDevice != null) { if (!first) { sb.append(", "); } else { first = false; } sb.append("xAdvDevice = " + yAdvDevice); } sb.append(" }"); return sb.toString(); } } /** * The <code>PairValues</code> class implements a pair value record, comprising a glyph id (or zero) * and two optional positioning values. */ public static class PairValues { private final int glyph; // glyph id (or 0) private final Value value1; // value for first glyph in pair (or null) private final Value value2; // value for second glyph in pair (or null) /** * Instantiate a PairValues. * @param glyph the glyph id (or zero) * @param value1 the value of the first glyph in pair (or null) * @param value2 the value of the second glyph in pair (or null) */ public PairValues(int glyph, Value value1, Value value2) { assert glyph >= 0; this.glyph = glyph; this.value1 = value1; this.value2 = value2; } /** @return the glyph id */ public int getGlyph() { return glyph; } /** @return the first value */ public Value getValue1() { return value1; } /** @return the second value */ public Value getValue2() { return value2; } /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer(); boolean first = true; sb.append("{ "); if (glyph != 0) { if (!first) { sb.append(", "); } else { first = false; } sb.append("glyph = " + glyph); } if (value1 != null) { if (!first) { sb.append(", "); } else { first = false; } sb.append("value1 = " + value1); } if (value2 != null) { if (!first) { sb.append(", "); } else { first = false; } sb.append("value2 = " + value2); } sb.append(" }"); return sb.toString(); } } /** * The <code>Anchor</code> class implements a anchor record, comprising an X,Y coordinate pair, * an optional anchor point index (or -1), and optional X or Y device tables (or null if absent). */ public static class Anchor { private final int x; // xCoordinate (in design units) private final int y; // yCoordinate (in design units) private final int anchorPoint; // anchor point index (or -1) private final DeviceTable xDevice; // x device table private final DeviceTable yDevice; // y device table /** * Instantiate an Anchor (format 1). * @param x the x coordinate * @param y the y coordinate */ public Anchor(int x, int y) { this (x, y, -1, null, null); } /** * Instantiate an Anchor (format 2). * @param x the x coordinate * @param y the y coordinate * @param anchorPoint anchor index (or -1) */ public Anchor(int x, int y, int anchorPoint) { this (x, y, anchorPoint, null, null); } /** * Instantiate an Anchor (format 3). * @param x the x coordinate * @param y the y coordinate * @param xDevice the x device table (or null if not present) * @param yDevice the y device table (or null if not present) */ public Anchor(int x, int y, DeviceTable xDevice, DeviceTable yDevice) { this (x, y, -1, xDevice, yDevice); } /** * Instantiate an Anchor based on an existing anchor. * @param a the existing anchor */ protected Anchor(Anchor a) { this (a.x, a.y, a.anchorPoint, a.xDevice, a.yDevice); } private Anchor(int x, int y, int anchorPoint, DeviceTable xDevice, DeviceTable yDevice) { assert (anchorPoint >= 0) || (anchorPoint == -1); this.x = x; this.y = y; this.anchorPoint = anchorPoint; this.xDevice = xDevice; this.yDevice = yDevice; } /** @return the x coordinate */ public int getX() { return x; } /** @return the y coordinate */ public int getY() { return y; } /** @return the anchor point index (or -1 if not specified) */ public int getAnchorPoint() { return anchorPoint; } /** @return the x device table (or null if not specified) */ public DeviceTable getXDevice() { return xDevice; } /** @return the y device table (or null if not specified) */ public DeviceTable getYDevice() { return yDevice; } /** * Obtain adjustment value required to align the specified anchor * with this anchor. * @param a the anchor to align * @return the adjustment value needed to effect alignment */ public Value getAlignmentAdjustment(Anchor a) { assert a != null; // TODO - handle anchor point // TODO - handle device tables return new Value(x - a.x, y - a.y, 0, 0, null, null, null, null); } /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("{ [" + x + "," + y + "]"); if (anchorPoint != -1) { sb.append(", anchorPoint = " + anchorPoint); } if (xDevice != null) { sb.append(", xDevice = " + xDevice); } if (yDevice != null) { sb.append(", yDevice = " + yDevice); } sb.append(" }"); return sb.toString(); } } /** * The <code>MarkAnchor</code> class is a subclass of the <code>Anchor</code> class, adding a mark * class designation. */ public static class MarkAnchor extends Anchor { private final int markClass; // mark class /** * Instantiate a MarkAnchor * @param markClass the mark class * @param a the underlying anchor (whose fields are copied) */ public MarkAnchor(int markClass, Anchor a) { super(a); this.markClass = markClass; } /** @return the mark class */ public int getMarkClass() { return markClass; } /** {@inheritDoc} */ public String toString() { return "{ markClass = " + markClass + ", anchor = " + super.toString() + " }"; } } }
⏎ org/apache/fop/complexscripts/fonts/GlyphPositioningTable.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, 64355👍, 0💬
Popular Posts:
How to download and install mysql-connector-j-8.0.31 .zip?Connector/J Java library is a JDBC Driver ...
JAX-WS is an API for building web services and clients. It is the next generation Web Services API r...
JDK 11 jdk.crypto.cryptoki.jmod is the JMOD file for JDK 11 Crypto Cryptoki module. JDK 11 Crypto KI...
MXP1 is a stable XmlPull parsing engine that is based on ideas from XPP and in particular XPP2 but c...
How to read XML document from socket connections with the socket\DelayedInput.java provided in the A...