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 |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
18 */ | 18 */ |
19 | 19 |
20 #include "config.h" | 20 #include "config.h" |
21 #include "core/layout/svg/SVGTextQuery.h" | 21 #include "core/layout/svg/SVGTextQuery.h" |
22 | 22 |
23 #include "core/layout/LayoutBlockFlow.h" | 23 #include "core/layout/LayoutBlockFlow.h" |
24 #include "core/layout/LayoutInline.h" | 24 #include "core/layout/LayoutInline.h" |
25 #include "core/layout/line/InlineFlowBox.h" | 25 #include "core/layout/line/InlineFlowBox.h" |
26 #include "core/layout/svg/LayoutSVGInlineText.h" | 26 #include "core/layout/svg/LayoutSVGInlineText.h" |
| 27 #include "core/layout/svg/SVGTextFragment.h" |
27 #include "core/layout/svg/SVGTextMetrics.h" | 28 #include "core/layout/svg/SVGTextMetrics.h" |
28 #include "core/layout/svg/line/SVGInlineTextBox.h" | 29 #include "core/layout/svg/line/SVGInlineTextBox.h" |
29 #include "platform/FloatConversion.h" | 30 #include "platform/FloatConversion.h" |
30 #include "wtf/MathExtras.h" | 31 #include "wtf/MathExtras.h" |
| 32 #include "wtf/Vector.h" |
31 | 33 |
32 namespace blink { | 34 namespace blink { |
33 | 35 |
34 // Base structure for callback user data | 36 // Base structure for callback user data |
35 struct SVGTextQuery::Data { | 37 struct QueryData { |
36 Data() | 38 QueryData() |
37 : isVerticalText(false) | 39 : isVerticalText(false) |
38 , processedCharacters(0) | 40 , processedCharacters(0) |
39 , textLayoutObject(0) | 41 , textLayoutObject(0) |
40 , textBox(0) | 42 , textBox(0) |
41 { | 43 { |
42 } | 44 } |
43 | 45 |
44 bool isVerticalText; | 46 bool isVerticalText; |
45 unsigned processedCharacters; | 47 unsigned processedCharacters; |
46 LayoutSVGInlineText* textLayoutObject; | 48 LayoutSVGInlineText* textLayoutObject; |
(...skipping 23 matching lines...) Expand all Loading... |
70 // LayoutSVGInline only ever contains a single line box. | 72 // LayoutSVGInline only ever contains a single line box. |
71 InlineFlowBox* flowBox = layoutInline->firstLineBox(); | 73 InlineFlowBox* flowBox = layoutInline->firstLineBox(); |
72 ASSERT(flowBox == layoutInline->lastLineBox()); | 74 ASSERT(flowBox == layoutInline->lastLineBox()); |
73 return flowBox; | 75 return flowBox; |
74 } | 76 } |
75 | 77 |
76 ASSERT_NOT_REACHED(); | 78 ASSERT_NOT_REACHED(); |
77 return 0; | 79 return 0; |
78 } | 80 } |
79 | 81 |
80 SVGTextQuery::SVGTextQuery(LayoutObject* layoutObject) | 82 static void collectTextBoxesInFlowBox(InlineFlowBox* flowBox, Vector<SVGInlineTe
xtBox*>& textBoxes) |
81 { | |
82 collectTextBoxesInFlowBox(flowBoxForLayoutObject(layoutObject)); | |
83 } | |
84 | |
85 void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox) | |
86 { | 83 { |
87 if (!flowBox) | 84 if (!flowBox) |
88 return; | 85 return; |
89 | 86 |
90 for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnL
ine()) { | 87 for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnL
ine()) { |
91 if (child->isInlineFlowBox()) { | 88 if (child->isInlineFlowBox()) { |
92 // Skip generated content. | 89 // Skip generated content. |
93 if (!child->layoutObject().node()) | 90 if (!child->layoutObject().node()) |
94 continue; | 91 continue; |
95 | 92 |
96 collectTextBoxesInFlowBox(toInlineFlowBox(child)); | 93 collectTextBoxesInFlowBox(toInlineFlowBox(child), textBoxes); |
97 continue; | 94 continue; |
98 } | 95 } |
99 | 96 |
100 if (child->isSVGInlineTextBox()) | 97 if (child->isSVGInlineTextBox()) |
101 m_textBoxes.append(toSVGInlineTextBox(child)); | 98 textBoxes.append(toSVGInlineTextBox(child)); |
102 } | 99 } |
103 } | 100 } |
104 | 101 |
105 bool SVGTextQuery::executeQuery(Data* queryData, ProcessTextFragmentCallback fra
gmentCallback) const | 102 typedef bool ProcessTextFragmentCallback(QueryData*, const SVGTextFragment&); |
| 103 |
| 104 static bool executeQuery(LayoutObject* queryRoot, QueryData* queryData, ProcessT
extFragmentCallback fragmentCallback) |
106 { | 105 { |
| 106 Vector<SVGInlineTextBox*> textBoxes; |
| 107 collectTextBoxesInFlowBox(flowBoxForLayoutObject(queryRoot), textBoxes); |
| 108 |
107 unsigned processedCharacters = 0; | 109 unsigned processedCharacters = 0; |
108 unsigned textBoxCount = m_textBoxes.size(); | 110 unsigned textBoxCount = textBoxes.size(); |
109 | 111 |
110 // Loop over all text boxes | 112 // Loop over all text boxes |
111 for (unsigned textBoxPosition = 0; textBoxPosition < textBoxCount; ++textBox
Position) { | 113 for (unsigned textBoxPosition = 0; textBoxPosition < textBoxCount; ++textBox
Position) { |
112 queryData->textBox = m_textBoxes.at(textBoxPosition); | 114 queryData->textBox = textBoxes[textBoxPosition]; |
113 queryData->textLayoutObject = &toLayoutSVGInlineText(queryData->textBox-
>layoutObject()); | 115 queryData->textLayoutObject = &toLayoutSVGInlineText(queryData->textBox-
>layoutObject()); |
114 ASSERT(queryData->textLayoutObject->style()); | 116 ASSERT(queryData->textLayoutObject->style()); |
115 | 117 |
116 queryData->isVerticalText = queryData->textLayoutObject->style()->svgSty
le().isVerticalWritingMode(); | 118 queryData->isVerticalText = queryData->textLayoutObject->style()->svgSty
le().isVerticalWritingMode(); |
117 const Vector<SVGTextFragment>& fragments = queryData->textBox->textFragm
ents(); | 119 const Vector<SVGTextFragment>& fragments = queryData->textBox->textFragm
ents(); |
118 | 120 |
119 // Loop over all text fragments in this text box, firing a callback for
each. | 121 // Loop over all text fragments in this text box, firing a callback for
each. |
120 unsigned fragmentCount = fragments.size(); | 122 unsigned fragmentCount = fragments.size(); |
121 for (unsigned i = 0; i < fragmentCount; ++i) { | 123 for (unsigned i = 0; i < fragmentCount; ++i) { |
122 const SVGTextFragment& fragment = fragments.at(i); | 124 const SVGTextFragment& fragment = fragments.at(i); |
123 if ((this->*fragmentCallback)(queryData, fragment)) | 125 if (fragmentCallback(queryData, fragment)) |
124 return true; | 126 return true; |
125 | 127 |
126 processedCharacters += fragment.length; | 128 processedCharacters += fragment.length; |
127 } | 129 } |
128 | 130 |
129 queryData->processedCharacters = processedCharacters; | 131 queryData->processedCharacters = processedCharacters; |
130 } | 132 } |
131 | |
132 return false; | 133 return false; |
133 } | 134 } |
134 | 135 |
135 bool SVGTextQuery::mapStartEndPositionsIntoFragmentCoordinates(Data* queryData,
const SVGTextFragment& fragment, int& startPosition, int& endPosition) const | 136 static void modifyStartEndPositionsRespectingLigatures(const QueryData* queryDat
a, const SVGTextFragment& fragment, int& startPosition, int& endPosition) |
136 { | |
137 // Make <startPosition, endPosition> offsets relative to the current text bo
x. | |
138 startPosition -= queryData->processedCharacters; | |
139 endPosition -= queryData->processedCharacters; | |
140 | |
141 // Reuse the same logic used for text selection & painting, to map our | |
142 // query start/length into start/endPositions of the current text fragment. | |
143 if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragmen
t, startPosition, endPosition)) | |
144 return false; | |
145 | |
146 modifyStartEndPositionsRespectingLigatures(queryData, fragment, startPositio
n, endPosition); | |
147 ASSERT(startPosition < endPosition); | |
148 return true; | |
149 } | |
150 | |
151 void SVGTextQuery::modifyStartEndPositionsRespectingLigatures(Data* queryData, c
onst SVGTextFragment& fragment, int& startPosition, int& endPosition) const | |
152 { | 137 { |
153 const Vector<SVGTextMetrics>& textMetricsValues = queryData->textLayoutObjec
t->layoutAttributes()->textMetricsValues(); | 138 const Vector<SVGTextMetrics>& textMetricsValues = queryData->textLayoutObjec
t->layoutAttributes()->textMetricsValues(); |
154 | 139 |
155 unsigned textMetricsOffset = fragment.metricsListOffset; | 140 unsigned textMetricsOffset = fragment.metricsListOffset; |
156 int fragmentOffset = 0; | 141 int fragmentOffset = 0; |
157 int fragmentEnd = static_cast<int>(fragment.length); | 142 int fragmentEnd = static_cast<int>(fragment.length); |
158 | 143 |
159 // Find the text metrics cell that start at or contain the character startPo
sition. | 144 // Find the text metrics cell that start at or contain the character startPo
sition. |
160 while (fragmentOffset < fragmentEnd) { | 145 while (fragmentOffset < fragmentEnd) { |
161 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; | 146 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; |
(...skipping 11 matching lines...) Expand all Loading... |
173 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; | 158 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; |
174 fragmentOffset += metrics.length(); | 159 fragmentOffset += metrics.length(); |
175 if (fragmentOffset >= endPosition) | 160 if (fragmentOffset >= endPosition) |
176 break; | 161 break; |
177 textMetricsOffset++; | 162 textMetricsOffset++; |
178 } | 163 } |
179 | 164 |
180 endPosition = fragmentOffset; | 165 endPosition = fragmentOffset; |
181 } | 166 } |
182 | 167 |
| 168 static bool mapStartEndPositionsIntoFragmentCoordinates(const QueryData* queryDa
ta, const SVGTextFragment& fragment, int& startPosition, int& endPosition) |
| 169 { |
| 170 // Make <startPosition, endPosition> offsets relative to the current text bo
x. |
| 171 startPosition -= queryData->processedCharacters; |
| 172 endPosition -= queryData->processedCharacters; |
| 173 |
| 174 // Reuse the same logic used for text selection & painting, to map our |
| 175 // query start/length into start/endPositions of the current text fragment. |
| 176 if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragmen
t, startPosition, endPosition)) |
| 177 return false; |
| 178 |
| 179 modifyStartEndPositionsRespectingLigatures(queryData, fragment, startPositio
n, endPosition); |
| 180 ASSERT(startPosition < endPosition); |
| 181 return true; |
| 182 } |
| 183 |
183 // numberOfCharacters() implementation | 184 // numberOfCharacters() implementation |
184 bool SVGTextQuery::numberOfCharactersCallback(Data*, const SVGTextFragment&) con
st | 185 static bool numberOfCharactersCallback(QueryData*, const SVGTextFragment&) |
185 { | 186 { |
186 // no-op | 187 // no-op |
187 return false; | 188 return false; |
188 } | 189 } |
189 | 190 |
190 unsigned SVGTextQuery::numberOfCharacters() const | 191 unsigned SVGTextQuery::numberOfCharacters() const |
191 { | 192 { |
192 Data data; | 193 QueryData data; |
193 executeQuery(&data, &SVGTextQuery::numberOfCharactersCallback); | 194 executeQuery(m_queryRootLayoutObject, &data, numberOfCharactersCallback); |
194 return data.processedCharacters; | 195 return data.processedCharacters; |
195 } | 196 } |
196 | 197 |
197 // textLength() implementation | 198 // textLength() implementation |
198 struct TextLengthData : SVGTextQuery::Data { | 199 struct TextLengthData : QueryData { |
199 TextLengthData() | 200 TextLengthData() |
200 : textLength(0) | 201 : textLength(0) |
201 { | 202 { |
202 } | 203 } |
203 | 204 |
204 float textLength; | 205 float textLength; |
205 }; | 206 }; |
206 | 207 |
207 bool SVGTextQuery::textLengthCallback(Data* queryData, const SVGTextFragment& fr
agment) const | 208 static bool textLengthCallback(QueryData* queryData, const SVGTextFragment& frag
ment) |
208 { | 209 { |
209 TextLengthData* data = static_cast<TextLengthData*>(queryData); | 210 TextLengthData* data = static_cast<TextLengthData*>(queryData); |
210 data->textLength += queryData->isVerticalText ? fragment.height : fragment.w
idth; | 211 data->textLength += queryData->isVerticalText ? fragment.height : fragment.w
idth; |
211 return false; | 212 return false; |
212 } | 213 } |
213 | 214 |
214 float SVGTextQuery::textLength() const | 215 float SVGTextQuery::textLength() const |
215 { | 216 { |
216 TextLengthData data; | 217 TextLengthData data; |
217 executeQuery(&data, &SVGTextQuery::textLengthCallback); | 218 executeQuery(m_queryRootLayoutObject, &data, textLengthCallback); |
218 return data.textLength; | 219 return data.textLength; |
219 } | 220 } |
220 | 221 |
221 // subStringLength() implementation | 222 // subStringLength() implementation |
222 struct SubStringLengthData : SVGTextQuery::Data { | 223 struct SubStringLengthData : QueryData { |
223 SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) | 224 SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) |
224 : startPosition(queryStartPosition) | 225 : startPosition(queryStartPosition) |
225 , length(queryLength) | 226 , length(queryLength) |
226 , subStringLength(0) | 227 , subStringLength(0) |
227 { | 228 { |
228 } | 229 } |
229 | 230 |
230 unsigned startPosition; | 231 unsigned startPosition; |
231 unsigned length; | 232 unsigned length; |
232 | 233 |
233 float subStringLength; | 234 float subStringLength; |
234 }; | 235 }; |
235 | 236 |
236 bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragmen
t& fragment) const | 237 static bool subStringLengthCallback(QueryData* queryData, const SVGTextFragment&
fragment) |
237 { | 238 { |
238 SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); | 239 SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); |
239 | 240 |
240 int startPosition = data->startPosition; | 241 int startPosition = data->startPosition; |
241 int endPosition = startPosition + data->length; | 242 int endPosition = startPosition + data->length; |
242 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 243 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
243 return false; | 244 return false; |
244 | 245 |
245 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->te
xtLayoutObject, fragment.characterOffset + startPosition, endPosition - startPos
ition, queryData->textBox->direction()); | 246 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->te
xtLayoutObject, fragment.characterOffset + startPosition, endPosition - startPos
ition, queryData->textBox->direction()); |
246 data->subStringLength += queryData->isVerticalText ? metrics.height() : metr
ics.width(); | 247 data->subStringLength += queryData->isVerticalText ? metrics.height() : metr
ics.width(); |
247 return false; | 248 return false; |
248 } | 249 } |
249 | 250 |
250 float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) con
st | 251 float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) con
st |
251 { | 252 { |
252 SubStringLengthData data(startPosition, length); | 253 SubStringLengthData data(startPosition, length); |
253 executeQuery(&data, &SVGTextQuery::subStringLengthCallback); | 254 executeQuery(m_queryRootLayoutObject, &data, subStringLengthCallback); |
254 return data.subStringLength; | 255 return data.subStringLength; |
255 } | 256 } |
256 | 257 |
257 // startPositionOfCharacter() implementation | 258 // startPositionOfCharacter() implementation |
258 struct StartPositionOfCharacterData : SVGTextQuery::Data { | 259 struct StartPositionOfCharacterData : QueryData { |
259 StartPositionOfCharacterData(unsigned queryPosition) | 260 StartPositionOfCharacterData(unsigned queryPosition) |
260 : position(queryPosition) | 261 : position(queryPosition) |
261 { | 262 { |
262 } | 263 } |
263 | 264 |
264 unsigned position; | 265 unsigned position; |
265 FloatPoint startPosition; | 266 FloatPoint startPosition; |
266 }; | 267 }; |
267 | 268 |
268 static FloatPoint calculateGlyphPositionWithoutTransform(const SVGTextQuery::Dat
a* queryData, const SVGTextFragment& fragment, int offsetInFragment) | 269 static FloatPoint calculateGlyphPositionWithoutTransform(const QueryData* queryD
ata, const SVGTextFragment& fragment, int offsetInFragment) |
269 { | 270 { |
270 float glyphOffsetInDirection = 0; | 271 float glyphOffsetInDirection = 0; |
271 if (offsetInFragment) { | 272 if (offsetInFragment) { |
272 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData
->textLayoutObject, fragment.characterOffset, offsetInFragment, queryData->textB
ox->direction()); | 273 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData
->textLayoutObject, fragment.characterOffset, offsetInFragment, queryData->textB
ox->direction()); |
273 if (queryData->isVerticalText) | 274 if (queryData->isVerticalText) |
274 glyphOffsetInDirection = metrics.height(); | 275 glyphOffsetInDirection = metrics.height(); |
275 else | 276 else |
276 glyphOffsetInDirection = metrics.width(); | 277 glyphOffsetInDirection = metrics.width(); |
277 } | 278 } |
278 | 279 |
279 if (!queryData->textBox->isLeftToRightDirection()) { | 280 if (!queryData->textBox->isLeftToRightDirection()) { |
280 float fragmentExtent = queryData->isVerticalText ? fragment.height : fra
gment.width; | 281 float fragmentExtent = queryData->isVerticalText ? fragment.height : fra
gment.width; |
281 glyphOffsetInDirection = fragmentExtent - glyphOffsetInDirection; | 282 glyphOffsetInDirection = fragmentExtent - glyphOffsetInDirection; |
282 } | 283 } |
283 | 284 |
284 FloatPoint glyphPosition(fragment.x, fragment.y); | 285 FloatPoint glyphPosition(fragment.x, fragment.y); |
285 if (queryData->isVerticalText) | 286 if (queryData->isVerticalText) |
286 glyphPosition.move(0, glyphOffsetInDirection); | 287 glyphPosition.move(0, glyphOffsetInDirection); |
287 else | 288 else |
288 glyphPosition.move(glyphOffsetInDirection, 0); | 289 glyphPosition.move(glyphOffsetInDirection, 0); |
289 | 290 |
290 return glyphPosition; | 291 return glyphPosition; |
291 } | 292 } |
292 | 293 |
293 static FloatPoint calculateGlyphPosition(const SVGTextQuery::Data* queryData, co
nst SVGTextFragment& fragment, int offsetInFragment) | 294 static FloatPoint calculateGlyphPosition(const QueryData* queryData, const SVGTe
xtFragment& fragment, int offsetInFragment) |
294 { | 295 { |
295 FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData,
fragment, offsetInFragment); | 296 FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData,
fragment, offsetInFragment); |
296 AffineTransform fragmentTransform; | 297 AffineTransform fragmentTransform; |
297 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::Transfor
mIgnoringTextLength); | 298 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::Transfor
mIgnoringTextLength); |
298 if (!fragmentTransform.isIdentity()) | 299 if (!fragmentTransform.isIdentity()) |
299 glyphPosition = fragmentTransform.mapPoint(glyphPosition); | 300 glyphPosition = fragmentTransform.mapPoint(glyphPosition); |
300 | 301 |
301 return glyphPosition; | 302 return glyphPosition; |
302 } | 303 } |
303 | 304 |
304 bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGTe
xtFragment& fragment) const | 305 static bool startPositionOfCharacterCallback(QueryData* queryData, const SVGText
Fragment& fragment) |
305 { | 306 { |
306 StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterDat
a*>(queryData); | 307 StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterDat
a*>(queryData); |
307 | 308 |
308 int startPosition = data->position; | 309 int startPosition = data->position; |
309 int endPosition = startPosition + 1; | 310 int endPosition = startPosition + 1; |
310 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 311 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
311 return false; | 312 return false; |
312 | 313 |
313 data->startPosition = calculateGlyphPosition(queryData, fragment, startPosit
ion); | 314 data->startPosition = calculateGlyphPosition(queryData, fragment, startPosit
ion); |
314 return true; | 315 return true; |
315 } | 316 } |
316 | 317 |
317 FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const | 318 FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const |
318 { | 319 { |
319 StartPositionOfCharacterData data(position); | 320 StartPositionOfCharacterData data(position); |
320 executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback); | 321 executeQuery(m_queryRootLayoutObject, &data, startPositionOfCharacterCallbac
k); |
321 return data.startPosition; | 322 return data.startPosition; |
322 } | 323 } |
323 | 324 |
324 // endPositionOfCharacter() implementation | 325 // endPositionOfCharacter() implementation |
325 struct EndPositionOfCharacterData : SVGTextQuery::Data { | 326 struct EndPositionOfCharacterData : QueryData { |
326 EndPositionOfCharacterData(unsigned queryPosition) | 327 EndPositionOfCharacterData(unsigned queryPosition) |
327 : position(queryPosition) | 328 : position(queryPosition) |
328 { | 329 { |
329 } | 330 } |
330 | 331 |
331 unsigned position; | 332 unsigned position; |
332 FloatPoint endPosition; | 333 FloatPoint endPosition; |
333 }; | 334 }; |
334 | 335 |
335 bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGText
Fragment& fragment) const | 336 static bool endPositionOfCharacterCallback(QueryData* queryData, const SVGTextFr
agment& fragment) |
336 { | 337 { |
337 EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(
queryData); | 338 EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(
queryData); |
338 | 339 |
339 int startPosition = data->position; | 340 int startPosition = data->position; |
340 int endPosition = startPosition + 1; | 341 int endPosition = startPosition + 1; |
341 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 342 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
342 return false; | 343 return false; |
343 | 344 |
344 // TODO(fs): mapStartEndPositionsIntoFragmentCoordinates(...) above applies | 345 // TODO(fs): mapStartEndPositionsIntoFragmentCoordinates(...) above applies |
345 // some heuristics for ligatures, so why not just use endPosition here? | 346 // some heuristics for ligatures, so why not just use endPosition here? |
346 // (rather than startPosition+1) | 347 // (rather than startPosition+1) |
347 data->endPosition = calculateGlyphPosition(queryData, fragment, startPositio
n + 1); | 348 data->endPosition = calculateGlyphPosition(queryData, fragment, startPositio
n + 1); |
348 return true; | 349 return true; |
349 } | 350 } |
350 | 351 |
351 FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const | 352 FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const |
352 { | 353 { |
353 EndPositionOfCharacterData data(position); | 354 EndPositionOfCharacterData data(position); |
354 executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback); | 355 executeQuery(m_queryRootLayoutObject, &data, endPositionOfCharacterCallback)
; |
355 return data.endPosition; | 356 return data.endPosition; |
356 } | 357 } |
357 | 358 |
358 // rotationOfCharacter() implementation | 359 // rotationOfCharacter() implementation |
359 struct RotationOfCharacterData : SVGTextQuery::Data { | 360 struct RotationOfCharacterData : QueryData { |
360 RotationOfCharacterData(unsigned queryPosition) | 361 RotationOfCharacterData(unsigned queryPosition) |
361 : position(queryPosition) | 362 : position(queryPosition) |
362 , rotation(0) | 363 , rotation(0) |
363 { | 364 { |
364 } | 365 } |
365 | 366 |
366 unsigned position; | 367 unsigned position; |
367 float rotation; | 368 float rotation; |
368 }; | 369 }; |
369 | 370 |
370 bool SVGTextQuery::rotationOfCharacterCallback(Data* queryData, const SVGTextFra
gment& fragment) const | 371 static bool rotationOfCharacterCallback(QueryData* queryData, const SVGTextFragm
ent& fragment) |
371 { | 372 { |
372 RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryD
ata); | 373 RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryD
ata); |
373 | 374 |
374 int startPosition = data->position; | 375 int startPosition = data->position; |
375 int endPosition = startPosition + 1; | 376 int endPosition = startPosition + 1; |
376 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 377 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
377 return false; | 378 return false; |
378 | 379 |
379 AffineTransform fragmentTransform; | 380 AffineTransform fragmentTransform; |
380 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::Transfor
mIgnoringTextLength); | 381 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::Transfor
mIgnoringTextLength); |
381 if (fragmentTransform.isIdentity()) { | 382 if (fragmentTransform.isIdentity()) { |
382 data->rotation = 0; | 383 data->rotation = 0; |
383 } else { | 384 } else { |
384 fragmentTransform.scale(1 / fragmentTransform.xScale(), 1 / fragmentTran
sform.yScale()); | 385 fragmentTransform.scale(1 / fragmentTransform.xScale(), 1 / fragmentTran
sform.yScale()); |
385 data->rotation = narrowPrecisionToFloat(rad2deg(atan2(fragmentTransform.
b(), fragmentTransform.a()))); | 386 data->rotation = narrowPrecisionToFloat(rad2deg(atan2(fragmentTransform.
b(), fragmentTransform.a()))); |
386 } | 387 } |
387 | 388 |
388 return true; | 389 return true; |
389 } | 390 } |
390 | 391 |
391 float SVGTextQuery::rotationOfCharacter(unsigned position) const | 392 float SVGTextQuery::rotationOfCharacter(unsigned position) const |
392 { | 393 { |
393 RotationOfCharacterData data(position); | 394 RotationOfCharacterData data(position); |
394 executeQuery(&data, &SVGTextQuery::rotationOfCharacterCallback); | 395 executeQuery(m_queryRootLayoutObject, &data, rotationOfCharacterCallback); |
395 return data.rotation; | 396 return data.rotation; |
396 } | 397 } |
397 | 398 |
398 // extentOfCharacter() implementation | 399 // extentOfCharacter() implementation |
399 struct ExtentOfCharacterData : SVGTextQuery::Data { | 400 struct ExtentOfCharacterData : QueryData { |
400 ExtentOfCharacterData(unsigned queryPosition) | 401 ExtentOfCharacterData(unsigned queryPosition) |
401 : position(queryPosition) | 402 : position(queryPosition) |
402 { | 403 { |
403 } | 404 } |
404 | 405 |
405 unsigned position; | 406 unsigned position; |
406 FloatRect extent; | 407 FloatRect extent; |
407 }; | 408 }; |
408 | 409 |
409 const SVGTextMetrics& findMetricsForCharacter(const Vector<SVGTextMetrics>& text
MetricsValues, const SVGTextFragment& fragment, unsigned startInFragment) | 410 const SVGTextMetrics& findMetricsForCharacter(const Vector<SVGTextMetrics>& text
MetricsValues, const SVGTextFragment& fragment, unsigned startInFragment) |
410 { | 411 { |
411 // Find the text metrics cell that start at or contain the character at |sta
rtInFragment|. | 412 // Find the text metrics cell that start at or contain the character at |sta
rtInFragment|. |
412 unsigned textMetricsOffset = fragment.metricsListOffset; | 413 unsigned textMetricsOffset = fragment.metricsListOffset; |
413 unsigned fragmentOffset = 0; | 414 unsigned fragmentOffset = 0; |
414 while (fragmentOffset < fragment.length) { | 415 while (fragmentOffset < fragment.length) { |
415 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset++]; | 416 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset++]; |
416 unsigned glyphEnd = fragmentOffset + metrics.length(); | 417 unsigned glyphEnd = fragmentOffset + metrics.length(); |
417 if (startInFragment < glyphEnd) | 418 if (startInFragment < glyphEnd) |
418 break; | 419 break; |
419 fragmentOffset = glyphEnd; | 420 fragmentOffset = glyphEnd; |
420 } | 421 } |
421 return textMetricsValues[textMetricsOffset - 1]; | 422 return textMetricsValues[textMetricsOffset - 1]; |
422 } | 423 } |
423 | 424 |
424 static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const
SVGTextFragment& fragment, int startPosition, FloatRect& extent) | 425 static inline void calculateGlyphBoundaries(const QueryData* queryData, const SV
GTextFragment& fragment, int startPosition, FloatRect& extent) |
425 { | 426 { |
426 float scalingFactor = queryData->textLayoutObject->scalingFactor(); | 427 float scalingFactor = queryData->textLayoutObject->scalingFactor(); |
427 ASSERT(scalingFactor); | 428 ASSERT(scalingFactor); |
428 | 429 |
429 FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData,
fragment, startPosition); | 430 FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData,
fragment, startPosition); |
430 glyphPosition.move(0, -queryData->textLayoutObject->scaledFont().fontMetrics
().floatAscent() / scalingFactor); | 431 glyphPosition.move(0, -queryData->textLayoutObject->scaledFont().fontMetrics
().floatAscent() / scalingFactor); |
431 extent.setLocation(glyphPosition); | 432 extent.setLocation(glyphPosition); |
432 | 433 |
433 // Use the SVGTextMetrics computed by SVGTextMetricsBuilder (which spends | 434 // Use the SVGTextMetrics computed by SVGTextMetricsBuilder (which spends |
434 // time attempting to compute more correct glyph bounds already, handling | 435 // time attempting to compute more correct glyph bounds already, handling |
(...skipping 25 matching lines...) Expand all Loading... |
460 float scalingFactor = textLayoutObject.scalingFactor(); | 461 float scalingFactor = textLayoutObject.scalingFactor(); |
461 ASSERT(scalingFactor); | 462 ASSERT(scalingFactor); |
462 float baseline = textLayoutObject.scaledFont().fontMetrics().floatAscent() /
scalingFactor; | 463 float baseline = textLayoutObject.scaledFont().fontMetrics().floatAscent() /
scalingFactor; |
463 | 464 |
464 AffineTransform fragmentTransform; | 465 AffineTransform fragmentTransform; |
465 FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fr
agment.height); | 466 FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fr
agment.height); |
466 fragment.buildFragmentTransform(fragmentTransform); | 467 fragment.buildFragmentTransform(fragmentTransform); |
467 return fragmentTransform.mapRect(fragmentRect); | 468 return fragmentTransform.mapRect(fragmentRect); |
468 } | 469 } |
469 | 470 |
470 bool SVGTextQuery::extentOfCharacterCallback(Data* queryData, const SVGTextFragm
ent& fragment) const | 471 static bool extentOfCharacterCallback(QueryData* queryData, const SVGTextFragmen
t& fragment) |
471 { | 472 { |
472 ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData)
; | 473 ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData)
; |
473 | 474 |
474 int startPosition = data->position; | 475 int startPosition = data->position; |
475 int endPosition = startPosition + 1; | 476 int endPosition = startPosition + 1; |
476 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 477 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
477 return false; | 478 return false; |
478 | 479 |
479 calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent); | 480 calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent); |
480 return true; | 481 return true; |
481 } | 482 } |
482 | 483 |
483 FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const | 484 FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const |
484 { | 485 { |
485 ExtentOfCharacterData data(position); | 486 ExtentOfCharacterData data(position); |
486 executeQuery(&data, &SVGTextQuery::extentOfCharacterCallback); | 487 executeQuery(m_queryRootLayoutObject, &data, extentOfCharacterCallback); |
487 return data.extent; | 488 return data.extent; |
488 } | 489 } |
489 | 490 |
490 // characterNumberAtPosition() implementation | 491 // characterNumberAtPosition() implementation |
491 struct CharacterNumberAtPositionData : SVGTextQuery::Data { | 492 struct CharacterNumberAtPositionData : QueryData { |
492 CharacterNumberAtPositionData(const FloatPoint& queryPosition) | 493 CharacterNumberAtPositionData(const FloatPoint& queryPosition) |
493 : position(queryPosition) | 494 : position(queryPosition) |
494 { | 495 { |
495 } | 496 } |
496 | 497 |
497 FloatPoint position; | 498 FloatPoint position; |
498 }; | 499 }; |
499 | 500 |
500 bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGT
extFragment& fragment) const | 501 static bool characterNumberAtPositionCallback(QueryData* queryData, const SVGTex
tFragment& fragment) |
501 { | 502 { |
502 CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionD
ata*>(queryData); | 503 CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionD
ata*>(queryData); |
503 | 504 |
504 // Test the query point against the bounds of the entire fragment first. | 505 // Test the query point against the bounds of the entire fragment first. |
505 FloatRect fragmentExtents = calculateFragmentBoundaries(*queryData->textLayo
utObject, fragment); | 506 FloatRect fragmentExtents = calculateFragmentBoundaries(*queryData->textLayo
utObject, fragment); |
506 if (!fragmentExtents.contains(data->position)) | 507 if (!fragmentExtents.contains(data->position)) |
507 return false; | 508 return false; |
508 | 509 |
509 // Iterate through the glyphs in this fragment, and check if their extents | 510 // Iterate through the glyphs in this fragment, and check if their extents |
510 // contain the query point. | 511 // contain the query point. |
(...skipping 12 matching lines...) Expand all Loading... |
523 } | 524 } |
524 fragmentOffset += textMetrics[textMetricsOffset].length(); | 525 fragmentOffset += textMetrics[textMetricsOffset].length(); |
525 textMetricsOffset++; | 526 textMetricsOffset++; |
526 } | 527 } |
527 return false; | 528 return false; |
528 } | 529 } |
529 | 530 |
530 int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const | 531 int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const |
531 { | 532 { |
532 CharacterNumberAtPositionData data(position); | 533 CharacterNumberAtPositionData data(position); |
533 if (!executeQuery(&data, &SVGTextQuery::characterNumberAtPositionCallback)) | 534 if (!executeQuery(m_queryRootLayoutObject, &data, characterNumberAtPositionC
allback)) |
534 return -1; | 535 return -1; |
535 | 536 |
536 return data.processedCharacters; | 537 return data.processedCharacters; |
537 } | 538 } |
538 | 539 |
539 } | 540 } |
OLD | NEW |