Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  */
     19 
     20 #include "config.h"
     21 
     22 #include "core/rendering/svg/SVGTextLayoutAttributesBuilder.h"
     23 
     24 #include "core/rendering/svg/RenderSVGInlineText.h"
     25 #include "core/rendering/svg/RenderSVGText.h"
     26 #include "core/svg/SVGTextPositioningElement.h"
     27 
     28 namespace WebCore {
     29 
     30 SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder()
     31     : m_textLength(0)
     32 {
     33 }
     34 
     35 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer(RenderSVGInlineText* text)
     36 {
     37     ASSERT(text);
     38 
     39     RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text);
     40     if (!textRoot)
     41         return;
     42 
     43     if (m_textPositions.isEmpty()) {
     44         m_characterDataMap.clear();
     45 
     46         m_textLength = 0;
     47         UChar lastCharacter = ' ';
     48         collectTextPositioningElements(textRoot, lastCharacter);
     49 
     50         if (!m_textLength)
     51             return;
     52 
     53         buildCharacterDataMap(textRoot);
     54     }
     55 
     56     m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, text, m_characterDataMap);
     57 }
     58 
     59 bool SVGTextLayoutAttributesBuilder::buildLayoutAttributesForForSubtree(RenderSVGText* textRoot)
     60 {
     61     ASSERT(textRoot);
     62 
     63     m_characterDataMap.clear();
     64 
     65     if (m_textPositions.isEmpty()) {
     66         m_textLength = 0;
     67         UChar lastCharacter = ' ';
     68         collectTextPositioningElements(textRoot, lastCharacter);
     69     }
     70 
     71     if (!m_textLength)
     72         return false;
     73 
     74     buildCharacterDataMap(textRoot);
     75     m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, 0, m_characterDataMap);
     76     return true;
     77 }
     78 
     79 void SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer(RenderSVGInlineText* text)
     80 {
     81     ASSERT(text);
     82     m_metricsBuilder.measureTextRenderer(text);
     83 }
     84 
     85 static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, UChar& lastCharacter)
     86 {
     87     if (text->style()->whiteSpace() == PRE) {
     88         atCharacter += text->textLength();
     89         return;
     90     }
     91 
     92     unsigned textLength = text->textLength();
     93     for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) {
     94         UChar currentCharacter = text->characterAt(textPosition);
     95         if (currentCharacter == ' ' && lastCharacter == ' ')
     96             continue;
     97 
     98         lastCharacter = currentCharacter;
     99         ++atCharacter;
    100     }
    101 }
    102 
    103 void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, UChar& lastCharacter)
    104 {
    105     ASSERT(!start->isSVGText() || m_textPositions.isEmpty());
    106 
    107     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
    108         if (child->isSVGInlineText()) {
    109             processRenderSVGInlineText(toRenderSVGInlineText(child), m_textLength, lastCharacter);
    110             continue;
    111         }
    112 
    113         if (!child->isSVGInline())
    114             continue;
    115 
    116         SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(child);
    117         unsigned atPosition = m_textPositions.size();
    118         if (element)
    119             m_textPositions.append(TextPosition(element, m_textLength));
    120 
    121         collectTextPositioningElements(child, lastCharacter);
    122 
    123         if (!element)
    124             continue;
    125 
    126         // Update text position, after we're back from recursion.
    127         TextPosition& position = m_textPositions[atPosition];
    128         ASSERT(!position.length);
    129         position.length = m_textLength - position.start;
    130     }
    131 }
    132 
    133 void SVGTextLayoutAttributesBuilder::buildCharacterDataMap(RenderSVGText* textRoot)
    134 {
    135     SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot);
    136     ASSERT(outermostTextElement);
    137 
    138     // Grab outermost <text> element value lists and insert them in the character data map.
    139     TextPosition wholeTextPosition(outermostTextElement, 0, m_textLength);
    140     fillCharacterDataMap(wholeTextPosition);
    141 
    142     // Handle x/y default attributes.
    143     SVGCharacterDataMap::iterator it = m_characterDataMap.find(1);
    144     if (it == m_characterDataMap.end()) {
    145         SVGCharacterData data;
    146         data.x = 0;
    147         data.y = 0;
    148         m_characterDataMap.set(1, data);
    149     } else {
    150         SVGCharacterData& data = it->value;
    151         if (data.x == SVGTextLayoutAttributes::emptyValue())
    152             data.x = 0;
    153         if (data.y == SVGTextLayoutAttributes::emptyValue())
    154             data.y = 0;
    155     }
    156 
    157     // Fill character data map using child text positioning elements in top-down order.
    158     unsigned size = m_textPositions.size();
    159     for (unsigned i = 0; i < size; ++i)
    160         fillCharacterDataMap(m_textPositions[i]);
    161 }
    162 
    163 static inline void updateCharacterData(unsigned i, float& lastRotation, SVGCharacterData& data, const SVGLengthContext& lengthContext, const SVGLengthList* xList, const SVGLengthList* yList, const SVGLengthList* dxList, const SVGLengthList* dyList, const SVGNumberList* rotateList)
    164 {
    165     if (xList)
    166         data.x = xList->at(i).value(lengthContext);
    167     if (yList)
    168         data.y = yList->at(i).value(lengthContext);
    169     if (dxList)
    170         data.dx = dxList->at(i).value(lengthContext);
    171     if (dyList)
    172         data.dy = dyList->at(i).value(lengthContext);
    173     if (rotateList) {
    174         data.rotate = rotateList->at(i).value();
    175         lastRotation = data.rotate;
    176     }
    177 }
    178 
    179 void SVGTextLayoutAttributesBuilder::fillCharacterDataMap(const TextPosition& position)
    180 {
    181     const SVGLengthList& xList = position.element->xCurrentValue();
    182     const SVGLengthList& yList = position.element->yCurrentValue();
    183     const SVGLengthList& dxList = position.element->dxCurrentValue();
    184     const SVGLengthList& dyList = position.element->dyCurrentValue();
    185     const SVGNumberList& rotateList = position.element->rotateCurrentValue();
    186 
    187     unsigned xListSize = xList.size();
    188     unsigned yListSize = yList.size();
    189     unsigned dxListSize = dxList.size();
    190     unsigned dyListSize = dyList.size();
    191     unsigned rotateListSize = rotateList.size();
    192     if (!xListSize && !yListSize && !dxListSize && !dyListSize && !rotateListSize)
    193         return;
    194 
    195     float lastRotation = SVGTextLayoutAttributes::emptyValue();
    196     SVGLengthContext lengthContext(position.element);
    197     for (unsigned i = 0; i < position.length; ++i) {
    198         const SVGLengthList* xListPtr = i < xListSize ? &xList : 0;
    199         const SVGLengthList* yListPtr = i < yListSize ? &yList : 0;
    200         const SVGLengthList* dxListPtr = i < dxListSize ? &dxList : 0;
    201         const SVGLengthList* dyListPtr = i < dyListSize ? &dyList : 0;
    202         const SVGNumberList* rotateListPtr = i < rotateListSize ? &rotateList : 0;
    203         if (!xListPtr && !yListPtr && !dxListPtr && !dyListPtr && !rotateListPtr)
    204             break;
    205 
    206         SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1);
    207         if (it == m_characterDataMap.end()) {
    208             SVGCharacterData data;
    209             updateCharacterData(i, lastRotation, data, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr);
    210             m_characterDataMap.set(position.start + i + 1, data);
    211             continue;
    212         }
    213 
    214         updateCharacterData(i, lastRotation, it->value, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr);
    215     }
    216 
    217     // The last rotation value always spans the whole scope.
    218     if (lastRotation == SVGTextLayoutAttributes::emptyValue())
    219         return;
    220 
    221     for (unsigned i = rotateList.size(); i < position.length; ++i) {
    222         SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1);
    223         if (it == m_characterDataMap.end()) {
    224             SVGCharacterData data;
    225             data.rotate = lastRotation;
    226             m_characterDataMap.set(position.start + i + 1, data);
    227             continue;
    228         }
    229 
    230         it->value.rotate = lastRotation;
    231     }
    232 }
    233 
    234 }
    235