Esempio n. 1
0
        public void addWordBreak(int offset, ParaWidth preBreak, ParaWidth postBreak, int preSpaceCount, int postSpaceCount, float penalty, HyphenationType hyph)
        {
            Candidate cand  = new Candidate();
            ParaWidth width = mCandidates.back().preBreak;

            if (postBreak - width > currentLineWidth() != null)
            {
                // Add desperate breaks.
                // Note: these breaks are based on the shaping of the (non-broken) original
                // text; they are imprecise especially in the presence of kerning,
                // ligatures, and Arabic shaping.
                int i = mCandidates.back().offset;
                width += mCharWidths[i++];
                for (; i < offset; i++)
                {
                    float w = mCharWidths[i];
                    if (w > 0F)
                    {
                        cand.offset    = i;
                        cand.preBreak  = width;
                        cand.postBreak = width;
                        // postSpaceCount doesn't include trailing spaces
                        cand.preSpaceCount  = postSpaceCount;
                        cand.postSpaceCount = postSpaceCount;
                        cand.penalty        = SCORE_DESPERATE;
                        cand.hyphenType     = HyphenationType.BREAK_AND_DONT_INSERT_HYPHEN;
        #if VERBOSE_DEBUG
                        ALOGD("desperate cand: %zd %g:%g", mCandidates.size(), cand.postBreak, cand.preBreak);
        #endif
                        addCandidate(cand);
                        width += w;
                    }
                }
            }

            cand.offset         = offset;
            cand.preBreak       = preBreak;
            cand.postBreak      = postBreak;
            cand.penalty        = penalty;
            cand.preSpaceCount  = preSpaceCount;
            cand.postSpaceCount = postSpaceCount;
            cand.hyphenType     = hyph;
        #if VERBOSE_DEBUG
            ALOGD("cand: %zd %g:%g", mCandidates.size(), cand.postBreak, cand.preBreak);
        #endif
            addCandidate(cand);
        }
Esempio n. 2
0
        public void computeBreaksOptimal(bool isRectangle)
        {
            int   active          = 0;
            int   nCand           = mCandidates.size();
            float width           = mLineWidths.getLineWidth(0);
            float shortLineFactor = mJustified ? 0.75f : 0.5f;
            float maxShrink       = mJustified ? SHRINKABILITY * getSpaceWidth() : 0.0f;

            // "i" iterates through candidates for the end of the line.
            for (int i = 1; i < nCand; i++)
            {
                bool  atEnd          = i == nCand - 1;
                float best           = SCORE_INFTY;
                int   bestPrev       = 0;
                int   lineNumberLast = 0;

                if (!isRectangle)
                {
                    int lineNumberLast = mCandidates[active].lineNumber;
                    width = mLineWidths.getLineWidth(lineNumberLast);
                }
                ParaWidth leftEdge = mCandidates[i].postBreak - width;
                float     bestHope = 0F;

                // "j" iterates through candidates for the beginning of the line.
                for (int j = active; j < i; j++)
                {
                    if (!isRectangle)
                    {
                        int lineNumber = mCandidates[j].lineNumber;
                        if (lineNumber != lineNumberLast)
                        {
                            float widthNew = mLineWidths.getLineWidth(lineNumber);
                            if (widthNew != width)
                            {
                                leftEdge = mCandidates[i].postBreak - width;
                                bestHope = 0F;
                                width    = widthNew;
                            }
                            lineNumberLast = lineNumber;
                        }
                    }
                    float jScore = mCandidates[j].score;
                    if (jScore + bestHope >= best)
                    {
                        continue;
                    }
                    float delta = mCandidates[j].preBreak - leftEdge;

                    // compute width score for line

                    // Note: the "bestHope" optimization makes the assumption that, when delta
                    // is non-negative, widthScore will increase monotonically as successive
                    // candidate breaks are considered.
                    float widthScore        = 0.0f;
                    float additionalPenalty = 0.0f;
                    if ((atEnd || !mJustified) && delta < 0F)
                    {
                        widthScore = SCORE_OVERFULL;
                    }
                    else if (atEnd && mStrategy != kBreakStrategy_Balanced)
                    {
                        // increase penalty for hyphen on last line
                        additionalPenalty = LAST_LINE_PENALTY_MULTIPLIER * mCandidates[j].penalty;
                        // Penalize very short (< 1 - shortLineFactor of total width) lines.
                        float underfill = delta - shortLineFactor * width;
                        widthScore = underfill > 0F ? underfill * underfill : 0;
                    }
                    else
                    {
                        widthScore = delta * delta;
                        if (delta < 0F)
                        {
                            if (-delta < maxShrink * (mCandidates[i].postSpaceCount - mCandidates[j].preSpaceCount))
                            {
                                widthScore *= SHRINK_PENALTY_MULTIPLIER;
                            }
                            else
                            {
                                widthScore = SCORE_OVERFULL;
                            }
                        }
                    }

                    if (delta < 0F)
                    {
                        //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                        //ORIGINAL LINE: active = j + 1;
                        active.CopyFrom(j + 1);
                    }
                    else
                    {
                        bestHope = widthScore;
                    }

                    float score = jScore + widthScore + additionalPenalty;
                    if (score <= best)
                    {
                        best = score;
                        //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                        //ORIGINAL LINE: bestPrev = j;
                        bestPrev.CopyFrom(j);
                    }
                }
                mCandidates[i].score      = best + mCandidates[i].penalty + mLinePenalty;
                mCandidates[i].prev       = bestPrev;
                mCandidates[i].lineNumber = mCandidates[bestPrev].lineNumber + 1;
        #if VERBOSE_DEBUG
                ALOGD("break %zd: score=%g, prev=%zd", i, mCandidates[i].score, mCandidates[i].prev);
        #endif
            }
            finishBreaksOptimal();
        }
Esempio n. 3
0
        // TODO: this class is actually fairly close to being general and not tied to
        // using Minikin to do the shaping of the strings. The main thing that would
        // need to be changed is having some kind of callback (or virtual class, or
        // maybe even template), which could easily be instantiated with Minikin's
        // Layout. Future work for when needed.
        public float addStyleRun(MinikinPaint paint, FontCollection typeface, FontStyle style, int start, int end, bool isRtl)
        {
            float width     = 0.0f;
            int   bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;

            float hyphenPenalty = 0.0F;

            if (paint != null)
            {
                width = Layout.measureText(mTextBuf.data(), start, end - start, mTextBuf.size(), bidiFlags, style, paint, typeface, mCharWidths.data() + start);

                // a heuristic that seems to perform well
                hyphenPenalty = 0.5 * paint.size * paint.scaleX * mLineWidths.getLineWidth(0);
                if (mHyphenationFrequency == kHyphenationFrequency_Normal)
                {
                    hyphenPenalty *= 4.0; // TODO: Replace with a better value after some testing
                }

                if (mJustified)
                {
                    // Make hyphenation more aggressive for fully justified text (so that
                    // "normal" in justified mode is the same as "full" in ragged-right).
                    hyphenPenalty *= 0.25;
                }
                else
                {
                    // Line penalty is zero for justified text.
                    mLinePenalty = Math.Max(mLinePenalty, hyphenPenalty * LINE_PENALTY_MULTIPLIER);
                }
            }

            int       current        = (int)mWordBreaker.current();
            int       afterWord      = start;
            int       lastBreak      = start;
            ParaWidth lastBreakWidth = mWidth;
            ParaWidth postBreak      = mWidth;
            int       postSpaceCount = mSpaceCount;

            for (int i = start; i < end; i++)
            {
                UInt16 c = mTextBuf[i];
                if (c == CHAR_TAB)
                {
                    mWidth = mPreBreak + mTabStops.nextTab(mWidth - mPreBreak);
                    if (mFirstTabIndex == INT_MAX)
                    {
                        mFirstTabIndex = (int)i;
                    }
                    // fall back to greedy; other modes don't know how to deal with tabs
                    mStrategy = kBreakStrategy_Greedy;
                }
                else
                {
                    if (isWordSpace(new UInt16(c)))
                    {
                        mSpaceCount += 1;
                    }
                    mWidth += mCharWidths[i];
                    if (!isLineEndSpace(new UInt16(c)))
                    {
                        postBreak      = mWidth;
                        postSpaceCount = mSpaceCount;
                        //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                        //ORIGINAL LINE: afterWord = i + 1;
                        afterWord.CopyFrom(i + 1);
                    }
                }
                if (i + 1 == current != null)
                {
                    int wordStart = mWordBreaker.wordStart();
                    int wordEnd   = mWordBreaker.wordEnd();
                    if (paint != null && mHyphenator != null && mHyphenationFrequency != kHyphenationFrequency_None && wordStart >= start != null && wordEnd > wordStart && wordEnd - wordStart <= LONGEST_HYPHENATED_WORD)
                    {
                        mHyphenator.hyphenate(mHyphBuf, mTextBuf[wordStart], wordEnd - wordStart, mLocale);
        #if VERBOSE_DEBUG
                        string hyphenatedString;
                        for (int j = wordStart; j < wordEnd; j++)
                        {
                            if (mHyphBuf[j - wordStart] == HyphenationType.BREAK_AND_INSERT_HYPHEN)
                            {
                                hyphenatedString.push_back('-');
                            }
                            // Note: only works with ASCII, should do UTF-8 conversion here
                            hyphenatedString.push_back(buffer()[j]);
                        }
                        ALOGD("hyphenated string: %s", hyphenatedString);
        #endif

                        // measure hyphenated substrings
                        for (int j = wordStart; j < wordEnd; j++)
                        {
                            HyphenationType hyph = mHyphBuf[j - wordStart];
                            if (hyph != HyphenationType.DONT_BREAK)
                            {
                                paint.hyphenEdit = HyphenEdit.editForThisLine(hyph);
                                float     firstPartWidth = Layout.measureText(mTextBuf.data(), lastBreak, j - lastBreak, mTextBuf.size(), bidiFlags, style, paint, typeface, null);
                                ParaWidth hyphPostBreak  = lastBreakWidth + firstPartWidth;

                                paint.hyphenEdit = HyphenEdit.editForNextLine(hyph);
                                float     secondPartWidth = Layout.measureText(mTextBuf.data(), j, afterWord - j, mTextBuf.size(), bidiFlags, style, paint, typeface, null);
                                ParaWidth hyphPreBreak    = postBreak - secondPartWidth;

                                addWordBreak(j, hyphPreBreak, hyphPostBreak, postSpaceCount, postSpaceCount, hyphenPenalty, hyph);

                                paint.hyphenEdit = HyphenEdit.NO_EDIT;
                            }
                        }
                    }

                    // Skip break for zero-width characters inside replacement span
                    if (paint != null || current == end || mCharWidths[current] > 0)
                    {
                        float penalty = hyphenPenalty * mWordBreaker.breakBadness();
                        addWordBreak(current, mWidth, postBreak, mSpaceCount, postSpaceCount, penalty, HyphenationType.DONT_BREAK);
                    }
                    //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                    //ORIGINAL LINE: lastBreak = current;
                    lastBreak.CopyFrom(current);
                    lastBreakWidth = mWidth;
                    //C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
                    //ORIGINAL LINE: current = (int)mWordBreaker.next();
                    current.CopyFrom((int)mWordBreaker.next());
                }
            }

            return(width);
        }