Example #1
0
    void appendFragmentAndCreateRunIfNeeded(const TextFragmentIterator::TextFragment& fragment, Layout::RunVector& runs)
    {
        // Adjust end position while collapsing.
        unsigned endPosition = fragment.isCollapsed() ? fragment.start() + 1 : fragment.end();
        // New line needs new run.
        if (!m_runsWidth)
            runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false));
        else {
            const auto& lastFragment = m_fragments.last();
            // Advance last completed fragment when the previous fragment is all set (including multiple parts across renderers)
            if ((lastFragment.type() != fragment.type()) || !lastFragment.overlapsToNextRenderer())
                m_lastCompleteFragment = lastFragment;
            // Collapse neighbouring whitespace, if they are across multiple renderers and are not collapsed yet.
            if (lastFragment.isCollapsible() && fragment.isCollapsible()) {
                ASSERT(lastFragment.isLastInRenderer());
                if (!lastFragment.isCollapsed()) {
                    // Line width needs to be reset so that now it takes collapsing into consideration.
                    m_runsWidth -= (lastFragment.width() - m_collapsedWhitespaceWidth);
                }
                // This fragment is collapsed completely. No run is needed.
                return;
            }
            if (lastFragment.isLastInRenderer() || lastFragment.isCollapsed())
                runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false));
            else {
                Run& lastRun = runs.last();
                lastRun.end = endPosition;
                lastRun.logicalRight += fragment.width();
            }
        }
        m_fragments.append(fragment);
        m_runsWidth += fragment.width();

        if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) {
            m_trailingWhitespaceLength += endPosition - fragment.start();
            m_trailingWhitespaceWidth += fragment.width();
        } else {
            m_trailingWhitespaceLength = 0;
            m_trailingWhitespaceWidth = 0;
        }

        if (!m_firstCharacterFits)
            m_firstCharacterFits = fragment.start() + 1 > endPosition || m_runsWidth <= m_availableWidth;
    }
Example #2
0
void createTextRuns(Layout::RunVector& runs, unsigned& lineCount, RenderBlockFlow& flow, RenderText& textRenderer)
{
    const Style style(flow.style());

    const CharacterType* text = textRenderer.text()->characters<CharacterType>();
    const unsigned textLength = textRenderer.textLength();

    LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
    LayoutUnit lineHeight = lineHeightFromFlow(flow);

    LazyLineBreakIterator lineBreakIterator(textRenderer.text(), flow.style().locale());

    unsigned lineEnd = 0;
    while (lineEnd < textLength) {
        if (style.collapseWhitespace)
            lineEnd = skipWhitespaces(text, lineEnd, textLength, style.preserveNewline);

        unsigned lineStart = lineEnd;

        // LineWidth reads the current y position from the flow so keep it updated.
        flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
        LineWidth lineWidth(flow, false, DoNotIndentText);

        auto lineRuns = createLineRuns(lineStart, lineWidth, lineBreakIterator, style, text, textLength, textRenderer);

        lineEnd = lineRuns.last().end;
        if (lineStart == lineEnd)
            continue;

        lineRuns.last().isEndOfLine = true;

        float lineLeft = computeLineLeft(style.textAlign, lineWidth);
        adjustRunOffsets(lineRuns, lineLeft);

        for (unsigned i = 0; i < lineRuns.size(); ++i)
            runs.append(lineRuns[i]);

        ++lineCount;
    }
}
Example #3
0
std::unique_ptr<Layout> create(RenderBlockFlow& flow)
{
    RenderText& textRenderer = toRenderText(*flow.firstChild());
    ASSERT(!textRenderer.firstTextBox());

    const RenderStyle& style = flow.style();
    const unsigned textLength = textRenderer.textLength();

    ETextAlign textAlign = style.textAlign();
    float wordTrailingSpaceWidth = style.font().width(TextRun(&space, 1));

    LazyLineBreakIterator lineBreakIterator(textRenderer.text(), style.locale());
    int nextBreakable = -1;

    Layout::RunVector runs;
    unsigned lineCount = 0;

    unsigned lineEndOffset = 0;
    while (lineEndOffset < textLength) {
        lineEndOffset = skipWhitespaces(textRenderer, lineEndOffset, textLength);
        unsigned lineStartOffset = lineEndOffset;
        unsigned wordEndOffset = lineEndOffset;
        LineWidth lineWidth(flow, false, DoNotIndentText);

        Vector<Run, 4> lineRuns;
        lineRuns.uncheckedAppend(Run(lineStartOffset, 0));

        while (wordEndOffset < textLength) {
            ASSERT(!isWhitespace(textRenderer.characterAt(wordEndOffset)));

            bool previousWasSpaceBetweenWords = wordEndOffset > lineStartOffset && isWhitespace(textRenderer.characterAt(wordEndOffset - 1));
            unsigned wordStartOffset = previousWasSpaceBetweenWords ? wordEndOffset - 1 : wordEndOffset;

            ++wordEndOffset;
            while (wordEndOffset < textLength) {
                if (wordEndOffset > lineStartOffset && isBreakable(lineBreakIterator, wordEndOffset, nextBreakable, false))
                    break;
                ++wordEndOffset;
            }

            unsigned wordLength = wordEndOffset - wordStartOffset;
            bool includeEndSpace = wordEndOffset < textLength && textRenderer.characterAt(wordEndOffset) == ' ';
            float wordWidth;
            if (includeEndSpace)
                wordWidth = textWidth(textRenderer, wordStartOffset, wordLength + 1, lineWidth.committedWidth(), style) - wordTrailingSpaceWidth;
            else
                wordWidth = textWidth(textRenderer, wordStartOffset, wordLength, lineWidth.committedWidth(), style);

            lineWidth.addUncommittedWidth(wordWidth);

            // Move to the next line if the current one is full and we have something on it.
            if (!lineWidth.fitsOnLine() && lineWidth.committedWidth())
                break;

            if (wordStartOffset > lineEndOffset) {
                // There were more than one consecutive whitespace.
                ASSERT(previousWasSpaceBetweenWords);
                // Include space to the end of the previous run.
                lineRuns.last().textLength++;
                lineRuns.last().right += wordTrailingSpaceWidth;
                // Start a new run on the same line.
                lineRuns.append(Run(wordStartOffset + 1, lineRuns.last().right));
            }

            lineWidth.commit();

            lineRuns.last().right = lineWidth.committedWidth();
            lineRuns.last().textLength = wordEndOffset - lineRuns.last().textOffset;

            lineEndOffset = wordEndOffset;
            wordEndOffset = skipWhitespaces(textRenderer, wordEndOffset, textLength);

            if (!lineWidth.fitsOnLine()) {
                // The first run on the line overflows.
                ASSERT(lineRuns.size() == 1);
                break;
            }
        }
        if (lineStartOffset == lineEndOffset)
            continue;

        adjustRunOffsets(lineRuns, textAlign, lineWidth.committedWidth(), lineWidth.availableWidth());

        for (unsigned i = 0; i < lineRuns.size(); ++i)
            runs.append(lineRuns[i]);

        runs.last().isEndOfLine = true;
        ++lineCount;
    }

    textRenderer.clearNeedsLayout();

    return Layout::create(runs, lineCount);
}