/**
   * Measure the text, stopping early if the measured width exceeds maxWidth. Return the number of
   * chars that were measured, and if measuredWidth is not null, return in it the actual width
   * measured.
   *
   * @param text The text to measure
   * @param start The offset into text to begin measuring at
   * @param end The end of the text slice to measure.
   * @param measureForwards If true, measure forwards, starting at start. Otherwise, measure
   *     backwards, starting with end.
   * @param maxWidth The maximum width to accumulate.
   * @param measuredWidth Optional. If not null, returns the actual width measured.
   * @return The number of chars that were measured. Will always be <= abs(end - start).
   */
  public int breakText(
      CharSequence text,
      int start,
      int end,
      boolean measureForwards,
      float maxWidth,
      float[] measuredWidth) {
    if (start == 0 && text instanceof String && end == text.length()) {
      return breakText((String) text, measureForwards, maxWidth, measuredWidth);
    }

    char[] buf = TemporaryBuffer.obtain(end - start);
    int result;

    TextUtils.getChars(text, start, end, buf, 0);

    if (measureForwards) {
      result = breakText(buf, 0, end - start, maxWidth, measuredWidth);
    } else {
      result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth);
    }

    TemporaryBuffer.recycle(buf);
    return result;
  }
 void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
   this.mText = text;
   this.mTextStart = start;
   int len = end - start;
   this.mLen = len;
   this.mPos = 0;
   if (this.mWidths == null || this.mWidths.length < len) {
     this.mWidths = ArrayUtils.newUnpaddedFloatArray(len);
   }
   if (this.mChars == null || this.mChars.length < len) {
     this.mChars = ArrayUtils.newUnpaddedCharArray(len);
   }
   TextUtils.getChars(text, start, end, this.mChars, 0);
   if (text instanceof Spanned) {
     Spanned spanned = (Spanned) text;
     ReplacementSpan[] spans =
         (ReplacementSpan[]) spanned.getSpans(start, end, ReplacementSpan.class);
     for (int i = 0; i < spans.length; i++) {
       int startInPara = spanned.getSpanStart(spans[i]) - start;
       int endInPara = spanned.getSpanEnd(spans[i]) - start;
       if (startInPara < 0) {
         startInPara = 0;
       }
       if (endInPara > len) {
         endInPara = len;
       }
       for (int j = startInPara; j < endInPara; j++) {
         this.mChars[j] = '\ufffc';
       }
     }
   }
   if ((textDir == TextDirectionHeuristics.LTR
           || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
           || textDir == TextDirectionHeuristics.ANYRTL_LTR)
       && TextUtils.doesNotNeedBidi(this.mChars, 0, len)) {
     this.mDir = 1;
     this.mEasy = true;
     return;
   }
   int bidiRequest;
   if (this.mLevels == null || this.mLevels.length < len) {
     this.mLevels = ArrayUtils.newUnpaddedByteArray(len);
   }
   if (textDir == TextDirectionHeuristics.LTR) {
     bidiRequest = 1;
   } else if (textDir == TextDirectionHeuristics.RTL) {
     bidiRequest = -1;
   } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
     bidiRequest = 2;
   } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
     bidiRequest = -2;
   } else {
     bidiRequest = textDir.isRtl(this.mChars, 0, len) ? -1 : 1;
   }
   this.mDir = AndroidBidi.bidi(bidiRequest, this.mChars, this.mLevels, len, false);
   this.mEasy = false;
 }
Example #3
0
  /**
   * Initializes a TextLine and prepares it for use.
   *
   * @param paint the base paint for the line
   * @param text the text, can be Styled
   * @param start the start of the line relative to the text
   * @param limit the limit of the line relative to the text
   * @param dir the paragraph direction of this line
   * @param directions the directions information of this line
   * @param hasTabs true if the line might contain tabs or emoji
   * @param tabStops the tabStops. Can be null.
   */
  void set(
      TextPaint paint,
      CharSequence text,
      int start,
      int limit,
      int dir,
      Directions directions,
      boolean hasTabs,
      TabStops tabStops) {
    mPaint = paint;
    mText = text;
    mStart = start;
    mLen = limit - start;
    mDir = dir;
    mDirections = directions;
    if (mDirections == null) {
      throw new IllegalArgumentException("Directions cannot be null");
    }
    mHasTabs = hasTabs;
    mSpanned = null;

    boolean hasReplacement = false;
    if (text instanceof Spanned) {
      mSpanned = (Spanned) text;
      mReplacementSpanSpanSet.init(mSpanned, start, limit);
      hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
    }

    mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;

    if (mCharsValid) {
      if (mChars == null || mChars.length < mLen) {
        mChars = new char[ArrayUtils.idealCharArraySize(mLen)];
      }
      TextUtils.getChars(text, start, limit, mChars, 0);
      if (hasReplacement) {
        // Handle these all at once so we don't have to do it as we go.
        // Replace the first character of each replacement run with the
        // object-replacement character and the remainder with zero width
        // non-break space aka BOM.  Cursor movement code skips these
        // zero-width characters.
        char[] chars = mChars;
        for (int i = start, inext; i < limit; i = inext) {
          inext = mReplacementSpanSpanSet.getNextTransition(i, limit);
          if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) {
            // transition into a span
            chars[i - start] = '\ufffc';
            for (int j = i - start + 1, e = inext - start; j < e; ++j) {
              chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
            }
          }
        }
      }
    }
    mTabs = tabStops;
  }
  /**
   * Return the width of the text.
   *
   * @param text The text to measure
   * @param start The index of the first character to start measuring
   * @param end 1 beyond the index of the last character to measure
   * @return The width of the text
   */
  public float measureText(CharSequence text, int start, int end) {
    if (text instanceof String) {
      return measureText((String) text, start, end);
    }
    if (text instanceof SpannedString || text instanceof SpannableString) {
      return measureText(text.toString(), start, end);
    }
    if (text instanceof GraphicsOperations) {
      return ((GraphicsOperations) text).measureText(start, end, this);
    }

    char[] buf = TemporaryBuffer.obtain(end - start);
    TextUtils.getChars(text, start, end, buf, 0);
    float result = measureText(buf, 0, end - start);
    TemporaryBuffer.recycle(buf);
    return result;
  }
  /**
   * Return the advance widths for the characters in the string.
   *
   * @param text The text to measure
   * @param start The index of the first char to to measure
   * @param end The end of the text slice to measure
   * @param widths array to receive the advance widths of the characters. Must be at least a large
   *     as (end - start).
   * @return the actual number of widths returned.
   */
  public int getTextWidths(CharSequence text, int start, int end, float[] widths) {
    if (text instanceof String) {
      return getTextWidths((String) text, start, end, widths);
    }
    if (text instanceof SpannedString || text instanceof SpannableString) {
      return getTextWidths(text.toString(), start, end, widths);
    }
    if (text instanceof GraphicsOperations) {
      return ((GraphicsOperations) text).getTextWidths(start, end, widths, this);
    }

    char[] buf = TemporaryBuffer.obtain(end - start);
    TextUtils.getChars(text, start, end, buf, 0);
    int result = getTextWidths(buf, 0, end - start, widths);
    TemporaryBuffer.recycle(buf);
    return result;
  }
 @Override
 public CharSequence filter(
     CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
   char[] v = new char[end - start];
   TextUtils.getChars(source, start, end, v, 0);
   Spannable emojified = EmojiProvider.getInstance(view.getContext()).emojify(new String(v), view);
   if (source instanceof Spanned) {
     TextUtils.copySpansFrom((Spanned) source, start, end, null, emojified, 0);
   }
   view.getViewTreeObserver().addOnGlobalLayoutListener(this);
   if (view.getWidth() == 0 || view.getEllipsize() != TruncateAt.END) {
     return emojified;
   } else {
     return TextUtils.ellipsize(
         emojified,
         view.getPaint(),
         view.getWidth() - view.getPaddingRight() - view.getPaddingLeft(),
         TruncateAt.END);
   }
 }