Search in sources :

Example 1 with MetricAffectingSpan

use of android.text.style.MetricAffectingSpan in project android_frameworks_base by ParanoidAndroid.

the class TextLine method getOffsetBeforeAfter.

/**
     * 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)) {
        // 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) {
                // 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;
    }
}
Also used : ReplacementSpan(android.text.style.ReplacementSpan) Paint(android.graphics.Paint) MetricAffectingSpan(android.text.style.MetricAffectingSpan)

Example 2 with MetricAffectingSpan

use of android.text.style.MetricAffectingSpan in project android_frameworks_base by ParanoidAndroid.

the class MeasuredText method addStyleRun.

float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len, Paint.FontMetricsInt fm) {
    TextPaint workPaint = mWorkPaint;
    workPaint.set(paint);
    // XXX paint should not have a baseline shift, but...
    workPaint.baselineShift = 0;
    ReplacementSpan replacement = null;
    for (int i = 0; i < spans.length; i++) {
        MetricAffectingSpan span = spans[i];
        if (span instanceof ReplacementSpan) {
            replacement = (ReplacementSpan) span;
        } else {
            span.updateMeasureState(workPaint);
        }
    }
    float wid;
    if (replacement == null) {
        wid = addStyleRun(workPaint, len, fm);
    } else {
        // Use original text.  Shouldn't matter.
        wid = replacement.getSize(workPaint, mText, mTextStart + mPos, mTextStart + mPos + len, fm);
        float[] w = mWidths;
        w[mPos] = wid;
        for (int i = mPos + 1, e = mPos + len; i < e; i++) w[i] = 0;
        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;
}
Also used : ReplacementSpan(android.text.style.ReplacementSpan) Paint(android.graphics.Paint) MetricAffectingSpan(android.text.style.MetricAffectingSpan)

Example 3 with MetricAffectingSpan

use of android.text.style.MetricAffectingSpan in project XobotOS by xamarin.

the class StaticLayout method generate.

/* package */
void generate(CharSequence source, int bufStart, int bufEnd, TextPaint paint, int outerWidth, Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, boolean includepad, boolean trackpad, float ellipsizedWidth, TextUtils.TruncateAt ellipsize) {
    mLineCount = 0;
    int v = 0;
    boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
    Paint.FontMetricsInt fm = mFontMetricsInt;
    int[] chooseHtv = null;
    MeasuredText measured = mMeasured;
    Spanned spanned = null;
    if (source instanceof Spanned)
        spanned = (Spanned) source;
    // XXX
    int DEFAULT_DIR = DIR_LEFT_TO_RIGHT;
    int paraEnd;
    for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
        paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
        if (paraEnd < 0)
            paraEnd = bufEnd;
        else
            paraEnd++;
        int firstWidthLineLimit = mLineCount + 1;
        int firstWidth = outerWidth;
        int restWidth = outerWidth;
        LineHeightSpan[] chooseHt = null;
        if (spanned != null) {
            LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, LeadingMarginSpan.class);
            for (int i = 0; i < sp.length; i++) {
                LeadingMarginSpan lms = sp[i];
                firstWidth -= sp[i].getLeadingMargin(true);
                restWidth -= sp[i].getLeadingMargin(false);
                // paragraph.
                if (lms instanceof LeadingMarginSpan2) {
                    LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
                    int lmsFirstLine = getLineForOffset(spanned.getSpanStart(lms2));
                    firstWidthLineLimit = lmsFirstLine + lms2.getLeadingMarginLineCount();
                }
            }
            chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
            if (chooseHt.length != 0) {
                if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
                    chooseHtv = new int[ArrayUtils.idealIntArraySize(chooseHt.length)];
                }
                for (int i = 0; i < chooseHt.length; i++) {
                    int o = spanned.getSpanStart(chooseHt[i]);
                    if (o < paraStart) {
                        // starts in this layout, before the
                        // current paragraph
                        chooseHtv[i] = getLineTop(getLineForOffset(o));
                    } else {
                        // starts in this paragraph
                        chooseHtv[i] = v;
                    }
                }
            }
        }
        measured.setPara(source, paraStart, paraEnd, textDir);
        char[] chs = measured.mChars;
        float[] widths = measured.mWidths;
        byte[] chdirs = measured.mLevels;
        int dir = measured.mDir;
        boolean easy = measured.mEasy;
        int width = firstWidth;
        float w = 0;
        int here = paraStart;
        int ok = paraStart;
        float okWidth = w;
        int okAscent = 0, okDescent = 0, okTop = 0, okBottom = 0;
        int fit = paraStart;
        float fitWidth = w;
        int fitAscent = 0, fitDescent = 0, fitTop = 0, fitBottom = 0;
        boolean hasTabOrEmoji = false;
        boolean hasTab = false;
        TabStops tabStops = null;
        for (int spanStart = paraStart, spanEnd = spanStart, nextSpanStart; spanStart < paraEnd; spanStart = nextSpanStart) {
            if (spanStart == spanEnd) {
                if (spanned == null)
                    spanEnd = paraEnd;
                else
                    spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, MetricAffectingSpan.class);
                int spanLen = spanEnd - spanStart;
                if (spanned == null) {
                    measured.addStyleRun(paint, spanLen, fm);
                } else {
                    MetricAffectingSpan[] spans = spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
                    spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
                    measured.addStyleRun(paint, spans, spanLen, fm);
                }
            }
            nextSpanStart = spanEnd;
            int fmTop = fm.top;
            int fmBottom = fm.bottom;
            int fmAscent = fm.ascent;
            int fmDescent = fm.descent;
            for (int j = spanStart; j < spanEnd; j++) {
                char c = chs[j - paraStart];
                if (c == CHAR_NEW_LINE) {
                // intentionally left empty
                } else if (c == CHAR_TAB) {
                    if (hasTab == false) {
                        hasTab = true;
                        hasTabOrEmoji = true;
                        if (spanned != null) {
                            // First tab this para, check for tabstops
                            TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, paraEnd, TabStopSpan.class);
                            if (spans.length > 0) {
                                tabStops = new TabStops(TAB_INCREMENT, spans);
                            }
                        }
                    }
                    if (tabStops != null) {
                        w = tabStops.nextTab(w);
                    } else {
                        w = TabStops.nextDefaultStop(w, TAB_INCREMENT);
                    }
                } else if (c >= CHAR_FIRST_HIGH_SURROGATE && c <= CHAR_LAST_LOW_SURROGATE && j + 1 < spanEnd) {
                    int emoji = Character.codePointAt(chs, j - paraStart);
                    if (emoji >= MIN_EMOJI && emoji <= MAX_EMOJI) {
                        Bitmap bm = EMOJI_FACTORY.getBitmapFromAndroidPua(emoji);
                        if (bm != null) {
                            Paint whichPaint;
                            if (spanned == null) {
                                whichPaint = paint;
                            } else {
                                whichPaint = mWorkPaint;
                            }
                            float wid = bm.getWidth() * -whichPaint.ascent() / bm.getHeight();
                            w += wid;
                            hasTabOrEmoji = true;
                            j++;
                        } else {
                            w += widths[j - paraStart];
                        }
                    } else {
                        w += widths[j - paraStart];
                    }
                } else {
                    w += widths[j - paraStart];
                }
                if (w <= width) {
                    fitWidth = w;
                    fit = j + 1;
                    if (fmTop < fitTop)
                        fitTop = fmTop;
                    if (fmAscent < fitAscent)
                        fitAscent = fmAscent;
                    if (fmDescent > fitDescent)
                        fitDescent = fmDescent;
                    if (fmBottom > fitBottom)
                        fitBottom = fmBottom;
                    if (c == CHAR_SPACE || c == CHAR_TAB || ((c == CHAR_DOT || c == CHAR_COMMA || c == CHAR_COLON || c == CHAR_SEMICOLON) && (j - 1 < here || !Character.isDigit(chs[j - 1 - paraStart])) && (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) || ((c == CHAR_SLASH || c == CHAR_HYPHEN) && (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) || (c >= CHAR_FIRST_CJK && isIdeographic(c, true) && j + 1 < spanEnd && isIdeographic(chs[j + 1 - paraStart], false))) {
                        okWidth = w;
                        ok = j + 1;
                        if (fitTop < okTop)
                            okTop = fitTop;
                        if (fitAscent < okAscent)
                            okAscent = fitAscent;
                        if (fitDescent > okDescent)
                            okDescent = fitDescent;
                        if (fitBottom > okBottom)
                            okBottom = fitBottom;
                    }
                } else {
                    final boolean moreChars = (j + 1 < spanEnd);
                    if (ok != here) {
                        while (ok < spanEnd && chs[ok - paraStart] == CHAR_SPACE) {
                            ok++;
                        }
                        v = out(source, here, ok, okAscent, okDescent, okTop, okBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, ok == bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, okWidth, paint, moreChars);
                        here = ok;
                    } else if (fit != here) {
                        // Log.e("text", "output fit " + here + " to " +fit);
                        v = out(source, here, fit, fitAscent, fitDescent, fitTop, fitBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, fit == bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, fitWidth, paint, moreChars);
                        here = fit;
                    } else {
                        // Log.e("text", "output one " + here + " to " +(here + 1));
                        // XXX not sure why the existing fm wasn't ok.
                        // measureText(paint, mWorkPaint,
                        //             source, here, here + 1, fm, tab,
                        //             null);
                        v = out(source, here, here + 1, fm.ascent, fm.descent, fm.top, fm.bottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, here + 1 == bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, widths[here - paraStart], paint, moreChars);
                        here = here + 1;
                    }
                    if (here < spanStart) {
                        // didn't output all the text for this span
                        // we've measured the raw widths, though, so
                        // just reset the start point
                        j = nextSpanStart = here;
                    } else {
                        // continue looping
                        j = here - 1;
                    }
                    ok = fit = here;
                    w = 0;
                    fitAscent = fitDescent = fitTop = fitBottom = 0;
                    okAscent = okDescent = okTop = okBottom = 0;
                    if (--firstWidthLineLimit <= 0) {
                        width = restWidth;
                    }
                }
                if (mLineCount >= mMaximumVisibleLineCount) {
                    break;
                }
            }
        }
        if (paraEnd != here && mLineCount < mMaximumVisibleLineCount) {
            if ((fitTop | fitBottom | fitDescent | fitAscent) == 0) {
                paint.getFontMetricsInt(fm);
                fitTop = fm.top;
                fitBottom = fm.bottom;
                fitAscent = fm.ascent;
                fitDescent = fm.descent;
            }
            // Log.e("text", "output rest " + here + " to " + end);
            v = out(source, here, paraEnd, fitAscent, fitDescent, fitTop, fitBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, hasTabOrEmoji, needMultiply, paraStart, chdirs, dir, easy, paraEnd == bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, w, paint, paraEnd != bufEnd);
        }
        paraStart = paraEnd;
        if (paraEnd == bufEnd)
            break;
    }
    if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) && mLineCount < mMaximumVisibleLineCount) {
        // Log.e("text", "output last " + bufEnd);
        paint.getFontMetricsInt(fm);
        v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, false, needMultiply, bufEnd, null, DEFAULT_DIR, true, true, includepad, trackpad, null, null, bufStart, ellipsize, ellipsizedWidth, 0, paint, false);
    }
}
Also used : TabStopSpan(android.text.style.TabStopSpan) LineHeightSpan(android.text.style.LineHeightSpan) Paint(android.graphics.Paint) Paint(android.graphics.Paint) LeadingMarginSpan2(android.text.style.LeadingMarginSpan.LeadingMarginSpan2) Bitmap(android.graphics.Bitmap) LeadingMarginSpan(android.text.style.LeadingMarginSpan) MetricAffectingSpan(android.text.style.MetricAffectingSpan)

Example 4 with MetricAffectingSpan

use of android.text.style.MetricAffectingSpan in project XobotOS by xamarin.

the class TextLine method ascent.

/**
     * 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();
}
Also used : MetricAffectingSpan(android.text.style.MetricAffectingSpan)

Example 5 with MetricAffectingSpan

use of android.text.style.MetricAffectingSpan in project android_frameworks_base by ResurrectionRemix.

the class TextLine method getOffsetBeforeAfter.

/**
     * 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)) {
        // 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) {
                // the start or end of this span.
                return after ? spanLimit : spanStart;
            }
        }
    }
    int dir = 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, dir, offset, cursorOpt);
    } else {
        return wp.getTextRunCursor(mText, mStart + spanStart, mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart;
    }
}
Also used : ReplacementSpan(android.text.style.ReplacementSpan) Paint(android.graphics.Paint) MetricAffectingSpan(android.text.style.MetricAffectingSpan)

Aggregations

MetricAffectingSpan (android.text.style.MetricAffectingSpan)32 Paint (android.graphics.Paint)30 ReplacementSpan (android.text.style.ReplacementSpan)22 CharacterStyle (android.text.style.CharacterStyle)7 LeadingMarginSpan (android.text.style.LeadingMarginSpan)7 LeadingMarginSpan2 (android.text.style.LeadingMarginSpan.LeadingMarginSpan2)7 LineHeightSpan (android.text.style.LineHeightSpan)7 TabStopSpan (android.text.style.TabStopSpan)7 Bitmap (android.graphics.Bitmap)2 TextPaint (android.text.TextPaint)2 Spanned (android.text.Spanned)1