/** * Returns the ascent of the text at start. This is used for scaling emoji. * * @param pos the line-relative position * @return the ascent of the text at start */ float ascent(int pos) { if (mSpanned == null) { return mPaint.ascent(); } pos += mStart; MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, MetricAffectingSpan.class); if (spans.length == 0) { return mPaint.ascent(); } TextPaint wp = mWorkPaint; wp.set(mPaint); for (MetricAffectingSpan span : spans) { span.updateMeasureState(wp); } return wp.ascent(); }
float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len, FontMetricsInt fm) { int i; float wid; TextPaint workPaint = this.mWorkPaint; workPaint.set(paint); workPaint.baselineShift = 0; ReplacementSpan replacement = null; for (MetricAffectingSpan span : spans) { if (span instanceof ReplacementSpan) { replacement = (ReplacementSpan) span; } else { span.updateMeasureState(workPaint); } } if (replacement == null) { wid = addStyleRun(workPaint, len, fm); } else { wid = (float) replacement.getSize( workPaint, this.mText, this.mTextStart + this.mPos, (this.mTextStart + this.mPos) + len, fm); float[] w = this.mWidths; w[this.mPos] = wid; int e = this.mPos + len; for (i = this.mPos + 1; i < e; i++) { w[i] = 0.0f; } this.mPos += len; } if (fm != null) { if (workPaint.baselineShift < 0) { fm.ascent += workPaint.baselineShift; fm.top += workPaint.baselineShift; } else { fm.descent += workPaint.baselineShift; fm.bottom += workPaint.baselineShift; } } return wid; }
/** * Returns the advance widths for a uniform left-to-right run of text with no style changes in the * middle of the run. If any style is replacement text, the first character will isCancelled the * width of the replacement and the remaining characters will isCancelled a width of 0. * * @param paint the paint, will not be modified * @param workPaint a paint to modify; on return will reflect the original paint plus the effect * of all spans on the run * @param text the text * @param start the start of the run * @param end the limit of the run * @param widths array to receive the advance widths of the characters. Must be at least a large * as (end - start). * @param fmi FontMetrics information; can be null * @return the actual number of widths returned */ public static int getTextWidths( TextPaint paint, TextPaint workPaint, Spanned text, int start, int end, float[] widths, Paint.FontMetricsInt fmi) { MetricAffectingSpan[] spans = text.getSpans(start, end, MetricAffectingSpan.class); ReplacementSpan replacement = null; workPaint.set(paint); for (MetricAffectingSpan span : spans) { if (span instanceof ReplacementSpan) { replacement = (ReplacementSpan) span; } else { span.updateMeasureState(workPaint); } } if (replacement == null) { workPaint.getFontMetricsInt(fmi); workPaint.getTextWidths(text, start, end, widths); } else { int wid = replacement.getSize(workPaint, text, start, end, fmi); if (end > start) { widths[0] = wid; for (int i = start + 1; i < end; i++) { widths[i - start] = 0; } } } return end - start; }
/** * Returns the next valid offset within this directional run, skipping conjuncts and zero-width * characters. This should not be called to walk off the end of the line, since the returned * values might not be valid on neighboring lines. If the returned offset is less than zero or * greater than the line length, the offset should be recomputed on the preceding or following * line, respectively. * * @param runIndex the run index * @param runStart the start of the run * @param runLimit the limit of the run * @param runIsRtl true if the run is right-to-left * @param offset the offset * @param after true if the new offset should logically follow the provided offset * @return the new offset */ private int getOffsetBeforeAfter( int runIndex, int runStart, int runLimit, boolean runIsRtl, int offset, boolean after) { if (runIndex < 0 || offset == (after ? mLen : 0)) { // Walking off end of line. Since we don't know // what cursor positions are available on other lines, we can't // return accurate values. These are a guess. if (after) { return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart; } return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart; } TextPaint wp = mWorkPaint; wp.set(mPaint); int spanStart = runStart; int spanLimit; if (mSpanned == null) { spanLimit = runLimit; } else { int target = after ? offset + 1 : offset; int limit = mStart + runLimit; while (true) { spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit, MetricAffectingSpan.class) - mStart; if (spanLimit >= target) { break; } spanStart = spanLimit; } MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart, mStart + spanLimit, MetricAffectingSpan.class); spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class); if (spans.length > 0) { ReplacementSpan replacement = null; for (int j = 0; j < spans.length; j++) { MetricAffectingSpan span = spans[j]; if (span instanceof ReplacementSpan) { replacement = (ReplacementSpan) span; } else { span.updateMeasureState(wp); } } if (replacement != null) { // If we have a replacement span, we're moving either to // the start or end of this span. return after ? spanLimit : spanStart; } } } int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE; if (mCharsValid) { return wp.getTextRunCursor( mChars, spanStart, spanLimit - spanStart, flags, offset, cursorOpt); } else { return wp.getTextRunCursor( mText, mStart + spanStart, mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart; } }