/* * 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 processors = new HashMap(); /** * 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/*>>*/ 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/*>*/ 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/*>*/ 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; } }