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/rendering/svg/SVGTextMetricsBuilder.h"
     27 #include "core/svg/SVGTextPositioningElement.h"
     28 
     29 namespace blink {
     30 
     31 SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder()
     32     : m_textLength(0)
     33 {
     34 }
     35 
     36 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer(RenderSVGInlineText* text)
     37 {
     38     ASSERT(text);
     39 
     40     RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text);
     41     if (!textRoot)
     42         return;
     43 
     44     if (m_textPositions.isEmpty()) {
     45         m_characterDataMap.clear();
     46 
     47         m_textLength = 0;
     48         UChar lastCharacter = ' ';
     49         collectTextPositioningElements(textRoot, lastCharacter);
     50 
     51         if (!m_textLength)
     52             return;
     53 
     54         buildCharacterDataMap(textRoot);
     55     }
     56 
     57     SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(textRoot, text, m_characterDataMap);
     58 }
     59 
     60 bool SVGTextLayoutAttributesBuilder::buildLayoutAttributesForForSubtree(RenderSVGText* textRoot)
     61 {
     62     ASSERT(textRoot);
     63 
     64     m_characterDataMap.clear();
     65 
     66     if (m_textPositions.isEmpty()) {
     67         m_textLength = 0;
     68         UChar lastCharacter = ' ';
     69         collectTextPositioningElements(textRoot, lastCharacter);
     70     }
     71 
     72     if (!m_textLength)
     73         return false;
     74 
     75     buildCharacterDataMap(textRoot);
     76     SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(textRoot, 0, m_characterDataMap);
     77     return true;
     78 }
     79 
     80 void SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer(RenderSVGInlineText* text)
     81 {
     82     ASSERT(text);
     83     SVGTextMetricsBuilder::measureTextRenderer(text);
     84 }
     85 
     86 static inline void processRenderSVGInlineText(RenderSVGInlineText* text, unsigned& atCharacter, UChar& lastCharacter)
     87 {
     88     if (text->style()->whiteSpace() == PRE) {
     89         atCharacter += text->textLength();
     90         return;
     91     }
     92 
     93     unsigned textLength = text->textLength();
     94     for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) {
     95         UChar currentCharacter = text->characterAt(textPosition);
     96         if (currentCharacter == ' ' && lastCharacter == ' ')
     97             continue;
     98 
     99         lastCharacter = currentCharacter;
    100         ++atCharacter;
    101     }
    102 }
    103 
    104 void SVGTextLayoutAttributesBuilder::collectTextPositioningElements(RenderObject* start, UChar& lastCharacter)
    105 {
    106     ASSERT(!start->isSVGText() || m_textPositions.isEmpty());
    107 
    108     for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) {
    109         if (child->isSVGInlineText()) {
    110             processRenderSVGInlineText(toRenderSVGInlineText(child), m_textLength, lastCharacter);
    111             continue;
    112         }
    113 
    114         if (!child->isSVGInline())
    115             continue;
    116 
    117         SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(child);
    118         unsigned atPosition = m_textPositions.size();
    119         if (element)
    120             m_textPositions.append(TextPosition(element, m_textLength));
    121 
    122         collectTextPositioningElements(child, lastCharacter);
    123 
    124         if (!element)
    125             continue;
    126 
    127         // Update text position, after we're back from recursion.
    128         TextPosition& position = m_textPositions[atPosition];
    129         ASSERT(!position.length);
    130         position.length = m_textLength - position.start;
    131     }
    132 }
    133 
    134 void SVGTextLayoutAttributesBuilder::buildCharacterDataMap(RenderSVGText* textRoot)
    135 {
    136     SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot);
    137     ASSERT(outermostTextElement);
    138 
    139     // Grab outermost <text> element value lists and insert them in the character data map.
    140     TextPosition wholeTextPosition(outermostTextElement, 0, m_textLength);
    141     fillCharacterDataMap(wholeTextPosition);
    142 
    143     // Handle x/y default attributes.
    144     SVGCharacterDataMap::iterator it = m_characterDataMap.find(1);
    145     if (it == m_characterDataMap.end()) {
    146         SVGCharacterData data;
    147         data.x = 0;
    148         data.y = 0;
    149         m_characterDataMap.set(1, data);
    150     } else {
    151         SVGCharacterData& data = it->value;
    152         if (data.x == SVGTextLayoutAttributes::emptyValue())
    153             data.x = 0;
    154         if (data.y == SVGTextLayoutAttributes::emptyValue())
    155             data.y = 0;
    156     }
    157 
    158     // Fill character data map using child text positioning elements in top-down order.
    159     unsigned size = m_textPositions.size();
    160     for (unsigned i = 0; i < size; ++i)
    161         fillCharacterDataMap(m_textPositions[i]);
    162 }
    163 
    164 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)
    165 {
    166     if (xList)
    167         data.x = xList->at(i)->value(lengthContext);
    168     if (yList)
    169         data.y = yList->at(i)->value(lengthContext);
    170     if (dxList)
    171         data.dx = dxList->at(i)->value(lengthContext);
    172     if (dyList)
    173         data.dy = dyList->at(i)->value(lengthContext);
    174     if (rotateList) {
    175         data.rotate = rotateList->at(i)->value();
    176         lastRotation = data.rotate;
    177     }
    178 }
    179 
    180 void SVGTextLayoutAttributesBuilder::fillCharacterDataMap(const TextPosition& position)
    181 {
    182     RefPtr<SVGLengthList> xList = position.element->x()->currentValue();
    183     RefPtr<SVGLengthList> yList = position.element->y()->currentValue();
    184     RefPtr<SVGLengthList> dxList = position.element->dx()->currentValue();
    185     RefPtr<SVGLengthList> dyList = position.element->dy()->currentValue();
    186     RefPtr<SVGNumberList> rotateList = position.element->rotate()->currentValue();
    187 
    188     unsigned xListSize = xList->length();
    189     unsigned yListSize = yList->length();
    190     unsigned dxListSize = dxList->length();
    191     unsigned dyListSize = dyList->length();
    192     unsigned rotateListSize = rotateList->length();
    193     if (!xListSize && !yListSize && !dxListSize && !dyListSize && !rotateListSize)
    194         return;
    195 
    196     float lastRotation = SVGTextLayoutAttributes::emptyValue();
    197     SVGLengthContext lengthContext(position.element);
    198     for (unsigned i = 0; i < position.length; ++i) {
    199         const SVGLengthList* xListPtr = i < xListSize ? xList.get() : 0;
    200         const SVGLengthList* yListPtr = i < yListSize ? yList.get() : 0;
    201         const SVGLengthList* dxListPtr = i < dxListSize ? dxList.get() : 0;
    202         const SVGLengthList* dyListPtr = i < dyListSize ? dyList.get() : 0;
    203         const SVGNumberList* rotateListPtr = i < rotateListSize ? rotateList.get() : 0;
    204         if (!xListPtr && !yListPtr && !dxListPtr && !dyListPtr && !rotateListPtr)
    205             break;
    206 
    207         SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1);
    208         if (it == m_characterDataMap.end()) {
    209             SVGCharacterData data;
    210             updateCharacterData(i, lastRotation, data, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr);
    211             m_characterDataMap.set(position.start + i + 1, data);
    212             continue;
    213         }
    214 
    215         updateCharacterData(i, lastRotation, it->value, lengthContext, xListPtr, yListPtr, dxListPtr, dyListPtr, rotateListPtr);
    216     }
    217 
    218     // The last rotation value always spans the whole scope.
    219     if (lastRotation == SVGTextLayoutAttributes::emptyValue())
    220         return;
    221 
    222     for (unsigned i = rotateList->length(); i < position.length; ++i) {
    223         SVGCharacterDataMap::iterator it = m_characterDataMap.find(position.start + i + 1);
    224         if (it == m_characterDataMap.end()) {
    225             SVGCharacterData data;
    226             data.rotate = lastRotation;
    227             m_characterDataMap.set(position.start + i + 1, data);
    228             continue;
    229         }
    230 
    231         it->value.rotate = lastRotation;
    232     }
    233 }
    234 
    235 }
    236