/*
 * 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.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import org.apache.fop.complexscripts.util.CharAssociation;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;

// CSOFF: LineLengthCheck

The GlyphSubstitutionState implements an state object used during glyph substitution processing.

This work was originally authored by Glenn Adams (gadams@apache.org).

/** * <p>The <code>GlyphSubstitutionState</code> implements an state object used during glyph substitution * processing.</p> * * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p> */
public class GlyphSubstitutionState extends GlyphProcessingState {
alternates index
/** alternates index */
private int[] alternatesIndex;
current output glyph sequence
/** current output glyph sequence */
private IntBuffer ogb;
current output glyph to character associations
/** current output glyph to character associations */
private List oal;
character association predications
/** character association predications */
private boolean predications;
Construct default (reset) glyph substitution state.
/** * Construct default (reset) glyph substitution state. */
public GlyphSubstitutionState() { }
Construct glyph substitution state.
Params:
  • gs – input glyph sequence
  • script – script identifier
  • language – language identifier
  • feature – feature identifier
  • sct – script context tester (or null)
/** * Construct 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(GlyphSequence gs, String script, String language, String feature, ScriptContextTester sct) { super(gs, script, language, feature, sct); this.ogb = IntBuffer.allocate(gs.getGlyphCount()); this.oal = new ArrayList(gs.getGlyphCount()); this.predications = gs.getPredications(); }
Construct glyph substitution state using an existing state object using shallow copy except as follows: input glyph sequence is copied deep except for its characters array.
Params:
  • ss – existing positioning state to copy from
/** * Construct glyph substitution state using an existing state object using shallow copy * except as follows: input glyph sequence is copied deep except for its characters array. * @param ss existing positioning state to copy from */
public GlyphSubstitutionState(GlyphSubstitutionState ss) { super(ss); this.ogb = IntBuffer.allocate(indexLast); this.oal = new ArrayList(indexLast); }
Reset glyph substitution state.
Params:
  • gs – input glyph sequence
  • script – script identifier
  • language – language identifier
  • feature – feature identifier
  • sct – script context tester (or null)
/** * 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.
Params:
  • alternates – array of alternates indices ordered by coverage index
/** * Set alternates indices. * @param alternates array of alternates indices ordered by coverage index */
public void setAlternates(int[] alternates) { this.alternatesIndex = alternates; }
Obtain alternates index associated with specified coverage index. An alternates index is used to select among stylistic alternates of a glyph at a particular coverage index. This information must be provided by the document itself (in the form of an extension attribute value), since a font has no way to determine which alternate the user desires.
Params:
  • ci – coverage index
Returns:an alternates index
/** * Obtain alternates index associated with specified coverage index. An alternates * index is used to select among stylistic alternates of a glyph at a particular * coverage index. This information must be provided by the document itself (in the * form of an extension attribute value), since a font has no way to determine which * alternate the user desires. * @param ci coverage index * @return an alternates index */
public int getAlternatesIndex(int ci) { if (alternatesIndex == null) { return 0; } else if ((ci < 0) || (ci > alternatesIndex.length)) { return 0; } else { return alternatesIndex [ ci ]; } }
Put (write) glyph into glyph output buffer.
Params:
  • glyph – to write
  • a – character association that applies to glyph
  • predication – a predication value to add to association A if predications enabled
/** * Put (write) glyph into glyph output buffer. * @param glyph to write * @param a character association that applies to glyph * @param predication a predication value to add to association A if predications enabled */
public void putGlyph(int glyph, CharAssociation a, Object predication) { if (!ogb.hasRemaining()) { ogb = growBuffer(ogb); } ogb.put(glyph); if (predications && (predication != null)) { a.setPredication(feature, predication); } oal.add(a); }
Put (write) array of glyphs into glyph output buffer.
Params:
  • glyphs – to write
  • associations – array of character associations that apply to glyphs
  • predication – optional predicaion object to be associated with glyphs' associations
/** * Put (write) array of glyphs into glyph output buffer. * @param glyphs to write * @param associations array of character associations that apply to glyphs * @param predication optional predicaion object to be associated with glyphs' associations */
public void putGlyphs(int[] glyphs, CharAssociation[] associations, Object predication) { assert glyphs != null; assert associations != null; assert associations.length >= glyphs.length; for (int i = 0, n = glyphs.length; i < n; i++) { putGlyph(glyphs [ i ], associations [ i ], predication); } }
Obtain output glyph sequence.
Returns:newly constructed glyph sequence comprised of original characters, output glyphs, and output associations
/** * Obtain output glyph sequence. * @return newly constructed glyph sequence comprised of original * characters, output glyphs, and output associations */
public GlyphSequence getOutput() { int position = ogb.position(); if (position > 0) { ogb.limit(position); ogb.rewind(); return new GlyphSequence(igs.getCharacters(), ogb, oal); } else { return igs; } }
Apply substitution subtable to current state at current position (only), resulting in the consumption of zero or more input glyphs, and possibly replacing the current input glyphs starting at the current position, in which case it is possible that indexLast is altered to be either less than or greater than its value prior to this application.
Params:
  • st – the glyph substitution subtable to apply
Returns:true if subtable applied, or false if it did not (e.g., its input coverage table did not match current input context)
/** * Apply substitution subtable to current state at current position (only), * resulting in the consumption of zero or more input glyphs, and possibly * replacing the current input glyphs starting at the current position, in * which case it is possible that indexLast is altered to be either less than * or greater than its value prior to this application. * @param st the glyph substitution subtable to apply * @return true if subtable applied, or false if it did not (e.g., its * input coverage table did not match current input context) */
public boolean apply(GlyphSubstitutionSubtable st) { assert st != null; updateSubtableState(st); boolean applied = st.substitute(this); return applied; }
Apply a sequence of matched rule lookups to the nig input glyphs starting at the current position. If lookups are non-null and non-empty, then all input glyphs specified by nig are consumed irregardless of whether any specified lookup applied.
Params:
  • lookups – array of matched lookups (or null)
  • nig – number of glyphs in input sequence, starting at current position, to which the lookups are to apply, and to be consumed once the application has finished
Returns:true if lookups are non-null and non-empty; otherwise, false
/** * Apply a sequence of matched rule lookups to the <code>nig</code> input glyphs * starting at the current position. If lookups are non-null and non-empty, then * all input glyphs specified by <code>nig</code> are consumed irregardless of * whether any specified lookup applied. * @param lookups array of matched lookups (or null) * @param nig number of glyphs in input sequence, starting at current position, to which * the lookups are to apply, and to be consumed once the application has finished * @return true if lookups are non-null and non-empty; otherwise, false */
public boolean apply(GlyphTable.RuleLookup[] lookups, int nig) { // int nbg = index; int nlg = indexLast - (index + nig); int nog = 0; if ((lookups != null) && (lookups.length > 0)) { // apply each rule lookup to extracted input glyph array for (GlyphTable.RuleLookup l : lookups) { if (l != null) { GlyphTable.LookupTable lt = l.getLookup(); if (lt != null) { // perform substitution on a copy of previous state GlyphSubstitutionState ss = new GlyphSubstitutionState(this); // apply lookup table substitutions GlyphSequence gs = lt.substitute(ss, l.getSequenceIndex()); // replace current input sequence starting at current position with result if (replaceInput(0, -1, gs)) { nog = gs.getGlyphCount() - nlg; } } } } // output glyphs and associations putGlyphs(getGlyphs(0, nog, false, null, null, null), getAssociations(0, nog, false, null, null, null), null); // consume replaced input glyphs consume(nog); return true; } else { return false; } }
Apply default application semantices; namely, consume one input glyph, writing that glyph (and its association) to the output glyphs (and associations).
/** * Apply default application semantices; namely, consume one input glyph, * writing that glyph (and its association) to the output glyphs (and associations). */
public void applyDefault() { super.applyDefault(); int gi = getGlyph(); if (gi != 65535) { putGlyph(gi, getAssociation(), null); } } private static IntBuffer growBuffer(IntBuffer ib) { int capacity = ib.capacity(); int capacityNew = capacity * 2; IntBuffer ibNew = IntBuffer.allocate(capacityNew); ib.rewind(); return ibNew.put(ib); } }