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