Ejemplo n.º 1
0
IntRect SVGInlineTextBox::calculateBoundaries() const
{
    FloatRect textRect;

    RenderText* textRenderer = this->textRenderer();
    ASSERT(textRenderer);

    RenderStyle* style = textRenderer->style();
    ASSERT(style);

    int baseline = baselinePosition(AlphabeticBaseline);
    int heightDifference = baseline - style->fontMetrics().ascent();

    unsigned textFragmentsSize = m_textFragments.size();
    for (unsigned i = 0; i < textFragmentsSize; ++i) {
        const SVGTextFragment& fragment = m_textFragments.at(i);
        FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height + heightDifference);

        if (!fragment.transform.isIdentity())
            fragmentRect = fragment.transform.mapRect(fragmentRect);

        textRect.unite(fragmentRect);
    }

    return enclosingIntRect(textRect);
}
Ejemplo n.º 2
0
bool TextIterator::handleTextNode()
{
    RenderText* renderer = static_cast<RenderText*>(m_node->renderer());
    if (renderer->style()->visibility() != VISIBLE)
        return false;
        
    m_lastTextNode = m_node;
    String str = renderer->text();

    // handle pre-formatted text
    if (!renderer->style()->collapseWhiteSpace()) {
        int runStart = m_offset;
        if (m_lastTextNodeEndedWithCollapsedSpace) {
            emitCharacter(' ', m_node, 0, runStart, runStart);
            return false;
        }
        int strLength = str.length();
        int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX;
        int runEnd = min(strLength, end);

        if (runStart >= runEnd)
            return true;

        emitText(m_node, runStart, runEnd);
        return true;
    }

    if (!renderer->firstTextBox() && str.length() > 0) {
        m_lastTextNodeEndedWithCollapsedSpace = true; // entire block is collapsed space
        return true;
    }

    // Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text)
    if (renderer->containsReversedText()) {
        m_sortedTextBoxes.clear();
        for (InlineTextBox * textBox = renderer->firstTextBox(); textBox; textBox = textBox->nextTextBox()) {
            m_sortedTextBoxes.append(textBox);
        }
        std::sort(m_sortedTextBoxes.begin(), m_sortedTextBoxes.end(), compareBoxStart); 
        m_sortedTextBoxesPosition = 0;
    }
    
    m_textBox = renderer->containsReversedText() ? m_sortedTextBoxes[0] : renderer->firstTextBox();
    handleTextBox();
    return true;
}
Ejemplo n.º 3
0
TextDecorationPainter::TextDecorationPainter(GraphicsContext& context, TextDecoration decoration, const RenderText& renderer, bool isFirstLine)
    : m_context(context)
    , m_decoration(decoration)
    , m_wavyOffset(wavyOffsetFromDecoration())
    , m_isPrinting(renderer.document().printing())
    , m_styles(stylesForRenderer(renderer, m_decoration, isFirstLine))
    , m_lineStyle(isFirstLine ? renderer.firstLineStyle() : renderer.style())
{
}
Ejemplo n.º 4
0
int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const
{
    if (isLineBreak())
        return 0;

    RenderText* text = static_cast<RenderText*>(m_object);
    RenderStyle *style = text->style(m_firstLine);
    const Font* f = &style->font();
    return f->offsetForPosition(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()),
                                _x - m_x, includePartialGlyphs);
}
void Text::updateTextRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData)
{
    if (!attached())
        return;
    RenderText* textRenderer = toRenderText(renderer());
    if (!textRenderer || !textRendererIsNeeded(NodeRenderingContext(this, textRenderer->style()))) {
        reattach();
        return;
    }
    textRenderer->setTextWithOffset(dataImpl(), offsetOfReplacedData, lengthOfReplacedData);
}
Ejemplo n.º 6
0
void Text::updateTextRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData, RecalcStyleBehavior recalcStyleBehavior)
{
    if (!inActiveDocument())
        return;
    RenderText* textRenderer = renderer();
    if (!textRenderer || !textRendererIsNeeded(*textRenderer->style(), *textRenderer->parent())) {
        lazyReattachIfAttached();
        // FIXME: Editing should be updated so this is not neccesary.
        if (recalcStyleBehavior == DeprecatedRecalcStyleImmediatlelyForEditing)
            document().updateRenderTreeIfNeeded();
        return;
    }
    textRenderer->setTextWithOffset(dataImpl(), offsetOfReplacedData, lengthOfReplacedData);
}
Ejemplo n.º 7
0
void TextAutoSizingValue::reset()
{
    HashSet<RefPtr<Node> >::iterator end = m_autoSizedNodes.end();
    for (HashSet<RefPtr<Node> >::iterator i = m_autoSizedNodes.begin(); i != end; ++i) {
        const RefPtr<Node>& autoSizingNode = *i;
        RenderText* text = static_cast<RenderText*>(autoSizingNode->renderer());
        if (!text)
            continue;
        // Reset the font size back to the original specified size
        FontDescription fontDescription = text->style().fontDescription();
        float originalSize = fontDescription.specifiedSize();
        if (fontDescription.computedSize() != originalSize) {
            fontDescription.setComputedSize(originalSize);
            RefPtr<RenderStyle> style = cloneRenderStyleWithState(text->style());
            style->setFontDescription(fontDescription);
            style->font().update(autoSizingNode->document().ensureStyleResolver().fontSelector());
            text->parent()->setStyle(style.releaseNonNull());
        }
        // Reset the line height of the parent.
        RenderElement* parentRenderer = text->parent();
        if (!parentRenderer)
            continue;
        
        if (parentRenderer->isAnonymousBlock())
            parentRenderer = parentRenderer->parent();
        
        const RenderStyle& parentStyle = parentRenderer->style();
        Length originalLineHeight = parentStyle.specifiedLineHeight();
        if (originalLineHeight != parentStyle.lineHeight()) {
            RefPtr<RenderStyle> newParentStyle = cloneRenderStyleWithState(parentStyle);
            newParentStyle->setLineHeight(originalLineHeight);
            newParentStyle->setFontDescription(fontDescription);
            newParentStyle->font().update(autoSizingNode->document().ensureStyleResolver().fontSelector());
            parentRenderer->setStyle(newParentStyle.releaseNonNull());
        }
    }
}
Ejemplo n.º 8
0
int InlineTextBox::positionForOffset(int offset) const
{
    ASSERT(offset >= m_start);
    ASSERT(offset <= m_start + m_len);

    if (isLineBreak())
        return m_x;

    RenderText* text = static_cast<RenderText*>(m_object);
    const Font& f = text->style(m_firstLine)->font();
    int from = direction() == RTL ? offset - m_start : 0;
    int to = direction() == RTL ? m_len : offset - m_start;
    // FIXME: Do we need to add rightBearing here?
    return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride),
                                                   IntPoint(m_x, 0), 0, from, to)).right();
}
Ejemplo n.º 9
0
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const
{
    RenderText* textRenderer = this->textRenderer();
    ASSERT(textRenderer);

    RenderStyle* style = textRenderer->style();
    ASSERT(style);

    TextRun textRun(constructTextRun(style, fragment));

    // Eventually handle lengthAdjust="spacingAndGlyphs".
    // FIXME: Handle vertical text.
    if (!fragment.transform.isIdentity())
        textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragment.transform.xScale()));

    return fragment.positionListOffset - start() + style->font().offsetForPosition(textRun, position, includePartialGlyphs);
}
Ejemplo n.º 10
0
void updateTextRendererAfterContentChange(Text& textNode, unsigned offsetOfReplacedData, unsigned lengthOfReplacedData)
{
    if (!textNode.attached())
        return;
    RenderText* textRenderer = toRenderText(textNode.renderer());
    if (!textRenderer) {
        attachTextRenderer(textNode);
        return;
    }
    RenderObject* parentRenderer = NodeRenderingTraversal::parent(&textNode)->renderer();
    if (!textRendererIsNeeded(textNode, *parentRenderer, *textRenderer->style())) {
        detachTextRenderer(textNode);
        attachTextRenderer(textNode);
        return;
    }
    textRenderer->setTextWithOffset(textNode.dataImpl(), offsetOfReplacedData, lengthOfReplacedData);
}
Ejemplo n.º 11
0
VisiblePosition RenderTextLineBoxes::positionForPoint(const RenderText& renderer, const LayoutPoint& point) const
{
    if (!m_first || !renderer.textLength())
        return renderer.createVisiblePosition(0, DOWNSTREAM);

    LayoutUnit pointLineDirection = m_first->isHorizontal() ? point.x() : point.y();
    LayoutUnit pointBlockDirection = m_first->isHorizontal() ? point.y() : point.x();
    bool blocksAreFlipped = renderer.style().isFlippedBlocksWritingMode();

    InlineTextBox* lastBox = nullptr;
    for (auto box = m_first; box; box = box->nextTextBox()) {
        if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak())
            box = box->nextTextBox();

        auto& rootBox = box->root();
        LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop());
        if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) {
            LayoutUnit bottom = rootBox.selectionBottom();
            if (rootBox.nextRootBox())
                bottom = std::min(bottom, rootBox.nextRootBox()->lineTop());

            if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) {
                ShouldAffinityBeDownstream shouldAffinityBeDownstream;
#if PLATFORM(IOS)
                if (pointLineDirection != box->logicalLeft() && point.x() < box->x() + box->logicalWidth()) {
                    int half = box->x() + box->logicalWidth() / 2;
                    EAffinity affinity = point.x() < half ? DOWNSTREAM : VP_UPSTREAM_IF_POSSIBLE;
                    return renderer.createVisiblePosition(box->offsetForPosition(pointLineDirection) + box->start(), affinity);
                }
#endif
                if (lineDirectionPointFitsInBox(pointLineDirection, *box, shouldAffinityBeDownstream))
                    return createVisiblePositionAfterAdjustingOffsetForBiDi(*box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream);
            }
        }
        lastBox = box;
    }

    if (lastBox) {
        ShouldAffinityBeDownstream shouldAffinityBeDownstream;
        lineDirectionPointFitsInBox(pointLineDirection, *lastBox, shouldAffinityBeDownstream);
        return createVisiblePositionAfterAdjustingOffsetForBiDi(*lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream);
    }
    return renderer.createVisiblePosition(0, DOWNSTREAM);
}
Ejemplo n.º 12
0
Vector<FloatQuad> RenderTextLineBoxes::absoluteQuads(const RenderText& renderer, bool* wasFixed, ClippingOption option) const
{
    Vector<FloatQuad> quads;
    for (auto box = m_first; box; box = box->nextTextBox()) {
        FloatRect boundaries = box->calculateBoundaries();

        // Shorten the width of this text box if it ends in an ellipsis.
        // FIXME: ellipsisRectForBox should switch to return FloatRect soon with the subpixellayout branch.
        IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(*box, 0, renderer.textLength()) : IntRect();
        if (!ellipsisRect.isEmpty()) {
            if (renderer.style().isHorizontalWritingMode())
                boundaries.setWidth(ellipsisRect.maxX() - boundaries.x());
            else
                boundaries.setHeight(ellipsisRect.maxY() - boundaries.y());
        }
        quads.append(renderer.localToAbsoluteQuad(boundaries, 0, wasFixed));
    }
    return quads;
}
Ejemplo n.º 13
0
static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
{
    // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
    // to detect any changes caused by the conversion to floating point. :(
    int x = run.x();
    int y = run.y();
    int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;

    ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
    if (!run.isLeftToRightDirection() || run.dirOverride()) {
        ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
        if (run.dirOverride())
            ts << " override";
    }
    ts << ": "
        << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
    if (run.hasHyphen())
        ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
    ts << "\n";
}
Ejemplo n.º 14
0
IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
{
    int sPos = max(startPos - m_start, 0);
    int ePos = min(endPos - m_start, (int)m_len);
    
    if (sPos >= ePos)
        return IntRect();

    RenderText* textObj = textObject();
    int selTop = selectionTop();
    int selHeight = selectionHeight();
    const Font& f = textObj->style(m_firstLine)->font();

    IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(textObj->text()->characters() + m_start, m_len, textObj->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride),
                                                        IntPoint(tx + m_x, ty + selTop), selHeight, sPos, ePos));
    if (r.x() > tx + m_x + m_width)
        r.setWidth(0);
    else if (r.right() - 1 > tx + m_x + m_width)
        r.setWidth(tx + m_x + m_width - r.x());
    return r;
}
Ejemplo n.º 15
0
LayoutRect RenderTextLineBoxes::visualOverflowBoundingBox(const RenderText& renderer) const
{
    if (!m_first)
        return LayoutRect();

    // Return the width of the minimal left side and the maximal right side.
    auto logicalLeftSide = LayoutUnit::max();
    auto logicalRightSide = LayoutUnit::min();
    for (auto current = m_first; current; current = current->nextTextBox()) {
        logicalLeftSide = std::min(logicalLeftSide, current->logicalLeftVisualOverflow());
        logicalRightSide = std::max(logicalRightSide, current->logicalRightVisualOverflow());
    }
    
    auto logicalTop = m_first->logicalTopVisualOverflow();
    auto logicalWidth = logicalRightSide - logicalLeftSide;
    auto logicalHeight = m_last->logicalBottomVisualOverflow() - logicalTop;
    
    LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
    if (!renderer.style().isHorizontalWritingMode())
        rect = rect.transposedRect();
    return rect;
}
LayoutRect SVGInlineTextBox::localSelectionRect(int startPosition, int endPosition)
{
    int boxStart = start();
    startPosition = max(startPosition - boxStart, 0);
    endPosition = min(endPosition - boxStart, static_cast<int>(len()));
    if (startPosition >= endPosition)
        return LayoutRect();

    RenderText* text = textRenderer();
    ASSERT(text);

    RenderStyle* style = text->style();
    ASSERT(style);

    AffineTransform fragmentTransform;
    FloatRect selectionRect;
    int fragmentStartPosition = 0;
    int fragmentEndPosition = 0;

    unsigned textFragmentsSize = m_textFragments.size();
    for (unsigned i = 0; i < textFragmentsSize; ++i) {
        const SVGTextFragment& fragment = m_textFragments.at(i);

        fragmentStartPosition = startPosition;
        fragmentEndPosition = endPosition;
        if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
            continue;

        FloatRect fragmentRect = selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style);
        fragment.buildFragmentTransform(fragmentTransform);
        if (!fragmentTransform.isIdentity())
            fragmentRect = fragmentTransform.mapRect(fragmentRect);

        selectionRect.unite(fragmentRect);
    }

    return enclosingIntRect(selectionRect);
}
Ejemplo n.º 17
0
IntRect RenderTextLineBoxes::boundingBox(const RenderText& renderer) const
{
    if (!m_first)
        return IntRect();

    // Return the width of the minimal left side and the maximal right side.
    float logicalLeftSide = 0;
    float logicalRightSide = 0;
    for (auto current = m_first; current; current = current->nextTextBox()) {
        if (current == m_first || current->logicalLeft() < logicalLeftSide)
            logicalLeftSide = current->logicalLeft();
        if (current == m_first || current->logicalRight() > logicalRightSide)
            logicalRightSide = current->logicalRight();
    }
    
    bool isHorizontal = renderer.style().isHorizontalWritingMode();
    
    float x = isHorizontal ? logicalLeftSide : m_first->x();
    float y = isHorizontal ? m_first->y() : logicalLeftSide;
    float width = isHorizontal ? logicalRightSide - logicalLeftSide : m_last->logicalBottom() - x;
    float height = isHorizontal ? m_last->logicalBottom() - y : logicalRightSide - logicalLeftSide;
    return enclosingIntRect(FloatRect(x, y, width, height));
}
Ejemplo n.º 18
0
static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
{
    // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
    // to detect any changes caused by the conversion to floating point. :(
    int x = run.m_x;
    int y = run.m_y;
    int logicalWidth = ceilf(run.m_x + run.m_logicalWidth) - x;

    // FIXME: Table cell adjustment is temporary until results can be updated.
    if (o.containingBlock()->isTableCell())
        y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
        
    ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
    if (!run.isLeftToRightDirection() || run.m_dirOverride) {
        ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
        if (run.m_dirOverride)
            ts << " override";
    }
    ts << ": "
        << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
    if (run.hasHyphen())
        ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
    ts << "\n";
}
Ejemplo n.º 19
0
static int placePositionedBoxesHorizontally(InlineFlowBox* flow, int x, int& leftPosition, int& rightPosition, int& leftAlign, int& rightAlign, bool& needsWordSpacing, int xPos, bool positioned)
{
    int mn = INT_MAX;
    int mx = INT_MIN;
    int amn = INT_MAX;
    int amx = INT_MIN;
    int startx = x;
    bool seenPositionedElement = false;
    flow->setXPos(x);
    for (InlineBox* curr = flow->firstChild(); curr; curr = curr->nextOnLine()) {
        if (curr->object()->isText()) {
            mn = min(mn, x);
            amn = min(amn, x);
            InlineTextBox* text = static_cast<InlineTextBox*>(curr);
            RenderText* rt = static_cast<RenderText*>(text->object());
            if (rt->textLength()) {
                if (needsWordSpacing && DeprecatedChar(rt->characters()[text->start()]).isSpace())
                    x += rt->style(flow->isFirstLineStyle())->font().wordSpacing();
                needsWordSpacing = !DeprecatedChar(rt->characters()[text->end()]).isSpace();
            }
            text->setXPos(x);
            x += text->width();
            mx = max(mx, x);
            amx = max(amx, x);
        } else if (curr->object()->isInlineFlow()) {
            InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr);
            if (flow->object()->element()->hasTagName(aTag)) {
                x = placePositionedBoxesHorizontally(flow, x, mn, mx, amn, amx, needsWordSpacing, xPos, false);
            } else {
                SVGTextPositioningElement* text = static_cast<SVGTextPositioningElement*>(flow->object()->element());
                x += (int)(text->dx()->getFirst().value());
                if (text->x()->numberOfItems() > 0)
                    x = (int)(text->x()->getFirst().value() - xPos);
                if (text->x()->numberOfItems() > 0 || text->y()->numberOfItems() > 0 ||
                    text->dx()->numberOfItems() > 0 || text->dy()->numberOfItems() > 0) {
                    seenPositionedElement = true;
                    needsWordSpacing = false;
                    int ignoreX, ignoreY;
                    x = placePositionedBoxesHorizontally(flow, x, mn, mx, ignoreX, ignoreY, needsWordSpacing, xPos, true);
                } else if (seenPositionedElement) {
                    int ignoreX, ignoreY;
                    x = placePositionedBoxesHorizontally(flow, x, mn, mx, ignoreX, ignoreY, needsWordSpacing, xPos, false);
                } else
                    x = placePositionedBoxesHorizontally(flow, x, mn, mx, amn, amx, needsWordSpacing, xPos, false);
            }
        }
    }

    if (mn > mx)
        mn = mx = startx;
    if (amn > amx)
        amn = amx = startx;

    int width = mx - mn;
    flow->setWidth(width);

    int awidth = amx - amn;
    int dx = 0;
    if (positioned) {
        switch (flow->object()->style()->svgStyle()->textAnchor()) {
            case TA_MIDDLE:
                translateBox(flow, dx = -awidth / 2, 0, true);
                break;
            case TA_END:
                translateBox(flow, dx = -awidth, 0, true);
                break;
            case TA_START:
            default:
                break;
        }

        if (dx) {
            x += dx;
            mn += dx;
            mx += dx;
        }
    }

    leftPosition = min(leftPosition, mn);
    rightPosition = max(rightPosition, mx);
    leftAlign = min(leftAlign, amn);
    rightAlign = max(rightAlign, amx);

    return x;
}
void SVGTextChunkLayoutInfo::recursiveBuildTextChunks(InlineFlowBox* start)
{
#if DEBUG_CHUNK_BUILDING > 1
    fprintf(stderr, " -> buildTextChunks(start=%p)\n", start);
#endif

    for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
        if (curr->renderer()->isText()) {
            InlineTextBox* textBox = static_cast<InlineTextBox*>(curr);

            unsigned length = textBox->len();
            ASSERT(length > 0);

#if DEBUG_CHUNK_BUILDING > 1
            fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n",
                            textBox, length, textBox->start(), textBox->end(), (int) m_handlingTextPath);
#endif

            RenderText* text = textBox->textRenderer();
            ASSERT(text);
            ASSERT(text->node());

            SVGTextContentElement* textContent = 0;
            Node* node = text->node()->parent();
            while (node && node->isSVGElement() && !textContent) {
                if (static_cast<SVGElement*>(node)->isTextContent())
                    textContent = static_cast<SVGTextContentElement*>(node);
                else
                    node = node->parentNode();
            }
            ASSERT(textContent);

            // Start new character range for the first chunk
            bool isFirstCharacter = m_svgTextChunks.isEmpty() && m_chunk.start == m_charsIt && m_chunk.start == m_chunk.end;
            if (isFirstCharacter) {
                ASSERT(m_chunk.boxes.isEmpty());
                m_chunk.boxes.append(SVGInlineBoxCharacterRange());
            } else
                ASSERT(!m_chunk.boxes.isEmpty());

            // Walk string to find out new chunk positions, if existent
            for (unsigned i = 0; i < length; ++i) {
                ASSERT(m_charsIt != m_charsEnd);

                SVGInlineBoxCharacterRange& range = m_chunk.boxes.last();
                if (range.isOpen()) {
                    range.box = curr;
                    range.startOffset = !i ? 0 : i - 1;

#if DEBUG_CHUNK_BUILDING > 1
                    fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset);
#endif
                }

                // If a new (or the first) chunk has been started, record it's text-anchor and writing mode.
                if (m_assignChunkProperties) {
                    m_assignChunkProperties = false;

                    m_chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
                    m_chunk.isTextPath = m_handlingTextPath;
                    m_chunk.anchor = text->style()->svgStyle()->textAnchor();
                    m_chunk.textLength = textContent->textLength().value(textContent);
                    m_chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();

#if DEBUG_CHUNK_BUILDING > 1
                    fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", m_chunk.isVerticalText, m_chunk.anchor);
#endif
                }

                if (i > 0 && !isFirstCharacter && m_charsIt->newTextChunk) {
                    // Close mid chunk & character range
                    ASSERT(!range.isOpen());
                    ASSERT(!range.isClosed());

                    range.endOffset = i;
                    closeTextChunk();

#if DEBUG_CHUNK_BUILDING > 1
                    fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset);
#endif
    
                    // Prepare for next chunk, if we're not at the end
                    startTextChunk();
                    if (i + 1 == length) {
#if DEBUG_CHUNK_BUILDING > 1
                        fprintf(stderr, " | -> Record last chunk of inline text box!\n");
#endif

                        startTextChunk();
                        SVGInlineBoxCharacterRange& range = m_chunk.boxes.last();

                        m_assignChunkProperties = false;
                        m_chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
                        m_chunk.isTextPath = m_handlingTextPath;
                        m_chunk.anchor = text->style()->svgStyle()->textAnchor();
                        m_chunk.textLength = textContent->textLength().value(textContent);
                        m_chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();

                        range.box = curr;
                        range.startOffset = i;

                        ASSERT(!range.isOpen());
                        ASSERT(!range.isClosed());
                    }
                }

                // This should only hold true for the first character of the first chunk
                if (isFirstCharacter)
                    isFirstCharacter = false;
    
                ++m_charsIt;
            }

#if DEBUG_CHUNK_BUILDING > 1    
            fprintf(stderr, " -> Finished inline text box!\n");
#endif

            SVGInlineBoxCharacterRange& range = m_chunk.boxes.last();
            if (!range.isOpen() && !range.isClosed()) {
#if DEBUG_CHUNK_BUILDING > 1
                fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length);
#endif

                // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk.
                range.endOffset = length;

                if (m_charsIt != m_charsEnd) {
#if DEBUG_CHUNK_BUILDING > 1
                    fprintf(stderr, " -> Not at last character yet!\n");
#endif

                    // If we're not at the end of the last box to be processed, and if the next
                    // character starts a new chunk, then close the current chunk and start a new one.
                    if (m_charsIt->newTextChunk) {
#if DEBUG_CHUNK_BUILDING > 1
                        fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n");
#endif

                        closeTextChunk();
                        startTextChunk();
                    } else {
                        // Just start a new character range
                        m_chunk.boxes.append(SVGInlineBoxCharacterRange());

#if DEBUG_CHUNK_BUILDING > 1
                        fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n");
#endif
                    }
                } else {
#if DEBUG_CHUNK_BUILDING > 1
                    fprintf(stderr, " -> Closing final chunk! Finished processing!\n");
#endif

                    // Close final chunk, once we're at the end of the last box
                    closeTextChunk();
                }
            }
        } else {
            ASSERT(curr->isInlineFlowBox());
            InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);

            // Skip generated content.
            if (!flowBox->renderer()->node())
                continue;

            bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag);

#if DEBUG_CHUNK_BUILDING > 1
            fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath);
#endif

            if (isTextPath)
                m_handlingTextPath = true;

            recursiveBuildTextChunks(flowBox);

            if (isTextPath)
                m_handlingTextPath = false;
        }
    }

#if DEBUG_CHUNK_BUILDING > 1
    fprintf(stderr, " <- buildTextChunks(start=%p)\n", start);
#endif
}
Ejemplo n.º 21
0
bool TextAutoSizingValue::adjustNodeSizes()
{
    bool objectsRemoved = false;
    
    // Remove stale nodes.  Nodes may have had their renderers detached.  We'll
    // also need to remove the style from the documents m_textAutoSizedNodes
    // collection.  Return true indicates we need to do that removal.
    Vector<RefPtr<Node> > nodesForRemoval;
    HashSet<RefPtr<Node> >::iterator end = m_autoSizedNodes.end();
    for (HashSet<RefPtr<Node> >::iterator i = m_autoSizedNodes.begin(); i != end; ++i) {
        RefPtr<Node> autoSizingNode = *i;
        RenderText* text = static_cast<RenderText*>(autoSizingNode->renderer());
        if (!text || !text->style().textSizeAdjust().isAuto() || !text->candidateComputedTextSize()) {
            // remove node.
            nodesForRemoval.append(autoSizingNode);
            objectsRemoved = true;
        }
    }
    
    unsigned count = nodesForRemoval.size();
    for (unsigned i = 0; i < count; i++)
        m_autoSizedNodes.remove(nodesForRemoval[i]);
    
    // If we only have one piece of text with the style on the page don't
    // adjust it's size.
    if (m_autoSizedNodes.size() <= 1)
        return objectsRemoved;
    
    // Compute average size
    float cumulativeSize = 0;
    end = m_autoSizedNodes.end();
    for (HashSet<RefPtr<Node> >::iterator i = m_autoSizedNodes.begin(); i != end; ++i) {
        RefPtr<Node> autoSizingNode = *i;
        RenderText* renderText = static_cast<RenderText*>(autoSizingNode->renderer());
        cumulativeSize += renderText->candidateComputedTextSize();
    }
    
    float averageSize = roundf(cumulativeSize / m_autoSizedNodes.size());
    
    // Adjust sizes
    bool firstPass = true;
    end = m_autoSizedNodes.end();
    for (HashSet<RefPtr<Node> >::iterator i = m_autoSizedNodes.begin(); i != end; ++i) {
        const RefPtr<Node>& autoSizingNode = *i;
        RenderText* text = static_cast<RenderText*>(autoSizingNode->renderer());
        if (text && text->style().fontDescription().computedSize() != averageSize) {
            float specifiedSize = text->style().fontDescription().specifiedSize();
            float scaleChange = averageSize / specifiedSize;
            if (scaleChange > MAX_SCALE_INCREASE && firstPass) {
                firstPass = false;
                averageSize = roundf(specifiedSize * MAX_SCALE_INCREASE);
                scaleChange = averageSize / specifiedSize;
            }
            
            RefPtr<RenderStyle> style = cloneRenderStyleWithState(text->style());
            FontDescription fontDescription = style->fontDescription();
            fontDescription.setComputedSize(averageSize);
            style->setFontDescription(fontDescription);
            style->font().update(autoSizingNode->document().ensureStyleResolver().fontSelector());
            text->parent()->setStyle(style.releaseNonNull());
            
            RenderElement* parentRenderer = text->parent();
            if (parentRenderer->isAnonymousBlock())
                parentRenderer = parentRenderer->parent();
            
            // If we have a list we should resize ListMarkers separately.
            RenderObject* listMarkerRenderer = parentRenderer->firstChild();
            if (listMarkerRenderer->isListMarker()) {
                RefPtr<RenderStyle> style = cloneRenderStyleWithState(listMarkerRenderer->style());
                style->setFontDescription(fontDescription);
                style->font().update(autoSizingNode->document().ensureStyleResolver().fontSelector());
                toRenderListMarker(*listMarkerRenderer).setStyle(style.releaseNonNull());
            }
            
            // Resize the line height of the parent.
            const RenderStyle& parentStyle = parentRenderer->style();
            Length lineHeightLength = parentStyle.specifiedLineHeight();
            
            int specifiedLineHeight = 0;
            if (lineHeightLength.isPercent())
                specifiedLineHeight = minimumValueForLength(lineHeightLength, fontDescription.specifiedSize());
            else
                specifiedLineHeight = lineHeightLength.value();
            
            int lineHeight = specifiedLineHeight * scaleChange;
            if (!lineHeightLength.isFixed() || lineHeightLength.value() != lineHeight) {
                RefPtr<RenderStyle> newParentStyle = cloneRenderStyleWithState(parentStyle);
                newParentStyle->setLineHeight(Length(lineHeight, Fixed));
                newParentStyle->setSpecifiedLineHeight(lineHeightLength);
                newParentStyle->setFontDescription(fontDescription);
                newParentStyle->font().update(autoSizingNode->document().ensureStyleResolver().fontSelector());
                parentRenderer->setStyle(newParentStyle.releaseNonNull());
            }
        }
    }
    
    return objectsRemoved;
}