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