com.adobe.xfa.gfx.GFXMappingList Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
package com.adobe.xfa.gfx;
import java.util.Collections;
import java.util.Comparator;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.Storage;
/**
* A mapping list represents the complete set of mappings for some set
* of glyphs and corresponding Unicode characters. The mapping list
* does not store Unicode character values nor Glyph IDs; instead it
* stores simply indexes. The caller must maintain its character and
* glyph arrays separately.
*
* The mapping list is made up of individual mappings. Each mapping
* describes the smallest indivisible relationship between one or more
* characters and one or more glyphs. For example, a mapping list that
* describes a simple 1:1 relationship between N left-to-right
* characters and their corresponding N glyphs would contain N
* individual mappings.
*
*
* Character indexes and lengths refer to actual Unicode characters
* stored elsewhere by the application. If it chooses to use class
* String to store Unicode characters, it needs to be aware that these
* indexes are not the same as the UTF-8 byte indexes prevalent in many
* String method signatures. Fortunately, that class also provides
* Unicode character iteration methods.
*
*
* Both Unicode character indexes and glyph indexes start at zero.
*
* @exclude from published api.
*/
public class GFXMappingList {
public final static int MAPPING_ERR_CHAR_OVERLAP = 0; // TODO: error handling
public final static int MAPPING_ERR_CHAR_UNMAPPED = 1;
public final static int MAPPING_ERR_CHAR_MAP_RANGE = 2;
public final static int MAPPING_ERR_GLYPH_OVERLAP = 3;
public final static int MAPPING_ERR_GLYPH_UNMAPPED = 4;
public final static int MAPPING_ERR_GLYPH_MAP_RANGE = 5;
private final Storage moMappings = new Storage();
/**
* Default constructor.
*
* Constructs a mapping list that is initially empty of any mappings.
*/
public GFXMappingList () {
}
/**
* Constructor that initializes the mapping list.
*
* The caller can provide a size hint to reduce the number of
* reallocations that occur in building the mapping. In addition, the
* caller can request an automatic mapuseful if the text is simple 1:1
* left-to-right Latin, with no substitutions.
* @param nSizeHint - Size to pre-allocate the internal list of
* mappings. This can be larger or smaller than the eventual number of
* mappings; the implementation will not use excess space, and will
* reallocate if necessary when mappings are added.
* @param bAutoMap - If true, the mapping list is initialized to
* represent a simple 1:1 left-to-right Latin mapping, with no
* substitutions. The size hint indicates the number of mappings
* required.
*/
public GFXMappingList (int nSizeHint, boolean bAutoMap) {
if (bAutoMap) {
autoMap (nSizeHint);
} else {
moMappings.ensureCapacity (nSizeHint);
}
}
public GFXMappingList (int nSizeHint) {
this (nSizeHint, false);
}
public GFXMappingList (GFXMappingList source) {
copyFrom (source);
}
/**
* Reset the mapping list to its initial state.
*
* The client typically calls this method after consuming the contents
* of a mapping list, if it wishes to reuse the object instance. There
* is no harm in calling Reset() repeatedly or calling it on a
* just-created mapping list object.
*/
public void reset () {
moMappings.setSize (0);
}
/**
* Repopulate the mapping list with a simple Latin-based 1:1 LTR
* mapping.
*
* This is a convenience method that maps glyphs to characters 1:1 from
* left to right. Any previous contents of the mapping list are first
* removed.
* @param nMappings - Number of 1:1 mappings to create.
*/
public void autoMap (int nMappings) {
moMappings.setSize (nMappings);
for (int i = 0; i < nMappings; i++) {
moMappings.set (i, new GFXMapping (i, i));
}
}
/**
* Add one character/glyph mapping to the line. This overload allows
* the caller to add a single mapping to the list, with complete control
* over the contents of that mapping. For more information, see the
* documentation for class GFXMapping.
* @param oMapping - Mapping to add to the list.
*/
public void addMapping (GFXMapping oMapping) {
moMappings.add (oMapping);
}
/**
* Add one character/glyph mapping to the line.
*
* This is a convenience method that eliminates the need to create a
* GFXMapping object, but does not allow for the rare case of disjoint
* character or glyph ranges.
*
*
* The client specifies the mapping in terms of four parameters
* identifying both a sequential run of characters and a sequential run
* of glyphs. Given the parameter names listed in the method
* declaration, the mapping is interpreted as the nCharLength characters
* in the Unicode text, starting at index nCharIndex, map to the
* nGlyphLength glyphs in the rendering starting at nGlyphIndex.
*
*
* Note that it takes multiple mappings to ensure all characters and
* glyphs are mapped, even when the mapping for each is 1:1. For
* example, specifying that two consecutive characters map to two
* consecutive glyphs in no way implies that the first character of the
* pair maps to the first glyph of the pair. It simply means there is a
* mapping between the pair of glyphs and the pair of characters that
* cannot be further refined.
*
*
* Typically (though not always) at least one length parameter has a
* value of one; often both lengths have a value of one.
*
* @param nCharStart - Starting character index of the mapping.
* @param nGlyphStart - Starting glyph index of the mapping.
* @param nCharLength - Number of consecutive Unicode characters in the
* mapping.
* @param nGlyphLength - Number of consecutive glyphs in the mapping.
*/
public void addMapping (int nCharStart, int nGlyphStart, int nCharLength, int nGlyphLength) {
moMappings.add (new GFXMapping (nCharStart, nGlyphStart, nCharLength, nGlyphLength));
}
public void addMapping (int nCharStart, int nGlyphStart, int nCharLength) {
addMapping (nCharStart, nGlyphStart, nCharLength, 1);
}
public void addMapping (int nCharStart, int nGlyphStart) {
addMapping (nCharStart, nGlyphStart, 1, 1);
}
/**
* Return the number of mappings currently in the list.
* @return Number of mappings currently in the list.
*/
public int getMappingCount () {
return moMappings.size();
}
/**
* Extract one mapping from the list, by index.
* @param nIndex - Index of the desired mapping. Index numbers start at
* zero. Unpredictable results will occur if the index is out of range.
* @return The desired mapping.
*/
public GFXMapping getMapping (int nIndex) {
return moMappings.get (nIndex);
}
/**
* Validate the contents of the mapping list.
*
* Validation ensures that no glyph or character is included in two or
* more mappings. In addition, the caller can request that characters
* and/or glyphs be tested for coverage and overflow. If the client
* specifies the number of characters expected, the validation process
* also ensures that every character index is included in a mapping and
* that no character index exceeds the maximum. The caller can request
* similar testing of glyph indexes by providing the maximum number of
* glyphs expected.
*
*
* This method reports errors as exceptions, using the XTG exception
* mechanism.
*
* @param pnExpectedChars - Pointer to number of characters. If null,
* no character index coverage or overflow testing occurs.
* @param pnExpectedGlyphs - Pointer to number of glyphs. If null, no
* glyph index coverage or overflow testing occurs.
*/
public void validate (int pnExpectedChars, int pnExpectedGlyphs) {
validateMappings (false, pnExpectedChars, MAPPING_ERR_CHAR_OVERLAP, MAPPING_ERR_CHAR_UNMAPPED, MAPPING_ERR_CHAR_MAP_RANGE);
validateMappings (true, pnExpectedGlyphs, MAPPING_ERR_GLYPH_OVERLAP, MAPPING_ERR_GLYPH_UNMAPPED, MAPPING_ERR_GLYPH_MAP_RANGE);
}
public void validate (int pnExpectedChars) {
validate (pnExpectedChars, -1);
}
public void validate () {
validate (-1, -1);
}
/**
* Rearrange the mappings in the list, so that they are ordered by
* lowest character index in each mapping.
*
* This is useful for applications that are driven by the Unicode
* content.
*/
public void orderByCharacter () {
sortMappings (false);
}
/**
* Rearrange the mappings in the list, so that they are ordered by
* lowest glyph index in each mapping.
*
* This is useful for applications that are driven by the rendered
* glyphs.
*/
public void orderByGlyph () {
sortMappings (true);
}
public void copyFrom (GFXMappingList source) {
int size = source.moMappings.size();
moMappings.setSize (size);
for (int i = 0; i < size; i++) {
moMappings.set (i, new GFXMapping (source.getMapping (i)));
}
}
@FindBugsSuppress(pattern="NP_NULL_ON_SOME_PATH") // oVisited
private void validateMappings (boolean bGlyph, int pnExpected, int nErrorDuplicate, int nErrorMissing, int nErrorOverflow) {
int i;
int nMinIndex = 0;
int nIndexCount = 0;
if (pnExpected < 0) {
nMinIndex = Integer.MAX_VALUE;
int nMaxIndex = 0;
for (i = 0; i < moMappings.size(); i++) {
GFXMapping oMapping = getMapping (i);
int nLow = oMapping.getLowestIndex (bGlyph);
int nHigh = oMapping.getHighestIndex (bGlyph);
if (nLow < nMinIndex) {
nMinIndex = nLow;
}
if (nHigh > nMaxIndex) {
nMaxIndex = nHigh;
}
}
if (nMinIndex <= nMaxIndex) {
nIndexCount = nMaxIndex + nMinIndex + 1;
}
}
else {
nMinIndex = 0;
nIndexCount = pnExpected;
}
boolean[] oVisited = null;
if (nIndexCount > 0) {
oVisited = new boolean [nIndexCount];
for (i = 0; i < nIndexCount; i++) {
oVisited[i] = false;
}
}
int nIndexLimit = nMinIndex + nIndexCount;
for (i = 0; i < moMappings.size(); i++) {
GFXMapping oMapping = getMapping (i);
int nCount = oMapping.getCount (bGlyph);
for (int j = 0; j < nCount; j++) {
int nIndex = oMapping.getIndex (bGlyph, j);
if (nIndex >= nIndexLimit) {
throwError (nErrorOverflow, nIndex);
}
int nVisitedIndex = nIndex - nMinIndex;
if ((oVisited != null) && oVisited[nVisitedIndex]) {
throwError (nErrorDuplicate, nIndex);
}
oVisited[nVisitedIndex] = true;
}
}
if (pnExpected >= 0) {
for (i = 0; i < nIndexCount; i++) {
if (! oVisited[i]) {
throwError (nErrorMissing, i);
}
}
}
}
private void sortMappings (final boolean bGlyph) {
Collections.sort(moMappings, new Comparator() {
public int compare(GFXMapping mapping1, GFXMapping mapping2) {
int lowestIndex1 = mapping1.getLowestIndex(bGlyph);
int lowestIndex2 = mapping2.getLowestIndex(bGlyph);
return lowestIndex1 < lowestIndex2 ? -1 :
lowestIndex1 > lowestIndex2 ? 1 : 0;
}
});
}
private static void throwError (int nError, int nValue) {
assert (false);
// FormatPos oFormat (nError);
// oFormat << nValue;
// ExFull oEx (oFormat);
// throw oEx;
}
}