• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KHTML

SVGTextContentElement.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
00003                   2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "config.h"
00022 #include "wtf/Platform.h"
00023 
00024 #if ENABLE(SVG)
00025 #include "SVGTextContentElement.h"
00026 
00027 /*#include "CSSPropertyNames.h"
00028 #include "CSSValueKeywords.h"*/
00029 #include "ExceptionCode.h"
00030 #include "FloatPoint.h"
00031 #include "FloatRect.h"
00032 /*#include "Frame.h"
00033 #include "Position.h"*/
00034 #include "RenderSVGText.h"
00035 /*#include "SelectionController.h"*/
00036 #include "SVGCharacterLayoutInfo.h"
00037 #include "SVGRootInlineBox.h"
00038 #include "SVGLength.h"
00039 #include "SVGInlineTextBox.h"
00040 #include "SVGNames.h"
00041 //#include "XMLNames.h"
00042 
00043 namespace WebCore {
00044 
00045 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document* doc)
00046     : SVGStyledElement(tagName, doc)
00047     , SVGTests()
00048     , SVGLangSpace()
00049     , SVGExternalResourcesRequired()
00050     , m_textLength(this, LengthModeOther)
00051     , m_lengthAdjust(LENGTHADJUST_SPACING)
00052 {
00053 }
00054 
00055 SVGTextContentElement::~SVGTextContentElement()
00056 {
00057 }
00058 
00059 ANIMATED_PROPERTY_DEFINITIONS(SVGTextContentElement, SVGLength, Length, length, TextLength, textLength, SVGNames::textLengthAttr, m_textLength)
00060 ANIMATED_PROPERTY_DEFINITIONS(SVGTextContentElement, int, Enumeration, enumeration, LengthAdjust, lengthAdjust, SVGNames::lengthAdjustAttr, m_lengthAdjust)
00061 
00062 static inline float cumulativeCharacterRangeLength(const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end, SVGInlineTextBox* textBox,
00063                                                    int startOffset, long startPosition, long length, bool isVerticalText, long& atCharacter)
00064 {
00065     if (!length)
00066         return 0.0f;
00067 
00068     float textLength = 0.0f;
00069     RenderStyle* style = textBox->renderText()->style();
00070 
00071     bool usesFullRange = (startPosition == -1 && length == -1);
00072 
00073     for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
00074         if (usesFullRange || (atCharacter >= startPosition && atCharacter <= startPosition + length)) {
00075             unsigned int newOffset = textBox->start() + (it - start) + startOffset;
00076 
00077             // Take RTL text into account and pick right glyph width/height.
00078             /*FIXME khtml if (textBox->direction() == RTL)
00079                 newOffset = textBox->start() + textBox->end() - newOffset;*/
00080 
00081             // FIXME: does this handle multichar glyphs ok? not sure
00082             int charsConsumed = 0;
00083             String glyphName;
00084             if (isVerticalText)
00085                 textLength += textBox->calculateGlyphHeight(style, newOffset, 0);
00086             else
00087                 textLength += textBox->calculateGlyphWidth(style, newOffset, 0, charsConsumed, glyphName);
00088         }
00089 
00090         if (!usesFullRange) {
00091             if (atCharacter == startPosition + length - 1)
00092                 break;
00093 
00094             atCharacter++;
00095         }
00096     }
00097 
00098     return textLength;
00099 }
00100 
00101 // Helper class for querying certain glyph information
00102 struct SVGInlineTextBoxQueryWalker {
00103     typedef enum {
00104         NumberOfCharacters,
00105         TextLength,
00106         SubStringLength,
00107         StartPosition,
00108         EndPosition,
00109         Extent,
00110         Rotation,
00111         CharacterNumberAtPosition
00112     } QueryMode;
00113 
00114     SVGInlineTextBoxQueryWalker(const SVGTextContentElement* reference, QueryMode mode)
00115         : m_reference(reference)
00116         , m_mode(mode)
00117         , m_queryStartPosition(0)
00118         , m_queryLength(0)
00119         , m_queryPointInput()
00120         , m_queryLongResult(0)
00121         , m_queryFloatResult(0.0f)
00122         , m_queryPointResult()
00123         , m_queryRectResult()
00124         , m_stopProcessing(true)    
00125         , m_atCharacter(0)
00126     {
00127     }
00128 
00129     void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm,
00130                               const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end)
00131     {
00132         RenderStyle* style = textBox->renderText()->style();
00133         bool isVerticalText = style->svgStyle()->writingMode() == WM_TBRL || style->svgStyle()->writingMode() == WM_TB;
00134 
00135         switch (m_mode) {
00136         case NumberOfCharacters:
00137         {    
00138             m_queryLongResult += (end - start);
00139             m_stopProcessing = false;
00140             return;
00141         }
00142         case TextLength:
00143         {
00144             float textLength = cumulativeCharacterRangeLength(start, end, textBox, startOffset, -1, -1, isVerticalText, m_atCharacter);
00145 
00146             if (isVerticalText)
00147                 m_queryFloatResult += textLength;
00148             else
00149                 m_queryFloatResult += textLength;
00150 
00151             m_stopProcessing = false;
00152             return;
00153         }
00154         case SubStringLength:
00155         {
00156             long startPosition = m_queryStartPosition;
00157             long length = m_queryLength;
00158 
00159             float textLength = cumulativeCharacterRangeLength(start, end, textBox, startOffset, startPosition, length, isVerticalText, m_atCharacter);
00160 
00161             if (isVerticalText)
00162                 m_queryFloatResult += textLength;
00163             else
00164                 m_queryFloatResult += textLength;
00165 
00166             if (m_atCharacter == startPosition + length)
00167                 m_stopProcessing = true;
00168             else
00169                 m_stopProcessing = false;
00170 
00171             return;
00172         }
00173         case StartPosition:
00174         {
00175             for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
00176                 if (m_atCharacter == m_queryStartPosition) {
00177                     m_queryPointResult = FloatPoint(it->x, it->y);
00178                     m_stopProcessing = true;
00179                     return;
00180                 }
00181 
00182                 m_atCharacter++;
00183             }
00184 
00185             m_stopProcessing = false;
00186             return;
00187         }
00188         case EndPosition:
00189         {
00190             for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
00191                 if (m_atCharacter == m_queryStartPosition) {
00192                     unsigned int newOffset = textBox->start() + (it - start) + startOffset;
00193 
00194                     // Take RTL text into account and pick right glyph width/height.
00195                     /*FIXME khtml if (textBox->direction() == RTL)
00196                         newOffset = textBox->start() + textBox->end() - newOffset;*/
00197 
00198                     int charsConsumed;
00199                     String glyphName;
00200                     if (isVerticalText)
00201                         m_queryPointResult.move(it->x, it->y + textBox->calculateGlyphHeight(style, newOffset, end - it));
00202                     else
00203                         m_queryPointResult.move(it->x + textBox->calculateGlyphWidth(style, newOffset, end - it, charsConsumed, glyphName), it->y);
00204 
00205                     m_stopProcessing = true;
00206                     return;
00207                 }
00208 
00209                 m_atCharacter++;
00210             }
00211 
00212             m_stopProcessing = false;
00213             return;
00214         }
00215         case Extent:
00216         {
00217             for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
00218                 if (m_atCharacter == m_queryStartPosition) {
00219                     unsigned int newOffset = textBox->start() + (it - start) + startOffset;
00220                     m_queryRectResult = textBox->calculateGlyphBoundaries(style, newOffset, *it);
00221                     m_stopProcessing = true;
00222                     return;
00223                 }
00224 
00225                 m_atCharacter++;
00226             }
00227 
00228             m_stopProcessing = false;
00229             return;
00230         }
00231         case Rotation:
00232         {
00233             for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
00234                 if (m_atCharacter == m_queryStartPosition) {
00235                     m_queryFloatResult = it->angle;
00236                     m_stopProcessing = true;
00237                     return;
00238                 }
00239 
00240                 m_atCharacter++;
00241             }
00242 
00243             m_stopProcessing = false;
00244             return;
00245         }
00246         case CharacterNumberAtPosition:
00247         {
00248             int offset = 0;
00249             SVGChar* charAtPos = textBox->closestCharacterToPosition(m_queryPointInput.x(), m_queryPointInput.y(), offset);
00250 
00251             offset += m_atCharacter;
00252             if (charAtPos && offset > m_queryLongResult)
00253                 m_queryLongResult = offset;
00254 
00255             m_atCharacter += end - start;
00256             m_stopProcessing = false;
00257             return;
00258         }
00259         default:
00260             ASSERT_NOT_REACHED();
00261             m_stopProcessing = true;
00262             return;
00263         }
00264     }
00265 
00266     void setQueryInputParameters(long startPosition, long length, FloatPoint referencePoint)
00267     {
00268         m_queryStartPosition = startPosition;
00269         m_queryLength = length;
00270         m_queryPointInput = referencePoint;
00271     }
00272 
00273     long longResult() const { return m_queryLongResult; }
00274     float floatResult() const { return m_queryFloatResult; }
00275     FloatPoint pointResult() const { return m_queryPointResult; }
00276     FloatRect rectResult() const { return m_queryRectResult; }
00277     bool stopProcessing() const { return m_stopProcessing; }
00278 
00279 private:
00280     const SVGTextContentElement* m_reference;
00281     QueryMode m_mode;
00282 
00283     long m_queryStartPosition;
00284     long m_queryLength;
00285     FloatPoint m_queryPointInput;
00286 
00287     long m_queryLongResult;
00288     float m_queryFloatResult;
00289     FloatPoint m_queryPointResult;
00290     FloatRect m_queryRectResult;
00291 
00292     bool m_stopProcessing;
00293     long m_atCharacter;
00294 };
00295 
00296 static Vector<SVGInlineTextBox*> findInlineTextBoxInTextChunks(const SVGTextContentElement* element, const Vector<SVGTextChunk>& chunks)
00297 {   
00298     Vector<SVGTextChunk>::const_iterator it = chunks.begin();
00299     const Vector<SVGTextChunk>::const_iterator end = chunks.end();
00300 
00301     Vector<SVGInlineTextBox*> boxes;
00302 
00303     for (; it != end; ++it) {
00304         Vector<SVGInlineBoxCharacterRange>::const_iterator boxIt = it->boxes.begin();
00305         const Vector<SVGInlineBoxCharacterRange>::const_iterator boxEnd = it->boxes.end();
00306 
00307         for (; boxIt != boxEnd; ++boxIt) {
00308             SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(boxIt->box);
00309 
00310             Node* textElement = textBox->renderText()->parent()->element();
00311             ASSERT(textElement);
00312 
00313             if (textElement == element || textElement->parent() == element)
00314                 boxes.append(textBox);
00315         }
00316     }
00317 
00318     return boxes;
00319 }
00320 
00321 static inline SVGRootInlineBox* rootInlineBoxForTextContentElement(const SVGTextContentElement* element)
00322 {
00323     RenderObject* object = element->renderer();
00324     
00325     if (!object || !object->isSVGText() || object->isText())
00326         return 0;
00327 
00328     RenderSVGText* svgText = static_cast<RenderSVGText*>(object);
00329 
00330     // Find root inline box
00331     SVGRootInlineBox* rootBox = static_cast<SVGRootInlineBox*>(svgText->firstRootBox());
00332     if (!rootBox) {
00333         // Layout is not sync yet!
00334         /*FIXME khtml element->document()->updateLayoutIgnorePendingStylesheets();*/
00335         rootBox = static_cast<SVGRootInlineBox*>(svgText->firstRootBox());
00336     }
00337 
00338     ASSERT(rootBox);
00339     return rootBox;
00340 }
00341 
00342 static inline SVGInlineTextBoxQueryWalker executeTextQuery(const SVGTextContentElement* element, SVGInlineTextBoxQueryWalker::QueryMode mode,
00343                                                            long startPosition = 0, long length = 0, FloatPoint referencePoint = FloatPoint())
00344 {
00345     SVGRootInlineBox* rootBox = rootInlineBoxForTextContentElement(element);
00346     if (!rootBox)
00347         return SVGInlineTextBoxQueryWalker(0, mode);
00348 
00349     // Find all inline text box associated with our renderer
00350     Vector<SVGInlineTextBox*> textBoxes = findInlineTextBoxInTextChunks(element, rootBox->svgTextChunks());
00351 
00352     // Walk text chunks to find chunks associated with our inline text box
00353     SVGInlineTextBoxQueryWalker walkerCallback(element, mode);
00354     walkerCallback.setQueryInputParameters(startPosition, length, referencePoint);
00355 
00356     SVGTextChunkWalker<SVGInlineTextBoxQueryWalker> walker(&walkerCallback, &SVGInlineTextBoxQueryWalker::chunkPortionCallback);
00357 
00358     Vector<SVGInlineTextBox*>::iterator it = textBoxes.begin();
00359     Vector<SVGInlineTextBox*>::iterator end = textBoxes.end();
00360 
00361     for (; it != end; ++it) {
00362         rootBox->walkTextChunks(&walker, *it);
00363 
00364         if (walkerCallback.stopProcessing())
00365             break;
00366     }
00367 
00368     return walkerCallback;
00369 }
00370 
00371 long SVGTextContentElement::getNumberOfChars() const
00372 {
00373     return executeTextQuery(this, SVGInlineTextBoxQueryWalker::NumberOfCharacters).longResult();
00374 }
00375 
00376 float SVGTextContentElement::getComputedTextLength() const
00377 {
00378     return executeTextQuery(this, SVGInlineTextBoxQueryWalker::TextLength).floatResult();
00379 }
00380 
00381 float SVGTextContentElement::getSubStringLength(long charnum, long nchars, ExceptionCode& ec) const
00382 {
00383     // Differences to SVG 1.1 spec, as the spec is clearly wrong. TODO: Raise SVG WG issue!
00384     // #1: We accept a 'long nchars' parameter instead of 'unsigned long nchars' to be able
00385     //     to catch cases where someone called us with a negative 'nchars' value - in those
00386     //     cases we'll just throw a 'INDEX_SIZE_ERR' (acid3 implicitly agrees with us)
00387     //
00388     // #2: We only throw if 'charnum + nchars' is greater than the number of characters, not
00389     //     if it's equal, as this really doesn't make any sense (no way to measure the last character!)
00390     //
00391     // #3: If 'charnum' is greater than or equal to 'numberOfChars', we're throwing an exception here
00392     //     as the result is undefined for every other value of 'nchars' than '0'.
00393 
00394     long numberOfChars = getNumberOfChars();
00395     if (charnum < 0 || nchars < 0 || numberOfChars <= charnum || charnum + nchars > numberOfChars) {
00396         ec = DOMException::INDEX_SIZE_ERR;
00397         return 0.0f;
00398     }
00399 
00400     return executeTextQuery(this, SVGInlineTextBoxQueryWalker::SubStringLength, charnum, nchars).floatResult();
00401 }
00402 
00403 FloatPoint SVGTextContentElement::getStartPositionOfChar(long charnum, ExceptionCode& ec) const
00404 {
00405     if (charnum < 0 || charnum > getNumberOfChars()) {
00406         ec = DOMException::INDEX_SIZE_ERR;
00407         return FloatPoint();
00408     }
00409 
00410     return executeTextQuery(this, SVGInlineTextBoxQueryWalker::StartPosition, charnum).pointResult();
00411 }
00412 
00413 FloatPoint SVGTextContentElement::getEndPositionOfChar(long charnum, ExceptionCode& ec) const
00414 {
00415     if (charnum < 0 || charnum > getNumberOfChars()) {
00416         ec = DOMException::INDEX_SIZE_ERR;
00417         return FloatPoint();
00418     }
00419 
00420     return executeTextQuery(this, SVGInlineTextBoxQueryWalker::EndPosition, charnum).pointResult();
00421 }
00422 
00423 FloatRect SVGTextContentElement::getExtentOfChar(long charnum, ExceptionCode& ec) const
00424 {
00425     if (charnum < 0 || charnum > getNumberOfChars()) {
00426         ec = DOMException::INDEX_SIZE_ERR;
00427         return FloatRect();
00428     }
00429 
00430     return executeTextQuery(this, SVGInlineTextBoxQueryWalker::Extent, charnum).rectResult();
00431 }
00432 
00433 float SVGTextContentElement::getRotationOfChar(long charnum, ExceptionCode& ec) const
00434 {
00435     if (charnum < 0 || charnum > getNumberOfChars()) {
00436         ec = DOMException::INDEX_SIZE_ERR;
00437         return 0.0f;
00438     }
00439 
00440     return executeTextQuery(this, SVGInlineTextBoxQueryWalker::Rotation, charnum).floatResult();
00441 }
00442 
00443 long SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) const
00444 {
00445     return executeTextQuery(this, SVGInlineTextBoxQueryWalker::CharacterNumberAtPosition, 0.0f, 0.0f, point).longResult();
00446 }
00447 
00448 void SVGTextContentElement::selectSubString(long charnum, long nchars, ExceptionCode& ec) const
00449 {
00450     long numberOfChars = getNumberOfChars();
00451     if (charnum < 0 || nchars < 0 || charnum > numberOfChars) {
00452         ec = DOMException::INDEX_SIZE_ERR;
00453         return;
00454     }
00455 
00456     if (nchars > numberOfChars - charnum)
00457         nchars = numberOfChars - charnum;
00458 
00459     ASSERT(document());
00460     //khtml ASSERT(document()->frame());
00461 
00462     /*FIXME SelectionController* controller = document()->frame()->selectionController();
00463     if (!controller)
00464         return;
00465 
00466     // Find selection start
00467     VisiblePosition start(const_cast<SVGTextContentElement*>(this), 0, SEL_DEFAULT_AFFINITY);
00468     for (long i = 0; i < charnum; ++i)
00469         start = start.next();
00470 
00471     // Find selection end
00472     VisiblePosition end(start);
00473     for (long i = 0; i < nchars; ++i)
00474         end = end.next();
00475 
00476     controller->setSelection(Selection(start, end));*/
00477 }
00478 
00479 void SVGTextContentElement::parseMappedAttribute(MappedAttribute* attr)
00480 {
00481     if (attr->name() == SVGNames::lengthAdjustAttr) {
00482         if (attr->value() == "spacing")
00483             setLengthAdjustBaseValue(LENGTHADJUST_SPACING);
00484         else if (attr->value() == "spacingAndGlyphs")
00485             setLengthAdjustBaseValue(LENGTHADJUST_SPACINGANDGLYPHS);
00486     } else if (attr->name() == SVGNames::textLengthAttr) {
00487         setTextLengthBaseValue(SVGLength(this, LengthModeOther, attr->value()));
00488         if (textLength().value() < 0.0)
00489             document()->accessSVGExtensions()->reportError("A negative value for text attribute <textLength> is not allowed");
00490     } else {
00491         if (SVGTests::parseMappedAttribute(attr))
00492             return;
00493         if (SVGLangSpace::parseMappedAttribute(attr)) {
00494             /*if (attr->name().matches(XMLNames::spaceAttr)) {
00495                 static const AtomicString preserveString("preserve");
00496 
00497                 if (attr->value() == preserveString)
00498                     addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValuePre);
00499                 else
00500                     addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValueNowrap);
00501             }*/
00502             return;
00503         }
00504         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
00505             return;
00506 
00507         SVGStyledElement::parseMappedAttribute(attr);
00508     }
00509 }
00510 
00511 bool SVGTextContentElement::isKnownAttribute(const QualifiedName& attrName)
00512 {
00513     return (attrName.matches(SVGNames::lengthAdjustAttr) ||
00514             attrName.matches(SVGNames::textLengthAttr) ||
00515             SVGTests::isKnownAttribute(attrName) ||
00516             SVGLangSpace::isKnownAttribute(attrName) ||
00517             SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
00518             SVGStyledElement::isKnownAttribute(attrName));
00519 }
00520 
00521 }
00522 
00523 #endif // ENABLE(SVG)

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal