示例#1
0
static FloatRect localQuadForTextBox(const InlineTextBox& box, unsigned start, unsigned end, bool useSelectionHeight)
{
    unsigned realEnd = std::min(box.end() + 1, end);
    LayoutRect boxSelectionRect = box.localSelectionRect(start, realEnd);
    if (!boxSelectionRect.height())
        return FloatRect();
    if (useSelectionHeight)
        return boxSelectionRect;
    // Change the height and y position (or width and x for vertical text)
    // because selectionRect uses selection-specific values.
    if (box.isHorizontal()) {
        boxSelectionRect.setHeight(box.height());
        boxSelectionRect.setY(box.y());
    } else {
        boxSelectionRect.setWidth(box.width());
        boxSelectionRect.setX(box.x());
    }
    return boxSelectionRect;
}
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
}
static gchar* textForRenderer(RenderObject* renderer)
{
    GString* resultText = g_string_new(0);

    if (!renderer)
        return g_string_free(resultText, FALSE);

    // For RenderBlocks, piece together the text from the RenderText objects they contain.
    for (RenderObject* object = renderer->firstChild(); object; object = object->nextSibling()) {
        if (object->isBR()) {
            g_string_append(resultText, "\n");
            continue;
        }

        RenderText* renderText;
        if (object->isText())
            renderText = toRenderText(object);
        else {
            // List item's markers will be treated in an special way
            // later on this function, so ignore them here.
            if (object->isReplaced() && !renderer->isListItem())
                g_string_append_unichar(resultText, objectReplacementCharacter);

            // We need to check children, if any, to consider when
            // current object is not a text object but some of its
            // children are, in order not to miss those portions of
            // text by not properly handling those situations
            if (object->firstChild())
                g_string_append(resultText, textForRenderer(object));

            continue;
        }

        InlineTextBox* box = renderText ? renderText->firstTextBox() : 0;
        while (box) {
            // WebCore introduces line breaks in the text that do not reflect
            // the layout you see on the screen, replace them with spaces.
            String text = String(renderText->characters(), renderText->textLength()).replace("\n", " ");
            g_string_append(resultText, text.substring(box->start(), box->end() - box->start() + 1).utf8().data());

            // Newline chars in the source result in separate text boxes, so check
            // before adding a newline in the layout. See bug 25415 comment #78.
            // If the next sibling is a BR, we'll add the newline when we examine that child.
            if (!box->nextOnLineExists() && !(object->nextSibling() && object->nextSibling()->isBR())) {
                // If there was a '\n' in the last position of the
                // current text box, it would have been converted to a
                // space in String::replace(), so remove it first.
                if (renderText->characters()[box->end()] == '\n')
                    g_string_erase(resultText, resultText->len - 1, -1);

                g_string_append(resultText, "\n");
            }
            box = box->nextTextBox();
        }
    }

    // Insert the text of the marker for list item in the right place, if present
    if (renderer->isListItem()) {
        String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
        if (renderer->style()->direction() == LTR)
            g_string_prepend(resultText, markerText.utf8().data());
        else
            g_string_append(resultText, markerText.utf8().data());
    }

    return g_string_free(resultText, FALSE);
}
Position Position::downstream(EStayInBlock stayInBlock) const
{
    Position start = equivalentDeepPosition();
    NodeImpl *startNode = start.node();
    if (!startNode)
        return Position();

    NodeImpl *block = startNode->enclosingBlockFlowOrTableElement();
    Position lastVisible;
    
    PositionIterator it(start);            
    for (; !it.atEnd(); it.next()) {   
        NodeImpl *currentNode = it.current().node();

        if (stayInBlock) {
            NodeImpl *currentBlock = currentNode->enclosingBlockFlowOrTableElement();
            if (block != currentBlock)
                return it.previous();
        }

        RenderObject *renderer = currentNode->renderer();
        if (!renderer)
            continue;

        if (renderer->style()->visibility() != VISIBLE)
            continue;

        lastVisible = it.current();

        if (currentNode != startNode && renderer->isBlockFlow()) {
            if (it.current().offset() == 0) {
                // If no first child, or first visible child is a not a block, return; otherwise continue.
                if (!currentNode->firstChild())
                    return Position(currentNode, 0);
                for (NodeImpl *child = currentNode->firstChild(); child; child = child->nextSibling()) {
                    RenderObject *r = child->renderer();
                    if (r && r->style()->visibility() == VISIBLE) {
                         if (r->isBlockFlow())
                            break; // break causes continue code below to run.
                         else
                            return Position(child, 0);
                    }
                }
                continue;
            }
        }

        if (renderer->isReplaced() || renderer->isBR()) {
            if (it.current().offset() <= renderer->caretMinOffset())
                return Position(currentNode, renderer->caretMinOffset());
            else
                continue;
        }

        if (renderer->isText() && static_cast<RenderText *>(renderer)->firstTextBox()) {
            if (currentNode != start.node())
                return Position(currentNode, renderer->caretMinOffset());

            if (it.current().offset() < 0)
                continue;
            uint textOffset = it.current().offset();

            RenderText *textRenderer = static_cast<RenderText *>(renderer);
            for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
                if (textOffset >= box->start() && textOffset <= box->end())
                    return it.current();
                else if (box != textRenderer->lastTextBox() && 
                         !box->nextOnLine() && 
                         textOffset == box->start() + box->len())
                    return it.current();
            }
        }
    }
    
    return lastVisible.isNotNull() ? lastVisible : *this;
}
示例#5
0
// P.downstream() returns the end of the range of positions that map to the same VisiblePosition as P.
Position Position::downstream() const
{
    Node* startNode = node();
    if (!startNode)
        return Position();

    // iterate forward from there, looking for a qualified position
    Node* block = enclosingBlock(startNode);
    PositionIterator lastVisible = *this;
    PositionIterator currentPos = lastVisible;
    Node* originalRoot = node()->rootEditableElement();
    for (; !currentPos.atEnd(); currentPos.increment()) {   
        Node* currentNode = currentPos.node();
        
        if (currentNode->rootEditableElement() != originalRoot)
            break;

        // stop before going above the body, up into the head
        // return the last visible streamer position
        if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode())
            break;
            
        // Do not enter a new enclosing block flow or table element, and don't leave the original one.
        if (block != enclosingBlock(currentNode))
            return lastVisible;

        // skip position in unrendered or invisible node
        RenderObject* renderer = currentNode->renderer();
        if (!renderer || renderer->style()->visibility() != VISIBLE)
            continue;
            
        // track last visible streamer position
        if (isStreamer(currentPos))
            lastVisible = currentPos;

        // Return position before brs, tables, and nodes which have content that can be ignored.
        if (editingIgnoresContent(currentNode) || renderer->isBR() || isTableElement(currentNode)) {
            if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset())
                return Position(currentNode, renderer->caretMinOffset());
            continue;
        }

        // return current position if it is in rendered text
        if (renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) {
            if (currentNode != startNode) {
                ASSERT(currentPos.atStartOfNode());
                return Position(currentNode, renderer->caretMinOffset());
            }

            unsigned textOffset = currentPos.offsetInLeafNode();

            RenderText* textRenderer = static_cast<RenderText*>(renderer);
            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
                if (textOffset >= box->start() && textOffset <= box->end())
                    return currentPos;
                
                if (box != textRenderer->lastTextBox() && 
                     !box->nextOnLine() &&
                     textOffset == box->start() + box->len()) {
                    return currentPos;
                }
            }
        }
    }
    
    return lastVisible;
}
示例#6
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;
}
static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
{
    AccessibilityObject* coreObject = core(textObject);

    HostWindow* hostWindow = coreObject->document()->view()->hostWindow();
    if (!hostWindow)
        return 0;
    PlatformPageClient webView = hostWindow->platformPageClient();
    if (!webView)
        return 0;

    GString* str = g_string_new(NULL);

    AccessibilityRenderObject* accObject = static_cast<AccessibilityRenderObject*>(coreObject);
    if (!accObject)
        return 0;
    RenderText* renderText = toRenderText(accObject->renderer());
    if (!renderText)
        return 0;

    // Create a string with the layout as it appears on the screen
    InlineTextBox* box = renderText->firstTextBox();
    while (box) {
        gchar *text = convertUniCharToUTF8(renderText->characters(), renderText->textLength(), box->start(), box->end());
        g_string_append(str, text);
        g_string_append(str, "\n");
        box = box->nextTextBox();
    }

    PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*>(webView), g_string_free(str, FALSE));
    g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-pango-layout", layout, g_object_unref);
    return layout;
}