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

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

The newest version!
package com.adobe.xfa.text;

/**
 * Manage complex character/glyph mappings during layout.
 * 

* This class is used during layout to generate glyph locations for * wrapped lines. Note that the raw line glyph locations are generated * by raw formatting, from information accumulated in the AFE run. A * mapping manager is not required if all mappings are simple; that is, * every character maps 1:1 to a glyph and there is no reordering. *

*

* Higher level code uses a mapping manager instance in the following * sequence of operations: *

*
    *
  1. * Call the analyze() method once, with the raw line. *
  2. *
  3. * Call the applyToWrappedLine() method once for each wrapped line * generated from the original raw line. *
  4. *
*

* Once that sequence is finished, the mapping manager instance can be * reused for another complete cycle. *

*

* The presence of RTL text in a raw line implies complex mapping. AFE * formatting occurs on the raw line, and is dependent on the BIDI run * levels. These are determined by the mapping manager. Thus, if the * raw line has any RTL text, analysis must occur before AFE formatting. *

*

* If the line has no RTL text, it may or may not have complex mapping. * This can be determined only after AFE formatting is complete. Thus, * the caller can defer analysis (if there is no RTL text), but will * have to check again after AFE formatting to determine whether * analysis is now required. *

*

* The bulk of the code in this class implements the Unicode BIDI * Algorithm, UAX9. *

* @exclude from published api. */ class MappingManager { /** * Resolve explicit direction control. *

* The first stage of the BIDI algorithm is to check for runs in the * text whose direction is controlled by markup. There are two * constructs for representing such mark-up: HTML DIR attributes * (represented at this point in TextAttr objects), and by the explicit * Unicode direction control characters: *

*
    *
  • * LRE - Left to right embedding *
  • *
  • * LRO - Left to right override *
  • *
  • * RLE - Right to left embedding *
  • *
  • * RLO - Right to left override *
  • *
*

* A run introduced by one of the above characters is terminated by PDF * (pop direction format). Runs may nest. *

*

* This reusable class performs the initial population of the directions * and levels arrays based on explicit direction control. *

*/ private static class ExplicitRunProcessor { private DispLine mLine; private int mCharCount; private int [] mDirections; private byte[] mLevels; private boolean mDefaultRTL; private int mDefaultEmbedding; private int mRunIndex; private int mRunLimit; /** * Initialize the processor for processing a line's text. Upon return, * the directions and levels arrays will be populated for further * processing. * @param line - Line to process. * @param directions - Pre-created directions array. Must be large * enough to hold one element for each character in the corresponding * line. * @param levels - Precreated levels array. Must be large enough to * hold one element for each character in the corresponding line. */ void setup (DispLine line, int [] directions, byte [] levels) { mLine = line; mCharCount = mLine.getCharCount(); mDirections = directions; mLevels = levels; mDefaultRTL = mLine.isRTL(); mDefaultEmbedding = mDefaultRTL ? TextCharProp.BIDI_RLE : TextCharProp.BIDI_LRE; mRunIndex = 0; mRunLimit = 0; byte defaultLevel = (byte) (mDefaultRTL ? 1 : 0); for (int i = 0; i < mCharCount; i++) { mDirections[i] = line.getBreakData (i); mLevels[i] = defaultLevel; } } /** * Process the line's content. Using the parameters specified in the * setup() method, process the content of the line to perform the * initial population of the directions and levels arrays. */ void run () { processExplicitRun (0, mDefaultEmbedding, mDefaultRTL ? 1 : 0, TextCharProp.BIDI_ON); } /** * Recursive run processing. *

* This method processes the text by running through the text from start * to end, calling itself recursively each time a new run is introduced * and returning when the corresponding end-of-run is encountered. *

* @param index - Index to start processing at. This should be zero for * the initial call from the "outside". * @param level - The level to start processing out. This should be the * paragraph level for the initial call from the "outside". * @param override - Direction override, or neutral, to enforce. This * should be TextCharProp.BIDI_ON (other neutral) the initial call from * the "outside". * @return Index to carry on from when a recursive call returns. */ private int processExplicitRun (int index, int dispRunDir, int level, int override) { boolean atInitialDepth = index == 0; // The loop normally iterates once for each character in the line. // Calling recursively happens within the loop, causing a skip ahead at // this level of recursion. If the end of the run that triggered this // call is detected, the call returns. Otherwise, processing continues // to the end of the text. while (index < mLine.getCharCount()) { // First check to see whether there is an AXTE direction change at this // position. This could happen when we step into a new display run, but // not at any other time. if (index >= mRunLimit) { mRunIndex++; if (mRunIndex >= mLine.getRunCount()) { mRunLimit = mLine.getCharCount(); } else { DispRun run = mLine.getRun (mRunIndex); mRunLimit = run.getMapIndex() + run.getMapLength(); TextAttr textAttr = run.getAttr(); if (textAttr != null) { int newDispRunDir; switch (textAttr.direction()) { case TextAttr.DIRECTION_LTR: newDispRunDir = TextCharProp.BIDI_LRE; break; case TextAttr.DIRECTION_RTL: newDispRunDir = TextCharProp.BIDI_RLE; break; default: newDispRunDir = mDefaultEmbedding; break; } // AXTE runs do not nest. If there is a change in AXTE direction, it is // either a change away from the paragraph direction, or a change back to // it. Changing away is treated as a new embedded run (recursive call). // Changing back is treated as the end of an embedded run (return). if (newDispRunDir != dispRunDir) { if (newDispRunDir == mDefaultEmbedding) { return index; // not incremented; process this char in new run } int newLevel = (newDispRunDir == TextCharProp.BIDI_LRE) ? nextEvenLevel (level) : nextOddLevel (level); index = processExplicitRun (index, newDispRunDir, newLevel, override); if (index >= mCharCount) { return index; } } } } } // Now the current character can be processed. The code below examines // it for one of the Unicode direction control characters that introduces // an embedded run. If one is present, it triggers a recursive call. If // the PDF character is encountered, it's time to return. Otherwise, the // character has no effect on run level. int charData = mLine.getBreakData (index); int direction = TextCharProp.getBIDIClass (charData); int newLevel = level; int newOverride = override; switch (direction) { case TextCharProp.BIDI_LRE: // rule X3 newLevel = nextEvenLevel (level); newOverride = TextCharProp.BIDI_ON; break; case TextCharProp.BIDI_LRO: // rule X5 newLevel = nextEvenLevel (level); newOverride = TextCharProp.BIDI_L; break; case TextCharProp.BIDI_RLE: // rule X2 newLevel = nextOddLevel (level); newOverride = TextCharProp.BIDI_ON; break; case TextCharProp.BIDI_RLO: // rule X4 newLevel = nextOddLevel (level); newOverride = TextCharProp.BIDI_R; break; case TextCharProp.BIDI_PDF: // rule X7 if (! atInitialDepth) { mLevels[index] = (byte) level; mDirections[index] = resolveDirection (override, direction); return index + 1; // continue after PDF } } if (newLevel != level) { if (newLevel > 61) { // maximum UAX9 level newLevel = level; newOverride = override; } } mLevels[index] = (byte) newLevel; mDirections[index] = resolveDirection (newOverride, direction); if (newLevel == level) { index++; // increment only if not pushing new level } else { index = processExplicitRun (index+1, dispRunDir, newLevel, newOverride); if (index >= mCharCount) { return index; } } } return index; } } /** * Base class for level run processing. *

* A level run is a maximal set of consecutive characters at the same * BIDI level. The entire text can be partitioned into a set of level * runs. *

*

* This is the base class for different kinds of processing on level * runs. The caller creates an instance of a derived class and then * calls the execute() method to perform (derived class) processing on * each level run in the text. *

*/ private abstract class LevelRunProcessor { LevelRunProcessor () { } /** * Invoke level run processing. */ void execute () { // The loop iterates once for each level run. At the start of each // iteration, variable i is positioned at the start of the run. int i = 0; while (i < mCharCount) { int runStart = i; int sorLevel = mLevels[i]; // sor is defined in UAX9 if ((i > 0) && (mLevels[i-1] > sorLevel)) { sorLevel = mLevels[i-1]; } // Now, scan from the next character until one is found at a different // level (or end of text encountered). i++; int eorLevel = sorLevel; // eor is defined in UAX9 while (i < mCharCount) { int nextLevel = mLevels[i]; if (nextLevel != eorLevel) { if (nextLevel > eorLevel) { eorLevel = nextLevel; } break; } i++; } int runLimit = i; processRun (runStart, runLimit, sorLevel, eorLevel); } } /** * Process one character in the level run. *

* Implemented by the derived class. Typically the implementation * examines the character's direction code in the context of the * previous and next characters' direction and may alter the current * character's direction accordingly. *

* @param index - Index of the character being processed. * @param prevDirection - Direction of the preceding character. * @param direction - Direction of this character. * @param nextDirection - Direction of the following character. * @return Overriding direction code to be used as the previous * direction code next time. Alternatively, this can be -1, indicating * that the (possibly altered) current character's direction code is to * be used. */ abstract int processChar (int index, int prevDirection, int direction, int nextDirection); void finishRun () { } final int resolveENDirection (int prevStrong) { return (prevStrong == TextCharProp.BIDI_L) ? TextCharProp.BIDI_L : TextCharProp.BIDI_EN; } final int getDirection (int index) { return mDirections[index]; } final void setDirection (int index, int direction) { // JavaPort TODO: What was this ported from - same code in both branches doesn't make sense. // if (direction == 0) { mDirections[index] = direction; // }else { // mDirections[index] = direction; // } } final void setDirection (int start, int limit, int direction) { if (start >= 0) { // rule W5 for (int i = start; i < limit; i = findNonExplicit (i+1, limit)) { setDirection (i, direction); } } } private void processRun (int runStart, int runLimit, int sorLevel, int eorLevel) { int index = findNonExplicit (runStart, runLimit); if (index >= runLimit) { return; } int direction = mDirections[index]; int prevDirection = getDirectionFromLevel (sorLevel); while (index < runLimit) { int nextIndex = findNonExplicit (index+1, runLimit); int nextDirection = (nextIndex < runLimit) ? mDirections[nextIndex] : getDirectionFromLevel (eorLevel); int prevOverride = processChar (index, prevDirection, direction, nextDirection); prevDirection = (prevOverride == -1) ? mDirections[index] : prevOverride; direction = nextDirection; index = nextIndex; } finishRun(); } } /** * Resolves weak direction types (UAX9, section 3.3.3). */ private class WeakTypeResolver extends LevelRunProcessor { private int mPrevStrong; private int mETRunStart; WeakTypeResolver () { } int processChar (int index, int prevDirection, int direction, int nextDirection) { // Note: UAX9 describes weak type resolution in terms of several // iterations (with sub-iterations) over the text. It could conceivably // be an order N**2 algorithm as described. This implementation allows // the resolution to occur in a single pass of the text (with the // occasional sub-iteration). Consequently, the UAX9 rules may not be // immediately evident in a quick scan of the code. int prevOverride = -1; boolean preserveETRun = false; switch (direction) { case TextCharProp.BIDI_NSM: // rule W1 setDirection (index, prevDirection); break; case TextCharProp.BIDI_EN: if (mPrevStrong == TextCharProp.BIDI_AL) { setDirection (index, TextCharProp.BIDI_AN); // rule W2 } else { int newDirection = resolveENDirection (mPrevStrong); // rule W7 setDirection (index, newDirection); setDirection (mETRunStart, index, newDirection); // rule W5 } prevOverride = TextCharProp.BIDI_EN; // in case changed to L or AN break; case TextCharProp.BIDI_AL: // rule W3 setDirection (index, TextCharProp.BIDI_R); break; case TextCharProp.BIDI_ES: if ((prevDirection == TextCharProp.BIDI_EN) && (nextDirection == TextCharProp.BIDI_EN)) { // rule W4 setDirection (index, resolveENDirection (mPrevStrong)); } else { setDirection (index, TextCharProp.BIDI_ON); } break; case TextCharProp.BIDI_CS: boolean usePrev = false; if (prevDirection == nextDirection) { switch (prevDirection) { case TextCharProp.BIDI_AN: case TextCharProp.BIDI_EN: usePrev = true; } } if (usePrev) { setDirection (index, prevDirection); // rule W4 } else { setDirection (index, TextCharProp.BIDI_ON); // rule W6 } break; case TextCharProp.BIDI_ET: if (prevDirection == TextCharProp.BIDI_EN) { setDirection (index, resolveENDirection (mPrevStrong)); // rule W5, W7 prevOverride = TextCharProp.BIDI_EN; // in case changed to L } else { preserveETRun = true; if (mETRunStart < 0) { mETRunStart = index; // for rule W5 later } } break; default: if (isStrong (direction)) { mPrevStrong = direction; } } if (! preserveETRun) { mETRunStart = -1; } return prevOverride; } } private class NeutralTypeResolver extends LevelRunProcessor { private int mRunDirection; private int mPrevStrongDirection; private int mNextStrongDirection; private int mFirstNeutralIndex = -1; private int mNextIndex; private boolean mFirstTime = true; NeutralTypeResolver () { } int processChar (int index, int prevDirection, int direction, int nextDirection) { // As with weak type processing, the resolution of neutral types (UAX9, // section 3.3.4) is collapsed into a single pass. if (mFirstTime) { mRunDirection = getDirectionFromLevel (getDirection (index)); mPrevStrongDirection = prevDirection; mFirstTime = false; } int strongDir = TextCharProp.BIDI_ON; switch (direction) { // rule N1 case TextCharProp.BIDI_AL: case TextCharProp.BIDI_AN: case TextCharProp.BIDI_EN: case TextCharProp.BIDI_R: strongDir = TextCharProp.BIDI_R; break; case TextCharProp.BIDI_L: strongDir = TextCharProp.BIDI_L; break; case TextCharProp.BIDI_B: case TextCharProp.BIDI_S: case TextCharProp.BIDI_WS: case TextCharProp.BIDI_ON: if (mFirstNeutralIndex < 0) { mFirstNeutralIndex = index; } break; } if (strongDir != TextCharProp.BIDI_ON) { flushRun (index, strongDir); mPrevStrongDirection = strongDir; } mNextStrongDirection = nextDirection; mNextIndex = index + 1; return -1; } void finishRun () { flushRun (mNextIndex, mNextStrongDirection); } private final void flushRun (int limit, int direction) { if (mFirstNeutralIndex >= 0) { int neutralDir = (direction == mPrevStrongDirection) ? direction // rule N1 : mRunDirection; // rule N2 setDirection (mFirstNeutralIndex, limit, neutralDir); mFirstNeutralIndex = -1; } } } private final ExplicitRunProcessor mExplicitRunProcessor = new ExplicitRunProcessor(); private DispLine mLine; private int mCharCount; private byte[] mLevels; private int[] mDirections; // TODO: use char prop indexes and byte(?); cache in text context /** * Constructor. */ MappingManager () { } /** * Analyze direction information in a raw line and build internal * tables. *

* This method is called once for each raw line, to collect all the * necessary BIDI information. Method applyToWrappedLine() is then * called once for each wrapped line generated from the raw line. *

*

* Information is cached in this object by this call. This method must * not be called on the same instance until all wrapped lines have been * dealt with in calls to applyToWrappedLine(). Once that occurs, the * mapping manager instance can be used for a new raw line. *

* @param line - Raw line to analyze. */ void analyze (DispLine line) { mLine = line; mCharCount = line.getCharCount(); if (mCharCount == 0) { return; } if (! mLine.hasBIDI()) { return; } if ((mDirections == null) || (mDirections.length < mCharCount)) { mDirections = new int[mCharCount]; mLevels = new byte[mCharCount]; } // Perform the initial population of the BIDI levels, based on embedded // Unicode direction control characters and AXTE direction changes. mExplicitRunProcessor.setup (mLine, mDirections, mLevels); mExplicitRunProcessor.run(); // Resolve weak and neutral types, respectively. WeakTypeResolver weakResolver = new WeakTypeResolver(); weakResolver.execute(); NeutralTypeResolver neutralResolver = new NeutralTypeResolver(); neutralResolver.execute(); // At this point, direction codes should now be collapsed to the types // AN, EN, R, L, or the Unicode direction control types (which are // ignored). Based on those four key types, this loop adjusts the levels // according to UAX9, section 3.3.5. for (int i = 0; i < mCharCount; i++) { int level = mLevels[i]; int direction = mDirections[i]; int defaultDirection = getDirectionFromLevel (level); int add = 0; switch (direction) { case TextCharProp.BIDI_AN: case TextCharProp.BIDI_EN: if (defaultDirection == TextCharProp.BIDI_R) { add = 1; // rule I2 } else { add = 2; // rule I1 } break; case TextCharProp.BIDI_L: if (defaultDirection == TextCharProp.BIDI_R) { add = 1; // rule I2 } break; case TextCharProp.BIDI_R: if (defaultDirection == TextCharProp.BIDI_L) { add = 1; // rule I1 } break; } mLevels[i] = (byte) (level + add); } } /** * Apply direction information to one wrapped line. *

* Once raw line analysis is complete (see method analyze()), this * method is called once for each wrapped line generated from the raw * line. It must be called only once for each wrapped line, as the call * does alter cached information pertaining to that line. *

*

* This method is called after the wrapped line's character content has * been created, but before its glyphs have. Upon return, this method * will have populated the glyph locations for the line. The caller can * use these locations to populate glyphs in the correct order. *

* @param wrappedLine - Wrapped line to be altered with mapping * information. * @param afeRun - AFE run to update. * @param start - Starting character index of the wrapped line in the * originating raw line. */ void applyToWrappedLine (DispLineWrapped wrappedLine, AFERun afeRun, int start) { int length = wrappedLine.getCharCount(); if (length <= 0) { return; } int i; int limit = start + length; DispMap glyphLocMap = mLine.getGlyphLocMap(); int glyphLocCount = glyphLocMap.size(); if (glyphLocCount == 0) { glyphLocMap = null; } int[] indexes = null; if (mLine.hasBIDI()) { // UAX9 rule L1 requires that certain spaces and related characters drop // back to the default run level after line breaking. This call does // that and also determines the maximum level present. int maxLevel = resetTrailingSpaces (start, limit, afeRun); // TODO: shortcut if maxLevel==0? // This is where run reordering occurs. The result is an array of // _character_ indexes in rendering order. indexes = reorderRuns (start, limit, maxLevel); } boolean[] visited = new boolean [length]; // TODO: cache this? for (i = 0; i < length; i++) { visited[i] = false; } // This loop creates the wrapped line's glyph locations, using the raw // line's locations as input, but ordering them appropriately. int glyphIndex = 0; GlyphLoc glyphLoc = new GlyphLoc(); for (i = 0; i < length; i++) { int rawIndex = (indexes == null) ? (i + start) : indexes[i]; int visitedIndex = rawIndex - start; assert ((visitedIndex >= 0) && (visitedIndex < length)); if (! visited[visitedIndex]) { // There are rare cases where a single character can generate multiple // consecutive glyphs. The following code sets start and limit indexes // for source glyph locations, corresponding to the current character. // If necessary, work both backward and forward in the parent glyph // location map to find the range of glyph locations that refer to this // character. int glyphLocIndex = rawIndex; int glyphLocLimit = rawIndex + 1; if (glyphLocMap != null) { int foundIndex = glyphLocMap.findItem (rawIndex); boolean charPresent = false; if (glyphLocMap.isValidMapIndex (foundIndex)) { DispMapItem testGlyphLoc = glyphLocMap.getItem (foundIndex); int charStart = testGlyphLoc.getMapIndex(); int charLimit = charStart + testGlyphLoc.getMapLength(); if ((charStart <= rawIndex) && (rawIndex < charLimit)) { charPresent = true; } } if (charPresent) { glyphLocIndex = foundIndex; int referenceIndex = mLine.getGlyphLoc(foundIndex).getMapIndex(); int testIndex; for (testIndex = foundIndex; testIndex > 0; ) { testIndex--; if (glyphLocMap.getItem(testIndex).getMapIndex() != referenceIndex) { break; } glyphLocIndex = testIndex; } glyphLocLimit = foundIndex + 1; for (testIndex = glyphLocLimit; testIndex < glyphLocCount; testIndex++) { if (glyphLocMap.getItem(testIndex).getMapIndex() != referenceIndex) { break; } glyphLocLimit = testIndex; } } else { glyphLocLimit = glyphLocIndex; // deleted during AFE formatting } } // Step through the range of glyph locations determined above (usually // its length is one), creating a corresponding glyph location in the // wrapped line for each one. while (glyphLocIndex < glyphLocLimit) { glyphLoc.copyFrom (mLine.getGlyphLoc (glyphLocIndex)); int wrappedIndex = glyphLoc.getMapIndex() - start; assert (wrappedIndex >= 0); for (int j = 0; j < glyphLoc.getMapLength(); j++) { int index = wrappedIndex + j; assert (index < length); visited[index] = true; } glyphLoc.setGlyphIndex (glyphIndex); wrappedLine.add (glyphLoc, wrappedIndex, glyphLoc.getMapLength()); glyphIndex++; glyphLocIndex++; } } } } int getLevel (int index) { return mLevels[index]; } /** * Reset trailing space levels. *

* Once the text has been broken into lines, the run levels of all * trailing spaces are set to the paragraph level. This keeps those * spaces at the end of the line (by paragraph direction), rather than * appearing in the middle (if the line ends in text of the opposite * direction). Spaces before tabs must be similarly adjusted. *

* @param start - Index in the parent raw line designating the start of * this wrapped line. * @param linit - limit in the parent raw line designating the end of * this wrapped line. * @param afeRun - AFE run to update. * @return Maximum BIDI level within this wrapped line. */ private int resetTrailingSpaces (int start, int limit, AFERun afeRun) { int maxLevel = 0; byte defaultLevel = (byte) getDefaultLevel(); boolean defaultWhitespaceLevels = true; for (int i = limit; i > 0; ) { // rule L1 i--; int type = TextCharProp.getBIDIClass (mLine.getBreakData (i)); if (type == TextCharProp.BIDI_WS) { if (mLine.tabAt(i) != null) { // if space substituted for tab type = TextCharProp.BIDI_S; // use tab type } } switch (type) { case TextCharProp.BIDI_B: case TextCharProp.BIDI_S: overrideLevel (i, defaultLevel, afeRun); defaultWhitespaceLevels = true; break; case TextCharProp.BIDI_WS: if (defaultWhitespaceLevels) { overrideLevel (i, defaultLevel, afeRun); } break; default: if (! isExplicitControl (type)) { defaultWhitespaceLevels = false; } } if (mLevels[i] > maxLevel) { maxLevel = mLevels[i]; } } return maxLevel; } /** * Perform the BIDI reordering. *

* This method reorders the characters in a wrapped line according to * the results of the BIDI algorithm. *

* @param start - Index in the parent raw line designating the start of * this wrapped line. * @param linit - limit in the parent raw line designating the end of * this wrapped line. * @param maxLevel - Maximum BIDI level within this wrapped line. * @return Array of reordered character positions. */ private int[] reorderRuns (int start, int limit, int maxLevel) { int length = limit - start; if (length <= 0) { return null; } int i; int[] indexes = new int [length]; // TODO: cache and reuse this? for (i = 0; i < length; i++) { indexes[i] = i + start; } // This loop performs the actual reordering. Starting with the deepest // runs (those with the highest level), reverse the text within the run. // Iterate until level 1 (lowest RTL) has been reordered. Note that this // is the brute force implementation from UAX9. The content of any given // run will be reordered multiple times, equal to its run level. There // are likely optimizations that could be applied if performance analysis // highlights problems. while (maxLevel > 0) { // rule L2 int runStart = -1; for (i = 0; i < length; i++) { if (mLevels[i+start] >= maxLevel) { if (runStart < 0) { runStart = i; } } else { if (runStart >= 0) { reverseRun (indexes, runStart, i); runStart = -1; } } } if (runStart >= 0) { reverseRun (indexes, runStart, length); } maxLevel--; } // AFE positioning is based on the assumption that combining characters // are rendered after their base characters. This means that such // sequences in RTL text must be reversed to LTR order. int accentRunStart = -1; for (i = 0; i < length; i++) { // rule L3 int index = indexes[i]; int breakType = mLine.getBreakClass (index); if (breakType == TextCharProp.BREAK_CM) { if (accentRunStart < 0) { accentRunStart = i; } } else { if ((accentRunStart >= 0) && isRTL (mLevels[index])) { reverseRun (indexes, accentRunStart, i + 1); } accentRunStart = -1; } } return indexes; } private void overrideLevel (int index, byte newLevel, AFERun afeRun) { mLevels[index] = newLevel; afeRun.getElement(index).setBIDILevel (newLevel); } private static boolean isRTL (int level) { return (level & 1) != 0; } private static int getDirectionFromLevel (int level) { return isRTL (level) ? TextCharProp.BIDI_R : TextCharProp.BIDI_L; } private static int nextEvenLevel (int level) { return isRTL (level) ? (level + 1) : (level + 2); } private static int nextOddLevel (int level) { return isRTL (level) ? (level + 2) : (level + 1); } private int getDefaultLevel () { return mLine.isRTL() ? 1 : 0; } private static int resolveDirection (int override, int direction) { return (override == TextCharProp.BIDI_ON) ? direction : override; } private static boolean isStrong (int direction) { switch (direction) { case TextCharProp.BIDI_AL: case TextCharProp.BIDI_L: case TextCharProp.BIDI_LRE: case TextCharProp.BIDI_LRO: case TextCharProp.BIDI_R: case TextCharProp.BIDI_RLE: case TextCharProp.BIDI_RLO: return true; } return false; } private static boolean isExplicitControl (int direction) { switch (direction) { case TextCharProp.BIDI_LRE: case TextCharProp.BIDI_LRO: case TextCharProp.BIDI_RLE: case TextCharProp.BIDI_RLO: case TextCharProp.BIDI_PDF: return true; } return false; } private final int findNonExplicit (int index, int limit) { for (; index < limit; index++) { if (! isExplicitControl (mDirections[index])) { break; } } return index; } private static void reverseRun (int[] indexes, int start, int limit) { int mid = (start + limit) / 2; int end = limit - 1; while (start < mid) { int swap = indexes[start]; indexes[start] = indexes[end]; indexes[end] = swap; start++; end--; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy