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

KHTML

  • sources
  • kde-4.12
  • kdelibs
  • khtml
  • svg
SVGTextContentElement.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  2004, 2005, 2006, 2007, 2008 Rob Buis <buis@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 "wtf/Platform.h"
23 
24 #if ENABLE(SVG)
25 #include "SVGTextContentElement.h"
26 
27 #include "cssvalues.h"
28 
29 /*#include "CSSPropertyNames.h"
30 #include "CSSValueKeywords.h"*/
31 #include "ExceptionCode.h"
32 #include "FloatPoint.h"
33 #include "FloatRect.h"
34 /*#include "Frame.h"
35 #include "Position.h"*/
36 #include "RenderSVGText.h"
37 /*#include "SelectionController.h"*/
38 #include "SVGCharacterLayoutInfo.h"
39 #include "SVGRootInlineBox.h"
40 #include "SVGLength.h"
41 #include "SVGInlineTextBox.h"
42 #include "SVGNames.h"
43 
44 namespace WebCore {
45 
46 SVGTextContentElement::SVGTextContentElement(const QualifiedName& tagName, Document* doc)
47  : SVGStyledElement(tagName, doc)
48  , SVGTests()
49  , SVGLangSpace()
50  , SVGExternalResourcesRequired()
51  , m_textLength(this, LengthModeOther)
52  , m_lengthAdjust(LENGTHADJUST_SPACING)
53 {
54 }
55 
56 SVGTextContentElement::~SVGTextContentElement()
57 {
58 }
59 
60 ANIMATED_PROPERTY_DEFINITIONS(SVGTextContentElement, SVGLength, Length, length, TextLength, textLength, SVGNames::textLengthAttr, m_textLength)
61 ANIMATED_PROPERTY_DEFINITIONS(SVGTextContentElement, int, Enumeration, enumeration, LengthAdjust, lengthAdjust, SVGNames::lengthAdjustAttr, m_lengthAdjust)
62 
63 static inline float cumulativeCharacterRangeLength(const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end, SVGInlineTextBox* textBox,
64  int startOffset, long startPosition, long length, bool isVerticalText, long& atCharacter)
65 {
66  if (!length)
67  return 0.0f;
68 
69  float textLength = 0.0f;
70  RenderStyle* style = textBox->renderText()->style();
71 
72  bool usesFullRange = (startPosition == -1 && length == -1);
73 
74  for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
75  if (usesFullRange || (atCharacter >= startPosition && atCharacter <= startPosition + length)) {
76  unsigned int newOffset = textBox->start() + (it - start) + startOffset;
77 
78  // Take RTL text into account and pick right glyph width/height.
79  /*FIXME khtml if (textBox->direction() == RTL)
80  newOffset = textBox->start() + textBox->end() - newOffset;*/
81 
82  // FIXME: does this handle multichar glyphs ok? not sure
83  int charsConsumed = 0;
84  String glyphName;
85  if (isVerticalText)
86  textLength += textBox->calculateGlyphHeight(style, newOffset, 0);
87  else
88  textLength += textBox->calculateGlyphWidth(style, newOffset, 0, charsConsumed, glyphName);
89  }
90 
91  if (!usesFullRange) {
92  if (atCharacter == startPosition + length - 1)
93  break;
94 
95  atCharacter++;
96  }
97  }
98 
99  return textLength;
100 }
101 
102 // Helper class for querying certain glyph information
103 struct SVGInlineTextBoxQueryWalker {
104  typedef enum {
105  NumberOfCharacters,
106  TextLength,
107  SubStringLength,
108  StartPosition,
109  EndPosition,
110  Extent,
111  Rotation,
112  CharacterNumberAtPosition
113  } QueryMode;
114 
115  SVGInlineTextBoxQueryWalker(const SVGTextContentElement* reference, QueryMode mode)
116  : m_reference(reference)
117  , m_mode(mode)
118  , m_queryStartPosition(0)
119  , m_queryLength(0)
120  , m_queryPointInput()
121  , m_queryLongResult(0)
122  , m_queryFloatResult(0.0f)
123  , m_queryPointResult()
124  , m_queryRectResult()
125  , m_stopProcessing(true)
126  , m_atCharacter(0)
127  {
128  }
129 
130  void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm,
131  const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end)
132  {
133  Q_UNUSED(chunkCtm);
134  RenderStyle* style = textBox->renderText()->style();
135  bool isVerticalText = style->svgStyle()->writingMode() == WM_TBRL || style->svgStyle()->writingMode() == WM_TB;
136 
137  switch (m_mode) {
138  case NumberOfCharacters:
139  {
140  m_queryLongResult += (end - start);
141  m_stopProcessing = false;
142  return;
143  }
144  case TextLength:
145  {
146  float textLength = cumulativeCharacterRangeLength(start, end, textBox, startOffset, -1, -1, isVerticalText, m_atCharacter);
147 
148  if (isVerticalText)
149  m_queryFloatResult += textLength;
150  else
151  m_queryFloatResult += textLength;
152 
153  m_stopProcessing = false;
154  return;
155  }
156  case SubStringLength:
157  {
158  long startPosition = m_queryStartPosition;
159  long length = m_queryLength;
160 
161  float textLength = cumulativeCharacterRangeLength(start, end, textBox, startOffset, startPosition, length, isVerticalText, m_atCharacter);
162 
163  if (isVerticalText)
164  m_queryFloatResult += textLength;
165  else
166  m_queryFloatResult += textLength;
167 
168  if (m_atCharacter == startPosition + length)
169  m_stopProcessing = true;
170  else
171  m_stopProcessing = false;
172 
173  return;
174  }
175  case StartPosition:
176  {
177  for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
178  if (m_atCharacter == m_queryStartPosition) {
179  m_queryPointResult = FloatPoint(it->x, it->y);
180  m_stopProcessing = true;
181  return;
182  }
183 
184  m_atCharacter++;
185  }
186 
187  m_stopProcessing = false;
188  return;
189  }
190  case EndPosition:
191  {
192  for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
193  if (m_atCharacter == m_queryStartPosition) {
194  unsigned int newOffset = textBox->start() + (it - start) + startOffset;
195 
196  // Take RTL text into account and pick right glyph width/height.
197  /*FIXME khtml if (textBox->direction() == RTL)
198  newOffset = textBox->start() + textBox->end() - newOffset;*/
199 
200  int charsConsumed;
201  String glyphName;
202  if (isVerticalText)
203  m_queryPointResult.move(it->x, it->y + textBox->calculateGlyphHeight(style, newOffset, end - it));
204  else
205  m_queryPointResult.move(it->x + textBox->calculateGlyphWidth(style, newOffset, end - it, charsConsumed, glyphName), it->y);
206 
207  m_stopProcessing = true;
208  return;
209  }
210 
211  m_atCharacter++;
212  }
213 
214  m_stopProcessing = false;
215  return;
216  }
217  case Extent:
218  {
219  for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
220  if (m_atCharacter == m_queryStartPosition) {
221  unsigned int newOffset = textBox->start() + (it - start) + startOffset;
222  m_queryRectResult = textBox->calculateGlyphBoundaries(style, newOffset, *it);
223  m_stopProcessing = true;
224  return;
225  }
226 
227  m_atCharacter++;
228  }
229 
230  m_stopProcessing = false;
231  return;
232  }
233  case Rotation:
234  {
235  for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
236  if (m_atCharacter == m_queryStartPosition) {
237  m_queryFloatResult = it->angle;
238  m_stopProcessing = true;
239  return;
240  }
241 
242  m_atCharacter++;
243  }
244 
245  m_stopProcessing = false;
246  return;
247  }
248  case CharacterNumberAtPosition:
249  {
250  int offset = 0;
251  SVGChar* charAtPos = textBox->closestCharacterToPosition(m_queryPointInput.x(), m_queryPointInput.y(), offset);
252 
253  offset += m_atCharacter;
254  if (charAtPos && offset > m_queryLongResult)
255  m_queryLongResult = offset;
256 
257  m_atCharacter += end - start;
258  m_stopProcessing = false;
259  return;
260  }
261  default:
262  ASSERT_NOT_REACHED();
263  m_stopProcessing = true;
264  return;
265  }
266  }
267 
268  void setQueryInputParameters(long startPosition, long length, FloatPoint referencePoint)
269  {
270  m_queryStartPosition = startPosition;
271  m_queryLength = length;
272  m_queryPointInput = referencePoint;
273  }
274 
275  long longResult() const { return m_queryLongResult; }
276  float floatResult() const { return m_queryFloatResult; }
277  FloatPoint pointResult() const { return m_queryPointResult; }
278  FloatRect rectResult() const { return m_queryRectResult; }
279  bool stopProcessing() const { return m_stopProcessing; }
280 
281 private:
282  const SVGTextContentElement* m_reference;
283  QueryMode m_mode;
284 
285  long m_queryStartPosition;
286  long m_queryLength;
287  FloatPoint m_queryPointInput;
288 
289  long m_queryLongResult;
290  float m_queryFloatResult;
291  FloatPoint m_queryPointResult;
292  FloatRect m_queryRectResult;
293 
294  bool m_stopProcessing;
295  long m_atCharacter;
296 };
297 
298 static Vector<SVGInlineTextBox*> findInlineTextBoxInTextChunks(const SVGTextContentElement* element, const Vector<SVGTextChunk>& chunks)
299 {
300  Vector<SVGTextChunk>::const_iterator it = chunks.begin();
301  const Vector<SVGTextChunk>::const_iterator end = chunks.end();
302 
303  Vector<SVGInlineTextBox*> boxes;
304 
305  for (; it != end; ++it) {
306  Vector<SVGInlineBoxCharacterRange>::const_iterator boxIt = it->boxes.begin();
307  const Vector<SVGInlineBoxCharacterRange>::const_iterator boxEnd = it->boxes.end();
308 
309  for (; boxIt != boxEnd; ++boxIt) {
310  SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(boxIt->box);
311 
312  Node* textElement = textBox->renderText()->parent()->element();
313  ASSERT(textElement);
314 
315  if (textElement == element || textElement->parent() == element)
316  boxes.append(textBox);
317  }
318  }
319 
320  return boxes;
321 }
322 
323 static inline SVGRootInlineBox* rootInlineBoxForTextContentElement(const SVGTextContentElement* element)
324 {
325  RenderObject* object = element->renderer();
326 
327  if (!object || !object->isSVGText() || object->isText())
328  return 0;
329 
330  RenderSVGText* svgText = static_cast<RenderSVGText*>(object);
331 
332  // Find root inline box
333  SVGRootInlineBox* rootBox = static_cast<SVGRootInlineBox*>(svgText->firstRootBox());
334  if (!rootBox) {
335  // Layout is not sync yet!
336  /*FIXME khtml element->document()->updateLayoutIgnorePendingStylesheets();*/
337  rootBox = static_cast<SVGRootInlineBox*>(svgText->firstRootBox());
338  }
339 
340  ASSERT(rootBox);
341  return rootBox;
342 }
343 
344 static inline SVGInlineTextBoxQueryWalker executeTextQuery(const SVGTextContentElement* element, SVGInlineTextBoxQueryWalker::QueryMode mode,
345  long startPosition = 0, long length = 0, FloatPoint referencePoint = FloatPoint())
346 {
347  SVGRootInlineBox* rootBox = rootInlineBoxForTextContentElement(element);
348  if (!rootBox)
349  return SVGInlineTextBoxQueryWalker(0, mode);
350 
351  // Find all inline text box associated with our renderer
352  Vector<SVGInlineTextBox*> textBoxes = findInlineTextBoxInTextChunks(element, rootBox->svgTextChunks());
353 
354  // Walk text chunks to find chunks associated with our inline text box
355  SVGInlineTextBoxQueryWalker walkerCallback(element, mode);
356  walkerCallback.setQueryInputParameters(startPosition, length, referencePoint);
357 
358  SVGTextChunkWalker<SVGInlineTextBoxQueryWalker> walker(&walkerCallback, &SVGInlineTextBoxQueryWalker::chunkPortionCallback);
359 
360  Vector<SVGInlineTextBox*>::iterator it = textBoxes.begin();
361  Vector<SVGInlineTextBox*>::iterator end = textBoxes.end();
362 
363  for (; it != end; ++it) {
364  rootBox->walkTextChunks(&walker, *it);
365 
366  if (walkerCallback.stopProcessing())
367  break;
368  }
369 
370  return walkerCallback;
371 }
372 
373 long SVGTextContentElement::getNumberOfChars() const
374 {
375  return executeTextQuery(this, SVGInlineTextBoxQueryWalker::NumberOfCharacters).longResult();
376 }
377 
378 float SVGTextContentElement::getComputedTextLength() const
379 {
380  return executeTextQuery(this, SVGInlineTextBoxQueryWalker::TextLength).floatResult();
381 }
382 
383 float SVGTextContentElement::getSubStringLength(long charnum, long nchars, ExceptionCode& ec) const
384 {
385  // Differences to SVG 1.1 spec, as the spec is clearly wrong. TODO: Raise SVG WG issue!
386  // #1: We accept a 'long nchars' parameter instead of 'unsigned long nchars' to be able
387  // to catch cases where someone called us with a negative 'nchars' value - in those
388  // cases we'll just throw a 'INDEX_SIZE_ERR' (acid3 implicitly agrees with us)
389  //
390  // #2: We only throw if 'charnum + nchars' is greater than the number of characters, not
391  // if it's equal, as this really doesn't make any sense (no way to measure the last character!)
392  //
393  // #3: If 'charnum' is greater than or equal to 'numberOfChars', we're throwing an exception here
394  // as the result is undefined for every other value of 'nchars' than '0'.
395 
396  long numberOfChars = getNumberOfChars();
397  if (charnum < 0 || nchars < 0 || numberOfChars <= charnum || charnum + nchars > numberOfChars) {
398  ec = DOMException::INDEX_SIZE_ERR;
399  return 0.0f;
400  }
401 
402  return executeTextQuery(this, SVGInlineTextBoxQueryWalker::SubStringLength, charnum, nchars).floatResult();
403 }
404 
405 FloatPoint SVGTextContentElement::getStartPositionOfChar(long charnum, ExceptionCode& ec) const
406 {
407  if (charnum < 0 || charnum > getNumberOfChars()) {
408  ec = DOMException::INDEX_SIZE_ERR;
409  return FloatPoint();
410  }
411 
412  return executeTextQuery(this, SVGInlineTextBoxQueryWalker::StartPosition, charnum).pointResult();
413 }
414 
415 FloatPoint SVGTextContentElement::getEndPositionOfChar(long charnum, ExceptionCode& ec) const
416 {
417  if (charnum < 0 || charnum > getNumberOfChars()) {
418  ec = DOMException::INDEX_SIZE_ERR;
419  return FloatPoint();
420  }
421 
422  return executeTextQuery(this, SVGInlineTextBoxQueryWalker::EndPosition, charnum).pointResult();
423 }
424 
425 FloatRect SVGTextContentElement::getExtentOfChar(long charnum, ExceptionCode& ec) const
426 {
427  if (charnum < 0 || charnum > getNumberOfChars()) {
428  ec = DOMException::INDEX_SIZE_ERR;
429  return FloatRect();
430  }
431 
432  return executeTextQuery(this, SVGInlineTextBoxQueryWalker::Extent, charnum).rectResult();
433 }
434 
435 float SVGTextContentElement::getRotationOfChar(long charnum, ExceptionCode& ec) const
436 {
437  if (charnum < 0 || charnum > getNumberOfChars()) {
438  ec = DOMException::INDEX_SIZE_ERR;
439  return 0.0f;
440  }
441 
442  return executeTextQuery(this, SVGInlineTextBoxQueryWalker::Rotation, charnum).floatResult();
443 }
444 
445 long SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) const
446 {
447  return executeTextQuery(this, SVGInlineTextBoxQueryWalker::CharacterNumberAtPosition, 0.0f, 0.0f, point).longResult();
448 }
449 
450 void SVGTextContentElement::selectSubString(long charnum, long nchars, ExceptionCode& ec) const
451 {
452  long numberOfChars = getNumberOfChars();
453  if (charnum < 0 || nchars < 0 || charnum > numberOfChars) {
454  ec = DOMException::INDEX_SIZE_ERR;
455  return;
456  }
457 
458  if (nchars > numberOfChars - charnum)
459  nchars = numberOfChars - charnum;
460 
461  ASSERT(document());
462  //khtml ASSERT(document()->frame());
463 
464  /*FIXME SelectionController* controller = document()->frame()->selectionController();
465  if (!controller)
466  return;
467 
468  // Find selection start
469  VisiblePosition start(const_cast<SVGTextContentElement*>(this), 0, SEL_DEFAULT_AFFINITY);
470  for (long i = 0; i < charnum; ++i)
471  start = start.next();
472 
473  // Find selection end
474  VisiblePosition end(start);
475  for (long i = 0; i < nchars; ++i)
476  end = end.next();
477 
478  controller->setSelection(Selection(start, end));*/
479 }
480 
481 void SVGTextContentElement::parseMappedAttribute(MappedAttribute* attr)
482 {
483  if (attr->name() == SVGNames::lengthAdjustAttr) {
484  if (attr->value() == "spacing")
485  setLengthAdjustBaseValue(LENGTHADJUST_SPACING);
486  else if (attr->value() == "spacingAndGlyphs")
487  setLengthAdjustBaseValue(LENGTHADJUST_SPACINGANDGLYPHS);
488  } else if (attr->name() == SVGNames::textLengthAttr) {
489  setTextLengthBaseValue(SVGLength(this, LengthModeOther, attr->value()));
490  if (textLength().value() < 0.0)
491  document()->accessSVGExtensions()->reportError("A negative value for text attribute <textLength> is not allowed");
492  } else {
493  if (SVGTests::parseMappedAttribute(attr))
494  return;
495  if (SVGLangSpace::parseMappedAttribute(attr)) {
496  if (attr->id() == ATTR_XML_SPACE) {
497  static const DOMString preserveString("preserve");
498 
499  if (attr->value() == preserveString)
500  addCSSProperty(attr, CSS_PROP_WHITE_SPACE, CSS_VAL_PRE);
501  else
502  addCSSProperty(attr, CSS_PROP_WHITE_SPACE, CSS_VAL_NOWRAP);
503  }
504  return;
505  }
506  if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
507  return;
508 
509  SVGStyledElement::parseMappedAttribute(attr);
510  }
511 }
512 
513 bool SVGTextContentElement::isKnownAttribute(const QualifiedName& attrName)
514 {
515  return (attrName.matches(SVGNames::lengthAdjustAttr) ||
516  attrName.matches(SVGNames::textLengthAttr) ||
517  SVGTests::isKnownAttribute(attrName) ||
518  SVGLangSpace::isKnownAttribute(attrName) ||
519  SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
520  SVGStyledElement::isKnownAttribute(attrName));
521 }
522 
523 }
524 
525 #endif // ENABLE(SVG)
SVGLength.h
SVGTextContentElement.h
WebCore::SVGNames::textLengthAttr
DOM::QualifiedName textLengthAttr
Definition: SVGNames.cpp:309
FloatPoint.h
SVGNames.h
WebCore::SVGNames::lengthAdjustAttr
DOM::QualifiedName lengthAdjustAttr
Definition: SVGNames.cpp:207
ExceptionCode.h
FloatRect.h
WebCore::String
DOM::DOMString String
Definition: PlatformString.h:8
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:51:22 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KHTML

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal