1 /* 2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20 #include "config.h" 21 #include "core/rendering/svg/SVGTextQuery.h" 22 23 #include "core/platform/FloatConversion.h" 24 #include "core/rendering/InlineFlowBox.h" 25 #include "core/rendering/RenderBlock.h" 26 #include "core/rendering/RenderInline.h" 27 #include "core/rendering/svg/RenderSVGInlineText.h" 28 #include "core/rendering/svg/SVGInlineTextBox.h" 29 #include "core/rendering/svg/SVGTextMetrics.h" 30 31 #include "wtf/MathExtras.h" 32 33 namespace WebCore { 34 35 // Base structure for callback user data 36 struct SVGTextQuery::Data { 37 Data() 38 : isVerticalText(false) 39 , processedCharacters(0) 40 , textRenderer(0) 41 , textBox(0) 42 { 43 } 44 45 bool isVerticalText; 46 unsigned processedCharacters; 47 RenderSVGInlineText* textRenderer; 48 const SVGInlineTextBox* textBox; 49 }; 50 51 static inline InlineFlowBox* flowBoxForRenderer(RenderObject* renderer) 52 { 53 if (!renderer) 54 return 0; 55 56 if (renderer->isRenderBlock()) { 57 // If we're given a block element, it has to be a RenderSVGText. 58 ASSERT(renderer->isSVGText()); 59 RenderBlock* renderBlock = toRenderBlock(renderer); 60 61 // RenderSVGText only ever contains a single line box. 62 InlineFlowBox* flowBox = renderBlock->firstLineBox(); 63 ASSERT(flowBox == renderBlock->lastLineBox()); 64 return flowBox; 65 } 66 67 if (renderer->isRenderInline()) { 68 // We're given a RenderSVGInline or objects that derive from it (RenderSVGTSpan / RenderSVGTextPath) 69 RenderInline* renderInline = toRenderInline(renderer); 70 71 // RenderSVGInline only ever contains a single line box. 72 InlineFlowBox* flowBox = renderInline->firstLineBox(); 73 ASSERT(flowBox == renderInline->lastLineBox()); 74 return flowBox; 75 } 76 77 ASSERT_NOT_REACHED(); 78 return 0; 79 } 80 81 SVGTextQuery::SVGTextQuery(RenderObject* renderer) 82 { 83 collectTextBoxesInFlowBox(flowBoxForRenderer(renderer)); 84 } 85 86 void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox) 87 { 88 if (!flowBox) 89 return; 90 91 for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnLine()) { 92 if (child->isInlineFlowBox()) { 93 // Skip generated content. 94 if (!child->renderer()->node()) 95 continue; 96 97 collectTextBoxesInFlowBox(static_cast<InlineFlowBox*>(child)); 98 continue; 99 } 100 101 if (child->isSVGInlineTextBox()) 102 m_textBoxes.append(toSVGInlineTextBox(child)); 103 } 104 } 105 106 bool SVGTextQuery::executeQuery(Data* queryData, ProcessTextFragmentCallback fragmentCallback) const 107 { 108 ASSERT(!m_textBoxes.isEmpty()); 109 110 unsigned processedCharacters = 0; 111 unsigned textBoxCount = m_textBoxes.size(); 112 113 // Loop over all text boxes 114 for (unsigned textBoxPosition = 0; textBoxPosition < textBoxCount; ++textBoxPosition) { 115 queryData->textBox = m_textBoxes.at(textBoxPosition); 116 queryData->textRenderer = toRenderSVGInlineText(queryData->textBox->textRenderer()); 117 ASSERT(queryData->textRenderer); 118 ASSERT(queryData->textRenderer->style()); 119 ASSERT(queryData->textRenderer->style()->svgStyle()); 120 121 queryData->isVerticalText = queryData->textRenderer->style()->svgStyle()->isVerticalWritingMode(); 122 const Vector<SVGTextFragment>& fragments = queryData->textBox->textFragments(); 123 124 // Loop over all text fragments in this text box, firing a callback for each. 125 unsigned fragmentCount = fragments.size(); 126 for (unsigned i = 0; i < fragmentCount; ++i) { 127 const SVGTextFragment& fragment = fragments.at(i); 128 if ((this->*fragmentCallback)(queryData, fragment)) 129 return true; 130 131 processedCharacters += fragment.length; 132 } 133 134 queryData->processedCharacters = processedCharacters; 135 } 136 137 return false; 138 } 139 140 bool SVGTextQuery::mapStartEndPositionsIntoFragmentCoordinates(Data* queryData, const SVGTextFragment& fragment, int& startPosition, int& endPosition) const 141 { 142 // Reuse the same logic used for text selection & painting, to map our query start/length into start/endPositions of the current text fragment. 143 startPosition -= queryData->processedCharacters; 144 endPosition -= queryData->processedCharacters; 145 146 if (startPosition >= endPosition || startPosition < 0 || endPosition < 0) 147 return false; 148 149 modifyStartEndPositionsRespectingLigatures(queryData, startPosition, endPosition); 150 if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition)) 151 return false; 152 153 ASSERT(startPosition < endPosition); 154 return true; 155 } 156 157 void SVGTextQuery::modifyStartEndPositionsRespectingLigatures(Data* queryData, int& startPosition, int& endPosition) const 158 { 159 SVGTextLayoutAttributes* layoutAttributes = queryData->textRenderer->layoutAttributes(); 160 Vector<SVGTextMetrics>& textMetricsValues = layoutAttributes->textMetricsValues(); 161 unsigned boxStart = queryData->textBox->start(); 162 unsigned boxLength = queryData->textBox->len(); 163 164 unsigned textMetricsOffset = 0; 165 unsigned textMetricsSize = textMetricsValues.size(); 166 167 unsigned positionOffset = 0; 168 unsigned positionSize = layoutAttributes->context()->textLength(); 169 170 bool alterStartPosition = true; 171 bool alterEndPosition = true; 172 173 int lastPositionOffset = -1; 174 for (; textMetricsOffset < textMetricsSize && positionOffset < positionSize; ++textMetricsOffset) { 175 SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; 176 177 // Advance to text box start location. 178 if (positionOffset < boxStart) { 179 positionOffset += metrics.length(); 180 continue; 181 } 182 183 // Stop if we've finished processing this text box. 184 if (positionOffset >= boxStart + boxLength) 185 break; 186 187 // If the start position maps to a character in the metrics list, we don't need to modify it. 188 if (startPosition == static_cast<int>(positionOffset)) 189 alterStartPosition = false; 190 191 // If the start position maps to a character in the metrics list, we don't need to modify it. 192 if (endPosition == static_cast<int>(positionOffset)) 193 alterEndPosition = false; 194 195 // Detect ligatures. 196 if (lastPositionOffset != -1 && lastPositionOffset - positionOffset > 1) { 197 if (alterStartPosition && startPosition > lastPositionOffset && startPosition < static_cast<int>(positionOffset)) { 198 startPosition = lastPositionOffset; 199 alterStartPosition = false; 200 } 201 202 if (alterEndPosition && endPosition > lastPositionOffset && endPosition < static_cast<int>(positionOffset)) { 203 endPosition = positionOffset; 204 alterEndPosition = false; 205 } 206 } 207 208 if (!alterStartPosition && !alterEndPosition) 209 break; 210 211 lastPositionOffset = positionOffset; 212 positionOffset += metrics.length(); 213 } 214 215 if (!alterStartPosition && !alterEndPosition) 216 return; 217 218 if (lastPositionOffset != -1 && lastPositionOffset - positionOffset > 1) { 219 if (alterStartPosition && startPosition > lastPositionOffset && startPosition < static_cast<int>(positionOffset)) { 220 startPosition = lastPositionOffset; 221 alterStartPosition = false; 222 } 223 224 if (alterEndPosition && endPosition > lastPositionOffset && endPosition < static_cast<int>(positionOffset)) { 225 endPosition = positionOffset; 226 alterEndPosition = false; 227 } 228 } 229 } 230 231 // numberOfCharacters() implementation 232 bool SVGTextQuery::numberOfCharactersCallback(Data*, const SVGTextFragment&) const 233 { 234 // no-op 235 return false; 236 } 237 238 unsigned SVGTextQuery::numberOfCharacters() const 239 { 240 if (m_textBoxes.isEmpty()) 241 return 0; 242 243 Data data; 244 executeQuery(&data, &SVGTextQuery::numberOfCharactersCallback); 245 return data.processedCharacters; 246 } 247 248 // textLength() implementation 249 struct TextLengthData : SVGTextQuery::Data { 250 TextLengthData() 251 : textLength(0) 252 { 253 } 254 255 float textLength; 256 }; 257 258 bool SVGTextQuery::textLengthCallback(Data* queryData, const SVGTextFragment& fragment) const 259 { 260 TextLengthData* data = static_cast<TextLengthData*>(queryData); 261 data->textLength += queryData->isVerticalText ? fragment.height : fragment.width; 262 return false; 263 } 264 265 float SVGTextQuery::textLength() const 266 { 267 if (m_textBoxes.isEmpty()) 268 return 0; 269 270 TextLengthData data; 271 executeQuery(&data, &SVGTextQuery::textLengthCallback); 272 return data.textLength; 273 } 274 275 // subStringLength() implementation 276 struct SubStringLengthData : SVGTextQuery::Data { 277 SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) 278 : startPosition(queryStartPosition) 279 , length(queryLength) 280 , subStringLength(0) 281 { 282 } 283 284 unsigned startPosition; 285 unsigned length; 286 287 float subStringLength; 288 }; 289 290 bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragment& fragment) const 291 { 292 SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); 293 294 int startPosition = data->startPosition; 295 int endPosition = startPosition + data->length; 296 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) 297 return false; 298 299 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, endPosition - startPosition); 300 data->subStringLength += queryData->isVerticalText ? metrics.height() : metrics.width(); 301 return false; 302 } 303 304 float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) const 305 { 306 if (m_textBoxes.isEmpty()) 307 return 0; 308 309 SubStringLengthData data(startPosition, length); 310 executeQuery(&data, &SVGTextQuery::subStringLengthCallback); 311 return data.subStringLength; 312 } 313 314 // startPositionOfCharacter() implementation 315 struct StartPositionOfCharacterData : SVGTextQuery::Data { 316 StartPositionOfCharacterData(unsigned queryPosition) 317 : position(queryPosition) 318 { 319 } 320 321 unsigned position; 322 FloatPoint startPosition; 323 }; 324 325 bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const 326 { 327 StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterData*>(queryData); 328 329 int startPosition = data->position; 330 int endPosition = startPosition + 1; 331 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) 332 return false; 333 334 data->startPosition = FloatPoint(fragment.x, fragment.y); 335 336 if (startPosition) { 337 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition); 338 if (queryData->isVerticalText) 339 data->startPosition.move(0, metrics.height()); 340 else 341 data->startPosition.move(metrics.width(), 0); 342 } 343 344 AffineTransform fragmentTransform; 345 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength); 346 if (fragmentTransform.isIdentity()) 347 return true; 348 349 data->startPosition = fragmentTransform.mapPoint(data->startPosition); 350 return true; 351 } 352 353 FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const 354 { 355 if (m_textBoxes.isEmpty()) 356 return FloatPoint(); 357 358 StartPositionOfCharacterData data(position); 359 executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback); 360 return data.startPosition; 361 } 362 363 // endPositionOfCharacter() implementation 364 struct EndPositionOfCharacterData : SVGTextQuery::Data { 365 EndPositionOfCharacterData(unsigned queryPosition) 366 : position(queryPosition) 367 { 368 } 369 370 unsigned position; 371 FloatPoint endPosition; 372 }; 373 374 bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const 375 { 376 EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(queryData); 377 378 int startPosition = data->position; 379 int endPosition = startPosition + 1; 380 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) 381 return false; 382 383 data->endPosition = FloatPoint(fragment.x, fragment.y); 384 385 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition + 1); 386 if (queryData->isVerticalText) 387 data->endPosition.move(0, metrics.height()); 388 else 389 data->endPosition.move(metrics.width(), 0); 390 391 AffineTransform fragmentTransform; 392 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength); 393 if (fragmentTransform.isIdentity()) 394 return true; 395 396 data->endPosition = fragmentTransform.mapPoint(data->endPosition); 397 return true; 398 } 399 400 FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const 401 { 402 if (m_textBoxes.isEmpty()) 403 return FloatPoint(); 404 405 EndPositionOfCharacterData data(position); 406 executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback); 407 return data.endPosition; 408 } 409 410 // rotationOfCharacter() implementation 411 struct RotationOfCharacterData : SVGTextQuery::Data { 412 RotationOfCharacterData(unsigned queryPosition) 413 : position(queryPosition) 414 , rotation(0) 415 { 416 } 417 418 unsigned position; 419 float rotation; 420 }; 421 422 bool SVGTextQuery::rotationOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const 423 { 424 RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryData); 425 426 int startPosition = data->position; 427 int endPosition = startPosition + 1; 428 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) 429 return false; 430 431 AffineTransform fragmentTransform; 432 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength); 433 if (fragmentTransform.isIdentity()) 434 data->rotation = 0; 435 else { 436 fragmentTransform.scale(1 / fragmentTransform.xScale(), 1 / fragmentTransform.yScale()); 437 data->rotation = narrowPrecisionToFloat(rad2deg(atan2(fragmentTransform.b(), fragmentTransform.a()))); 438 } 439 440 return true; 441 } 442 443 float SVGTextQuery::rotationOfCharacter(unsigned position) const 444 { 445 if (m_textBoxes.isEmpty()) 446 return 0; 447 448 RotationOfCharacterData data(position); 449 executeQuery(&data, &SVGTextQuery::rotationOfCharacterCallback); 450 return data.rotation; 451 } 452 453 // extentOfCharacter() implementation 454 struct ExtentOfCharacterData : SVGTextQuery::Data { 455 ExtentOfCharacterData(unsigned queryPosition) 456 : position(queryPosition) 457 { 458 } 459 460 unsigned position; 461 FloatRect extent; 462 }; 463 464 static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const SVGTextFragment& fragment, int startPosition, FloatRect& extent) 465 { 466 float scalingFactor = queryData->textRenderer->scalingFactor(); 467 ASSERT(scalingFactor); 468 469 extent.setLocation(FloatPoint(fragment.x, fragment.y - queryData->textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor)); 470 471 if (startPosition) { 472 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition); 473 if (queryData->isVerticalText) 474 extent.move(0, metrics.height()); 475 else 476 extent.move(metrics.width(), 0); 477 } 478 479 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, 1); 480 extent.setSize(FloatSize(metrics.width(), metrics.height())); 481 482 AffineTransform fragmentTransform; 483 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength); 484 if (fragmentTransform.isIdentity()) 485 return; 486 487 extent = fragmentTransform.mapRect(extent); 488 } 489 490 bool SVGTextQuery::extentOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const 491 { 492 ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData); 493 494 int startPosition = data->position; 495 int endPosition = startPosition + 1; 496 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) 497 return false; 498 499 calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent); 500 return true; 501 } 502 503 SVGRect SVGTextQuery::extentOfCharacter(unsigned position) const 504 { 505 if (m_textBoxes.isEmpty()) 506 return SVGRect(); 507 508 ExtentOfCharacterData data(position); 509 executeQuery(&data, &SVGTextQuery::extentOfCharacterCallback); 510 return data.extent; 511 } 512 513 // characterNumberAtPosition() implementation 514 struct CharacterNumberAtPositionData : SVGTextQuery::Data { 515 CharacterNumberAtPositionData(const FloatPoint& queryPosition) 516 : position(queryPosition) 517 { 518 } 519 520 FloatPoint position; 521 }; 522 523 bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGTextFragment& fragment) const 524 { 525 CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionData*>(queryData); 526 527 FloatRect extent; 528 for (unsigned i = 0; i < fragment.length; ++i) { 529 int startPosition = data->processedCharacters + i; 530 int endPosition = startPosition + 1; 531 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) 532 continue; 533 534 calculateGlyphBoundaries(queryData, fragment, startPosition, extent); 535 if (extent.contains(data->position)) { 536 data->processedCharacters += i; 537 return true; 538 } 539 } 540 541 return false; 542 } 543 544 int SVGTextQuery::characterNumberAtPosition(const SVGPoint& position) const 545 { 546 if (m_textBoxes.isEmpty()) 547 return -1; 548 549 CharacterNumberAtPositionData data(position); 550 if (!executeQuery(&data, &SVGTextQuery::characterNumberAtPositionCallback)) 551 return -1; 552 553 return data.processedCharacters; 554 } 555 556 } 557