Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2006 Oliver Hunt <ojh16 (at) student.canterbury.ac.nz>
      3  * Copyright (C) 2006 Apple Computer Inc.
      4  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      5  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  */
     22 
     23 #include "config.h"
     24 #include "SVGRootInlineBox.h"
     25 
     26 #if ENABLE(SVG)
     27 #include "GraphicsContext.h"
     28 #include "RenderSVGInlineText.h"
     29 #include "RenderSVGText.h"
     30 #include "SVGInlineFlowBox.h"
     31 #include "SVGInlineTextBox.h"
     32 #include "SVGNames.h"
     33 #include "SVGRenderSupport.h"
     34 #include "SVGTextPositioningElement.h"
     35 
     36 namespace WebCore {
     37 
     38 void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int, int, int)
     39 {
     40     ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
     41     ASSERT(!paintInfo.context->paintingDisabled());
     42 
     43     RenderObject* boxRenderer = renderer();
     44     ASSERT(boxRenderer);
     45 
     46     bool isPrinting = renderer()->document()->printing();
     47     bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
     48 
     49     PaintInfo childPaintInfo(paintInfo);
     50     if (hasSelection) {
     51         for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
     52             if (child->isSVGInlineTextBox())
     53                 static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo);
     54             else if (child->isSVGInlineFlowBox())
     55                 static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo);
     56         }
     57     }
     58 
     59     childPaintInfo.context->save();
     60 
     61     if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) {
     62         for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
     63             if (child->isSVGInlineTextBox())
     64                 SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer()));
     65 
     66             child->paint(childPaintInfo, 0, 0, 0, 0);
     67         }
     68     }
     69 
     70     SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context);
     71     childPaintInfo.context->restore();
     72 }
     73 
     74 void SVGRootInlineBox::computePerCharacterLayoutInformation()
     75 {
     76     RenderSVGText* parentBlock = toRenderSVGText(block());
     77     ASSERT(parentBlock);
     78 
     79     Vector<SVGTextLayoutAttributes>& attributes = parentBlock->layoutAttributes();
     80     if (parentBlock->needsReordering())
     81         reorderValueLists(attributes);
     82 
     83     // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
     84     SVGTextLayoutEngine characterLayout(attributes);
     85     layoutCharactersInTextBoxes(this, characterLayout);
     86 
     87     // Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
     88     characterLayout.finishLayout();
     89 
     90     // Perform SVG text layout phase four
     91     // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block.
     92     layoutChildBoxes(this);
     93     layoutRootBox();
     94 }
     95 
     96 void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout)
     97 {
     98     for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
     99         if (child->isSVGInlineTextBox()) {
    100             ASSERT(child->renderer());
    101             ASSERT(child->renderer()->isSVGInlineText());
    102 
    103             SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
    104             characterLayout.layoutInlineTextBox(textBox);
    105         } else {
    106             // Skip generated content.
    107             Node* node = child->renderer()->node();
    108             if (!node)
    109                 continue;
    110 
    111             ASSERT(child->isInlineFlowBox());
    112 
    113             SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
    114             bool isTextPath = node->hasTagName(SVGNames::textPathTag);
    115             if (isTextPath) {
    116                 // Build text chunks for all <textPath> children, using the line layout algorithm.
    117                 // This is needeed as text-anchor is just an additional startOffset for text paths.
    118                 RenderSVGText* parentBlock = toRenderSVGText(block());
    119                 ASSERT(parentBlock);
    120 
    121                 SVGTextLayoutEngine lineLayout(parentBlock->layoutAttributes());
    122                 layoutCharactersInTextBoxes(flowBox, lineLayout);
    123 
    124                 characterLayout.beginTextPathLayout(child->renderer(), lineLayout);
    125             }
    126 
    127             layoutCharactersInTextBoxes(flowBox, characterLayout);
    128 
    129             if (isTextPath)
    130                 characterLayout.endTextPathLayout();
    131         }
    132     }
    133 }
    134 
    135 void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start)
    136 {
    137     for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
    138         if (child->isSVGInlineTextBox()) {
    139             ASSERT(child->renderer());
    140             ASSERT(child->renderer()->isSVGInlineText());
    141 
    142             SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
    143             IntRect boxRect = textBox->calculateBoundaries();
    144             textBox->setX(boxRect.x());
    145             textBox->setY(boxRect.y());
    146             textBox->setLogicalWidth(boxRect.width());
    147             textBox->setLogicalHeight(boxRect.height());
    148         } else {
    149             // Skip generated content.
    150             if (!child->renderer()->node())
    151                 continue;
    152 
    153             ASSERT(child->isInlineFlowBox());
    154 
    155             SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
    156             layoutChildBoxes(flowBox);
    157 
    158             IntRect boxRect = flowBox->calculateBoundaries();
    159             flowBox->setX(boxRect.x());
    160             flowBox->setY(boxRect.y());
    161             flowBox->setLogicalWidth(boxRect.width());
    162             flowBox->setLogicalHeight(boxRect.height());
    163         }
    164     }
    165 }
    166 
    167 void SVGRootInlineBox::layoutRootBox()
    168 {
    169     RenderBlock* parentBlock = block();
    170     ASSERT(parentBlock);
    171 
    172     IntRect childRect;
    173     for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
    174         // Skip generated content.
    175         if (!child->renderer()->node())
    176             continue;
    177         childRect.unite(child->calculateBoundaries());
    178     }
    179 
    180     int xBlock = childRect.x();
    181     int yBlock = childRect.y();
    182     int widthBlock = childRect.width();
    183     int heightBlock = childRect.height();
    184 
    185     // Finally, assign the root block position, now that all content is laid out.
    186     parentBlock->setLocation(xBlock, yBlock);
    187     parentBlock->setWidth(widthBlock);
    188     parentBlock->setHeight(heightBlock);
    189 
    190     // Position all children relative to the parent block.
    191     for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
    192         // Skip generated content.
    193         if (!child->renderer()->node())
    194             continue;
    195         child->adjustPosition(-xBlock, -yBlock);
    196     }
    197 
    198     // Position ourselves.
    199     setX(0);
    200     setY(0);
    201     setLogicalWidth(widthBlock);
    202     setLogicalHeight(heightBlock);
    203     setBlockLogicalHeight(heightBlock);
    204     setLineTopBottomPositions(0, heightBlock);
    205 }
    206 
    207 InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const IntPoint& point)
    208 {
    209     InlineBox* firstLeaf = firstLeafChild();
    210     InlineBox* lastLeaf = lastLeafChild();
    211     if (firstLeaf == lastLeaf)
    212         return firstLeaf;
    213 
    214     // FIXME: Check for vertical text!
    215     InlineBox* closestLeaf = 0;
    216     for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) {
    217         if (!leaf->isSVGInlineTextBox())
    218             continue;
    219         if (point.y() < leaf->m_y)
    220             continue;
    221         if (point.y() > leaf->m_y + leaf->virtualLogicalHeight())
    222             continue;
    223 
    224         closestLeaf = leaf;
    225         if (point.x() < leaf->m_x + leaf->m_logicalWidth)
    226             return leaf;
    227     }
    228 
    229     return closestLeaf ? closestLeaf : lastLeaf;
    230 }
    231 
    232 static inline void swapItemsInVector(Vector<float>& firstVector, Vector<float>& lastVector, unsigned first, unsigned last)
    233 {
    234     float temp = firstVector.at(first);
    235     firstVector.at(first) = lastVector.at(last);
    236     lastVector.at(last) = temp;
    237 }
    238 
    239 static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes& firstAttributes, SVGTextLayoutAttributes& lastAttributes, unsigned firstPosition, unsigned lastPosition)
    240 {
    241     swapItemsInVector(firstAttributes.xValues(), lastAttributes.xValues(), firstPosition, lastPosition);
    242     swapItemsInVector(firstAttributes.yValues(), lastAttributes.yValues(), firstPosition, lastPosition);
    243     swapItemsInVector(firstAttributes.dxValues(), lastAttributes.dxValues(), firstPosition, lastPosition);
    244     swapItemsInVector(firstAttributes.dyValues(), lastAttributes.dyValues(), firstPosition, lastPosition);
    245     swapItemsInVector(firstAttributes.rotateValues(), lastAttributes.rotateValues(), firstPosition, lastPosition);
    246 }
    247 
    248 static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext,
    249                                                       SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last)
    250 {
    251     first = 0;
    252     last = 0;
    253 
    254     unsigned attributesSize = attributes.size();
    255     for (unsigned i = 0; i < attributesSize; ++i) {
    256         SVGTextLayoutAttributes& current = attributes.at(i);
    257         if (!first && firstContext == current.context())
    258             first = &current;
    259         if (!last && lastContext == current.context())
    260             last = &current;
    261         if (first && last)
    262             break;
    263     }
    264 
    265     ASSERT(first);
    266     ASSERT(last);
    267 }
    268 
    269 static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last)
    270 {
    271     ASSERT(userData);
    272     Vector<SVGTextLayoutAttributes>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes>*>(userData);
    273 
    274     // This is a copy of std::reverse(first, last). It additionally assure that the value lists within the InlineBoxes are reordered as well.
    275     while (true)  {
    276         if (first == last || first == --last)
    277             return;
    278 
    279         if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) {
    280             InlineBox* temp = *first;
    281             *first = *last;
    282             *last = temp;
    283             ++first;
    284             continue;
    285         }
    286 
    287         SVGInlineTextBox* firstTextBox = static_cast<SVGInlineTextBox*>(*first);
    288         SVGInlineTextBox* lastTextBox = static_cast<SVGInlineTextBox*>(*last);
    289 
    290         // Reordering is only necessary for BiDi text that is _absolutely_ positioned.
    291         if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) {
    292             RenderSVGInlineText* firstContext = toRenderSVGInlineText(firstTextBox->textRenderer());
    293             RenderSVGInlineText* lastContext = toRenderSVGInlineText(lastTextBox->textRenderer());
    294 
    295             SVGTextLayoutAttributes* firstAttributes = 0;
    296             SVGTextLayoutAttributes* lastAttributes = 0;
    297             findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes);
    298 
    299             unsigned firstBoxPosition = firstTextBox->start();
    300             unsigned firstBoxEnd = firstTextBox->end();
    301 
    302             unsigned lastBoxPosition = lastTextBox->start();
    303             unsigned lastBoxEnd = lastTextBox->end();
    304             for (; firstBoxPosition <= firstBoxEnd && lastBoxPosition <= lastBoxEnd; ++lastBoxPosition, ++firstBoxPosition)
    305                 swapItemsInLayoutAttributes(*firstAttributes, *lastAttributes, firstBoxPosition, lastBoxPosition);
    306         }
    307 
    308         InlineBox* temp = *first;
    309         *first = *last;
    310         *last = temp;
    311 
    312         ++first;
    313     }
    314 }
    315 
    316 void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes>& attributes)
    317 {
    318     Vector<InlineBox*> leafBoxesInLogicalOrder;
    319     collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes);
    320 }
    321 
    322 } // namespace WebCore
    323 
    324 #endif // ENABLE(SVG)
    325