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/RenderSVGInlineText.h"
     25 #include "core/rendering/svg/RenderSVGText.h"
     26 
     27 namespace WebCore {
     28 
     29 SVGTextMetricsBuilder::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 
     38 inline 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 
     43 bool 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 
     57 void 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 
     76 void 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 
     94 void 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 
    112 struct 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 
    131 void 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 
    181 void 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 
    207 void 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 
    219 void 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