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
22#include "core/rendering/svg/SVGTextMetricsBuilder.h"
23
24#include "core/rendering/svg/RenderSVGInlineText.h"
25#include "core/rendering/svg/RenderSVGText.h"
26
27namespace WebCore {
28
29SVGTextMetricsBuilder::SVGTextMetricsBuilder()
30    : m_text(0)
31    , m_run(static_cast<const UChar*>(0), 0)
32    , m_textPosition(0)
33    , m_isComplexText(false)
34    , m_totalWidth(0)
35{
36}
37
38inline bool SVGTextMetricsBuilder::currentCharacterStartsSurrogatePair() const
39{
40    return U16_IS_LEAD(m_run[m_textPosition]) && int(m_textPosition + 1) < m_run.charactersLength() && U16_IS_TRAIL(m_run[m_textPosition + 1]);
41}
42
43bool SVGTextMetricsBuilder::advance()
44{
45    m_textPosition += m_currentMetrics.length();
46    if (int(m_textPosition) >= m_run.charactersLength())
47        return false;
48
49    if (m_isComplexText)
50        advanceComplexText();
51    else
52        advanceSimpleText();
53
54    return m_currentMetrics.length() > 0;
55}
56
57void SVGTextMetricsBuilder::advanceSimpleText()
58{
59    GlyphBuffer glyphBuffer;
60    unsigned metricsLength = m_simpleWidthIterator->advance(m_textPosition + 1, &glyphBuffer);
61    if (!metricsLength) {
62        m_currentMetrics = SVGTextMetrics();
63        return;
64    }
65
66    float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth;
67    m_totalWidth = m_simpleWidthIterator->runWidthSoFar();
68
69#if ENABLE(SVG_FONTS)
70    m_currentMetrics = SVGTextMetrics(m_text, m_textPosition, metricsLength, currentWidth, m_simpleWidthIterator->lastGlyphName());
71#else
72    m_currentMetrics = SVGTextMetrics(m_text, m_textPosition, metricsLength, currentWidth, emptyString());
73#endif
74}
75
76void SVGTextMetricsBuilder::advanceComplexText()
77{
78    unsigned metricsLength = currentCharacterStartsSurrogatePair() ? 2 : 1;
79    m_currentMetrics = SVGTextMetrics::measureCharacterRange(m_text, m_textPosition, metricsLength);
80    m_complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, 0, m_textPosition + metricsLength);
81    ASSERT(m_currentMetrics.length() == metricsLength);
82
83    // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
84    // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
85    // So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is
86    // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
87    float currentWidth = m_complexStartToCurrentMetrics.width() - m_totalWidth;
88    if (currentWidth != m_currentMetrics.width())
89        m_currentMetrics.setWidth(currentWidth);
90
91    m_totalWidth = m_complexStartToCurrentMetrics.width();
92}
93
94void SVGTextMetricsBuilder::initializeMeasurementWithTextRenderer(RenderSVGInlineText* text)
95{
96    m_text = text;
97    m_textPosition = 0;
98    m_currentMetrics = SVGTextMetrics();
99    m_complexStartToCurrentMetrics = SVGTextMetrics();
100    m_totalWidth = 0;
101
102    const Font& scaledFont = text->scaledFont();
103    m_run = SVGTextMetrics::constructTextRun(text, 0, text->textLength());
104    m_isComplexText = scaledFont.codePath(m_run) == Font::Complex;
105
106    if (m_isComplexText)
107        m_simpleWidthIterator.clear();
108    else
109        m_simpleWidthIterator = adoptPtr(new WidthIterator(&scaledFont, m_run));
110}
111
112struct MeasureTextData {
113    MeasureTextData(SVGCharacterDataMap* characterDataMap)
114        : allCharactersMap(characterDataMap)
115        , hasLastCharacter(false)
116        , lastCharacter(0)
117        , processRenderer(false)
118        , valueListPosition(0)
119        , skippedCharacters(0)
120    {
121    }
122
123    SVGCharacterDataMap* allCharactersMap;
124    bool hasLastCharacter;
125    UChar lastCharacter;
126    bool processRenderer;
127    unsigned valueListPosition;
128    unsigned skippedCharacters;
129};
130
131void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data)
132{
133    ASSERT(text);
134
135    SVGTextLayoutAttributes* attributes = text->layoutAttributes();
136    Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues();
137    if (data->processRenderer) {
138        if (data->allCharactersMap)
139            attributes->clear();
140        else
141            textMetricsValues->clear();
142    }
143
144    initializeMeasurementWithTextRenderer(text);
145    bool preserveWhiteSpace = text->style()->whiteSpace() == PRE;
146    int surrogatePairCharacters = 0;
147
148    while (advance()) {
149        UChar currentCharacter = m_run[m_textPosition];
150        if (currentCharacter == ' ' && !preserveWhiteSpace && (!data->hasLastCharacter || data->lastCharacter == ' ')) {
151            if (data->processRenderer)
152                textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics));
153            if (data->allCharactersMap)
154                data->skippedCharacters += m_currentMetrics.length();
155            continue;
156        }
157
158        if (data->processRenderer) {
159            if (data->allCharactersMap) {
160                const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + m_textPosition - data->skippedCharacters - surrogatePairCharacters + 1);
161                if (it != data->allCharactersMap->end())
162                    attributes->characterDataMap().set(m_textPosition + 1, it->value);
163            }
164            textMetricsValues->append(m_currentMetrics);
165        }
166
167        if (data->allCharactersMap && currentCharacterStartsSurrogatePair())
168            surrogatePairCharacters++;
169
170        data->hasLastCharacter = true;
171        data->lastCharacter = currentCharacter;
172    }
173
174    if (!data->allCharactersMap)
175        return;
176
177    data->valueListPosition += m_textPosition - data->skippedCharacters;
178    data->skippedCharacters = 0;
179}
180
181void SVGTextMetricsBuilder::walkTree(RenderObject* start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data)
182{
183    for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
184        if (child->isSVGInlineText()) {
185            RenderSVGInlineText* text = toRenderSVGInlineText(child);
186            if (stopAtLeaf && stopAtLeaf != text) {
187                data->processRenderer = false;
188                measureTextRenderer(text, data);
189                continue;
190            }
191
192            data->processRenderer = true;
193            measureTextRenderer(text, data);
194            if (stopAtLeaf)
195                return;
196
197            continue;
198        }
199
200        if (!child->isSVGInline())
201            continue;
202
203        walkTree(child, stopAtLeaf, data);
204    }
205}
206
207void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text)
208{
209    ASSERT(text);
210
211    RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text);
212    if (!textRoot)
213        return;
214
215    MeasureTextData data(0);
216    walkTree(textRoot, text, &data);
217}
218
219void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(RenderSVGText* textRoot, RenderSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap)
220{
221    ASSERT(textRoot);
222    MeasureTextData data(&allCharactersMap);
223    walkTree(textRoot, stopAtLeaf, &data);
224}
225
226}
227