Search in sources :

Example 1 with TextAlignmentSpan

use of com.bluejamesbond.text.style.TextAlignmentSpan in project TextJustify-Android by bluejamesbond.

the class SpannableDocumentLayout method onMeasure.

@SuppressWarnings("ConstantConditions")
@Override
public boolean onMeasure(IProgress<Float> progress, ICancel<Boolean> cancelled) {
    boolean done = true;
    float parentWidth = params.getParentWidth();
    float boundWidth = params.getParentWidth() - params.getInsetPaddingLeft() - params.getInsetPaddingRight();
    mLeadMarginSpanDrawEvents = new LinkedList<>();
    StaticLayout staticLayout = new StaticLayout(getText(), (TextPaint) getPaint(), (int) boundWidth, Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
    int[] newTokens = new int[TOKEN_LENGTH * 1000];
    LeadingMarginSpan[] activeLeadSpans = new LeadingMarginSpan[0];
    HashMap<LeadingMarginSpan, Integer> leadSpans = new HashMap<>();
    TextAlignment defAlign = params.textAlignment;
    Spannable textCpy = (Spannable) this.text;
    Paint.FontMetricsInt fmi = paint.getFontMetricsInt();
    int maxTextIndex = textCpy.length() - 1;
    int lines = staticLayout.getLineCount();
    int enableLineBreak = 0;
    int index = 0;
    int lineNumber;
    float x;
    float y = params.insetPaddingTop;
    float left = params.insetPaddingLeft;
    float right = params.insetPaddingRight;
    float lineHeightAdd = params.lineHeightMultiplier;
    float lastAscent;
    float lastDescent;
    boolean isParaStart = true;
    boolean isReverse = params.reverse;
    for (lineNumber = 0; lineNumber < lines; lineNumber++) {
        if (cancelled.isCancelled()) {
            done = false;
            break;
        }
        progress.onUpdate((float) lineNumber / (float) lines);
        newTokens = ammortizeArray(newTokens, index);
        int start = staticLayout.getLineStart(lineNumber);
        int end = staticLayout.getLineEnd(lineNumber);
        float realWidth = boundWidth;
        if (params.debugging) {
            Console.log(start + " => " + end + " :: " + " " + -staticLayout.getLineAscent(lineNumber) + " " + staticLayout.getLineDescent(lineNumber) + " " + textCpy.subSequence(start, end).toString());
        }
        // start == end => end of textCpy
        if (start == end || lineNumber >= params.maxLines) {
            break;
        }
        // Get textCpy alignment for the line
        TextAlignmentSpan[] textAlignmentSpans = textCpy.getSpans(start, end, TextAlignmentSpan.class);
        TextAlignment lineTextAlignment = textAlignmentSpans.length == 0 ? defAlign : textAlignmentSpans[0].getTextAlignment();
        // Calculate components of line height
        lastAscent = -staticLayout.getLineAscent(lineNumber);
        lastDescent = staticLayout.getLineDescent(lineNumber) + lineHeightAdd;
        // Handle reverse
        DirectionSpan[] directionSpans = textCpy.getSpans(start, end, DirectionSpan.class);
        isReverse = directionSpans.length > 0 ? directionSpans[0].isReverse() : params.reverse;
        // Line is ONLY a <br/> or \n
        if (start + 1 == end && (Character.getNumericValue(textCpy.charAt(start)) == -1 || textCpy.charAt(start) == '\n')) {
            // Line break indicates a new paragraph
            // is next
            isParaStart = true;
            // Use the line-height of the next line
            if (lineNumber + 1 < lines) {
                y += enableLineBreak * (-staticLayout.getLineAscent(lineNumber + 1) + staticLayout.getLineDescent(lineNumber + 1));
            }
            // Don't ignore the next line breaks
            enableLineBreak = 1;
            continue;
        } else {
            // Ignore the next line break
            enableLineBreak = 0;
        }
        x = lineTextAlignment == TextAlignment.RIGHT ? right : left;
        y += lastAscent;
        // Line CONTAINS a \n
        boolean isParaEnd = end == maxTextIndex || textCpy.charAt(Math.min(end, maxTextIndex)) == '\n' || textCpy.charAt(end - 1) == '\n';
        if (isParaEnd) {
            enableLineBreak = 1;
        }
        // LeadingMarginSpan block
        if (isParaStart) {
            // Process LeadingMarginSpan
            activeLeadSpans = textCpy.getSpans(start, end, LeadingMarginSpan.class);
            // Set up all the spans
            if (activeLeadSpans.length > 0) {
                for (LeadingMarginSpan leadSpan : activeLeadSpans) {
                    if (!leadSpans.containsKey(leadSpan)) {
                        // Default margin is everything
                        int marginLineCount = -1;
                        if (leadSpan instanceof LeadingMarginSpan.LeadingMarginSpan2) {
                            LeadingMarginSpan.LeadingMarginSpan2 leadSpan2 = ((LeadingMarginSpan.LeadingMarginSpan2) leadSpan);
                            marginLineCount = leadSpan2.getLeadingMarginLineCount();
                        }
                        leadSpans.put(leadSpan, marginLineCount);
                    }
                }
            }
        }
        float totalMargin = 0.0f;
        int top = (int) (y - lastAscent);
        int baseline = (int) (y);
        int bottom = (int) (y + lastDescent);
        for (LeadingMarginSpan leadSpan : activeLeadSpans) {
            // TOKEN_X based on alignment
            float calcX = x;
            // LineAlignment
            int lineAlignmentVal = 1;
            if (lineTextAlignment == TextAlignment.RIGHT) {
                lineAlignmentVal = -1;
                calcX = parentWidth - x;
            }
            // Get current line count
            int spanLines = leadSpans.get(leadSpan);
            // Update only if the valid next valid
            if (spanLines > 0 || spanLines == -1) {
                leadSpans.put(leadSpan, spanLines == -1 ? -1 : spanLines - 1);
                mLeadMarginSpanDrawEvents.add(new LeadingMarginSpanDrawParameters(leadSpan, (int) calcX, lineAlignmentVal, top, baseline, bottom, start, end, isParaStart));
                // Is margin required?
                totalMargin += leadSpan.getLeadingMargin(isParaStart);
            }
        }
        x += totalMargin;
        realWidth -= totalMargin;
        // Disable/enable new paragraph
        isParaStart = isParaEnd;
        // TextAlignmentSpan block
        if (isParaEnd && lineTextAlignment == TextAlignment.JUSTIFIED) {
            lineTextAlignment = isReverse ? TextAlignment.RIGHT : TextAlignment.LEFT;
        }
        if (params.debugging) {
            Console.log(String.format("Align: %s, X: %fpx, Y: %fpx, PWidth: %fpx", lineTextAlignment, x, y, parentWidth));
        }
        switch(lineTextAlignment) {
            case RIGHT:
                {
                    float lineWidth = Styled.measureText(paint, workPaint, textCpy, start, end, fmi);
                    index = pushToken(newTokens, index, start, end, parentWidth - x - lineWidth, y, lastAscent, lastDescent, lineNumber);
                    y += lastDescent;
                    continue;
                }
            case CENTER:
                {
                    float lineWidth = Styled.measureText(paint, workPaint, textCpy, start, end, fmi);
                    index = pushToken(newTokens, index, start, end, x + (realWidth - lineWidth) / 2, y, lastAscent, lastDescent, lineNumber);
                    y += lastDescent;
                    continue;
                }
            case LEFT:
                {
                    index = pushToken(newTokens, index, start, end, x, y, lastAscent, lastDescent, lineNumber);
                    y += lastDescent;
                    continue;
                }
        }
        // FIXME: Space at the end of each line, possibly due to scrollbar offset
        //            LinkedList<Integer> tokenized = tokenize(textCpy, start, end - 1);
        // FIXME: 2016/4/16 Issue #105 bug
        LinkedList<Integer> tokenized = tokenize(textCpy, start, end);
        // If one long word without any spaces
        if (tokenized.size() == 1) {
            int stop = tokenized.get(0);
            // characters individually
            if (getTrimmedLength(textCpy, start, stop) != 0) {
                float[] textWidths = new float[stop - start];
                float sum = 0.0f, textsOffset = 0.0f, offset;
                int m = 0;
                Styled.getTextWidths(paint, workPaint, textCpy, start, stop, textWidths, fmi);
                for (float tw : textWidths) {
                    sum += tw;
                }
                offset = (realWidth - sum) / (textWidths.length - 1);
                for (int k = start; k < stop; k++) {
                    index = pushToken(newTokens, index, k, k + 1, x + textsOffset + (offset * m), y, lastAscent, lastDescent, lineNumber);
                    newTokens = ammortizeArray(newTokens, index);
                    textsOffset += textWidths[m++];
                }
            }
        } else //  Handle multiple words
        {
            int m = 1;
            int indexOffset = 0;
            int startIndex = index;
            int reqSpaces = (tokenized.size() - 1) * TOKEN_LENGTH;
            int rtlZero = 0;
            float rtlRight = 0;
            float rtlMul = 1;
            float lineWidth = 0;
            float offset;
            if (isReverse) {
                indexOffset = -2 * TOKEN_LENGTH;
                rtlRight = parentWidth;
                rtlMul = -1;
                rtlZero = 1;
                // reverse index
                index += reqSpaces;
            }
            // more space
            newTokens = ammortizeArray(newTokens, index + reqSpaces);
            for (int stop : tokenized) {
                float wordWidth = Styled.measureText(paint, workPaint, textCpy, start, stop, fmi);
                // add word
                index = pushToken(newTokens, index, start, stop, rtlRight + rtlMul * (x + lineWidth + rtlZero * wordWidth), y, lastAscent, lastDescent, lineNumber);
                lineWidth += wordWidth;
                start = stop + 1;
                // based on if rtl
                index += indexOffset;
            }
            if (isReverse) {
                index = startIndex + reqSpaces + TOKEN_LENGTH;
            }
            offset = (realWidth - lineWidth) / (float) (tokenized.size() - 1);
            if (isReverse) {
                for (int pos = index - TOKEN_LENGTH * 2; pos >= startIndex; pos -= TOKEN_LENGTH) {
                    newTokens[pos + TOKEN_X] = (int) (((float) newTokens[pos + TOKEN_X]) - (offset * (float) m++));
                }
            } else {
                for (int pos = startIndex + TOKEN_LENGTH; pos < index; pos += TOKEN_LENGTH) {
                    newTokens[pos + TOKEN_X] = (int) (((float) newTokens[pos + TOKEN_X]) + (offset * (float) m++));
                }
            }
        }
        y += lastDescent;
    }
    lineCount = lineNumber;
    tokens = newTokens;
    params.changed = false;
    textChange = !done;
    measuredHeight = (int) (y - lineHeightAdd + params.insetPaddingBottom);
    return done;
}
Also used : DirectionSpan(com.bluejamesbond.text.style.DirectionSpan) HashMap(java.util.HashMap) StaticLayout(android.text.StaticLayout) TextPaint(android.text.TextPaint) Paint(android.graphics.Paint) TextPaint(android.text.TextPaint) Paint(android.graphics.Paint) TextAlignmentSpan(com.bluejamesbond.text.style.TextAlignmentSpan) TextAlignment(com.bluejamesbond.text.style.TextAlignment) LeadingMarginSpan(android.text.style.LeadingMarginSpan) Spannable(android.text.Spannable)

Aggregations

Paint (android.graphics.Paint)1 Spannable (android.text.Spannable)1 StaticLayout (android.text.StaticLayout)1 TextPaint (android.text.TextPaint)1 LeadingMarginSpan (android.text.style.LeadingMarginSpan)1 DirectionSpan (com.bluejamesbond.text.style.DirectionSpan)1 TextAlignment (com.bluejamesbond.text.style.TextAlignment)1 TextAlignmentSpan (com.bluejamesbond.text.style.TextAlignmentSpan)1 HashMap (java.util.HashMap)1