Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis (at) kde.org>
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "config.h"
     22 
     23 #if ENABLE(SVG)
     24 #include "SVGTextContentElement.h"
     25 
     26 #include "CSSPropertyNames.h"
     27 #include "CSSValueKeywords.h"
     28 #include "Frame.h"
     29 #include "RenderObject.h"
     30 #include "RenderSVGResource.h"
     31 #include "SVGDocumentExtensions.h"
     32 #include "SVGNames.h"
     33 #include "SVGTextQuery.h"
     34 #include "SelectionController.h"
     35 #include "XMLNames.h"
     36 
     37 namespace WebCore {
     38 
     39 // Animated property definitions
     40 DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust)
     41 DEFINE_ANIMATED_BOOLEAN(SVGTextContentElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
     42 
     43 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document* document)
     44     : SVGStyledElement(tagName, document)
     45     , m_specifiedTextLength(LengthModeOther)
     46     , m_textLength(LengthModeOther)
     47     , m_lengthAdjust(LENGTHADJUST_SPACING)
     48 {
     49 }
     50 
     51 void SVGTextContentElement::synchronizeTextLength()
     52 {
     53     if (!m_textLength.shouldSynchronize)
     54         return;
     55     AtomicString value(SVGPropertyTraits<SVGLength>::toString(m_specifiedTextLength));
     56     SVGAnimatedPropertySynchronizer<true>::synchronize(this, SVGNames::textLengthAttr, value);
     57 }
     58 
     59 PassRefPtr<SVGAnimatedLength> SVGTextContentElement::textLengthAnimated()
     60 {
     61     DEFINE_STATIC_LOCAL(SVGLength, defaultTextLength, (LengthModeOther));
     62     if (m_specifiedTextLength == defaultTextLength) {
     63         ExceptionCode ec = 0;
     64         m_textLength.value.newValueSpecifiedUnits(LengthTypeNumber, getComputedTextLength(), ec);
     65         ASSERT(!ec);
     66     }
     67 
     68     m_textLength.shouldSynchronize = true;
     69     return SVGAnimatedProperty::lookupOrCreateWrapper<SVGAnimatedLength, SVGLength>(this, SVGNames::textLengthAttr, SVGNames::textLengthAttr.localName(), m_textLength.value);
     70 }
     71 
     72 unsigned SVGTextContentElement::getNumberOfChars() const
     73 {
     74     document()->updateLayoutIgnorePendingStylesheets();
     75     return SVGTextQuery(renderer()).numberOfCharacters();
     76 }
     77 
     78 float SVGTextContentElement::getComputedTextLength() const
     79 {
     80     document()->updateLayoutIgnorePendingStylesheets();
     81     return SVGTextQuery(renderer()).textLength();
     82 }
     83 
     84 float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionCode& ec) const
     85 {
     86     document()->updateLayoutIgnorePendingStylesheets();
     87 
     88     unsigned numberOfChars = getNumberOfChars();
     89     if (charnum >= numberOfChars) {
     90         ec = INDEX_SIZE_ERR;
     91         return 0.0f;
     92     }
     93 
     94     return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
     95 }
     96 
     97 FloatPoint SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionCode& ec) const
     98 {
     99     document()->updateLayoutIgnorePendingStylesheets();
    100 
    101     if (charnum > getNumberOfChars()) {
    102         ec = INDEX_SIZE_ERR;
    103         return FloatPoint();
    104     }
    105 
    106     return SVGTextQuery(renderer()).startPositionOfCharacter(charnum);
    107 }
    108 
    109 FloatPoint SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionCode& ec) const
    110 {
    111     document()->updateLayoutIgnorePendingStylesheets();
    112 
    113     if (charnum > getNumberOfChars()) {
    114         ec = INDEX_SIZE_ERR;
    115         return FloatPoint();
    116     }
    117 
    118     return SVGTextQuery(renderer()).endPositionOfCharacter(charnum);
    119 }
    120 
    121 FloatRect SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionCode& ec) const
    122 {
    123     document()->updateLayoutIgnorePendingStylesheets();
    124 
    125     if (charnum > getNumberOfChars()) {
    126         ec = INDEX_SIZE_ERR;
    127         return FloatRect();
    128     }
    129 
    130     return SVGTextQuery(renderer()).extentOfCharacter(charnum);
    131 }
    132 
    133 float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionCode& ec) const
    134 {
    135     document()->updateLayoutIgnorePendingStylesheets();
    136 
    137     if (charnum > getNumberOfChars()) {
    138         ec = INDEX_SIZE_ERR;
    139         return 0.0f;
    140     }
    141 
    142     return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
    143 }
    144 
    145 int SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) const
    146 {
    147     document()->updateLayoutIgnorePendingStylesheets();
    148     return SVGTextQuery(renderer()).characterNumberAtPosition(point);
    149 }
    150 
    151 void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionCode& ec) const
    152 {
    153     unsigned numberOfChars = getNumberOfChars();
    154     if (charnum >= numberOfChars) {
    155         ec = INDEX_SIZE_ERR;
    156         return;
    157     }
    158 
    159     if (nchars > numberOfChars - charnum)
    160         nchars = numberOfChars - charnum;
    161 
    162     ASSERT(document());
    163     ASSERT(document()->frame());
    164 
    165     SelectionController* controller = document()->frame()->selection();
    166     if (!controller)
    167         return;
    168 
    169     // Find selection start
    170     VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this)));
    171     for (unsigned i = 0; i < charnum; ++i)
    172         start = start.next();
    173 
    174     // Find selection end
    175     VisiblePosition end(start);
    176     for (unsigned i = 0; i < nchars; ++i)
    177         end = end.next();
    178 
    179     controller->setSelection(VisibleSelection(start, end));
    180 }
    181 
    182 void SVGTextContentElement::parseMappedAttribute(Attribute* attr)
    183 {
    184     if (attr->name() == SVGNames::lengthAdjustAttr) {
    185         if (attr->value() == "spacing")
    186             setLengthAdjustBaseValue(LENGTHADJUST_SPACING);
    187         else if (attr->value() == "spacingAndGlyphs")
    188             setLengthAdjustBaseValue(LENGTHADJUST_SPACINGANDGLYPHS);
    189     } else if (attr->name() == SVGNames::textLengthAttr) {
    190         m_textLength.value = SVGLength(LengthModeOther, attr->value());
    191         if (m_textLength.value.value(this) < 0)
    192             document()->accessSVGExtensions()->reportError("A negative value for text attribute <textLength> is not allowed");
    193     } else {
    194         if (SVGTests::parseMappedAttribute(attr))
    195             return;
    196         if (SVGLangSpace::parseMappedAttribute(attr)) {
    197             if (attr->name().matches(XMLNames::spaceAttr)) {
    198                 DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve"));
    199 
    200                 if (attr->value() == preserveString)
    201                     addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValuePre);
    202                 else
    203                     addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValueNowrap);
    204             }
    205             return;
    206         }
    207         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
    208             return;
    209 
    210         SVGStyledElement::parseMappedAttribute(attr);
    211     }
    212 }
    213 
    214 void SVGTextContentElement::synchronizeProperty(const QualifiedName& attrName)
    215 {
    216     SVGStyledElement::synchronizeProperty(attrName);
    217 
    218     if (attrName == anyQName()) {
    219         synchronizeLengthAdjust();
    220         synchronizeTextLength();
    221         synchronizeExternalResourcesRequired();
    222         SVGTests::synchronizeProperties(this, attrName);
    223         return;
    224     }
    225 
    226     if (attrName == SVGNames::lengthAdjustAttr)
    227         synchronizeLengthAdjust();
    228     else if (attrName == SVGNames::textLengthAttr)
    229         synchronizeTextLength();
    230     else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
    231         synchronizeExternalResourcesRequired();
    232     else if (SVGTests::isKnownAttribute(attrName))
    233         SVGTests::synchronizeProperties(this, attrName);
    234 }
    235 
    236 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
    237 {
    238     SVGStyledElement::svgAttributeChanged(attrName);
    239 
    240     if (SVGTests::handleAttributeChange(this, attrName))
    241         return;
    242 
    243     if (attrName == SVGNames::textLengthAttr)
    244         m_specifiedTextLength = m_textLength.value;
    245 
    246     RenderObject* renderer = this->renderer();
    247     if (!renderer)
    248         return;
    249 
    250     if (attrName == SVGNames::textLengthAttr
    251         || attrName == SVGNames::lengthAdjustAttr)
    252         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    253 }
    254 
    255 void SVGTextContentElement::fillPassedAttributeToPropertyTypeMap(AttributeToPropertyTypeMap& attributeToPropertyTypeMap)
    256 {
    257     SVGStyledElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap);
    258 
    259     attributeToPropertyTypeMap.set(SVGNames::textLengthAttr, AnimatedLength);
    260     attributeToPropertyTypeMap.set(SVGNames::lengthAdjustAttr, AnimatedEnumeration);
    261 }
    262 
    263 bool SVGTextContentElement::selfHasRelativeLengths() const
    264 {
    265     // Any element of the <text> subtree is advertized as using relative lengths.
    266     // On any window size change, we have to relayout the text subtree, as the
    267     // effective 'on-screen' font size may change.
    268     return true;
    269 }
    270 
    271 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
    272 {
    273     if (!renderer)
    274         return 0;
    275 
    276     if (!renderer->isSVGText() && !renderer->isSVGInline())
    277         return 0;
    278 
    279     Node* node = renderer->node();
    280     ASSERT(node);
    281     ASSERT(node->isSVGElement());
    282 
    283     if (!node->hasTagName(SVGNames::textTag)
    284         && !node->hasTagName(SVGNames::tspanTag)
    285 #if ENABLE(SVG_FONTS)
    286         && !node->hasTagName(SVGNames::altGlyphTag)
    287 #endif
    288         && !node->hasTagName(SVGNames::trefTag)
    289         && !node->hasTagName(SVGNames::textPathTag))
    290         return 0;
    291 
    292     return static_cast<SVGTextContentElement*>(node);
    293 }
    294 
    295 }
    296 
    297 #endif // ENABLE(SVG)
    298