Search in sources :

Example 21 with TabStopSpan

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

the class StaticLayout method generate.

/* package */
void generate(CharSequence source, int bufStart, int bufEnd, TextPaint paint, int outerWidth, 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;
        // here is the offset of the starting character of the line we are currently measuring
        int here = paraStart;
        // ok is a character offset located after a word separator (space, tab, number...) where
        // we would prefer to cut the current line. Equals to here when no such break was found.
        int ok = paraStart;
        float okWidth = w;
        int okAscent = 0, okDescent = 0, okTop = 0, okBottom = 0;
        // fit is a character offset such that the [here, fit[ range fits in the allowed width.
        // We will cut the line there if no ok position is found.
        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 < paraEnd; spanStart = spanEnd) {
            if (spanned == null) {
                spanEnd = paraEnd;
                int spanLen = spanEnd - spanStart;
                measured.addStyleRun(paint, spanLen, fm);
            } else {
                spanEnd = spanned.nextSpanTransition(spanStart, paraEnd, MetricAffectingSpan.class);
                int spanLen = spanEnd - spanStart;
                MetricAffectingSpan[] spans = spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
                spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
                measured.addStyleRun(paint, spans, spanLen, fm);
            }
            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];
                }
                boolean isSpaceOrTab = c == CHAR_SPACE || c == CHAR_TAB || c == CHAR_ZWSP;
                if (w <= width || isSpaceOrTab) {
                    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;
                    // From the Unicode Line Breaking Algorithm (at least approximately)
                    boolean isLineBreak = isSpaceOrTab || // / is class SY and - is class HY, except when followed by a digit
                    ((c == CHAR_SLASH || c == CHAR_HYPHEN) && (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) || // (non-starters), which can be broken after but not before
                    (c >= CHAR_FIRST_CJK && isIdeographic(c, true) && j + 1 < spanEnd && isIdeographic(chs[j + 1 - paraStart], false));
                    if (isLineBreak) {
                        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);
                    int endPos;
                    int above, below, top, bottom;
                    float currentTextWidth;
                    if (ok != here) {
                        endPos = ok;
                        above = okAscent;
                        below = okDescent;
                        top = okTop;
                        bottom = okBottom;
                        currentTextWidth = okWidth;
                    } else if (fit != here) {
                        endPos = fit;
                        above = fitAscent;
                        below = fitDescent;
                        top = fitTop;
                        bottom = fitBottom;
                        currentTextWidth = fitWidth;
                    } else {
                        endPos = here + 1;
                        above = fm.ascent;
                        below = fm.descent;
                        top = fm.top;
                        bottom = fm.bottom;
                        currentTextWidth = widths[here - paraStart];
                    }
                    v = out(source, here, endPos, above, below, top, bottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, hasTabOrEmoji, needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, currentTextWidth, paint, moreChars);
                    here = endPos;
                    // restart j-span loop from here, compensating for the j++
                    j = here - 1;
                    ok = fit = here;
                    w = 0;
                    fitAscent = fitDescent = fitTop = fitBottom = 0;
                    okAscent = okDescent = okTop = okBottom = 0;
                    if (--firstWidthLineLimit <= 0) {
                        width = restWidth;
                    }
                    if (here < spanStart) {
                        // The text was cut before the beginning of the current span range.
                        // Exit the span loop, and get spanStart to start over from here.
                        measured.setPos(here);
                        spanEnd = here;
                        break;
                    }
                    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, chdirs, dir, easy, 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, null, DEFAULT_DIR, true, bufEnd, 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 22 with TabStopSpan

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

the class Layout method measurePara.

/* package */
static float measurePara(TextPaint paint, CharSequence text, int start, int end) {
    MeasuredText mt = MeasuredText.obtain();
    TextLine tl = TextLine.obtain();
    try {
        mt.setPara(text, start, end, TextDirectionHeuristics.LTR);
        Directions directions;
        int dir;
        if (mt.mEasy) {
            directions = DIRS_ALL_LEFT_TO_RIGHT;
            dir = Layout.DIR_LEFT_TO_RIGHT;
        } else {
            directions = AndroidBidi.directions(mt.mDir, mt.mLevels, 0, mt.mChars, 0, mt.mLen);
            dir = mt.mDir;
        }
        char[] chars = mt.mChars;
        int len = mt.mLen;
        boolean hasTabs = false;
        TabStops tabStops = null;
        for (int i = 0; i < len; ++i) {
            if (chars[i] == '\t') {
                hasTabs = true;
                if (text instanceof Spanned) {
                    Spanned spanned = (Spanned) text;
                    int spanEnd = spanned.nextSpanTransition(start, end, TabStopSpan.class);
                    TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd, TabStopSpan.class);
                    if (spans.length > 0) {
                        tabStops = new TabStops(TAB_INCREMENT, spans);
                    }
                }
                break;
            }
        }
        tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops);
        return tl.metrics(null);
    } finally {
        TextLine.recycle(tl);
        MeasuredText.recycle(mt);
    }
}
Also used : TabStopSpan(android.text.style.TabStopSpan) Paint(android.graphics.Paint)

Example 23 with TabStopSpan

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

the class Layout method getHorizontal.

private float getHorizontal(int offset, boolean trailing, int line, boolean clamped) {
    int start = getLineStart(line);
    int end = getLineEnd(line);
    int dir = getParagraphDirection(line);
    boolean hasTabOrEmoji = getLineContainsTab(line);
    Directions directions = getLineDirections(line);
    TabStops tabStops = null;
    if (hasTabOrEmoji && mText instanceof Spanned) {
        // Just checking this line should be good enough, tabs should be
        // consistent across all lines in a paragraph.
        TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class);
        if (tabs.length > 0) {
            // XXX should reuse
            tabStops = new TabStops(TAB_INCREMENT, tabs);
        }
    }
    TextLine tl = TextLine.obtain();
    tl.set(mPaint, mText, start, end, dir, directions, hasTabOrEmoji, tabStops);
    float wid = tl.measure(offset - start, trailing, null);
    TextLine.recycle(tl);
    if (clamped && wid > mWidth) {
        wid = mWidth;
    }
    int left = getParagraphLeft(line);
    int right = getParagraphRight(line);
    return getLineStartPos(line, left, right) + wid;
}
Also used : TabStopSpan(android.text.style.TabStopSpan) Paint(android.graphics.Paint)

Example 24 with TabStopSpan

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

the class Layout method getLineStartPos.

/**
     * Return the start position of the line, given the left and right bounds
     * of the margins.
     *
     * @param line the line index
     * @param left the left bounds (0, or leading margin if ltr para)
     * @param right the right bounds (width, minus leading margin if rtl para)
     * @return the start position of the line (to right of line if rtl para)
     */
private int getLineStartPos(int line, int left, int right) {
    // Adjust the point at which to start rendering depending on the
    // alignment of the paragraph.
    Alignment align = getParagraphAlignment(line);
    int dir = getParagraphDirection(line);
    if (align == Alignment.ALIGN_LEFT) {
        align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
    } else if (align == Alignment.ALIGN_RIGHT) {
        align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
    }
    int x;
    if (align == Alignment.ALIGN_NORMAL) {
        if (dir == DIR_LEFT_TO_RIGHT) {
            x = left;
        } else {
            x = right;
        }
    } else {
        TabStops tabStops = null;
        if (mSpannedText && getLineContainsTab(line)) {
            Spanned spanned = (Spanned) mText;
            int start = getLineStart(line);
            int spanEnd = spanned.nextSpanTransition(start, spanned.length(), TabStopSpan.class);
            TabStopSpan[] tabSpans = getParagraphSpans(spanned, start, spanEnd, TabStopSpan.class);
            if (tabSpans.length > 0) {
                tabStops = new TabStops(TAB_INCREMENT, tabSpans);
            }
        }
        int max = (int) getLineExtent(line, tabStops, false);
        if (align == Alignment.ALIGN_OPPOSITE) {
            if (dir == DIR_LEFT_TO_RIGHT) {
                x = right - max;
            } else {
                // max is negative here
                x = left - max;
            }
        } else {
            // Alignment.ALIGN_CENTER
            max = max & ~1;
            x = (left + right - max) >> 1;
        }
    }
    return x;
}
Also used : TabStopSpan(android.text.style.TabStopSpan) Paint(android.graphics.Paint)

Example 25 with TabStopSpan

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

the class Layout method nextTab.

/**
     * Returns the position of the next tab stop after h on the line.
     *
     * @param text the text
     * @param start start of the line
     * @param end limit of the line
     * @param h the current horizontal offset
     * @param tabs the tabs, can be null.  If it is null, any tabs in effect
     * on the line will be used.  If there are no tabs, a default offset
     * will be used to compute the tab stop.
     * @return the offset of the next tab stop.
     */
/* package */
static float nextTab(CharSequence text, int start, int end, float h, Object[] tabs) {
    float nh = Float.MAX_VALUE;
    boolean alltabs = false;
    if (text instanceof Spanned) {
        if (tabs == null) {
            tabs = getParagraphSpans((Spanned) text, start, end, TabStopSpan.class);
            alltabs = true;
        }
        for (int i = 0; i < tabs.length; i++) {
            if (!alltabs) {
                if (!(tabs[i] instanceof TabStopSpan))
                    continue;
            }
            int where = ((TabStopSpan) tabs[i]).getTabStop();
            if (where < nh && where > h)
                nh = where;
        }
        if (nh != Float.MAX_VALUE)
            return nh;
    }
    return ((int) ((h + TAB_INCREMENT) / TAB_INCREMENT)) * TAB_INCREMENT;
}
Also used : TabStopSpan(android.text.style.TabStopSpan) Paint(android.graphics.Paint)

Aggregations

Paint (android.graphics.Paint)42 TabStopSpan (android.text.style.TabStopSpan)42 LeadingMarginSpan (android.text.style.LeadingMarginSpan)12 LeadingMarginSpan2 (android.text.style.LeadingMarginSpan.LeadingMarginSpan2)7 LineHeightSpan (android.text.style.LineHeightSpan)7 MetricAffectingSpan (android.text.style.MetricAffectingSpan)7 Bitmap (android.graphics.Bitmap)2