00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "config.h"
00022 #include "wtf/Platform.h"
00023
00024 #if ENABLE(SVG)
00025 #include "SVGTextContentElement.h"
00026
00027
00028
00029 #include "ExceptionCode.h"
00030 #include "FloatPoint.h"
00031 #include "FloatRect.h"
00032
00033
00034 #include "RenderSVGText.h"
00035
00036 #include "SVGCharacterLayoutInfo.h"
00037 #include "SVGRootInlineBox.h"
00038 #include "SVGLength.h"
00039 #include "SVGInlineTextBox.h"
00040 #include "SVGNames.h"
00041
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
00078
00079
00080
00081
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
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
00195
00196
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
00331 SVGRootInlineBox* rootBox = static_cast<SVGRootInlineBox*>(svgText->firstRootBox());
00332 if (!rootBox) {
00333
00334
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
00350 Vector<SVGInlineTextBox*> textBoxes = findInlineTextBoxInTextChunks(element, rootBox->svgTextChunks());
00351
00352
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
00384
00385
00386
00387
00388
00389
00390
00391
00392
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
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
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
00495
00496
00497
00498
00499
00500
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)