![JAR search and dependency download from the Maven repository](/logo.png)
com.adobe.xfa.text.TextLayoutLine Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
package com.adobe.xfa.text;
import com.adobe.xfa.gfx.GFXMapping;
import com.adobe.xfa.gfx.GFXMappingList;
import com.adobe.xfa.ut.Storage;
import com.adobe.xfa.ut.UnitSpan;
/**
* This class represents an instance of a line in a text layout. The
* client creates an instance of this class, adds runs to it and then
* adds the line to the layout. A line caches three pieces of
* information:
*
* -
* The Unicode content of the line
*
* -
* The runs that make up the rendering of the line
*
* -
* A set of mappings defining the relationships between the Unicode
* content characters and the glyphs in the runs
*
*
*
* Except for an empty line, a line is considered complete only if it
* has content, runs and a complete mapping that covers all glyphs and
* characters. An error will occur if the client attempts to add an
* incomplete line to a layout object. If a client doesnt have access
* to the rendering information, it should populate the text stream
* through the traditional content-based API methods.
*
*
* Once editing is complete and the client has requested a new layout
* from the edited stream, it can retrieve the individual lines from the
* new layout, and then extract the runs from each line.
*
* @exclude from published api.
*/
public class TextLayoutLine {
/**
* @exclude from published api.
*/
static class CharRun {
final TextAttr mpoAttr;
final TextGlyphRun mpoGlyphRun;
final int mnCharIndex;
final int mnCharLength;
public CharRun (TextAttr poAttr, TextGlyphRun poGlyphRun, int nCharIndex, int nCharLength) {
mpoAttr = poAttr;
mpoGlyphRun = poGlyphRun;
mnCharIndex = nCharIndex;
mnCharLength = nCharLength;
}
}
/**
* This enumeration describes the possible conditions in effect at the
* start and end of each line.
*
*
* Note that the value at the end of one line is the same as the value
* at the start of the next one. However, there are times when a
* previous line is not currently available (e.g., it is in a different
* frame--one that hasn't been loaded yet). Therefore AXTE requires
* that both start and end be attributed with these values.
*
*
* When loading layout into a text stream and its display, AXTE uses
* both the start and end states to control a number of aspects of that
* load operation. Of special note is the relationship between the last
* character in the line's Unicode content and the line's ending state.
* The following table describes the states and the last content
* character processing.
*
*
* This enumeration also identifies special cases resulting from
* hyphenation. Hyphenation typically results in the display of extra
* glyphs not included in the original content (e.g., the hyphen
* character). Additionally, hyphenation may result in spelling
* changes. When hyphenation appears in text layout, the glyphs provide
* the visual representation of the hyphenated content, while the line
* content contains the original text. Note that the hyphenated word
* will be split across the content of two lines. The client
* application must faithfully preserve the line start and end states,
* the glyphs and the line content in order to ensure that AXTE will
* stitch the content back together properly.
*
*
* -
*
* LINE_END_WORD_WRAP: This is the normal case. The line break
* was caused by word-wrapping during layout and does not exist as an
* explicit break in the Unicode content. There is no hyphenation
* involved in this line break.
*
*
* AXTE assumes that all content for the line is present. In most
* languages, this means that the line ends in a space at which the
* word-wrapping break occurred in the authoring application. If the
* Unicode content doesn't end in a space, AXTE assumes that the content
* at the end of this line runs together with the content at the start
* of the next line. This would be the case if the authoring
* application used dictionary-based line breaking (e.g., for Thai).
*
*
* When AXTE creates a layout from stream content and word-wrapping
* occurs at a space, that space will be placed at the end of the line,
* immediately after the word preceding the break. If a line break
* occurs due to dictionary-based line breaking or because a single word
* is larger than the entire line width, no such space appears.
*
*
* -
*
* LINE_END_HYPHEN: This value indicates the presence of
* hyphenation. As a line end state, it indicates that the last
* (logical) word in the line has been hyphenated, and therefore
* continues on the next line. As a line start state, it indicates that
* the first word of the line started on the previous line and continues
* on this one.
*
*
* Different languages indicate hyphenation in different ways. There
* may be one or more glyphs (for example a hyphen) added to the end of
* the line before the hyphen break and/or there may be glyphs added to
* the start of the next line. These embellishments are
* language-dependent.
*
*
* When AXTE creates a layout from hyphenated stream content, the line
* content will represent the original text, while the glyphs will
* represent the hyphenated display. The client application must
* preserve these faithfully and hand the same data back to AXTE when
* reconstructing text from layout.
*
*
* -
*
* LINE_END_LAST_WORD: Conceptually this is very similar to
* LINE_END_WORD_WRAP. It indicates that normal word-wrapping took
* place and there was no hyphenation. However, it also signifies that
* hyphenation wasn't attempted because the line after the break
* contained a single word (the last word of a paragraph is not normally
* hyphenated).
*
*
* The client must preserve this value and restore it to AXTE when
* loading layout into AXTE content. AXTE's incremental layout
* algorithm needs to distinguish this case from a "normal"
* word-wrapping break, otherwise hyphenation may not occur at
* appropriate times during incremental layout.
*
*
* -
*
* LINE_END_EMERGENCY: The line break was forced by emergency
* line breaking. This occurs if a word is too large to fit on a line
* by itself, and it could not be hyphenated.
*
*
* There wouldn't normally be a space at the end of the line before the
* break in this case. When reconstructing content from layout, AXTE
* assumes that the text of the previous line abuts the text of the next
* line, and doesn't insert any space.
*
*
* -
*
* LINE_END_FORCED: The line break was forced by Unicode content
* but is not a paragraph break. For example a line break
* character (U+000A) would cause this situation.
*
*
* If the line's Unicode content ends with a line break character, AXTE
* assumes that that character caused the break. The mapped glyph in
* the line's glyph runs must be the glyph for the space character. If
* the line's content does not end with a line break character, AXTE
* will insert one into the text stream content at the end of the line
* and add a space glyph corresponding to it.
*
*
* When generating layout from stream content, if the AXTE stream
* content contains a line break character, AXTE will place one at the
* end of the line, manufacture a space glyph corresponding to it, and
* set the line's end state to LINE_END_FORCED.
*
*
* -
*
* LINE_END_PARA: This indicates that a paragraph break caused
* the break between lines.
*
*
* If the line's Unicode content ends with a paragraph separator
* character (U+2029), AXTE replaces it with its own internal
* paragraph marker in the stream content. The corresponding glyph must
* be the glyph for the space character. If the line's content does not
* end with a paragraph separator character, AXTE will insert an
* internal paragraph marker into the text stream content at the end of
* the line and add a space glyph corresponding to it.
*
*
* When generating layout from stream content, if the AXTE stream
* content contains a paragraph marker, AXTE will place a Unicode
* paragraph separator character at the end of the line, manufacture a
* space glyph corresponding to it, and set the line's end state to
* LINE_END_PARA.
*
*
* -
*
* LINE_END_ULTIMATE: Reserved for the start of the first line
* (in the first frame) and the end of the last line (in the last
* frame).
*
*
* The absolute start and end of text do not have corresponding
* characters in the lines Unicode content.
*
*
* When AXTE creates a layout from stream content, AXTE will designate
* the start of the first line and the end of the last line with this
* value.
*
*
*
*
*/
public static final int LINE_END_WORD_WRAP = 0;
public static final int LINE_END_HYPHEN = 1;
public static final int LINE_END_LAST_WORD = 2;
public static final int LINE_END_EMERGENCY = 3;
public static final int LINE_END_FORCED = 4;
public static final int LINE_END_PARA = 5;
public static final int LINE_END_ULTIMATE = 6;
private String msContent = "";
private int mnChars;
private final Storage moRuns = new Storage();
private int mnGlyphs;
private GFXMappingList moMappings = new GFXMappingList();
private Storage moCharRuns = new Storage();
private int meLineStartState = LINE_END_WORD_WRAP;
private int meLineEndState = LINE_END_WORD_WRAP;
private UnitSpan moFullAscent;
private UnitSpan moFullDescent;
/**
* Default constructor.
*
* The line initially has no content, runs or mappings; and effectively
* describes an empty line.
*/
public TextLayoutLine () {
}
/**
* Copy Constructor.
*
* Initializes the line to be a copy of the given source line. Note
* that runs are copied by reference only.
* @param oSource - Source line to copy.
*/
public TextLayoutLine (TextLayoutLine oSource) {
copyFrom (oSource);
}
/**
* Reset the layout line object to its initial state.
*
* The client typically calls this method after adding a line to a
* layout if it wishes to reuse the line object. There is no harm in
* calling Reset() repeatedly or calling it on a just-created line
* object.
*/
public void reset () {
cleanup();
}
/**
* Add a run to the line.
* @param poRun - Pointer to run object to add to the line. Note that
* AXTE always caches its own copies of objects. In other words, a
* clone of the given run will be added to the line. The client is free
* to delete the run identified by this parameter as soon as the call
* returns.
* @return Pointer to the cloned run added to the line. This object is
* owned by AXTE and should not be deleted by the client. However, the
* client may wish to retain it for tracking subsequent changes.
*/
public TextGlyphRun addRun (TextGlyphRun poRun) {
TextGlyphRun poCopy = new TextGlyphRun (poRun);
moRuns.add (poCopy);
mnGlyphs += poCopy.getGlyphCount();
return poCopy;
}
/**
* Add a run to the line, by reference.
* @param poRun - Pointer to run object to add to the line. The run is
* assumed to be reference counted and AXTE will simply add its own
* reference. Therefore, the caller must not explicitly delete this
* object prematurely. This method exists to prevent unnecessary
* copying.
*/
public void addRunRef (TextGlyphRun poRun) {
moRuns.add (poRun);
mnGlyphs += poRun.getGlyphCount();
}
/**
* Return the number of runs in the line.
* @return Number of runs in the line.
*/
public int getRunCount () {
return moRuns.size();
}
/**
* Get one run from the line.
* @param nIndex - Index of run to retrieve.
* @return Pointer to the requested run.
*/
public TextGlyphRun getRun (int nIndex) {
return moRuns.get (nIndex);
}
/**
* Return the total number of glyphs in the line.
* @return Total number of glyphs in the line (accumulated across all
* runs).
*/
public int getGlyphCount () {
return mnGlyphs;
}
/**
* Specify the line's Unicode content.
*
*
* Content must be provided at the line level--as opposed to the run
* level--because it is possible for a single Unicode content character
* to span a run boundary.
*
*
* This method is not cumulative. The client must provide all the
* Unicode content in a single call; multiple calls to this method for a
* given line instance simply replace the content previously specified.
*
* @param sContent - Unicode content for the line.
*/
public void setContent (String sContent) {
msContent = sContent;
mnChars = sContent.length();
// mnChars = 0;
// for (int i = 0; i < msContent.length(); msContent.uniChar (i)) {
// mnChars++;
// }
}
/**
* Get the line's content.
* @return Unicode content of the line.
*/
public String getContent () {
return msContent;
}
/**
* Return the number of Unicode characters in the lines Unicode content.
* @return Number of Unicode characters in the line. Will differ from
* the number of UTF-8 bytes in the content string for non-Latin text.
*/
public int getCharCount () {
return mnChars;
}
/**
* Set the mappings for the line.
*
*
* Note: methods SetMappings() and GetMappings() are the intended way to
* manipulate and access the lines mappings. Other mapping methods
* below will remain in the interface for some time to come, but are
* considered deprecated.
*
*
* Within the given mapping list object, Unicode character indexes and
* lengths refer to the implicit Unicode characters from the String
* object provided in the SetContent() method. This is different from
* the UTF-8 bytes that make up the actual storage of the String
* object. Unfortunately many String methods accept index values
* specified in terms of UTF-8 bytes for historical reasons. However,
* that class also provides Unicode character methods, and those are
* used internally by AXTE. Unicode character indexes start at zero.
*
*
* When a mapping is specified on a line object, the range of glyph
* indexes spans all runs in the line. The first glyph of the first run
* has index number zero. The index number for the first glyph of the
* second run is equal to the number of glyphs in the first run.
*
* @param oMappings - Complete set of mappings for the line.
*/
public void setMappings (GFXMappingList oMappings) {
moMappings = new GFXMappingList (oMappings);
}
/**
* Return a description of the line's mappings.
* @return The line's mappings.
*/
public GFXMappingList getMappings () {
return moMappings;
}
/**
* (deprecated) Specify one character/glyph mapping for the line.
*
*
* The client specifies the mapping in terms of four parameters
* identifying both a sequential run of glyphs (in the line's rendering)
* and a sequential run of characters (in its Unicode content). 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 at least one length parameter has a value of one; often
* both lengths have a value of one.
*
*
* Unicode character indexes and lengths refer to the implicit Unicode
* characters from the String object provided in the SetContent()
* method. This is different from the UTF-8 bytes that make up the
* actual storage of the String object. Unfortunately many String
* methods accept index values specified in terms of UTF-8 bytes for
* historical reasons. However, that class also provides Unicode
* character methods, and those are used internally by AXTE. Unicode
* character indexes start at zero.
*
*
* When a mapping is specified on a line object, the range of glyph
* indexes spans all runs in the line. The first glyph of the first run
* has index number zero. The index number for the first glyph of the
* second run is equal to the number of glyphs in the first run.
*
* @param nCharIndex - Starting character index, in the underlying
* Unicode characters, of the mapping.
* @param nGlyphIndex - Starting glyph index, in the combined set of
* glyphs from all runs, 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 nCharIndex, int nGlyphIndex, int nCharLength, int nGlyphLength) {
if (moMappings == null) {
moMappings = new GFXMappingList();
}
moMappings.addMapping (nCharIndex, nGlyphIndex, nCharLength, nGlyphLength);
}
/**
* (deprecated) Specify one character/glyph mapping for the line,
* relative to a given run.
*
* For general information on specifying mappings, please see the other
* overload of this method. This version differs in that the glyph
* index is specified relative to a run in the line. For example, an
* index of zero refers to the first glyph in the run, even if the run
* isn't the first run in the line. This method cannot be used to
* specify a mapping that spans a run boundary.
* @param poRun - Pointer to a run that is a member of the line. In
* other words, this pointer must have been returned by AddRun() for
* this line instance, or it must have been added by reference.
* @param nCharIndex - Starting character index, in the underlying
* Unicode characters, of the mapping.
* @param nGlyphIndex - Starting glyph index, in the sequence of glyphs
* from this run, 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 (TextGlyphRun poRun, int nCharIndex, int nGlyphIndex, int nCharLength, int nGlyphLength) {
// TBD: this can be made more efficient if the run knows something about
// its line.
int nOffset = 0;
int i;
for (i = 0; i < moRuns.size(); i++) {
TextGlyphRun poTest = getRun (i);
if (poTest == poRun) {
break;
}
nOffset += poRun.getGlyphCount();
}
if (i == moRuns.size()) {
return; // TBD: error handling?
}
addMapping (nCharIndex, nGlyphIndex + nOffset, nCharLength, nGlyphLength);
}
/**
* Perform Latin-based LTR mapping. This is a convenience method that
* maps glyphs to characters 1:1 from left to right in the line. It is
* an error if the numbers of characters and glyphs differ.
*/
public void autoMap () {
if (mnChars != mnGlyphs) {
// ExFull oEx (TEXT_ERR_LAYOUT_AUTO_MAP_COUNT); // TODO:
// throw oEx;
}
moMappings.autoMap (mnChars);
}
/**
* (deprecated) Return the number of mappings currently in the line.
* @return Number of mappings currently in the line.
*/
public int getMappingCount () {
return moMappings.getMappingCount();
}
/*
* (deprecated) Extract one mapping from the line, 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.
* @param nCharIndex - Starting character index, in the underlying
* Unicode characters, of the mapping.
* @param nGlyphIndex - Starting glyph index, in the combined set of
* glyphs from all runs, of the mapping.
* @param nCharLength - Number of consecutive Unicode characters in the
* mapping.
* @param nGlyphLength - Number of consecutive glyphs in the mapping.
*/
// public void GetMapping (int nIndex, int nCharIndex, int nGlyphIndex, int nCharLength, int nGlyphLength) {
// GFXMapping oMapping = moMappings.getMapping (nIndex);
// nCharIndex = oMapping.getLowestCharIndex();
// nGlyphIndex = oMapping.getLowestGlyphIndex();
// nCharLength = oMapping.getHighestCharIndex() - nCharIndex + 1;
// nGlyphLength = oMapping.getHighestGlyphIndex() - nGlyphIndex + 1;
// }
public GFXMapping getMapping (int nIndex) {
return moMappings.getMapping (nIndex);
}
/**
* Set the line end state in effect at the start of this line.
*
* This normally duplicates the line end state in effect at the end of
* the previous line. However, that value may not be available yet if
* the previous line is in a different frame.
* @param eState - Line end state in effect at the start of this line.
*/
public void setLineStartState (int eState) {
meLineStartState = eState;
}
/**
* Retrieve the line end state in effect at the start of the line.
* @return Line end state in effect at the start of the line.
*/
public int getLineStartState () {
return meLineStartState;
}
/**
* Set the line end state in effect at the end of this line.
* @param eState - Line end state in effect at the end of this line.
*/
public void setLineEndState (int eState) {
meLineEndState = eState;
}
/**
* Retrieve the line end state in effect at the end of the line.
* @return Line end state in effect at the end of the line.
*/
public int getLineEndState () {
return meLineEndState;
}
/**
* Return the displacement of the line's baseline from the top of the
* line's bounding box.
* @return Full ascent of the line.
*/
public UnitSpan getFullAscent () {
return moFullAscent;
}
/**
* Return the displacement of the bottom of the line's bounding box from
* the line's baseline.
* @return Full descent of the line.
*/
public UnitSpan getFullDescent () {
return moFullDescent;
}
/**
* Assignment operator.
*
* Copies the content of the given source line to this line, replacing
* all of its data members. Note that runs are copied by reference
* only.
* @param oSource - Source line to copy.
*/
public void copyFrom (TextLayoutLine oSource) {
if (this != oSource) {
cleanup();
msContent = oSource.msContent;
moRuns.setSize (oSource.moRuns.size());
for (int i = 0; i < moRuns.size(); i++) {
moRuns.set (i, oSource.getRun (i)); // copies references only
}
moMappings.copyFrom (oSource.moMappings);
mnChars = oSource.mnChars;
mnGlyphs = oSource.mnGlyphs;
meLineStartState = oSource.meLineStartState;
meLineEndState = oSource.meLineEndState;
moFullAscent = oSource.moFullAscent;
moFullDescent = oSource.moFullDescent;
moCharRuns = null;
}
}
void reconcile () {
forceReconcile();
}
int getCharRunCount () {
reconcile();
return moCharRuns.size();
}
CharRun getCharRun (int nIndex) {
reconcile();
return moCharRuns.get (nIndex);
}
public void setFullAscent (UnitSpan oAscent) {
moFullAscent = oAscent;
}
public void setFullDescent (UnitSpan oDescent) {
moFullDescent = oDescent;
}
private void forceReconcile () {
// Need to do only once.
if (moCharRuns != null) {
return;
}
moCharRuns = new Storage();
moMappings.validate (mnChars, mnGlyphs);
// Initializations for processing the mappings.
TextGlyphRun poPrevRun = null;
int nPrevRunIndex = 0;
int nCharLimit = 0;
int i;
moMappings.orderByCharacter();
// TBD: generalize to non-contiguous character/glyph ranges
// Step through each mapping recorded. These come back in order of start
// character index. In a properly created line, the mappings should abut
// each-other, exactly covering all the Unicode characters in the line's
// text content.
for (int nMap = 0; nMap < moMappings.getMappingCount(); nMap++) {
GFXMapping oMapping = moMappings.getMapping (nMap);
int nCharStart = oMapping.getLowestCharIndex();
// This mapping's character index must be equal to the previous mapping's
// character limit--the index of the character immidately after the end
// of the previous mapping. Use two asserts to test the equality
// condition, so that separate error messages can be generated. Also,
// validate its range.
assert (nCharStart == nCharLimit);
nCharLimit = oMapping.getHighestCharIndex() + 1;
assert (nCharLimit <= mnChars);
// Step through the glyph runs looking for the which contains (the start
// of) the mapping's glyph range. TBD: handle a mapping that spans glyph
// runs(?).
int nGlyphStart = oMapping.getLowestGlyphIndex();
TextGlyphRun poRun = null;
int nRunOffset = 0;
for (i = 0; (i < moRuns.size()) && (poRun == null); i++) {
TextGlyphRun poTest = getRun (i);
int nRunLimit = nRunOffset + poTest.getGlyphCount();
if (nGlyphStart < nRunLimit) {
poRun = poTest;
}
nRunOffset = nRunLimit;
}
// Given the validations that have occurred so far, a run must be found.
// Hence a "soft" assert.
assert (poRun != null);
// On each run change (even if the attributes do not change), generate a
// new character run.
if (poRun != poPrevRun) {
if (nCharStart > nPrevRunIndex) {
moCharRuns.add (new CharRun (poPrevRun.getAttr(),
poPrevRun,
nPrevRunIndex,
nCharStart - nPrevRunIndex));
}
poPrevRun = poRun;
nPrevRunIndex = nCharStart;
}
}
assert (nCharLimit == mnChars);
// Unflushed character run: add it now.
if (nPrevRunIndex < mnChars) {
moCharRuns.add (new CharRun (poPrevRun.getAttr(),
poPrevRun,
nPrevRunIndex,
mnChars - nPrevRunIndex));
}
}
private void cleanup () {
moRuns.setSize (0);
msContent = "";
moMappings.reset();
moCharRuns.setSize (0);
mnChars = 0;
mnGlyphs = 0;
meLineStartState = LINE_END_WORD_WRAP;
meLineEndState = LINE_END_WORD_WRAP;
moFullAscent = UnitSpan.ZERO;
moFullDescent = UnitSpan.ZERO;
}
}