22 #include "wtf/Platform.h"
27 #include "cssvalues.h"
36 #include "RenderSVGText.h"
38 #include "SVGCharacterLayoutInfo.h"
39 #include "SVGRootInlineBox.h"
41 #include "SVGInlineTextBox.h"
46 SVGTextContentElement::SVGTextContentElement(
const QualifiedName& tagName, Document* doc)
47 : SVGStyledElement(tagName, doc)
50 , SVGExternalResourcesRequired()
51 , m_textLength(this, LengthModeOther)
52 , m_lengthAdjust(LENGTHADJUST_SPACING)
56 SVGTextContentElement::~SVGTextContentElement()
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)
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)
69 float textLength = 0.0f;
70 RenderStyle* style = textBox->renderText()->style();
72 bool usesFullRange = (startPosition == -1 && length == -1);
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;
83 int charsConsumed = 0;
86 textLength += textBox->calculateGlyphHeight(style, newOffset, 0);
88 textLength += textBox->calculateGlyphWidth(style, newOffset, 0, charsConsumed, glyphName);
92 if (atCharacter == startPosition + length - 1)
103 struct SVGInlineTextBoxQueryWalker {
112 CharacterNumberAtPosition
115 SVGInlineTextBoxQueryWalker(
const SVGTextContentElement* reference, QueryMode mode)
116 : m_reference(reference)
118 , m_queryStartPosition(0)
120 , m_queryPointInput()
121 , m_queryLongResult(0)
122 , m_queryFloatResult(0.0f)
123 , m_queryPointResult()
124 , m_queryRectResult()
125 , m_stopProcessing(true)
130 void chunkPortionCallback(SVGInlineTextBox* textBox,
int startOffset,
const AffineTransform& chunkCtm,
131 const Vector<SVGChar>::iterator& start,
const Vector<SVGChar>::iterator& end)
134 RenderStyle* style = textBox->renderText()->style();
135 bool isVerticalText = style->svgStyle()->writingMode() == WM_TBRL || style->svgStyle()->writingMode() == WM_TB;
138 case NumberOfCharacters:
140 m_queryLongResult += (end - start);
141 m_stopProcessing =
false;
146 float textLength = cumulativeCharacterRangeLength(start, end, textBox, startOffset, -1, -1, isVerticalText, m_atCharacter);
149 m_queryFloatResult += textLength;
151 m_queryFloatResult += textLength;
153 m_stopProcessing =
false;
156 case SubStringLength:
158 long startPosition = m_queryStartPosition;
159 long length = m_queryLength;
161 float textLength = cumulativeCharacterRangeLength(start, end, textBox, startOffset, startPosition, length, isVerticalText, m_atCharacter);
164 m_queryFloatResult += textLength;
166 m_queryFloatResult += textLength;
168 if (m_atCharacter == startPosition + length)
169 m_stopProcessing =
true;
171 m_stopProcessing =
false;
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;
187 m_stopProcessing =
false;
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;
203 m_queryPointResult.move(it->x, it->y + textBox->calculateGlyphHeight(style, newOffset, end - it));
205 m_queryPointResult.move(it->x + textBox->calculateGlyphWidth(style, newOffset, end - it, charsConsumed, glyphName), it->y);
207 m_stopProcessing =
true;
214 m_stopProcessing =
false;
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;
230 m_stopProcessing =
false;
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;
245 m_stopProcessing =
false;
248 case CharacterNumberAtPosition:
251 SVGChar* charAtPos = textBox->closestCharacterToPosition(m_queryPointInput.x(), m_queryPointInput.y(), offset);
253 offset += m_atCharacter;
254 if (charAtPos && offset > m_queryLongResult)
255 m_queryLongResult = offset;
257 m_atCharacter += end - start;
258 m_stopProcessing =
false;
262 ASSERT_NOT_REACHED();
263 m_stopProcessing =
true;
268 void setQueryInputParameters(
long startPosition,
long length, FloatPoint referencePoint)
270 m_queryStartPosition = startPosition;
271 m_queryLength = length;
272 m_queryPointInput = referencePoint;
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; }
282 const SVGTextContentElement* m_reference;
285 long m_queryStartPosition;
287 FloatPoint m_queryPointInput;
289 long m_queryLongResult;
290 float m_queryFloatResult;
291 FloatPoint m_queryPointResult;
292 FloatRect m_queryRectResult;
294 bool m_stopProcessing;
298 static Vector<SVGInlineTextBox*> findInlineTextBoxInTextChunks(
const SVGTextContentElement* element,
const Vector<SVGTextChunk>& chunks)
300 Vector<SVGTextChunk>::const_iterator it = chunks.begin();
301 const Vector<SVGTextChunk>::const_iterator end = chunks.end();
303 Vector<SVGInlineTextBox*> boxes;
305 for (; it != end; ++it) {
306 Vector<SVGInlineBoxCharacterRange>::const_iterator boxIt = it->boxes.begin();
307 const Vector<SVGInlineBoxCharacterRange>::const_iterator boxEnd = it->boxes.end();
309 for (; boxIt != boxEnd; ++boxIt) {
310 SVGInlineTextBox* textBox =
static_cast<SVGInlineTextBox*
>(boxIt->box);
312 Node* textElement = textBox->renderText()->parent()->element();
315 if (textElement == element || textElement->parent() == element)
316 boxes.append(textBox);
323 static inline SVGRootInlineBox* rootInlineBoxForTextContentElement(
const SVGTextContentElement* element)
325 RenderObject*
object = element->renderer();
327 if (!
object || !object->isSVGText() ||
object->isText())
330 RenderSVGText* svgText =
static_cast<RenderSVGText*
>(object);
333 SVGRootInlineBox* rootBox =
static_cast<SVGRootInlineBox*
>(svgText->firstRootBox());
337 rootBox =
static_cast<SVGRootInlineBox*
>(svgText->firstRootBox());
344 static inline SVGInlineTextBoxQueryWalker executeTextQuery(
const SVGTextContentElement* element, SVGInlineTextBoxQueryWalker::QueryMode mode,
345 long startPosition = 0,
long length = 0, FloatPoint referencePoint = FloatPoint())
347 SVGRootInlineBox* rootBox = rootInlineBoxForTextContentElement(element);
349 return SVGInlineTextBoxQueryWalker(0, mode);
352 Vector<SVGInlineTextBox*> textBoxes = findInlineTextBoxInTextChunks(element, rootBox->svgTextChunks());
355 SVGInlineTextBoxQueryWalker walkerCallback(element, mode);
356 walkerCallback.setQueryInputParameters(startPosition, length, referencePoint);
358 SVGTextChunkWalker<SVGInlineTextBoxQueryWalker> walker(&walkerCallback, &SVGInlineTextBoxQueryWalker::chunkPortionCallback);
360 Vector<SVGInlineTextBox*>::iterator it = textBoxes.begin();
361 Vector<SVGInlineTextBox*>::iterator end = textBoxes.end();
363 for (; it != end; ++it) {
364 rootBox->walkTextChunks(&walker, *it);
366 if (walkerCallback.stopProcessing())
370 return walkerCallback;
373 long SVGTextContentElement::getNumberOfChars()
const
375 return executeTextQuery(
this, SVGInlineTextBoxQueryWalker::NumberOfCharacters).longResult();
378 float SVGTextContentElement::getComputedTextLength()
const
380 return executeTextQuery(
this, SVGInlineTextBoxQueryWalker::TextLength).floatResult();
383 float SVGTextContentElement::getSubStringLength(
long charnum,
long nchars, ExceptionCode& ec)
const
396 long numberOfChars = getNumberOfChars();
397 if (charnum < 0 || nchars < 0 || numberOfChars <= charnum || charnum + nchars > numberOfChars) {
398 ec = DOMException::INDEX_SIZE_ERR;
402 return executeTextQuery(
this, SVGInlineTextBoxQueryWalker::SubStringLength, charnum, nchars).floatResult();
405 FloatPoint SVGTextContentElement::getStartPositionOfChar(
long charnum, ExceptionCode& ec)
const
407 if (charnum < 0 || charnum > getNumberOfChars()) {
408 ec = DOMException::INDEX_SIZE_ERR;
412 return executeTextQuery(
this, SVGInlineTextBoxQueryWalker::StartPosition, charnum).pointResult();
415 FloatPoint SVGTextContentElement::getEndPositionOfChar(
long charnum, ExceptionCode& ec)
const
417 if (charnum < 0 || charnum > getNumberOfChars()) {
418 ec = DOMException::INDEX_SIZE_ERR;
422 return executeTextQuery(
this, SVGInlineTextBoxQueryWalker::EndPosition, charnum).pointResult();
425 FloatRect SVGTextContentElement::getExtentOfChar(
long charnum, ExceptionCode& ec)
const
427 if (charnum < 0 || charnum > getNumberOfChars()) {
428 ec = DOMException::INDEX_SIZE_ERR;
432 return executeTextQuery(
this, SVGInlineTextBoxQueryWalker::Extent, charnum).rectResult();
435 float SVGTextContentElement::getRotationOfChar(
long charnum, ExceptionCode& ec)
const
437 if (charnum < 0 || charnum > getNumberOfChars()) {
438 ec = DOMException::INDEX_SIZE_ERR;
442 return executeTextQuery(
this, SVGInlineTextBoxQueryWalker::Rotation, charnum).floatResult();
445 long SVGTextContentElement::getCharNumAtPosition(
const FloatPoint& point)
const
447 return executeTextQuery(
this, SVGInlineTextBoxQueryWalker::CharacterNumberAtPosition, 0.0f, 0.0f, point).longResult();
450 void SVGTextContentElement::selectSubString(
long charnum,
long nchars, ExceptionCode& ec)
const
452 long numberOfChars = getNumberOfChars();
453 if (charnum < 0 || nchars < 0 || charnum > numberOfChars) {
454 ec = DOMException::INDEX_SIZE_ERR;
458 if (nchars > numberOfChars - charnum)
459 nchars = numberOfChars - charnum;
481 void SVGTextContentElement::parseMappedAttribute(MappedAttribute* attr)
484 if (attr->value() ==
"spacing")
485 setLengthAdjustBaseValue(LENGTHADJUST_SPACING);
486 else if (attr->value() ==
"spacingAndGlyphs")
487 setLengthAdjustBaseValue(LENGTHADJUST_SPACINGANDGLYPHS);
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");
493 if (SVGTests::parseMappedAttribute(attr))
495 if (SVGLangSpace::parseMappedAttribute(attr)) {
496 if (attr->id() == ATTR_XML_SPACE) {
497 static const DOMString preserveString(
"preserve");
499 if (attr->value() == preserveString)
500 addCSSProperty(attr, CSS_PROP_WHITE_SPACE, CSS_VAL_PRE);
502 addCSSProperty(attr, CSS_PROP_WHITE_SPACE, CSS_VAL_NOWRAP);
506 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
509 SVGStyledElement::parseMappedAttribute(attr);
513 bool SVGTextContentElement::isKnownAttribute(
const QualifiedName& attrName)
517 SVGTests::isKnownAttribute(attrName) ||
518 SVGLangSpace::isKnownAttribute(attrName) ||
519 SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
520 SVGStyledElement::isKnownAttribute(attrName));
525 #endif // ENABLE(SVG)
DOM::QualifiedName textLengthAttr
DOM::QualifiedName lengthAdjustAttr