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 "bindings/v8/ExceptionMessages.h"
     25 #include "bindings/v8/ExceptionState.h"
     26 #include "bindings/v8/ExceptionStatePlaceholder.h"
     27 #include "core/CSSPropertyNames.h"
     28 #include "core/CSSValueKeywords.h"
     29 #include "core/SVGNames.h"
     30 #include "core/XMLNames.h"
     31 #include "core/editing/FrameSelection.h"
     32 #include "core/frame/LocalFrame.h"
     33 #include "core/rendering/RenderObject.h"
     34 #include "core/rendering/svg/RenderSVGResource.h"
     35 #include "core/rendering/svg/SVGTextQuery.h"
     36 
     37 namespace WebCore {
     38 
     39 template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGLengthAdjustType>()
     40 {
     41     DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ());
     42     if (entries.isEmpty()) {
     43         entries.append(std::make_pair(SVGLengthAdjustSpacing, "spacing"));
     44         entries.append(std::make_pair(SVGLengthAdjustSpacingAndGlyphs, "spacingAndGlyphs"));
     45     }
     46     return entries;
     47 }
     48 
     49 // SVGTextContentElement's 'textLength' attribute needs special handling.
     50 // It should return getComputedTextLength() when textLength is not specified manually.
     51 class SVGAnimatedTextLength FINAL : public SVGAnimatedLength {
     52 public:
     53     static PassRefPtr<SVGAnimatedTextLength> create(SVGTextContentElement* contextElement)
     54     {
     55         return adoptRef(new SVGAnimatedTextLength(contextElement));
     56     }
     57 
     58     virtual SVGLengthTearOff* baseVal() OVERRIDE
     59     {
     60         SVGTextContentElement* textContentElement = toSVGTextContentElement(contextElement());
     61         if (!textContentElement->textLengthIsSpecifiedByUser())
     62             baseValue()->newValueSpecifiedUnits(LengthTypeNumber, textContentElement->getComputedTextLength());
     63 
     64         return SVGAnimatedLength::baseVal();
     65     }
     66 
     67 private:
     68     SVGAnimatedTextLength(SVGTextContentElement* contextElement)
     69         : SVGAnimatedLength(contextElement, SVGNames::textLengthAttr, SVGLength::create(LengthModeOther), ForbidNegativeLengths)
     70     {
     71     }
     72 };
     73 
     74 
     75 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document& document)
     76     : SVGGraphicsElement(tagName, document)
     77     , m_textLength(SVGAnimatedTextLength::create(this))
     78     , m_textLengthIsSpecifiedByUser(false)
     79     , m_lengthAdjust(SVGAnimatedEnumeration<SVGLengthAdjustType>::create(this, SVGNames::lengthAdjustAttr, SVGLengthAdjustSpacing))
     80 {
     81     addToPropertyMap(m_textLength);
     82     addToPropertyMap(m_lengthAdjust);
     83 }
     84 
     85 unsigned SVGTextContentElement::getNumberOfChars()
     86 {
     87     document().updateLayoutIgnorePendingStylesheets();
     88     return SVGTextQuery(renderer()).numberOfCharacters();
     89 }
     90 
     91 float SVGTextContentElement::getComputedTextLength()
     92 {
     93     document().updateLayoutIgnorePendingStylesheets();
     94     return SVGTextQuery(renderer()).textLength();
     95 }
     96 
     97 float SVGTextContentElement::getSubStringLength(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
     98 {
     99     document().updateLayoutIgnorePendingStylesheets();
    100 
    101     unsigned numberOfChars = getNumberOfChars();
    102     if (charnum >= numberOfChars) {
    103         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
    104         return 0.0f;
    105     }
    106 
    107     if (nchars > numberOfChars - charnum)
    108         nchars = numberOfChars - charnum;
    109 
    110     return SVGTextQuery(renderer()).subStringLength(charnum, nchars);
    111 }
    112 
    113 PassRefPtr<SVGPointTearOff> SVGTextContentElement::getStartPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
    114 {
    115     document().updateLayoutIgnorePendingStylesheets();
    116 
    117     if (charnum > getNumberOfChars()) {
    118         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
    119         return nullptr;
    120     }
    121 
    122     FloatPoint point = SVGTextQuery(renderer()).startPositionOfCharacter(charnum);
    123     return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
    124 }
    125 
    126 PassRefPtr<SVGPointTearOff> SVGTextContentElement::getEndPositionOfChar(unsigned charnum, ExceptionState& exceptionState)
    127 {
    128     document().updateLayoutIgnorePendingStylesheets();
    129 
    130     if (charnum > getNumberOfChars()) {
    131         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
    132         return nullptr;
    133     }
    134 
    135     FloatPoint point = SVGTextQuery(renderer()).endPositionOfCharacter(charnum);
    136     return SVGPointTearOff::create(SVGPoint::create(point), 0, PropertyIsNotAnimVal);
    137 }
    138 
    139 PassRefPtr<SVGRectTearOff> SVGTextContentElement::getExtentOfChar(unsigned charnum, ExceptionState& exceptionState)
    140 {
    141     document().updateLayoutIgnorePendingStylesheets();
    142 
    143     if (charnum > getNumberOfChars()) {
    144         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
    145         return nullptr;
    146     }
    147 
    148     FloatRect rect = SVGTextQuery(renderer()).extentOfCharacter(charnum);
    149     return SVGRectTearOff::create(SVGRect::create(rect), 0, PropertyIsNotAnimVal);
    150 }
    151 
    152 float SVGTextContentElement::getRotationOfChar(unsigned charnum, ExceptionState& exceptionState)
    153 {
    154     document().updateLayoutIgnorePendingStylesheets();
    155 
    156     if (charnum > getNumberOfChars()) {
    157         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
    158         return 0.0f;
    159     }
    160 
    161     return SVGTextQuery(renderer()).rotationOfCharacter(charnum);
    162 }
    163 
    164 int SVGTextContentElement::getCharNumAtPosition(PassRefPtr<SVGPointTearOff> point, ExceptionState& exceptionState)
    165 {
    166     document().updateLayoutIgnorePendingStylesheets();
    167     return SVGTextQuery(renderer()).characterNumberAtPosition(point->target()->value());
    168 }
    169 
    170 void SVGTextContentElement::selectSubString(unsigned charnum, unsigned nchars, ExceptionState& exceptionState)
    171 {
    172     unsigned numberOfChars = getNumberOfChars();
    173     if (charnum >= numberOfChars) {
    174         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("charnum", charnum, getNumberOfChars()));
    175         return;
    176     }
    177 
    178     if (nchars > numberOfChars - charnum)
    179         nchars = numberOfChars - charnum;
    180 
    181     ASSERT(document().frame());
    182 
    183     // Find selection start
    184     VisiblePosition start(firstPositionInNode(const_cast<SVGTextContentElement*>(this)));
    185     for (unsigned i = 0; i < charnum; ++i)
    186         start = start.next();
    187 
    188     // Find selection end
    189     VisiblePosition end(start);
    190     for (unsigned i = 0; i < nchars; ++i)
    191         end = end.next();
    192 
    193     document().frame()->selection().setSelection(VisibleSelection(start, end));
    194 }
    195 
    196 bool SVGTextContentElement::isSupportedAttribute(const QualifiedName& attrName)
    197 {
    198     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
    199     if (supportedAttributes.isEmpty()) {
    200         supportedAttributes.add(SVGNames::lengthAdjustAttr);
    201         supportedAttributes.add(SVGNames::textLengthAttr);
    202         supportedAttributes.add(XMLNames::spaceAttr);
    203     }
    204     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
    205 }
    206 
    207 bool SVGTextContentElement::isPresentationAttribute(const QualifiedName& name) const
    208 {
    209     if (name.matches(XMLNames::spaceAttr))
    210         return true;
    211     return SVGGraphicsElement::isPresentationAttribute(name);
    212 }
    213 
    214 void SVGTextContentElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
    215 {
    216     if (!isSupportedAttribute(name))
    217         SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
    218     else if (name.matches(XMLNames::spaceAttr)) {
    219         DEFINE_STATIC_LOCAL(const AtomicString, preserveString, ("preserve", AtomicString::ConstructFromLiteral));
    220 
    221         if (value == preserveString)
    222             addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValuePre);
    223         else
    224             addPropertyToPresentationAttributeStyle(style, CSSPropertyWhiteSpace, CSSValueNowrap);
    225     }
    226 }
    227 
    228 void SVGTextContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    229 {
    230     SVGParsingError parseError = NoError;
    231 
    232     if (!isSupportedAttribute(name))
    233         SVGGraphicsElement::parseAttribute(name, value);
    234     else if (name == SVGNames::lengthAdjustAttr) {
    235         m_lengthAdjust->setBaseValueAsString(value, parseError);
    236     } else if (name == SVGNames::textLengthAttr) {
    237         m_textLength->setBaseValueAsString(value, parseError);
    238     } else if (name.matches(XMLNames::spaceAttr)) {
    239     } else
    240         ASSERT_NOT_REACHED();
    241 
    242     reportAttributeParsingError(parseError, name, value);
    243 }
    244 
    245 void SVGTextContentElement::svgAttributeChanged(const QualifiedName& attrName)
    246 {
    247     if (!isSupportedAttribute(attrName)) {
    248         SVGGraphicsElement::svgAttributeChanged(attrName);
    249         return;
    250     }
    251 
    252     if (attrName == SVGNames::textLengthAttr)
    253         m_textLengthIsSpecifiedByUser = true;
    254 
    255     SVGElement::InvalidationGuard invalidationGuard(this);
    256 
    257     if (RenderObject* renderer = this->renderer())
    258         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
    259 }
    260 
    261 bool SVGTextContentElement::selfHasRelativeLengths() const
    262 {
    263     // Any element of the <text> subtree is advertized as using relative lengths.
    264     // On any window size change, we have to relayout the text subtree, as the
    265     // effective 'on-screen' font size may change.
    266     return true;
    267 }
    268 
    269 SVGTextContentElement* SVGTextContentElement::elementFromRenderer(RenderObject* renderer)
    270 {
    271     if (!renderer)
    272         return 0;
    273 
    274     if (!renderer->isSVGText() && !renderer->isSVGInline())
    275         return 0;
    276 
    277     SVGElement* element = toSVGElement(renderer->node());
    278     ASSERT(element);
    279     return isSVGTextContentElement(*element) ? toSVGTextContentElement(element) : 0;
    280 }
    281 
    282 }
    283