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 #include "core/svg/SVGTextContentElement.h"
     23 
     24 #include "CSSPropertyNames.h"
     25 #include "CSSValueKeywords.h"
     26 #include "SVGNames.h"
     27 #include "XMLNames.h"
     28 #include "bindings/v8/ExceptionState.h"
     29 #include "bindings/v8/ExceptionStatePlaceholder.h"
     30 #include "core/editing/FrameSelection.h"
     31 #include "core/frame/Frame.h"
     32 #include "core/rendering/RenderObject.h"
     33 #include "core/rendering/svg/RenderSVGResource.h"
     34 #include "core/rendering/svg/SVGTextQuery.h"
     35 #include "core/svg/SVGElementInstance.h"
     36 
     37 namespace WebCore {
     38 
     39 // Define custom animated property 'textLength'.
     40 const SVGPropertyInfo* SVGTextContentElement::textLengthPropertyInfo()
     41 {
     42     static const SVGPropertyInfo* s_propertyInfo = 0;
     43     if (!s_propertyInfo) {
     44         s_propertyInfo = new SVGPropertyInfo(AnimatedLength,
     45                                              PropertyIsReadWrite,
     46                                              SVGNames::textLengthAttr,
     47                                              SVGNames::textLengthAttr.localName(),
     48                                              &SVGTextContentElement::synchronizeTextLength,
     49                                              &SVGTextContentElement::lookupOrCreateTextLengthWrapper);
     50     }
     51     return s_propertyInfo;
     52 }
     53 
     54 // Animated property definitions
     55 DEFINE_ANIMATED_ENUMERATION(SVGTextContentElement, SVGNames::lengthAdjustAttr, LengthAdjust, lengthAdjust, SVGLengthAdjustType)
     56 DEFINE_ANIMATED_BOOLEAN(SVGTextContentElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
     57 
     58 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGTextContentElement)
     59     REGISTER_LOCAL_ANIMATED_PROPERTY(textLength)
     60     REGISTER_LOCAL_ANIMATED_PROPERTY(lengthAdjust)
     61     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
     62     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
     63 END_REGISTER_ANIMATED_PROPERTIES
     64 
     65 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document& document)
     66     : SVGGraphicsElement(tagName, document)
     67     , m_textLength(LengthModeOther)
     68     , m_specifiedTextLength(LengthModeOther)
     69     , m_lengthAdjust(SVGLengthAdjustSpacing)
     70 {
     71     ScriptWrappable::init(this);
     72     registerAnimatedPropertiesForSVGTextContentElement();
     73 }
     74 
     75 void SVGTextContentElement::synchronizeTextLength(SVGElement* contextElement)
     76 {
     77     ASSERT(contextElement);
     78     SVGTextContentElement* ownerType = toSVGTextContentElement(contextElement);
     79     if (!ownerType->m_textLength.shouldSynchronize)
     80         return;
     81     AtomicString value(SVGPropertyTraits<SVGLength>::toString(ownerType->m_specifiedTextLength));
     82     ownerType->m_textLength.synchronize(ownerType, textLengthPropertyInfo()->attributeName, value);
     83 }
     84 
     85 PassRefPtr<SVGAnimatedProperty> SVGTextContentElement::lookupOrCreateTextLengthWrapper(SVGElement* contextElement)
     86 {
     87     ASSERT(contextElement);
     88     SVGTextContentElement* ownerType = toSVGTextContentElement(contextElement);
     89     return SVGAnimatedProperty::lookupOrCreateWrapper<SVGTextContentElement, SVGAnimatedLength, SVGLength>
     90            (ownerType, textLengthPropertyInfo(), ownerType->m_textLength.value);
     91 }
     92 
     93 PassRefPtr<SVGAnimatedLength> SVGTextContentElement::textLength()
     94 {
     95     DEFINE_STATIC_LOCAL(SVGLength, defaultTextLength, (LengthModeOther));
     96     if (m_specifiedTextLength == defaultTextLength)
     97         m_textLength.value.newValueSpecifiedUnits(LengthTypeNumber, getComputedTextLength(), ASSERT_NO_EXCEPTION);
     98 
     99     m_textLength.shouldSynchronize = true;
    100     return static_pointer_cast<SVGAnimatedLength>(lookupOrCreateTextLengthWrapper(this));
    101 
    102 }
    103 
    104 unsigned SVGTextContentElement::getNumberOfChars()
    105 {
    106     document().updateLayoutIgnorePendingStylesheets();
    107     return SVGTextQuery(renderer()).numberOfCharacters();
    108 }
    109 
    110 float SVGTextContentElement::getComputedTextLength()
    111 {
    112     document().updateLayoutIgnorePendingStylesheets();
    113     return SVGTextQuery(renderer()).textLength();
    114 }
    115 
    116 float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
    117 {
    118     document().updateLayoutIgnorePendingStylesheets();
    119 
    120     unsigned numberOfChars = getNumberOfChars();
    121     if (charnum >= numberOfChars) {
    122         exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
    123         return 0.0f;
    124     }
    125 
    126     if (nchars > numberOfChars - charnum)
    127         nchars = numberOfChars - charnum;
    128 
    129     return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
    130 }
    131 
    132 SVGPoint SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
    133 {
    134     document().updateLayoutIgnorePendingStylesheets();
    135 
    136     if (charnum > getNumberOfChars()) {
    137         exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
    138         return FloatPoint();
    139     }
    140 
    141     return SVGTextQuery(renderer()).startPositionOfCharacter(charnum);
    142 }
    143 
    144 SVGPoint SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
    145 {
    146     document().updateLayoutIgnorePendingStylesheets();
    147 
    148     if (charnum > getNumberOfChars()) {
    149         exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
    150         return FloatPoint();
    151     }
    152 
    153     return SVGTextQuery(renderer()).endPositionOfCharacter(charnum);
    154 }
    155 
    156 SVGRect SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionState& exceptionState)
    157 {
    158     document().updateLayoutIgnorePendingStylesheets();
    159 
    160     if (charnum > getNumberOfChars()) {
    161         exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
    162         return SVGRect();
    163     }
    164 
    165     return SVGTextQuery(renderer()).extentOfCharacter(charnum);
    166 }
    167 
    168 float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionState& exceptionState)
    169 {
    170     document().updateLayoutIgnorePendingStylesheets();
    171 
    172     if (charnum > getNumberOfChars()) {
    173         exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
    174         return 0.0f;
    175     }
    176 
    177     return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
    178 }
    179 
    180 int SVGTextContentElement::getCharNumAtPosition(const SVGPoint& point)
    181 {
    182     document().updateLayoutIgnorePendingStylesheets();
    183     return SVGTextQuery(renderer()).characterNumberAtPosition(point);
    184 }
    185 
    186 void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
    187 {
    188     unsigned numberOfChars = getNumberOfChars();
    189     if (charnum >= numberOfChars) {
    190         exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
    191         return;
    192     }
    193 
    194     if (nchars > numberOfChars - charnum)
    195         nchars = numberOfChars - charnum;
    196 
    197     ASSERT(document().frame());
    198 
    199     // Find selection start
    200     VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this)));
    201     for (unsigned i = 0; i < charnum; ++i)
    202         start = start.next();
    203 
    204     // Find selection end
    205     VisiblePosition end(start);
    206     for (unsigned i = 0; i < nchars; ++i)
    207         end = end.next();
    208 
    209     document().frame()->selection().setSelection(VisibleSelection(start, end));
    210 }
    211 
    212 bool SVGTextContentElement::isSupportedAttribute(const QualifiedName& attrName)
    213 {
    214     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
    215     if (supportedAttributes.isEmpty()) {
    216         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
    217         supportedAttributes.add(SVGNames::lengthAdjustAttr);
    218         supportedAttributes.add(SVGNames::textLengthAttr);
    219         supportedAttributes.add(XMLNames::spaceAttr);
    220     }
    221     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
    222 }
    223 
    224 bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const
    225 {
    226     if (name.matches(XMLNames::spaceAttr))
    227         return true;
    228     return SVGGraphicsElement::isPresentationAttribute(name);
    229 }
    230 
    231 void SVGTextContentElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
    232 {
    233     if (!isSupportedAttribute(name))
    234         SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
    235     else if (name.matches(XMLNames::spaceAttr)) {
    236         DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve", AtomicString::ConstructFromLiteral));
    237 
    238         if (value == preserveString)
    239             addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValuePre);
    240         else
    241             addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValueNowrap);
    242     }
    243 }
    244 
    245 void SVGTextContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    246 {
    247     SVGParsingError parseError = NoError;
    248 
    249     if (!isSupportedAttribute(name))
    250         SVGGraphicsElement::parseAttribute(name, value);
    251     else if (name == SVGNames::lengthAdjustAttr) {
    252         SVGLengthAdjustType propertyValue = SVGPropertyTraits<SVGLengthAdjustType>::fromString(value);
    253         if (propertyValue > 0)
    254             setLengthAdjustBaseValue(propertyValue);
    255     } else if (name == SVGNames::textLengthAttr) {
    256         m_textLength.value = SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths);
    257     } else if (SVGExternalResourcesRequired::parseAttribute(name, value)) {
    258     } else if (name.matches(XMLNames::spaceAttr)) {
    259     } else
    260         ASSERT_NOT_REACHED();
    261 
    262     reportAttributeParsingError(parseError, name, value);
    263 }
    264 
    265 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
    266 {
    267     if (!isSupportedAttribute(attrName)) {
    268         SVGGraphicsElement::svgAttributeChanged(attrName);
    269         return;
    270     }
    271 
    272     SVGElementInstance::InvalidationGuard invalidationGuard(this);
    273 
    274     if (attrName == SVGNames::textLengthAttr)
    275         m_specifiedTextLength = m_textLength.value;
    276 
    277     if (RenderObject* renderer = this->renderer())
    278         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    279 }
    280 
    281 bool SVGTextContentElement::selfHasRelativeLengths() const
    282 {
    283     // Any element of the <text> subtree is advertized as using relative lengths.
    284     // On any window size change, we have to relayout the text subtree, as the
    285     // effective 'on-screen' font size may change.
    286     return true;
    287 }
    288 
    289 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
    290 {
    291     if (!renderer)
    292         return 0;
    293 
    294     if (!renderer->isSVGText() && !renderer->isSVGInline())
    295         return 0;
    296 
    297     SVGElement* element = toSVGElement(renderer->node());
    298     ASSERT(element);
    299 
    300     if (!element->isTextContent())
    301         return 0;
    302 
    303     return toSVGTextContentElement(element);
    304 }
    305 
    306 }
    307