Search in sources :

Example 1 with DirectionSpan

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

the class SpannableDocumentLayout method onDraw.

@Override
public void onDraw(Canvas canvas, int scrollTop, int scrollBottom) {
    if (tokens.length < TOKEN_LENGTH) {
        return;
    }
    Spannable textCpy = (Spannable) this.text;
    int startIndex = getTokenForVertical(scrollTop, TokenPosition.START_OF_LINE);
    int endIndex = getTokenForVertical(scrollBottom, TokenPosition.END_OF_LINE);
    boolean defIsReverse = false;
    for (LeadingMarginSpanDrawParameters parameters : mLeadMarginSpanDrawEvents) {
        // FIXME sort by Y and break out of loop
        int top = parameters.top - scrollTop;
        int bottom = parameters.bottom - scrollTop;
        if (bottom < 0 || top > scrollBottom)
            continue;
        parameters.span.drawLeadingMargin(canvas, paint, parameters.x, parameters.dir, top, parameters.baseline, bottom, textCpy, parameters.start, parameters.end, parameters.first, null);
    }
    int lastEndIndexY = tokens[endIndex + TOKEN_Y];
    int diffEndIndexYCount = 1;
    // FIXME Find next pos-y
    for (int s = endIndex; diffEndIndexYCount > 0 && s < tokens.length; s += TOKEN_LENGTH) {
        endIndex += TOKEN_LENGTH;
        if (lastEndIndexY != tokens[s + TOKEN_Y]) {
            diffEndIndexYCount--;
            lastEndIndexY = tokens[s + TOKEN_Y];
        }
    }
    for (int index = startIndex; index < endIndex; index += TOKEN_LENGTH) {
        if (tokens[index + TOKEN_START] == Integer.MAX_VALUE)
            break;
        DirectionSpan[] directionSpans = textCpy.getSpans(tokens[index + TOKEN_START], tokens[index + TOKEN_END], DirectionSpan.class);
        Styled.drawText(canvas, textCpy, tokens[index + TOKEN_START], tokens[index + TOKEN_END], Layout.DIR_LEFT_TO_RIGHT, directionSpans.length > 0 ? directionSpans[0].isReverse() : defIsReverse, tokens[index + TOKEN_X], 0, tokens[index + TOKEN_Y] - scrollTop, 0, paint, workPaint, false);
        if (params.debugging) {
            int lastColor = paint.getColor();
            float lastStrokeWidth = paint.getStrokeWidth();
            paint.setStrokeWidth(2);
            paint.setColor(Color.GREEN);
            canvas.drawLine(0, tokens[index + TOKEN_Y] - tokens[index + TOKEN_ASCENT] - scrollTop, params.parentWidth, tokens[index + TOKEN_Y] - tokens[index + TOKEN_ASCENT] - scrollTop, paint);
            paint.setColor(Color.CYAN);
            canvas.drawLine(0, tokens[index + TOKEN_Y] + tokens[index + TOKEN_DESCENT] - scrollTop, params.parentWidth, tokens[index + TOKEN_Y] + tokens[index + TOKEN_DESCENT] - scrollTop, paint);
            paint.setColor(lastColor);
            paint.setStrokeWidth(lastStrokeWidth);
        }
    }
}
Also used : DirectionSpan(com.bluejamesbond.text.style.DirectionSpan) Spannable(android.text.Spannable) TextPaint(android.text.TextPaint) Paint(android.graphics.Paint)

Example 2 with DirectionSpan

use of com.bluejamesbond.text.style.DirectionSpan 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)

Example 3 with DirectionSpan

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

the class MixedRTLTest method onCreate.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    addDocumentView(new ArticleBuilder().append(testName, false, new RelativeSizeSpan(2f), new StyleSpan(Typeface.BOLD), new LeftSpan()).append("<font color=0xFFC801>Justin Worland</font><font color=0x888888> @justinworland  Oct. 25, 2014</font>", false, new RelativeSizeSpan(0.8f), new StyleSpan(Typeface.BOLD), new DirectionSpan(Direction.RIGHT_TO_LEFT)).append("<font color=0x888888>Updated: Oct. 25, 2014 2:34 PM</font>".toUpperCase(), true, new RelativeSizeSpan(0.6f), new StyleSpan(Typeface.BOLD)).append("State health department staff will be on the ground at state airports", true, new RelativeSizeSpan(1.2f), new StyleSpan(Typeface.BOLD), new StyleSpan(Typeface.ITALIC)).append("Healthcare workers returning to New York or New Jersey after treating Ebola patients in West Africa will be placed under a mandatory quarantine, officials announced Friday, one day after a Doctors Without Borders doctor was diagnosed with the virus in New York City. Illinois announced a similar policy Saturday, meaning it will be enforced in states with three of the five airports through which passengers traveling from the Ebola-stricken West African countries must enter the United States.", true, new RelativeSizeSpan(1f), new JustifiedSpan(), new DirectionSpan(Direction.RIGHT_TO_LEFT)).append("N.J. Gov. Chris Christie and N.Y. Gov. Andrew Cuomo made the announcement as part of a broader procedural plan to help protect the densely packed, highly populated area from any further spread of the disease.", true, new RelativeSizeSpan(1f), new RightSpan(), new DirectionSpan(Direction.RIGHT_TO_LEFT)).append("“Since taking office, I have erred on the side of caution when it comes to the safety and protection of New Yorkers, and the current situation regarding Ebola will be no different,” Gov. Cuomo said. “The steps New York and New Jersey are taking today will strengthen our safeguards to protect our residents against this disease and help ensure those that may be infected by Ebola are treated with the highest precautions.”", true, new RelativeSizeSpan(1f), new JustifiedSpan(), new StyleSpan(Typeface.ITALIC)).append("New York and New Jersey state health department staff will be present on the ground at John F. Kennedy International Airport in New York and Newark Liberty Airport in New Jersey. In addition to implementing the mandatory quarantine of health care workers and others who had direct contact with Ebola patients, health department officials in each state will determine whether others should travelers should be hospitalized or quarantined.", true, new RelativeSizeSpan(1f), new JustifiedSpan(), new StyleSpan(Typeface.ITALIC)).append("“The announcements mark a dramatic escalation in measures designed to prevent the spread of Ebola in the United States. Previously, only individuals with symptoms of Ebola would be quarantined upon entry to the U.S. under a federal rule from the Centers for Diseases Control and the Department of Homeland Security.”", false, new RelativeSizeSpan(1f), new JustifiedSpan()), DocumentView.FORMATTED_TEXT);
}
Also used : DirectionSpan(com.bluejamesbond.text.style.DirectionSpan) JustifiedSpan(com.bluejamesbond.text.style.JustifiedSpan) LeftSpan(com.bluejamesbond.text.style.LeftSpan) StyleSpan(android.text.style.StyleSpan) ArticleBuilder(com.bluejamesbond.text.util.ArticleBuilder) RelativeSizeSpan(android.text.style.RelativeSizeSpan) RightSpan(com.bluejamesbond.text.style.RightSpan)

Aggregations

DirectionSpan (com.bluejamesbond.text.style.DirectionSpan)3 Paint (android.graphics.Paint)2 Spannable (android.text.Spannable)2 TextPaint (android.text.TextPaint)2 StaticLayout (android.text.StaticLayout)1 LeadingMarginSpan (android.text.style.LeadingMarginSpan)1 RelativeSizeSpan (android.text.style.RelativeSizeSpan)1 StyleSpan (android.text.style.StyleSpan)1 JustifiedSpan (com.bluejamesbond.text.style.JustifiedSpan)1 LeftSpan (com.bluejamesbond.text.style.LeftSpan)1 RightSpan (com.bluejamesbond.text.style.RightSpan)1 TextAlignment (com.bluejamesbond.text.style.TextAlignment)1 TextAlignmentSpan (com.bluejamesbond.text.style.TextAlignmentSpan)1 ArticleBuilder (com.bluejamesbond.text.util.ArticleBuilder)1 HashMap (java.util.HashMap)1