All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.text.TextLayoutLine Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
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; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy