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 "core/rendering/svg/RenderSVGInlineText.h"
     28 #include "core/rendering/svg/RenderSVGText.h"
     29 #include "core/rendering/svg/SVGInlineFlowBox.h"
     30 #include "core/rendering/svg/SVGInlineTextBox.h"
     31 #include "core/rendering/svg/SVGRenderingContext.h"
     32 
     33 namespace blink {
     34 
     35 void SVGRootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit, LayoutUnit)
     36 {
     37     ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
     38 
     39     bool isPrinting = renderer().document().printing();
     40     bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
     41 
     42     PaintInfo childPaintInfo(paintInfo);
     43     if (hasSelection) {
     44         for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
     45             if (child->isSVGInlineTextBox())
     46                 toSVGInlineTextBox(child)->paintSelectionBackground(childPaintInfo);
     47             else if (child->isSVGInlineFlowBox())
     48                 toSVGInlineFlowBox(child)->paintSelectionBackground(childPaintInfo);
     49         }
     50     }
     51 
     52     GraphicsContextStateSaver stateSaver(*paintInfo.context);
     53     SVGRenderingContext renderingContext(&renderer(), paintInfo);
     54     if (renderingContext.isRenderingPrepared()) {
     55         for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
     56             child->paint(paintInfo, paintOffset, 0, 0);
     57     }
     58 }
     59 
     60 void SVGRootInlineBox::markDirty()
     61 {
     62     for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
     63         child->markDirty();
     64     RootInlineBox::markDirty();
     65 }
     66 
     67 void SVGRootInlineBox::computePerCharacterLayoutInformation()
     68 {
     69     RenderSVGText& textRoot = toRenderSVGText(block());
     70 
     71     Vector<SVGTextLayoutAttributes*>& layoutAttributes = textRoot.layoutAttributes();
     72     if (layoutAttributes.isEmpty())
     73         return;
     74 
     75     if (textRoot.needsReordering())
     76         reorderValueLists(layoutAttributes);
     77 
     78     // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
     79     SVGTextLayoutEngine characterLayout(layoutAttributes);
     80     layoutCharactersInTextBoxes(this, characterLayout);
     81 
     82     // Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
     83     characterLayout.finishLayout();
     84 
     85     // Perform SVG text layout phase four
     86     // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block.
     87     FloatRect childRect;
     88     layoutChildBoxes(this, &childRect);
     89     layoutRootBox(childRect);
     90 }
     91 
     92 void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout)
     93 {
     94     for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
     95         if (child->isSVGInlineTextBox()) {
     96             ASSERT(child->renderer().isSVGInlineText());
     97             characterLayout.layoutInlineTextBox(toSVGInlineTextBox(child));
     98         } else {
     99             // Skip generated content.
    100             Node* node = child->renderer().node();
    101             if (!node)
    102                 continue;
    103 
    104             SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child);
    105             bool isTextPath = isSVGTextPathElement(*node);
    106             if (isTextPath) {
    107                 // Build text chunks for all <textPath> children, using the line layout algorithm.
    108                 // This is needeed as text-anchor is just an additional startOffset for text paths.
    109                 SVGTextLayoutEngine lineLayout(characterLayout.layoutAttributes());
    110                 layoutCharactersInTextBoxes(flowBox, lineLayout);
    111 
    112                 characterLayout.beginTextPathLayout(&child->renderer(), lineLayout);
    113             }
    114 
    115             layoutCharactersInTextBoxes(flowBox, characterLayout);
    116 
    117             if (isTextPath)
    118                 characterLayout.endTextPathLayout();
    119         }
    120     }
    121 }
    122 
    123 void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start, FloatRect* childRect)
    124 {
    125     for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
    126         FloatRect boxRect;
    127         if (child->isSVGInlineTextBox()) {
    128             ASSERT(child->renderer().isSVGInlineText());
    129 
    130             SVGInlineTextBox* textBox = toSVGInlineTextBox(child);
    131             boxRect = textBox->calculateBoundaries();
    132             textBox->setX(boxRect.x());
    133             textBox->setY(boxRect.y());
    134             textBox->setLogicalWidth(boxRect.width());
    135             textBox->setLogicalHeight(boxRect.height());
    136         } else {
    137             // Skip generated content.
    138             if (!child->renderer().node())
    139                 continue;
    140 
    141             SVGInlineFlowBox* flowBox = toSVGInlineFlowBox(child);
    142             layoutChildBoxes(flowBox);
    143 
    144             boxRect = flowBox->calculateBoundaries();
    145             flowBox->setX(boxRect.x());
    146             flowBox->setY(boxRect.y());
    147             flowBox->setLogicalWidth(boxRect.width());
    148             flowBox->setLogicalHeight(boxRect.height());
    149         }
    150         if (childRect)
    151             childRect->unite(boxRect);
    152     }
    153 }
    154 
    155 void SVGRootInlineBox::layoutRootBox(const FloatRect& childRect)
    156 {
    157     RenderBlockFlow& parentBlock = block();
    158 
    159     // Finally, assign the root block position, now that all content is laid out.
    160     LayoutRect boundingRect = enclosingLayoutRect(childRect);
    161     parentBlock.setLocation(boundingRect.location());
    162     parentBlock.setSize(boundingRect.size());
    163 
    164     // Position all children relative to the parent block.
    165     for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
    166         // Skip generated content.
    167         if (!child->renderer().node())
    168             continue;
    169         child->adjustPosition(-childRect.x(), -childRect.y());
    170     }
    171 
    172     // Position ourselves.
    173     setX(0);
    174     setY(0);
    175     setLogicalWidth(childRect.width());
    176     setLogicalHeight(childRect.height());
    177     setLineTopBottomPositions(0, boundingRect.height(), 0, boundingRect.height());
    178 }
    179 
    180 InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const LayoutPoint& point)
    181 {
    182     InlineBox* firstLeaf = firstLeafChild();
    183     InlineBox* lastLeaf = lastLeafChild();
    184     if (firstLeaf == lastLeaf)
    185         return firstLeaf;
    186 
    187     // FIXME: Check for vertical text!
    188     InlineBox* closestLeaf = 0;
    189     for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) {
    190         if (!leaf->isSVGInlineTextBox())
    191             continue;
    192         if (point.y() < leaf->y())
    193             continue;
    194         if (point.y() > leaf->y() + leaf->virtualLogicalHeight())
    195             continue;
    196 
    197         closestLeaf = leaf;
    198         if (point.x() < leaf->left() + leaf->logicalWidth())
    199             return leaf;
    200     }
    201 
    202     return closestLeaf ? closestLeaf : lastLeaf;
    203 }
    204 
    205 static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes* firstAttributes, SVGTextLayoutAttributes* lastAttributes, unsigned firstPosition, unsigned lastPosition)
    206 {
    207     SVGCharacterDataMap::iterator itFirst = firstAttributes->characterDataMap().find(firstPosition + 1);
    208     SVGCharacterDataMap::iterator itLast = lastAttributes->characterDataMap().find(lastPosition + 1);
    209     bool firstPresent = itFirst != firstAttributes->characterDataMap().end();
    210     bool lastPresent = itLast != lastAttributes->characterDataMap().end();
    211     // We only want to perform the swap if both inline boxes are absolutely
    212     // positioned.
    213     if (!firstPresent || !lastPresent)
    214         return;
    215     std::swap(itFirst->value, itLast->value);
    216 }
    217 
    218 static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes*>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext,
    219                                                       SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last)
    220 {
    221     first = 0;
    222     last = 0;
    223 
    224     unsigned attributesSize = attributes.size();
    225     for (unsigned i = 0; i < attributesSize; ++i) {
    226         SVGTextLayoutAttributes* current = attributes[i];
    227         if (!first && firstContext == current->context())
    228             first = current;
    229         if (!last && lastContext == current->context())
    230             last = current;
    231         if (first && last)
    232             break;
    233     }
    234 
    235     ASSERT(first);
    236     ASSERT(last);
    237 }
    238 
    239 static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last)
    240 {
    241     ASSERT(userData);
    242     Vector<SVGTextLayoutAttributes*>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes*>*>(userData);
    243 
    244     // 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.
    245     while (true)  {
    246         if (first == last || first == --last)
    247             return;
    248 
    249         if (!(*last)->isSVGInlineTextBox() || !(*first)->isSVGInlineTextBox()) {
    250             InlineBox* temp = *first;
    251             *first = *last;
    252             *last = temp;
    253             ++first;
    254             continue;
    255         }
    256 
    257         SVGInlineTextBox* firstTextBox = toSVGInlineTextBox(*first);
    258         SVGInlineTextBox* lastTextBox = toSVGInlineTextBox(*last);
    259 
    260         // Reordering is only necessary for BiDi text that is _absolutely_ positioned.
    261         if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) {
    262             RenderSVGInlineText& firstContext = toRenderSVGInlineText(firstTextBox->renderer());
    263             RenderSVGInlineText& lastContext = toRenderSVGInlineText(lastTextBox->renderer());
    264 
    265             SVGTextLayoutAttributes* firstAttributes = 0;
    266             SVGTextLayoutAttributes* lastAttributes = 0;
    267             findFirstAndLastAttributesInVector(attributes, &firstContext, &lastContext, firstAttributes, lastAttributes);
    268             swapItemsInLayoutAttributes(firstAttributes, lastAttributes, firstTextBox->start(), lastTextBox->start());
    269         }
    270 
    271         InlineBox* temp = *first;
    272         *first = *last;
    273         *last = temp;
    274 
    275         ++first;
    276     }
    277 }
    278 
    279 void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes*>& attributes)
    280 {
    281     Vector<InlineBox*> leafBoxesInLogicalOrder;
    282     collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes);
    283 }
    284 
    285 } // namespace blink
    286