Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. | 2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. |
| 3 * | 3 * |
| 4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
| 5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
| 6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
| 7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
| 8 * | 8 * |
| 9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
| 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 30 #include "platform/FloatConversion.h" | 30 #include "platform/FloatConversion.h" |
| 31 #include "wtf/MathExtras.h" | 31 #include "wtf/MathExtras.h" |
| 32 #include "wtf/Vector.h" | 32 #include "wtf/Vector.h" |
| 33 | 33 |
| 34 namespace blink { | 34 namespace blink { |
| 35 | 35 |
| 36 // Base structure for callback user data | 36 // Base structure for callback user data |
| 37 struct QueryData { | 37 struct QueryData { |
| 38 QueryData() | 38 QueryData() |
| 39 : isVerticalText(false) | 39 : isVerticalText(false) |
| 40 , processedCharacters(0) | 40 , currentOffset(0) |
| 41 , textLayoutObject(0) | 41 , textLayoutObject(0) |
| 42 , textBox(0) | 42 , textBox(0) |
| 43 { | 43 { |
| 44 } | 44 } |
| 45 | 45 |
| 46 bool isVerticalText; | 46 bool isVerticalText; |
| 47 unsigned processedCharacters; | 47 unsigned currentOffset; |
| 48 LayoutSVGInlineText* textLayoutObject; | 48 LayoutSVGInlineText* textLayoutObject; |
| 49 const SVGInlineTextBox* textBox; | 49 const SVGInlineTextBox* textBox; |
| 50 }; | 50 }; |
| 51 | 51 |
| 52 static inline InlineFlowBox* flowBoxForLayoutObject(LayoutObject* layoutObject) | 52 static inline InlineFlowBox* flowBoxForLayoutObject(LayoutObject* layoutObject) |
| 53 { | 53 { |
| 54 if (!layoutObject) | 54 if (!layoutObject) |
| 55 return 0; | 55 return 0; |
| 56 | 56 |
| 57 if (layoutObject->isLayoutBlock()) { | 57 if (layoutObject->isLayoutBlock()) { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 94 continue; | 94 continue; |
| 95 } | 95 } |
| 96 | 96 |
| 97 if (child->isSVGInlineTextBox()) | 97 if (child->isSVGInlineTextBox()) |
| 98 textBoxes.append(toSVGInlineTextBox(child)); | 98 textBoxes.append(toSVGInlineTextBox(child)); |
| 99 } | 99 } |
| 100 } | 100 } |
| 101 | 101 |
| 102 typedef bool ProcessTextFragmentCallback(QueryData*, const SVGTextFragment&); | 102 typedef bool ProcessTextFragmentCallback(QueryData*, const SVGTextFragment&); |
| 103 | 103 |
| 104 static bool executeQuery(LayoutObject* queryRoot, QueryData* queryData, ProcessT extFragmentCallback fragmentCallback) | 104 static bool queryTextBox(QueryData* queryData, const SVGInlineTextBox* textBox, ProcessTextFragmentCallback fragmentCallback) |
| 105 { | |
| 106 queryData->textBox = textBox; | |
| 107 queryData->textLayoutObject = &toLayoutSVGInlineText(textBox->layoutObject() ); | |
| 108 | |
| 109 queryData->isVerticalText = textBox->layoutObject().style()->svgStyle().isVe rticalWritingMode(); | |
| 110 | |
| 111 // Loop over all text fragments in this text box, firing a callback for each . | |
| 112 for (const SVGTextFragment& fragment : textBox->textFragments()) { | |
| 113 if (fragmentCallback(queryData, fragment)) | |
| 114 return true; | |
| 115 } | |
| 116 return false; | |
| 117 } | |
| 118 | |
| 119 // Execute a query in "spatial" order starting at |queryRoot|. This means | |
| 120 // walking the lines boxes in the order they would get painted. | |
| 121 static void spatialQuery(LayoutObject* queryRoot, QueryData* queryData, ProcessT extFragmentCallback fragmentCallback) | |
| 105 { | 122 { |
| 106 Vector<SVGInlineTextBox*> textBoxes; | 123 Vector<SVGInlineTextBox*> textBoxes; |
| 107 collectTextBoxesInFlowBox(flowBoxForLayoutObject(queryRoot), textBoxes); | 124 collectTextBoxesInFlowBox(flowBoxForLayoutObject(queryRoot), textBoxes); |
| 108 | 125 |
| 109 unsigned processedCharacters = 0; | 126 // Loop over all text boxes |
| 110 unsigned textBoxCount = textBoxes.size(); | 127 for (const SVGInlineTextBox* textBox : textBoxes) { |
| 128 if (queryTextBox(queryData, textBox, fragmentCallback)) | |
| 129 return; | |
| 130 } | |
| 131 } | |
| 111 | 132 |
| 112 // Loop over all text boxes | 133 static void collectTextBoxesInLogicalOrder(const LayoutSVGInlineText& textLayout Object, Vector<SVGInlineTextBox*>& textBoxes) |
| 113 for (unsigned textBoxPosition = 0; textBoxPosition < textBoxCount; ++textBox Position) { | 134 { |
| 114 queryData->textBox = textBoxes[textBoxPosition]; | 135 textBoxes.shrink(0); |
| 115 queryData->textLayoutObject = &toLayoutSVGInlineText(queryData->textBox- >layoutObject()); | 136 for (InlineTextBox* textBox = textLayoutObject.firstTextBox(); textBox; text Box = textBox->nextTextBox()) |
| 116 ASSERT(queryData->textLayoutObject->style()); | 137 textBoxes.append(toSVGInlineTextBox(textBox)); |
| 138 std::sort(textBoxes.begin(), textBoxes.end(), InlineTextBox::compareByStart) ; | |
| 139 } | |
| 117 | 140 |
| 118 queryData->isVerticalText = queryData->textLayoutObject->style()->svgSty le().isVerticalWritingMode(); | 141 // Execute a query in "logical" order starting at |queryRoot|. This means |
| 119 const Vector<SVGTextFragment>& fragments = queryData->textBox->textFragm ents(); | 142 // walking the lines boxes for each layout object in layout tree (pre)order. |
| 143 static void logicalQuery(LayoutObject* queryRoot, QueryData* queryData, ProcessT extFragmentCallback fragmentCallback) | |
| 144 { | |
| 145 if (!queryRoot) | |
| 146 return; | |
| 120 | 147 |
| 121 // Loop over all text fragments in this text box, firing a callback for each. | 148 // Walk the layout tree in pre-order, starting at the specified root, and |
| 122 unsigned fragmentCount = fragments.size(); | 149 // run the query for each text node. |
| 123 for (unsigned i = 0; i < fragmentCount; ++i) { | 150 Vector<SVGInlineTextBox*> textBoxes; |
| 124 const SVGTextFragment& fragment = fragments.at(i); | 151 for (LayoutObject* layoutObject = queryRoot->slowFirstChild(); layoutObject; layoutObject = layoutObject->nextInPreOrder(queryRoot)) { |
| 125 if (fragmentCallback(queryData, fragment)) | 152 if (!layoutObject->isSVGInlineText()) |
| 126 return true; | 153 continue; |
| 127 | 154 |
| 128 processedCharacters += fragment.length; | 155 LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(*layoutObj ect); |
| 156 ASSERT(textLayoutObject.style()); | |
| 157 | |
| 158 // TODO(fs): Allow filtering the search earlier, since we should be | |
| 159 // able to trivially reject (prune) at least some of the queries. | |
| 160 collectTextBoxesInLogicalOrder(textLayoutObject, textBoxes); | |
| 161 | |
| 162 for (const SVGInlineTextBox* textBox : textBoxes) { | |
| 163 if (queryTextBox(queryData, textBox, fragmentCallback)) | |
| 164 return; | |
| 165 queryData->currentOffset += textBox->len(); | |
| 129 } | 166 } |
| 130 | |
| 131 queryData->processedCharacters = processedCharacters; | |
| 132 } | 167 } |
| 133 return false; | |
| 134 } | 168 } |
| 135 | 169 |
| 136 static void modifyStartEndPositionsRespectingLigatures(const QueryData* queryDat a, const SVGTextFragment& fragment, int& startPosition, int& endPosition) | 170 static void modifyStartEndPositionsRespectingLigatures(const QueryData* queryDat a, const SVGTextFragment& fragment, int& startPosition, int& endPosition) |
| 137 { | 171 { |
| 138 const Vector<SVGTextMetrics>& textMetricsValues = queryData->textLayoutObjec t->layoutAttributes()->textMetricsValues(); | 172 const Vector<SVGTextMetrics>& textMetricsValues = queryData->textLayoutObjec t->layoutAttributes()->textMetricsValues(); |
| 139 | 173 |
| 140 unsigned textMetricsOffset = fragment.metricsListOffset; | 174 unsigned textMetricsOffset = fragment.metricsListOffset; |
| 141 int fragmentOffset = 0; | 175 int fragmentOffset = 0; |
| 142 int fragmentEnd = static_cast<int>(fragment.length); | 176 int fragmentEnd = static_cast<int>(fragment.length); |
| 143 | 177 |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 160 if (fragmentOffset >= endPosition) | 194 if (fragmentOffset >= endPosition) |
| 161 break; | 195 break; |
| 162 textMetricsOffset++; | 196 textMetricsOffset++; |
| 163 } | 197 } |
| 164 | 198 |
| 165 endPosition = fragmentOffset; | 199 endPosition = fragmentOffset; |
| 166 } | 200 } |
| 167 | 201 |
| 168 static bool mapStartEndPositionsIntoFragmentCoordinates(const QueryData* queryDa ta, const SVGTextFragment& fragment, int& startPosition, int& endPosition) | 202 static bool mapStartEndPositionsIntoFragmentCoordinates(const QueryData* queryDa ta, const SVGTextFragment& fragment, int& startPosition, int& endPosition) |
| 169 { | 203 { |
| 204 unsigned boxStart = queryData->currentOffset; | |
| 205 | |
| 170 // Make <startPosition, endPosition> offsets relative to the current text bo x. | 206 // Make <startPosition, endPosition> offsets relative to the current text bo x. |
| 171 startPosition -= queryData->processedCharacters; | 207 startPosition -= boxStart; |
| 172 endPosition -= queryData->processedCharacters; | 208 endPosition -= boxStart; |
| 173 | 209 |
| 174 // Reuse the same logic used for text selection & painting, to map our | 210 // Reuse the same logic used for text selection & painting, to map our |
| 175 // query start/length into start/endPositions of the current text fragment. | 211 // query start/length into start/endPositions of the current text fragment. |
| 176 if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragmen t, startPosition, endPosition)) | 212 if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragmen t, startPosition, endPosition)) |
| 177 return false; | 213 return false; |
| 178 | 214 |
| 179 modifyStartEndPositionsRespectingLigatures(queryData, fragment, startPositio n, endPosition); | 215 modifyStartEndPositionsRespectingLigatures(queryData, fragment, startPositio n, endPosition); |
| 180 ASSERT(startPosition < endPosition); | 216 ASSERT(startPosition < endPosition); |
| 181 return true; | 217 return true; |
| 182 } | 218 } |
| 183 | 219 |
| 184 // numberOfCharacters() implementation | 220 // numberOfCharacters() implementation |
| 185 static bool numberOfCharactersCallback(QueryData*, const SVGTextFragment&) | 221 static bool numberOfCharactersCallback(QueryData*, const SVGTextFragment&) |
| 186 { | 222 { |
| 187 // no-op | 223 // no-op |
| 188 return false; | 224 return false; |
| 189 } | 225 } |
| 190 | 226 |
| 191 unsigned SVGTextQuery::numberOfCharacters() const | 227 unsigned SVGTextQuery::numberOfCharacters() const |
| 192 { | 228 { |
| 193 QueryData data; | 229 QueryData data; |
| 194 executeQuery(m_queryRootLayoutObject, &data, numberOfCharactersCallback); | 230 logicalQuery(m_queryRootLayoutObject, &data, numberOfCharactersCallback); |
| 195 return data.processedCharacters; | 231 return data.currentOffset; |
| 196 } | 232 } |
| 197 | 233 |
| 198 // textLength() implementation | 234 // textLength() implementation |
| 199 struct TextLengthData : QueryData { | 235 struct TextLengthData : QueryData { |
| 200 TextLengthData() | 236 TextLengthData() |
| 201 : textLength(0) | 237 : textLength(0) |
| 202 { | 238 { |
| 203 } | 239 } |
| 204 | 240 |
| 205 float textLength; | 241 float textLength; |
| 206 }; | 242 }; |
| 207 | 243 |
| 208 static bool textLengthCallback(QueryData* queryData, const SVGTextFragment& frag ment) | 244 static bool textLengthCallback(QueryData* queryData, const SVGTextFragment& frag ment) |
| 209 { | 245 { |
| 210 TextLengthData* data = static_cast<TextLengthData*>(queryData); | 246 TextLengthData* data = static_cast<TextLengthData*>(queryData); |
| 211 data->textLength += queryData->isVerticalText ? fragment.height : fragment.w idth; | 247 data->textLength += queryData->isVerticalText ? fragment.height : fragment.w idth; |
| 212 return false; | 248 return false; |
| 213 } | 249 } |
| 214 | 250 |
| 215 float SVGTextQuery::textLength() const | 251 float SVGTextQuery::textLength() const |
| 216 { | 252 { |
| 217 TextLengthData data; | 253 TextLengthData data; |
| 218 executeQuery(m_queryRootLayoutObject, &data, textLengthCallback); | 254 logicalQuery(m_queryRootLayoutObject, &data, textLengthCallback); |
| 219 return data.textLength; | 255 return data.textLength; |
| 220 } | 256 } |
| 221 | 257 |
| 222 // subStringLength() implementation | 258 // subStringLength() implementation |
| 223 struct SubStringLengthData : QueryData { | 259 struct SubStringLengthData : QueryData { |
| 224 SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) | 260 SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) |
| 225 : startPosition(queryStartPosition) | 261 : startPosition(queryStartPosition) |
| 226 , length(queryLength) | 262 , length(queryLength) |
| 227 , subStringLength(0) | 263 , subStringLength(0) |
| 228 { | 264 { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 244 return false; | 280 return false; |
| 245 | 281 |
| 246 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->te xtLayoutObject, fragment.characterOffset + startPosition, endPosition - startPos ition, queryData->textBox->direction()); | 282 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->te xtLayoutObject, fragment.characterOffset + startPosition, endPosition - startPos ition, queryData->textBox->direction()); |
| 247 data->subStringLength += queryData->isVerticalText ? metrics.height() : metr ics.width(); | 283 data->subStringLength += queryData->isVerticalText ? metrics.height() : metr ics.width(); |
| 248 return false; | 284 return false; |
| 249 } | 285 } |
| 250 | 286 |
| 251 float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) con st | 287 float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) con st |
| 252 { | 288 { |
| 253 SubStringLengthData data(startPosition, length); | 289 SubStringLengthData data(startPosition, length); |
| 254 executeQuery(m_queryRootLayoutObject, &data, subStringLengthCallback); | 290 logicalQuery(m_queryRootLayoutObject, &data, subStringLengthCallback); |
| 255 return data.subStringLength; | 291 return data.subStringLength; |
| 256 } | 292 } |
| 257 | 293 |
| 258 // startPositionOfCharacter() implementation | 294 // startPositionOfCharacter() implementation |
| 259 struct StartPositionOfCharacterData : QueryData { | 295 struct StartPositionOfCharacterData : QueryData { |
| 260 StartPositionOfCharacterData(unsigned queryPosition) | 296 StartPositionOfCharacterData(unsigned queryPosition) |
| 261 : position(queryPosition) | 297 : position(queryPosition) |
| 262 { | 298 { |
| 263 } | 299 } |
| 264 | 300 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 311 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP osition, endPosition)) | 347 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP osition, endPosition)) |
| 312 return false; | 348 return false; |
| 313 | 349 |
| 314 data->startPosition = calculateGlyphPosition(queryData, fragment, startPosit ion); | 350 data->startPosition = calculateGlyphPosition(queryData, fragment, startPosit ion); |
| 315 return true; | 351 return true; |
| 316 } | 352 } |
| 317 | 353 |
| 318 FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const | 354 FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const |
| 319 { | 355 { |
| 320 StartPositionOfCharacterData data(position); | 356 StartPositionOfCharacterData data(position); |
| 321 executeQuery(m_queryRootLayoutObject, &data, startPositionOfCharacterCallbac k); | 357 logicalQuery(m_queryRootLayoutObject, &data, startPositionOfCharacterCallbac k); |
| 322 return data.startPosition; | 358 return data.startPosition; |
| 323 } | 359 } |
| 324 | 360 |
| 325 // endPositionOfCharacter() implementation | 361 // endPositionOfCharacter() implementation |
| 326 struct EndPositionOfCharacterData : QueryData { | 362 struct EndPositionOfCharacterData : QueryData { |
| 327 EndPositionOfCharacterData(unsigned queryPosition) | 363 EndPositionOfCharacterData(unsigned queryPosition) |
| 328 : position(queryPosition) | 364 : position(queryPosition) |
| 329 { | 365 { |
| 330 } | 366 } |
| 331 | 367 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 345 // TODO(fs): mapStartEndPositionsIntoFragmentCoordinates(...) above applies | 381 // TODO(fs): mapStartEndPositionsIntoFragmentCoordinates(...) above applies |
| 346 // some heuristics for ligatures, so why not just use endPosition here? | 382 // some heuristics for ligatures, so why not just use endPosition here? |
| 347 // (rather than startPosition+1) | 383 // (rather than startPosition+1) |
| 348 data->endPosition = calculateGlyphPosition(queryData, fragment, startPositio n + 1); | 384 data->endPosition = calculateGlyphPosition(queryData, fragment, startPositio n + 1); |
| 349 return true; | 385 return true; |
| 350 } | 386 } |
| 351 | 387 |
| 352 FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const | 388 FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const |
| 353 { | 389 { |
| 354 EndPositionOfCharacterData data(position); | 390 EndPositionOfCharacterData data(position); |
| 355 executeQuery(m_queryRootLayoutObject, &data, endPositionOfCharacterCallback) ; | 391 logicalQuery(m_queryRootLayoutObject, &data, endPositionOfCharacterCallback) ; |
| 356 return data.endPosition; | 392 return data.endPosition; |
| 357 } | 393 } |
| 358 | 394 |
| 359 // rotationOfCharacter() implementation | 395 // rotationOfCharacter() implementation |
| 360 struct RotationOfCharacterData : QueryData { | 396 struct RotationOfCharacterData : QueryData { |
| 361 RotationOfCharacterData(unsigned queryPosition) | 397 RotationOfCharacterData(unsigned queryPosition) |
| 362 : position(queryPosition) | 398 : position(queryPosition) |
| 363 , rotation(0) | 399 , rotation(0) |
| 364 { | 400 { |
| 365 } | 401 } |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 385 fragmentTransform.scale(1 / fragmentTransform.xScale(), 1 / fragmentTran sform.yScale()); | 421 fragmentTransform.scale(1 / fragmentTransform.xScale(), 1 / fragmentTran sform.yScale()); |
| 386 data->rotation = narrowPrecisionToFloat(rad2deg(atan2(fragmentTransform. b(), fragmentTransform.a()))); | 422 data->rotation = narrowPrecisionToFloat(rad2deg(atan2(fragmentTransform. b(), fragmentTransform.a()))); |
| 387 } | 423 } |
| 388 | 424 |
| 389 return true; | 425 return true; |
| 390 } | 426 } |
| 391 | 427 |
| 392 float SVGTextQuery::rotationOfCharacter(unsigned position) const | 428 float SVGTextQuery::rotationOfCharacter(unsigned position) const |
| 393 { | 429 { |
| 394 RotationOfCharacterData data(position); | 430 RotationOfCharacterData data(position); |
| 395 executeQuery(m_queryRootLayoutObject, &data, rotationOfCharacterCallback); | 431 logicalQuery(m_queryRootLayoutObject, &data, rotationOfCharacterCallback); |
| 396 return data.rotation; | 432 return data.rotation; |
| 397 } | 433 } |
| 398 | 434 |
| 399 // extentOfCharacter() implementation | 435 // extentOfCharacter() implementation |
| 400 struct ExtentOfCharacterData : QueryData { | 436 struct ExtentOfCharacterData : QueryData { |
| 401 ExtentOfCharacterData(unsigned queryPosition) | 437 ExtentOfCharacterData(unsigned queryPosition) |
| 402 : position(queryPosition) | 438 : position(queryPosition) |
| 403 { | 439 { |
| 404 } | 440 } |
| 405 | 441 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 477 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP osition, endPosition)) | 513 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP osition, endPosition)) |
| 478 return false; | 514 return false; |
| 479 | 515 |
| 480 calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent); | 516 calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent); |
| 481 return true; | 517 return true; |
| 482 } | 518 } |
| 483 | 519 |
| 484 FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const | 520 FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const |
| 485 { | 521 { |
| 486 ExtentOfCharacterData data(position); | 522 ExtentOfCharacterData data(position); |
| 487 executeQuery(m_queryRootLayoutObject, &data, extentOfCharacterCallback); | 523 logicalQuery(m_queryRootLayoutObject, &data, extentOfCharacterCallback); |
| 488 return data.extent; | 524 return data.extent; |
| 489 } | 525 } |
| 490 | 526 |
| 491 // characterNumberAtPosition() implementation | 527 // characterNumberAtPosition() implementation |
| 492 struct CharacterNumberAtPositionData : QueryData { | 528 struct CharacterNumberAtPositionData : QueryData { |
| 493 CharacterNumberAtPositionData(const FloatPoint& queryPosition) | 529 CharacterNumberAtPositionData(const FloatPoint& queryPosition) |
| 494 : position(queryPosition) | 530 : position(queryPosition) |
| 531 , hitLayoutObject(nullptr) | |
| 532 , offsetInTextNode(0) | |
| 495 { | 533 { |
| 496 } | 534 } |
| 497 | 535 |
| 536 int characterNumberWithin(const LayoutObject* queryRoot) const; | |
| 537 | |
| 498 FloatPoint position; | 538 FloatPoint position; |
| 539 LayoutObject* hitLayoutObject; | |
| 540 int offsetInTextNode; | |
| 499 }; | 541 }; |
| 500 | 542 |
| 543 int CharacterNumberAtPositionData::characterNumberWithin(const LayoutObject* que ryRoot) const | |
| 544 { | |
| 545 // https://svgwg.org/svg2-draft/single-page.html#text-__svg__SVGTextContentE lement__getCharNumAtPosition | |
|
pdr.
2015/04/16 21:13:25
Sorry for the churn. I use the svg2 draft for ever
fs
2015/04/17 11:05:40
Done.
| |
| 546 // "If no such character exists, a value of -1 is returned." | |
| 547 if (!hitLayoutObject) | |
| 548 return -1; | |
| 549 ASSERT(queryRoot); | |
| 550 int characterNumber = offsetInTextNode; | |
| 551 | |
| 552 // Accumulate the lengths of all the text nodes preceding the target layout | |
| 553 // object within the queried root, to get the complete character number. | |
| 554 for (const LayoutObject* layoutObject = hitLayoutObject->previousInPreOrder( queryRoot); | |
| 555 layoutObject; layoutObject = layoutObject->previousInPreOrder(queryRoot) ) { | |
| 556 if (!layoutObject->isSVGInlineText()) | |
| 557 continue; | |
| 558 characterNumber += toLayoutSVGInlineText(layoutObject)->renderedTextLeng th(); | |
| 559 } | |
| 560 return characterNumber; | |
| 561 } | |
| 562 | |
| 563 static unsigned logicalOffsetInTextNode(const LayoutSVGInlineText& textLayoutObj ect, const SVGInlineTextBox* startTextBox, unsigned fragmentOffset) | |
| 564 { | |
| 565 Vector<SVGInlineTextBox*> textBoxes; | |
| 566 collectTextBoxesInLogicalOrder(textLayoutObject, textBoxes); | |
| 567 | |
| 568 ASSERT(startTextBox); | |
| 569 size_t index = textBoxes.find(startTextBox); | |
| 570 ASSERT(index != kNotFound); | |
| 571 | |
| 572 unsigned offset = fragmentOffset; | |
| 573 while (index) { | |
| 574 --index; | |
| 575 offset += textBoxes[index]->len(); | |
| 576 } | |
| 577 return offset; | |
| 578 } | |
| 579 | |
| 501 static bool characterNumberAtPositionCallback(QueryData* queryData, const SVGTex tFragment& fragment) | 580 static bool characterNumberAtPositionCallback(QueryData* queryData, const SVGTex tFragment& fragment) |
| 502 { | 581 { |
| 503 CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionD ata*>(queryData); | 582 CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionD ata*>(queryData); |
| 504 | 583 |
| 505 // Test the query point against the bounds of the entire fragment first. | 584 // Test the query point against the bounds of the entire fragment first. |
| 506 FloatRect fragmentExtents = calculateFragmentBoundaries(*queryData->textLayo utObject, fragment); | 585 FloatRect fragmentExtents = calculateFragmentBoundaries(*queryData->textLayo utObject, fragment); |
| 507 if (!fragmentExtents.contains(data->position)) | 586 if (!fragmentExtents.contains(data->position)) |
| 508 return false; | 587 return false; |
| 509 | 588 |
| 510 // Iterate through the glyphs in this fragment, and check if their extents | 589 // Iterate through the glyphs in this fragment, and check if their extents |
| 511 // contain the query point. | 590 // contain the query point. |
| 512 FloatRect extent; | 591 FloatRect extent; |
| 513 const Vector<SVGTextMetrics>& textMetrics = queryData->textLayoutObject->lay outAttributes()->textMetricsValues(); | 592 const Vector<SVGTextMetrics>& textMetrics = queryData->textLayoutObject->lay outAttributes()->textMetricsValues(); |
| 514 unsigned textMetricsOffset = fragment.metricsListOffset; | 593 unsigned textMetricsOffset = fragment.metricsListOffset; |
| 515 unsigned fragmentOffset = 0; | 594 unsigned fragmentOffset = 0; |
| 516 while (fragmentOffset < fragment.length) { | 595 while (fragmentOffset < fragment.length) { |
| 517 calculateGlyphBoundaries(queryData, fragment, fragmentOffset, extent); | 596 calculateGlyphBoundaries(queryData, fragment, fragmentOffset, extent); |
| 518 if (extent.contains(data->position)) { | 597 if (extent.contains(data->position)) { |
| 519 // Compute the character offset of the glyph within the text box | 598 // Compute the character offset of the glyph within the text node. |
| 520 // and add to processedCharacters. | 599 unsigned offsetInBox = fragment.characterOffset - queryData->textBox ->start() + fragmentOffset; |
| 521 unsigned characterOffset = fragment.characterOffset + fragmentOffset ; | 600 data->offsetInTextNode = logicalOffsetInTextNode(*queryData->textLay outObject, queryData->textBox, offsetInBox); |
| 522 data->processedCharacters += characterOffset - data->textBox->start( ); | 601 data->hitLayoutObject = data->textLayoutObject; |
| 523 return true; | 602 return true; |
| 524 } | 603 } |
| 525 fragmentOffset += textMetrics[textMetricsOffset].length(); | 604 fragmentOffset += textMetrics[textMetricsOffset].length(); |
| 526 textMetricsOffset++; | 605 textMetricsOffset++; |
| 527 } | 606 } |
| 528 return false; | 607 return false; |
| 529 } | 608 } |
| 530 | 609 |
| 531 int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const | 610 int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const |
| 532 { | 611 { |
| 533 CharacterNumberAtPositionData data(position); | 612 CharacterNumberAtPositionData data(position); |
| 534 if (!executeQuery(m_queryRootLayoutObject, &data, characterNumberAtPositionC allback)) | 613 spatialQuery(m_queryRootLayoutObject, &data, characterNumberAtPositionCallba ck); |
| 535 return -1; | 614 return data.characterNumberWithin(m_queryRootLayoutObject); |
| 536 | |
| 537 return data.processedCharacters; | |
| 538 } | 615 } |
| 539 | 616 |
| 540 } | 617 } |
| OLD | NEW |