/** if true, then some adjustment was applied */
private boolean adjusted;
+ /**
+ * Construct default (reset) glyph positioning state.
+ */
+ public GlyphPositioningState() {
+ }
+
/**
* Construct glyph positioning state.
* @param gs input glyph sequence
this.adjustments = ps.adjustments;
}
+ /**
+ * Reset glyph positioning state.
+ * @param gs input glyph sequence
+ * @param script script identifier
+ * @param language language identifier
+ * @param feature feature identifier
+ * @param fontSize font size (in micropoints)
+ * @param widths array of design advancements (in glyph index order)
+ * @param adjustments positioning adjustments to which positioning is applied
+ * @param sct script context tester (or null)
+ */
+ public GlyphPositioningState reset ( GlyphSequence gs, String script, String language, String feature, int fontSize, int[] widths, int[][] adjustments, ScriptContextTester sct ) {
+ super.reset ( gs, script, language, feature, sct );
+ this.fontSize = fontSize;
+ this.widths = widths;
+ this.adjustments = adjustments;
+ this.adjusted = false;
+ return this;
+ }
+
/**
* Obtain design advancement (width) of glyph at specified index.
* @param gi glyph index
*/
public abstract class GlyphPositioningSubtable extends GlyphSubtable implements GlyphPositioning {
+ private static final GlyphPositioningState state = new GlyphPositioningState();
+
/**
* Instantiate a <code>GlyphPositioningSubtable</code>.
* @param id subtable identifier
* @return true if a non-zero adjustment occurred
*/
public static final boolean position ( GlyphSequence gs, String script, String language, String feature, int fontSize, GlyphPositioningSubtable[] sta, int[] widths, int[][] adjustments, ScriptContextTester sct ) {
- return position ( new GlyphPositioningState ( gs, script, language, feature, fontSize, widths, adjustments, sct ), sta, -1 );
+ synchronized ( state ) {
+ return position ( state.reset ( gs, script, language, feature, fontSize, widths, adjustments, sct ), sta, -1 );
+ }
}
}
/** current subtable */
private GlyphSubtable subtable;
+ /**
+ * Construct default (reset) glyph processing state.
+ */
+ public GlyphProcessingState() {
+ }
+
/**
* Construct glyph processing state.
* @param gs input glyph sequence
setPosition ( s.index );
}
+ /**
+ * Reset glyph processing state.
+ * @param gs input glyph sequence
+ * @param script script identifier
+ * @param language language identifier
+ * @param feature feature identifier
+ * @param sct script context tester (or null)
+ */
+ protected GlyphProcessingState reset ( GlyphSequence gs, String script, String language, String feature, ScriptContextTester sct ) {
+ this.gdef = null;
+ this.script = script;
+ this.language = language;
+ this.feature = feature;
+ this.igs = gs;
+ this.index = 0;
+ this.indexLast = gs.getGlyphCount();
+ this.consumed = 0;
+ this.lookupFlags = 0;
+ this.classMatchSet = 0;
+ this.sct = sct;
+ this.gct = ( sct != null ) ? sct.getTester ( feature ) : null;
+ this.ignoreBase = new GlyphTester() { public boolean test(int gi, int flags) { return isIgnoredBase(gi, flags); } };
+ this.ignoreLigature = new GlyphTester() { public boolean test(int gi, int flags) { return isIgnoredLigature(gi, flags); } };
+ this.ignoreMark = new GlyphTester() { public boolean test(int gi, int flags) { return isIgnoredMark(gi, flags); } };
+ this.ignoreDefault = null;
+ this.subtable = null;
+ return this;
+ }
+
/**
* Set governing glyph definition table.
* @param gdef glyph definition table (or null, to unset)
/** character association predications */
private boolean predications;
+ /**
+ * Construct default (reset) glyph substitution state.
+ */
+ public GlyphSubstitutionState() {
+ }
+
/**
* Construct glyph substitution state.
* @param gs input glyph sequence
this.oal = new ArrayList ( indexLast );
}
+ /**
+ * Reset glyph substitution state.
+ * @param gs input glyph sequence
+ * @param script script identifier
+ * @param language language identifier
+ * @param feature feature identifier
+ * @param sct script context tester (or null)
+ */
+ public GlyphSubstitutionState reset ( GlyphSequence gs, String script, String language, String feature, ScriptContextTester sct ) {
+ super.reset ( gs, script, language, feature, sct );
+ this.alternatesIndex = null;
+ this.ogb = IntBuffer.allocate ( gs.getGlyphCount() );
+ this.oal = new ArrayList ( gs.getGlyphCount() );
+ this.predications = gs.getPredications();
+ return this;
+ }
+
/**
* Set alternates indices.
* @param alternates array of alternates indices ordered by coverage index
*/
public abstract class GlyphSubstitutionSubtable extends GlyphSubtable implements GlyphSubstitution {
+ private static final GlyphSubstitutionState state = new GlyphSubstitutionState();
+
/**
* Instantiate a <code>GlyphSubstitutionSubtable</code>.
* @param id subtable identifier
* @return output glyph sequence
*/
public static final GlyphSequence substitute ( GlyphSequence gs, String script, String language, String feature, GlyphSubstitutionSubtable[] sta, ScriptContextTester sct ) {
- return substitute ( new GlyphSubstitutionState ( gs, script, language, feature, sct ), sta, -1 );
+ synchronized ( state ) {
+ return substitute ( state.reset ( gs, script, language, feature, sct ), sta, -1 );
+ }
}
}
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
// map from lookup identifiers to lookup tables
private Map/*<String,LookupTable>*/ lookupTables;
+ // cache for lookups matching
+ private Map/*<LookupSpec,Map<LookupSpec,List<LookupTable>>>*/ matchedLookups;
+
// if true, then prevent further subtable addition
private boolean frozen;
this.gdef = gdef;
this.lookups = lookups;
this.lookupTables = new LinkedHashMap/*<String,List<LookupTable>>*/();
+ this.matchedLookups = new HashMap/*<LookupSpec,Map<LookupSpec,List<LookupTable>>>*/();
}
}
* @return a (possibly empty) map from matching lookup specifications to lists of corresponding lookup tables
*/
public Map/*<LookupSpec,List<LookupTable>>*/ matchLookups ( String script, String language, String feature ) {
- List/*<LookupSpec>*/ lsl = matchLookupSpecs ( script, language, feature );
- Map lm = new LinkedHashMap();
- for ( Iterator it = lsl.iterator(); it.hasNext(); ) {
- LookupSpec ls = (LookupSpec) it.next();
- lm.put ( ls, findLookupTables ( ls ) );
+ LookupSpec lsm = new LookupSpec ( script, language, feature, true, true );
+ Map/*<LookupSpec,List<LookupTable>>*/ lm = (Map/*<LookupSpec,List<LookupTable>>*/) matchedLookups.get ( lsm );
+ if ( lm == null ) {
+ lm = new LinkedHashMap();
+ List/*<LookupSpec>*/ lsl = matchLookupSpecs ( script, language, feature );
+ for ( Iterator it = lsl.iterator(); it.hasNext(); ) {
+ LookupSpec ls = (LookupSpec) it.next();
+ lm.put ( ls, findLookupTables ( ls ) );
+ }
+ matchedLookups.put ( lsm, lm );
}
return lm;
}
* @param feature a feature identifier
*/
public LookupSpec ( String script, String language, String feature ) {
- if ( ( script == null ) || ( script.length() == 0 ) ) {
+ this ( script, language, feature, false, false );
+ }
+
+ /**
+ * Instantiate lookup spec.
+ * @param script a script identifier
+ * @param language a language identifier
+ * @param feature a feature identifier
+ * @param permitEmpty if true the permit empty script, language, or feature
+ * @param permitWildcard if true the permit wildcard script, language, or feature
+ */
+ LookupSpec ( String script, String language, String feature, boolean permitEmpty, boolean permitWildcard ) {
+ if ( ( script == null ) || ( ! permitEmpty && ( script.length() == 0 ) ) ) {
throw new AdvancedTypographicTableFormatException ( "script must be non-empty string" );
- } else if ( ( language == null ) || ( language.length() == 0 ) ) {
+ } else if ( ( language == null ) || ( ! permitEmpty && ( language.length() == 0 ) ) ) {
throw new AdvancedTypographicTableFormatException ( "language must be non-empty string" );
- } else if ( ( feature == null ) || ( feature.length() == 0 ) ) {
+ } else if ( ( feature == null ) || ( ! permitEmpty && ( feature.length() == 0 ) ) ) {
throw new AdvancedTypographicTableFormatException ( "feature must be non-empty string" );
- } else if ( script.equals("*") ) {
+ } else if ( ! permitWildcard && script.equals("*") ) {
throw new AdvancedTypographicTableFormatException ( "script must not be wildcard" );
- } else if ( language.equals("*") ) {
+ } else if ( ! permitWildcard && language.equals("*") ) {
throw new AdvancedTypographicTableFormatException ( "language must not be wildcard" );
- } else if ( feature.equals("*") ) {
+ } else if ( ! permitWildcard && feature.equals("*") ) {
throw new AdvancedTypographicTableFormatException ( "feature must not be wildcard" );
- } else {
- this.script = script.trim();
- this.language = language.trim();
- this.feature = feature.trim();
}
+ this.script = script.trim();
+ this.language = language.trim();
+ this.feature = feature.trim();
}
/** @return script identifier */
package org.apache.fop.complexscripts.scripts;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
private final String script;
+ private final Map/*<AssembledLookupsKey,GlyphTable.UseSpec[]>*/ assembledLookups;
+
private static Map<String, ScriptProcessor> processors = new HashMap<String, ScriptProcessor>();
/**
throw new IllegalArgumentException ( "script must be non-empty string" );
} else {
this.script = script;
+ this.assembledLookups = new HashMap/*<AssembledLookupsKey,GlyphTable.UseSpec[]>*/();
}
}
* @return ordered array of assembled lookup table use specifications
*/
public final GlyphTable.UseSpec[] assembleLookups ( GlyphTable table, String[] features, Map/*<LookupSpec,List<LookupTable>>*/ lookups ) {
- return table.assembleLookups ( features, lookups );
+ AssembledLookupsKey key = new AssembledLookupsKey ( table, features, lookups );
+ GlyphTable.UseSpec[] usa;
+ if ( ( usa = assembledLookupsGet ( key ) ) != null ) {
+ return usa;
+ } else {
+ return assembledLookupsPut ( key, table.assembleLookups ( features, lookups ) );
+ }
+ }
+
+ private GlyphTable.UseSpec[] assembledLookupsGet ( AssembledLookupsKey key ) {
+ return (GlyphTable.UseSpec[]) assembledLookups.get ( key );
+ }
+
+ private GlyphTable.UseSpec[] assembledLookupsPut ( AssembledLookupsKey key, GlyphTable.UseSpec[] usa ) {
+ assembledLookups.put ( key, usa );
+ return usa;
}
/**
return sp;
}
+ private static class AssembledLookupsKey {
+
+ private final GlyphTable table;
+ private final String[] features;
+ private final Map/*<LookupSpec,List<LookupTable>>*/ lookups;
+
+ AssembledLookupsKey ( GlyphTable table, String[] features, Map/*<LookupSpec,List<LookupTable>>*/ lookups ) {
+ this.table = table;
+ this.features = features;
+ this.lookups = lookups;
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ int hc = 0;
+ hc = 7 * hc + ( hc ^ table.hashCode() );
+ hc = 11 * hc + ( hc ^ Arrays.hashCode ( features ) );
+ hc = 17 * hc + ( hc ^ lookups.hashCode() );
+ return hc;
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals ( Object o ) {
+ if ( o instanceof AssembledLookupsKey ) {
+ AssembledLookupsKey k = (AssembledLookupsKey) o;
+ if ( ! table.equals ( k.table ) ) {
+ return false;
+ } else if ( ! Arrays.equals ( features, k.features ) ) {
+ return false;
+ } else if ( ! lookups.equals ( k.lookups ) ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ }
+
}
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Renderers" dev="GA" type="fix" fixes-bug="FOP-2191">
+ Cache matched lookups, assembled lookup spec uses; reduce glyph processing state allocation.
+ </action>
<action context="Renderers" dev="GA" type="fix" fixes-bug="FOP-2188">
Optimize string allocation in PDF output processing.
</action>