Home | History | Annotate | Download | only in svg
      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 
     38 namespace blink {
     39 
     40 class SVGTextMetricsCalculator {
     41 public:
     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 
     57 private:
     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 
     74 SVGTextMetricsCalculator::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 
     92 SVGTextMetricsCalculator::~SVGTextMetricsCalculator()
     93 {
     94     if (m_bidiRun)
     95         m_bidiResolver.runs().deleteRuns();
     96 }
     97 
     98 void 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 
    116 SVGTextMetrics 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 
    130 SVGTextMetrics 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 
    151 SVGTextMetrics 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 
    170 struct 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 
    183 static 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 
    239 static 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 
    259 void 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 
    271 void 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