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/RenderSVGInline.h"
25#include "core/rendering/svg/RenderSVGInlineText.h"
26#include "core/rendering/svg/RenderSVGText.h"
27#include "core/rendering/svg/SVGTextMetrics.h"
28#include "platform/fonts/GlyphBuffer.h"
29#include "platform/fonts/WidthIterator.h"
30#include "platform/text/BidiCharacterRun.h"
31#include "platform/text/BidiResolver.h"
32#include "platform/text/TextDirection.h"
33#include "platform/text/TextPath.h"
34#include "platform/text/TextRun.h"
35#include "platform/text/TextRunIterator.h"
36#include "wtf/Vector.h"
37
38namespace blink {
39
40class SVGTextMetricsCalculator {
41public:
42    SVGTextMetricsCalculator(RenderSVGInlineText*);
43    ~SVGTextMetricsCalculator();
44
45    SVGTextMetrics computeMetricsForCharacter(unsigned textPosition);
46    unsigned textLength() const { return static_cast<unsigned>(m_run.charactersLength()); }
47
48    bool characterStartsSurrogatePair(unsigned textPosition) const
49    {
50        return U16_IS_LEAD(m_run[textPosition]) && textPosition + 1 < textLength() && U16_IS_TRAIL(m_run[textPosition + 1]);
51    }
52    bool characterIsWhiteSpace(unsigned textPosition) const
53    {
54        return m_run[textPosition] == ' ';
55    }
56
57private:
58    void setupBidiRuns();
59    SVGTextMetrics computeMetricsForCharacterSimple(unsigned textPosition);
60    SVGTextMetrics computeMetricsForCharacterComplex(unsigned textPosition);
61
62    RenderSVGInlineText* m_text;
63    BidiCharacterRun* m_bidiRun;
64    TextRun m_run;
65    BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver;
66    bool m_isComplexText;
67    float m_totalWidth;
68    TextDirection m_textDirection;
69
70    // Simple text only.
71    OwnPtr<WidthIterator> m_simpleWidthIterator;
72};
73
74SVGTextMetricsCalculator::SVGTextMetricsCalculator(RenderSVGInlineText* text)
75    : m_text(text)
76    , m_bidiRun(0)
77    , m_run(SVGTextMetrics::constructTextRun(text, 0, text->textLength()))
78    , m_isComplexText(false)
79    , m_totalWidth(0)
80{
81    const Font& scaledFont = text->scaledFont();
82    CodePath codePath = scaledFont.codePath(m_run);
83    m_isComplexText = codePath == ComplexPath;
84    m_run.setCharacterScanForCodePath(!m_isComplexText);
85
86    if (!m_isComplexText)
87        m_simpleWidthIterator = adoptPtr(new WidthIterator(&scaledFont, m_run));
88    else
89        setupBidiRuns();
90}
91
92SVGTextMetricsCalculator::~SVGTextMetricsCalculator()
93{
94    if (m_bidiRun)
95        m_bidiResolver.runs().deleteRuns();
96}
97
98void SVGTextMetricsCalculator::setupBidiRuns()
99{
100    RenderStyle* style = m_text->style();
101    m_textDirection = style->direction();
102    if (isOverride(style->unicodeBidi()))
103        return;
104
105    BidiStatus status(LTR, false);
106    status.last = status.lastStrong = WTF::Unicode::OtherNeutral;
107    m_bidiResolver.setStatus(status);
108    m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0));
109    const bool hardLineBreak = false;
110    const bool reorderRuns = false;
111    m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()), NoVisualOverride, hardLineBreak, reorderRuns);
112    BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs();
113    m_bidiRun = bidiRuns.firstRun();
114}
115
116SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterSimple(unsigned textPosition)
117{
118    GlyphBuffer glyphBuffer;
119    unsigned metricsLength = m_simpleWidthIterator->advance(textPosition + 1, &glyphBuffer);
120    if (!metricsLength)
121        return SVGTextMetrics();
122
123    float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth;
124    m_totalWidth = m_simpleWidthIterator->runWidthSoFar();
125
126    Glyph glyphId = glyphBuffer.glyphAt(0);
127    return SVGTextMetrics(m_text, textPosition, metricsLength, currentWidth, glyphId);
128}
129
130SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterComplex(unsigned textPosition)
131{
132    unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1;
133    SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textPosition, metricsLength, m_textDirection);
134    ASSERT(metrics.length() == metricsLength);
135
136    unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0;
137    ASSERT(startPosition <= textPosition);
138    SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_textDirection);
139    // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken
140    // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping.
141    // So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is
142    // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated.
143    float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth;
144    if (currentWidth != metrics.width())
145        metrics.setWidth(currentWidth);
146
147    m_totalWidth = complexStartToCurrentMetrics.width();
148    return metrics;
149}
150
151SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacter(unsigned textPosition)
152{
153    if (m_bidiRun) {
154        if (textPosition >= static_cast<unsigned>(m_bidiRun->stop())) {
155            m_bidiRun = m_bidiRun->next();
156            // New BiDi run means new reference position for measurements, so reset |m_totalWidth|.
157            m_totalWidth = 0;
158        }
159        ASSERT(m_bidiRun);
160        ASSERT(static_cast<int>(textPosition) < m_bidiRun->stop());
161        m_textDirection = m_bidiRun->direction();
162    }
163
164    if (m_isComplexText)
165        return computeMetricsForCharacterComplex(textPosition);
166
167    return computeMetricsForCharacterSimple(textPosition);
168}
169
170struct MeasureTextData {
171    MeasureTextData(SVGCharacterDataMap* characterDataMap)
172        : allCharactersMap(characterDataMap)
173        , lastCharacterWasWhiteSpace(true)
174        , valueListPosition(0)
175    {
176    }
177
178    SVGCharacterDataMap* allCharactersMap;
179    bool lastCharacterWasWhiteSpace;
180    unsigned valueListPosition;
181};
182
183static void measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data, bool processRenderer)
184{
185    ASSERT(text);
186
187    SVGTextLayoutAttributes* attributes = text->layoutAttributes();
188    Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues();
189    if (processRenderer) {
190        if (data->allCharactersMap)
191            attributes->clear();
192        else
193            textMetricsValues->clear();
194    }
195
196    SVGTextMetricsCalculator calculator(text);
197    bool preserveWhiteSpace = text->style()->whiteSpace() == PRE;
198    unsigned surrogatePairCharacters = 0;
199    unsigned skippedCharacters = 0;
200    unsigned textPosition = 0;
201    unsigned textLength = calculator.textLength();
202
203    SVGTextMetrics currentMetrics;
204    for (; textPosition < textLength; textPosition += currentMetrics.length()) {
205        currentMetrics = calculator.computeMetricsForCharacter(textPosition);
206        if (!currentMetrics.length())
207            break;
208
209        bool characterIsWhiteSpace = calculator.characterIsWhiteSpace(textPosition);
210        if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterWasWhiteSpace) {
211            if (processRenderer)
212                textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics));
213            if (data->allCharactersMap)
214                skippedCharacters += currentMetrics.length();
215            continue;
216        }
217
218        if (processRenderer) {
219            if (data->allCharactersMap) {
220                const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + textPosition - skippedCharacters - surrogatePairCharacters + 1);
221                if (it != data->allCharactersMap->end())
222                    attributes->characterDataMap().set(textPosition + 1, it->value);
223            }
224            textMetricsValues->append(currentMetrics);
225        }
226
227        if (data->allCharactersMap && calculator.characterStartsSurrogatePair(textPosition))
228            surrogatePairCharacters++;
229
230        data->lastCharacterWasWhiteSpace = characterIsWhiteSpace;
231    }
232
233    if (!data->allCharactersMap)
234        return;
235
236    data->valueListPosition += textPosition - skippedCharacters;
237}
238
239static void walkTree(RenderSVGText* start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data)
240{
241    RenderObject* child = start->firstChild();
242    while (child) {
243        if (child->isSVGInlineText()) {
244            RenderSVGInlineText* text = toRenderSVGInlineText(child);
245            measureTextRenderer(text, data, !stopAtLeaf || stopAtLeaf == text);
246            if (stopAtLeaf && stopAtLeaf == text)
247                return;
248        } else if (child->isSVGInline()) {
249            // Visit children of text content elements.
250            if (RenderObject* inlineChild = toRenderSVGInline(child)->firstChild()) {
251                child = inlineChild;
252                continue;
253            }
254        }
255        child = child->nextInPreOrderAfterChildren(start);
256    }
257}
258
259void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text)
260{
261    ASSERT(text);
262
263    RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text);
264    if (!textRoot)
265        return;
266
267    MeasureTextData data(0);
268    walkTree(textRoot, text, &data);
269}
270
271void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(RenderSVGText* textRoot, RenderSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap)
272{
273    ASSERT(textRoot);
274    MeasureTextData data(&allCharactersMap);
275    walkTree(textRoot, stopAtLeaf, &data);
276}
277
278}
279