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 |