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