WebCore/rendering/SVGRootInlineBox.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/SVGRootInlineBox.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ *           (C) 2006 Apple Computer Inc.
+ *           (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "SVGRootInlineBox.h"
+
+#if ENABLE(SVG)
+#include "GraphicsContext.h"
+#include "RenderBlock.h"
+#include "SVGInlineFlowBox.h"
+#include "SVGInlineTextBox.h"
+#include "SVGRenderSupport.h"
+#include "SVGTextLayoutUtilities.h"
+#include "SVGTextPositioningElement.h"
+
+// Text chunk part propagation can be traced by setting this variable > 0.
+#define DEBUG_CHUNK_PART_PROPAGATION 0
+
+namespace WebCore {
+
+void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int)
+{
+    ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
+    ASSERT(!paintInfo.context->paintingDisabled());
+
+    RenderObject* boxRenderer = renderer();
+    ASSERT(boxRenderer);
+
+    PaintInfo childPaintInfo(paintInfo);
+    childPaintInfo.context->save();
+
+    if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) {
+        for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
+            child->paint(childPaintInfo, 0, 0);
+    }
+
+    SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context);
+    childPaintInfo.context->restore();
+}
+
+void SVGRootInlineBox::computePerCharacterLayoutInformation()
+{
+    // Clean up any previous layout information
+    m_svgChars.clear();
+    m_svgTextChunks.clear();
+
+    // Build layout information for all contained render objects
+    SVGCharacterLayoutInfo charInfo;
+    buildLayoutInformation(this, charInfo);
+    m_svgChars = charInfo.svgChars;
+
+    // Now all layout information are available for every character
+    // contained in any of our child inline/flow boxes. Build list
+    // of text chunks now, to be able to apply text-anchor shifts.
+    SVGTextChunkLayoutInfo chunkInfo;
+    chunkInfo.buildTextChunks(m_svgChars.begin(), m_svgChars.end(), this);
+
+    // Layout all text chunks
+    // text-anchor needs to be applied to individual chunks.
+    chunkInfo.layoutTextChunks();
+    m_svgTextChunks = chunkInfo.textChunks();
+
+    // Propagate text chunk part information to all SVGInlineTextBoxes, see SVGTextChunkLayoutInfo.h for details
+    propagateTextChunkPartInformation();
+
+    // Layout all child boxes.
+    layoutChildBoxes(this);
+
+    // Resize our root box and our RenderSVGText parent block
+    layoutRootBox();
+}
+
+void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info)
+{
+    if (start->isRootInlineBox()) {
+        ASSERT(start->renderer()->node()->hasTagName(SVGNames::textTag));
+
+        SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->renderer()->node());
+        ASSERT(positioningElement);
+        ASSERT(positioningElement->parentNode());
+
+        info.addLayoutInformation(positioningElement);
+    }
+
+    SVGLastGlyphInfo lastGlyph;
+    
+    for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
+        if (curr->renderer()->isText())
+            static_cast<SVGInlineTextBox*>(curr)->buildLayoutInformation(info, lastGlyph);
+        else {
+            ASSERT(curr->isInlineFlowBox());
+            InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
+
+            // Skip generated content.
+            if (!flowBox->renderer()->node())
+                continue;
+
+            bool isAnchor = flowBox->renderer()->node()->hasTagName(SVGNames::aTag);
+            bool isTextPath = flowBox->renderer()->node()->hasTagName(SVGNames::textPathTag);
+
+            if (!isTextPath && !isAnchor) {
+                SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->renderer()->node());
+                ASSERT(positioningElement);
+                ASSERT(positioningElement->parentNode());
+
+                info.addLayoutInformation(positioningElement);
+            } else if (!isAnchor) {
+                info.setInPathLayout(true);
+
+                // Handle text-anchor/textLength on path, which is special.
+                SVGTextContentElement* textContent = 0;
+                Node* node = flowBox->renderer()->node();
+                if (node && node->isSVGElement())
+                    textContent = static_cast<SVGTextContentElement*>(node);
+                ASSERT(textContent);
+
+                ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
+                ETextAnchor anchor = flowBox->renderer()->style()->svgStyle()->textAnchor();
+                float textAnchorStartOffset = 0.0f;
+
+                // Initialize sub-layout. We need to create text chunks from the textPath
+                // children using our standard layout code, to be able to measure the
+                // text length using our normal methods and not textPath specific hacks.
+                Vector<SVGTextChunk> tempChunks;
+
+                SVGCharacterLayoutInfo tempCharInfo;
+                buildLayoutInformation(flowBox, tempCharInfo);
+
+                SVGTextChunkLayoutInfo tempChunkInfo;
+                tempChunkInfo.buildTextChunks(tempCharInfo.svgChars.begin(), tempCharInfo.svgChars.end(), flowBox);
+                tempChunks = tempChunkInfo.textChunks();
+
+                Vector<SVGTextChunk>::iterator it = tempChunks.begin();
+                Vector<SVGTextChunk>::iterator end = tempChunks.end();
+
+                float computedLength = 0.0f;
+ 
+                for (; it != end; ++it) {
+                    SVGTextChunk& chunk = *it;
+
+                    // Apply text-length calculation
+                    info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength);
+
+                    if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
+                        info.pathTextLength += computedLength;
+                        info.pathChunkLength += chunk.textLength;
+                    }
+
+                    // Calculate text-anchor start offset
+                    if (anchor == TA_START)
+                        continue;
+
+                    textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor);
+                }
+
+                info.addLayoutInformation(flowBox, textAnchorStartOffset);
+            }
+
+            float shiftxSaved = info.shiftx;
+            float shiftySaved = info.shifty;
+
+            buildLayoutInformation(flowBox, info);
+            info.processedChunk(shiftxSaved, shiftySaved);
+
+            if (isTextPath)
+                info.setInPathLayout(false);
+        }
+    }
+}
+
+void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start)
+{
+    for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
+        if (child->renderer()->isText()) {
+            SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
+            IntRect boxRect = textBox->calculateBoundaries();
+            textBox->setX(boxRect.x());
+            textBox->setY(boxRect.y());
+            textBox->setWidth(boxRect.width());
+            textBox->setHeight(boxRect.height());
+        } else {
+            ASSERT(child->isInlineFlowBox());
+   
+            // Skip generated content.
+            if (!child->renderer()->node())
+                continue;
+
+            SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
+            layoutChildBoxes(flowBox);
+
+            IntRect boxRect = flowBox->calculateBoundaries();
+            flowBox->setX(boxRect.x());
+            flowBox->setY(boxRect.y());
+            flowBox->setWidth(boxRect.width());
+            flowBox->setHeight(boxRect.height());
+        }
+    }
+}
+
+void SVGRootInlineBox::layoutRootBox()
+{
+    RenderBlock* parentBlock = block();
+    ASSERT(parentBlock);
+
+    IntRect childRect;
+    for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
+        // Skip generated content.
+        if (!child->renderer()->node())
+            continue;
+        childRect.unite(child->calculateBoundaries());
+    }
+
+    int xBlock = childRect.x();
+    int yBlock = childRect.y();
+    int widthBlock = childRect.width();
+    int heightBlock = childRect.height();
+
+    // Finally, assign the root block position, now that all content is laid out.
+    parentBlock->setLocation(xBlock, yBlock);
+    parentBlock->setWidth(widthBlock);
+    parentBlock->setHeight(heightBlock);
+
+    // Position all children relative to the parent block.
+    for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
+        // Skip generated content.
+        if (!child->renderer()->node())
+            continue;
+        child->adjustPosition(-xBlock, -yBlock);
+    }
+
+    // Position ourselves.
+    setX(0);
+    setY(0);
+    setWidth(widthBlock);
+    setHeight(heightBlock);
+    setBlockHeight(heightBlock);
+    setLineTopBottomPositions(0, heightBlock);
+}
+
+void SVGRootInlineBox::propagateTextChunkPartInformation()
+{
+#if DEBUG_CHUNK_PART_PROPAGATION > 0
+    ListHashSet<SVGInlineTextBox*> boxes;
+#endif
+
+    // Loop through all text chunks
+    const Vector<SVGTextChunk>::const_iterator end = m_svgTextChunks.end();
+    for (Vector<SVGTextChunk>::const_iterator it = m_svgTextChunks.begin(); it != end; ++it) {
+        const SVGTextChunk& chunk = *it;
+        int processedChunkCharacters = 0;
+
+        // Loop through all ranges contained in this chunk
+        const Vector<SVGInlineBoxCharacterRange>::const_iterator boxEnd = chunk.boxes.end();
+        for (Vector<SVGInlineBoxCharacterRange>::const_iterator boxIt = chunk.boxes.begin(); boxIt != boxEnd; ++boxIt) {
+            const SVGInlineBoxCharacterRange& range = *boxIt;
+            ASSERT(range.box->isSVGInlineTextBox());
+
+            // Access style & font information of this text box
+            SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box);
+            rangeTextBox->setChunkTransformation(chunk.ctm);
+
+            RenderText* text = rangeTextBox->textRenderer();
+            ASSERT(text);
+
+            RenderStyle* style = text->style();
+            ASSERT(style);
+
+            const Font& font = style->font();
+ 
+            // Figure out first and last character of this range in this chunk
+            int rangeLength = range.endOffset - range.startOffset;
+            Vector<SVGChar>::iterator itCharBegin = chunk.start + processedChunkCharacters;
+            Vector<SVGChar>::iterator itCharEnd = chunk.start + processedChunkCharacters + rangeLength;
+            ASSERT(itCharEnd <= chunk.end);
+
+            // Loop through all characters in range 
+            int processedRangeCharacters = 0;
+            for (Vector<SVGChar>::iterator itChar = itCharBegin; itChar != itCharEnd; ++itChar) {
+                if (itChar->isHidden()) {
+                    ++processedRangeCharacters;
+                    continue;
+                }
+
+                // Determine how many characters - starting from the current - can be drawn at once.
+                Vector<SVGChar>::iterator itSearch = itChar + 1;
+                while (itSearch != itCharEnd) {
+                    if (itSearch->drawnSeperated || itSearch->isHidden())
+                        break;
+
+                    ++itSearch;
+                }
+
+                // Calculate text chunk part information for this chunk sub-range
+                const UChar* partStart = text->characters() + rangeTextBox->start() + range.startOffset + processedRangeCharacters;
+
+                SVGTextChunkPart part;
+                part.firstCharacter = itChar;
+                part.length = itSearch - itChar;
+                part.width = font.floatWidth(svgTextRunForInlineTextBox(partStart, part.length, style, rangeTextBox));
+                part.height = font.height();
+                part.offset = range.startOffset + processedRangeCharacters;
+                rangeTextBox->addChunkPartInformation(part);
+                processedRangeCharacters += part.length;
+
+                // Skip processed characters
+                itChar = itSearch - 1;
+            }
+
+            ASSERT(processedRangeCharacters == rangeLength);
+            processedChunkCharacters += rangeLength;
+
+#if DEBUG_CHUNK_PART_PROPAGATION > 0
+            boxes.add(rangeTextBox);
+#endif
+        }
+    }
+
+#if DEBUG_CHUNK_PART_PROPAGATION > 0
+    {
+        fprintf(stderr, "Propagated text chunk part information:\n");
+
+        ListHashSet<SVGInlineTextBox*>::const_iterator it = boxes.begin();
+        const ListHashSet<SVGInlineTextBox*>::const_iterator end = boxes.end();
+
+        for (; it != end; ++it) {
+            const SVGInlineTextBox* box = *it;
+            const Vector<SVGTextChunkPart>& parts = box->svgTextChunkParts();
+
+            fprintf(stderr, " Box %p contains %i text chunk parts:\n", box, static_cast<int>(parts.size()));
+            Vector<SVGTextChunkPart>::const_iterator partIt = parts.begin();
+            const Vector<SVGTextChunkPart>::const_iterator partEnd = parts.end();
+            for (; partIt != partEnd; ++partIt) {
+                const SVGTextChunkPart& part = *partIt;
+                fprintf(stderr, "   -> firstCharacter x=%lf, y=%lf, offset=%i, length=%i, width=%lf, height=%lf, textRenderer=%p\n"
+                              , part.firstCharacter->x, part.firstCharacter->y, part.offset, part.length, part.width, part.height, box->textRenderer());
+            }
+        }
+    }
+#endif
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)