/* * 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.util; import java.nio.IntBuffer; import java.util.ArrayList; import java.util.List; // CSOFF: LineLengthCheck /** *
A GlyphSequence encapsulates a sequence of character codes, a sequence of glyph codes, * and a sequence of character associations, where, for each glyph in the sequence of glyph * codes, there is a corresponding character association. Character associations server to * relate the glyph codes in a glyph sequence to the specific characters in an original * character code sequence with which the glyph codes are associated.
* *This work was originally authored by Glenn Adams (gadams@apache.org).
*/ public class GlyphSequence implements Cloneable { /** default character buffer capacity in case new character buffer is created */ private static final int DEFAULT_CHARS_CAPACITY = 8; /** character buffer */ private IntBuffer characters; /** glyph buffer */ private IntBuffer glyphs; /** association list */ private List associations; /** predications flag */ private boolean predications; /** * Instantiate a glyph sequence, reusing (i.e., not copying) the referenced * character and glyph buffers and associations. If characters is null, then * an empty character buffer is created. If glyphs is null, then a glyph buffer * is created whose capacity is that of the character buffer. If associations is * null, then identity associations are created. * @param characters a (possibly null) buffer of associated (originating) characters * @param glyphs a (possibly null) buffer of glyphs * @param associations a (possibly null) array of glyph to character associations * @param predications true if predications are enabled */ public GlyphSequence(IntBuffer characters, IntBuffer glyphs, List associations, boolean predications) { if (characters == null) { characters = IntBuffer.allocate(DEFAULT_CHARS_CAPACITY); } if (glyphs == null) { glyphs = IntBuffer.allocate(characters.capacity()); } if (associations == null) { associations = makeIdentityAssociations(characters.limit(), glyphs.limit()); } this.characters = characters; this.glyphs = glyphs; this.associations = associations; this.predications = predications; } /** * Instantiate a glyph sequence, reusing (i.e., not copying) the referenced * character and glyph buffers and associations. If characters is null, then * an empty character buffer is created. If glyphs is null, then a glyph buffer * is created whose capacity is that of the character buffer. If associations is * null, then identity associations are created. * @param characters a (possibly null) buffer of associated (originating) characters * @param glyphs a (possibly null) buffer of glyphs * @param associations a (possibly null) array of glyph to character associations */ public GlyphSequence(IntBuffer characters, IntBuffer glyphs, List associations) { this (characters, glyphs, associations, false); } /** * Instantiate a glyph sequence using an existing glyph sequence, where the new glyph sequence shares * the character array of the existing sequence (but not the buffer object), and creates new copies * of glyphs buffer and association list. * @param gs an existing glyph sequence */ public GlyphSequence(GlyphSequence gs) { this (gs.characters.duplicate(), copyBuffer(gs.glyphs), copyAssociations(gs.associations), gs.predications); } /** * Instantiate a glyph sequence using an existing glyph sequence, where the new glyph sequence shares * the character array of the existing sequence (but not the buffer object), but uses the specified * backtrack, input, and lookahead glyph arrays to populate the glyphs, and uses the specified * of glyphs buffer and association list. * backtrack, input, and lookahead association arrays to populate the associations. * @param gs an existing glyph sequence * @param bga backtrack glyph array * @param iga input glyph array * @param lga lookahead glyph array * @param bal backtrack association list * @param ial input association list * @param lal lookahead association list */ public GlyphSequence(GlyphSequence gs, int[] bga, int[] iga, int[] lga, CharAssociation[] bal, CharAssociation[] ial, CharAssociation[] lal) { this (gs.characters.duplicate(), concatGlyphs(bga, iga, lga), concatAssociations(bal, ial, lal), gs.predications); } /** * Obtain reference to underlying character buffer. * @return character buffer reference */ public IntBuffer getCharacters() { return characters; } /** * Obtain array of characters. Ifcopy
is true, then
* a newly instantiated array is returned, otherwise a reference to
* the underlying buffer's array is returned. N.B. in case a reference
* to the undelying buffer's array is returned, the length
* of the array is not necessarily the number of characters in array.
* To determine the number of characters, use {@link #getCharacterCount}.
* @param copy true if to return a newly instantiated array of characters
* @return array of characters
*/
public int[] getCharacterArray(boolean copy) {
if (copy) {
return toArray(characters);
} else {
return characters.array();
}
}
/**
* Obtain the number of characters in character array, where
* each character constitutes a unicode scalar value.
* @return number of characters available in character array
*/
public int getCharacterCount() {
return characters.limit();
}
/**
* Obtain glyph id at specified index.
* @param index to obtain glyph
* @return the glyph identifier of glyph at specified index
* @throws IndexOutOfBoundsException if index is less than zero
* or exceeds last valid position
*/
public int getGlyph(int index) throws IndexOutOfBoundsException {
return glyphs.get(index);
}
/**
* Set glyph id at specified index.
* @param index to set glyph
* @param gi glyph index
* @throws IndexOutOfBoundsException if index is greater or equal to
* the limit of the underlying glyph buffer
*/
public void setGlyph(int index, int gi) throws IndexOutOfBoundsException {
if (gi > 65535) {
gi = 65535;
}
glyphs.put(index, gi);
}
/**
* Obtain reference to underlying glyph buffer.
* @return glyph buffer reference
*/
public IntBuffer getGlyphs() {
return glyphs;
}
/**
* Obtain count glyphs starting at offset. If count
is
* negative, then it is treated as if the number of available glyphs
* were specified.
* @param offset into glyph sequence
* @param count of glyphs to obtain starting at offset, or negative,
* indicating all avaialble glyphs starting at offset
* @return glyph array
*/
public int[] getGlyphs(int offset, int count) {
int ng = getGlyphCount();
if (offset < 0) {
offset = 0;
} else if (offset > ng) {
offset = ng;
}
if (count < 0) {
count = ng - offset;
}
int[] ga = new int [ count ];
for (int i = offset, n = offset + count, k = 0; i < n; i++) {
if (k < ga.length) {
ga [ k++ ] = glyphs.get(i);
}
}
return ga;
}
/**
* Obtain array of glyphs. If copy
is true, then
* a newly instantiated array is returned, otherwise a reference to
* the underlying buffer's array is returned. N.B. in case a reference
* to the undelying buffer's array is returned, the length
* of the array is not necessarily the number of glyphs in array.
* To determine the number of glyphs, use {@link #getGlyphCount}.
* @param copy true if to return a newly instantiated array of glyphs
* @return array of glyphs
*/
public int[] getGlyphArray(boolean copy) {
if (copy) {
return toArray(glyphs);
} else {
return glyphs.array();
}
}
/**
* Obtain the number of glyphs in glyphs array, where
* each glyph constitutes a font specific glyph index.
* @return number of glyphs available in character array
*/
public int getGlyphCount() {
return glyphs.limit();
}
/**
* Obtain association at specified index.
* @param index into associations array
* @return glyph to character associations at specified index
* @throws IndexOutOfBoundsException if index is less than zero
* or exceeds last valid position
*/
public CharAssociation getAssociation(int index) throws IndexOutOfBoundsException {
return (CharAssociation) associations.get(index);
}
/**
* Obtain reference to underlying associations list.
* @return associations list
*/
public List getAssociations() {
return associations;
}
/**
* Obtain count associations starting at offset.
* @param offset into glyph sequence
* @param count of associations to obtain starting at offset, or negative,
* indicating all avaialble associations starting at offset
* @return associations
*/
public CharAssociation[] getAssociations(int offset, int count) {
int ng = getGlyphCount();
if (offset < 0) {
offset = 0;
} else if (offset > ng) {
offset = ng;
}
if (count < 0) {
count = ng - offset;
}
CharAssociation[] aa = new CharAssociation [ count ];
for (int i = offset, n = offset + count, k = 0; i < n; i++) {
if (k < aa.length) {
aa [ k++ ] = (CharAssociation) associations.get(i);
}
}
return aa;
}
/**
* Enable or disable predications.
* @param enable true if predications are to be enabled; otherwise false to disable
*/
public void setPredications(boolean enable) {
this.predications = enable;
}
/**
* Obtain predications state.
* @return true if predications are enabled
*/
public boolean getPredications() {
return this.predications;
}
/**
* Set predication