blob: 8fabe60286600840ca2e444eed30d7b5b5b53336 [file] [log] [blame]
/*
* 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.fonts;
import java.util.HashMap;
import java.util.Map;
import org.apache.fop.util.CharUtilities;
// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
// CSOFF: ParameterNumberCheck
/**
* Abstract script processor base class for which an implementation of the substitution and positioning methods
* must be supplied.
* @author Glenn Adams
*/
public abstract class ScriptProcessor {
private final String script;
private static Map<String, ScriptProcessor> processors = new HashMap<String, ScriptProcessor>();
/**
* Instantiate a script processor.
* @param script a script identifier
*/
protected ScriptProcessor ( String script ) {
if ( ( script == null ) || ( script.length() == 0 ) ) {
throw new IllegalArgumentException ( "script must be non-empty string" );
} else {
this.script = script;
}
}
/** @return script identifier */
public final String getScript() {
return script;
}
/**
* Obtain script specific required substitution features.
* @return array of suppported substitution features or null
*/
public abstract String[] getSubstitutionFeatures();
/**
* Obtain script specific optional substitution features.
* @return array of suppported substitution features or null
*/
public String[] getOptionalSubstitutionFeatures() {
return new String[0];
}
/**
* Obtain script specific substitution context tester.
* @return substitution context tester or null
*/
public abstract ScriptContextTester getSubstitutionContextTester();
/**
* Perform substitution processing using a specific set of lookup tables.
* @param gsub the glyph substitution table that applies
* @param gs an input glyph sequence
* @param script a script identifier
* @param language a language identifier
* @param lookups a mapping from lookup specifications to glyph subtables to use for substitution processing
* @return the substituted (output) glyph sequence
*/
public final GlyphSequence substitute ( GlyphSubstitutionTable gsub, GlyphSequence gs, String script, String language, Map/*<LookupSpec,List<LookupTable>>>*/ lookups ) {
return substitute ( gs, script, language, assembleLookups ( gsub, getSubstitutionFeatures(), lookups ), getSubstitutionContextTester() );
}
/**
* Perform substitution processing using a specific set of ordered glyph table use specifications.
* @param gs an input glyph sequence
* @param script a script identifier
* @param language a language identifier
* @param usa an ordered array of glyph table use specs
* @param sct a script specific context tester (or null)
* @return the substituted (output) glyph sequence
*/
public GlyphSequence substitute ( GlyphSequence gs, String script, String language, GlyphTable.UseSpec[] usa, ScriptContextTester sct ) {
assert usa != null;
for ( int i = 0, n = usa.length; i < n; i++ ) {
GlyphTable.UseSpec us = usa [ i ];
gs = us.substitute ( gs, script, language, sct );
}
return gs;
}
/**
* Reorder combining marks in glyph sequence so that they precede (within the sequence) the base
* character to which they are applied. N.B. In the case of RTL segments, marks are not reordered by this,
* method since when the segment is reversed by BIDI processing, marks are automatically reordered to precede
* their base glyph.
* @param gdef the glyph definition table that applies
* @param gs an input glyph sequence
* @param gpa associated glyph position adjustments (also reordered)
* @param script a script identifier
* @param language a language identifier
* @return the reordered (output) glyph sequence
*/
public GlyphSequence reorderCombiningMarks ( GlyphDefinitionTable gdef, GlyphSequence gs, int[][] gpa, String script, String language ) {
return gs;
}
/**
* Obtain script specific required positioning features.
* @return array of suppported positioning features or null
*/
public abstract String[] getPositioningFeatures();
/**
* Obtain script specific optional positioning features.
* @return array of suppported positioning features or null
*/
public String[] getOptionalPositioningFeatures() {
return new String[0];
}
/**
* Obtain script specific positioning context tester.
* @return positioning context tester or null
*/
public abstract ScriptContextTester getPositioningContextTester();
/**
* Perform positioning processing using a specific set of lookup tables.
* @param gpos the glyph positioning table that applies
* @param gs an input glyph sequence
* @param script a script identifier
* @param language a language identifier
* @param fontSize size in device units
* @param lookups a mapping from lookup specifications to glyph subtables to use for positioning processing
* @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 final boolean position ( GlyphPositioningTable gpos, GlyphSequence gs, String script, String language, int fontSize, Map/*<LookupSpec,List<LookupTable>>*/ lookups, int[] widths, int[][] adjustments ) {
return position ( gs, script, language, fontSize, assembleLookups ( gpos, getPositioningFeatures(), lookups ), widths, adjustments, getPositioningContextTester() );
}
/**
* Perform positioning processing using a specific set of ordered glyph table use specifications.
* @param gs an input glyph sequence
* @param script a script identifier
* @param language a language identifier
* @param fontSize size in device units
* @param usa an ordered array of glyph table use specs
* @param widths array of default advancements for each glyph in font
* @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
* @param sct a script specific context tester (or null)
* @return true if some adjustment is not zero; otherwise, false
*/
public boolean position ( GlyphSequence gs, String script, String language, int fontSize, GlyphTable.UseSpec[] usa, int[] widths, int[][] adjustments, ScriptContextTester sct ) {
assert usa != null;
boolean adjusted = false;
for ( int i = 0, n = usa.length; i < n; i++ ) {
GlyphTable.UseSpec us = usa [ i ];
if ( us.position ( gs, script, language, fontSize, widths, adjustments, sct ) ) {
adjusted = true;
}
}
return adjusted;
}
/**
* Assemble ordered array of lookup table use specifications according to the specified features and candidate lookups,
* where the order of the array is in accordance to the order of the applicable lookup list.
* @param table the governing glyph table
* @param features array of feature identifiers to apply
* @param lookups a mapping from lookup specifications to lists of look tables from which to select lookup tables according to the specified features
* @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 );
}
/**
* Obtain script processor instance associated with specified script.
* @param script a script identifier
* @return a script processor instance or null if none found
*/
public static synchronized ScriptProcessor getInstance ( String script ) {
ScriptProcessor sp = null;
assert processors != null;
if ( ( sp = processors.get ( script ) ) == null ) {
processors.put ( script, sp = createProcessor ( script ) );
}
return sp;
}
// [TBD] - rework to provide more configurable binding between script name and script processor constructor
private static ScriptProcessor createProcessor ( String script ) {
ScriptProcessor sp = null;
if ( "arab".equals ( script ) ) {
sp = new ArabicScriptProcessor ( script );
} else if ( CharUtilities.isIndicScript ( script ) ) {
sp = IndicScriptProcessor.makeProcessor ( script );
} else {
sp = new DefaultScriptProcessor ( script );
}
return sp;
}
}