Home | History | Annotate | Download | only in fonts
      1 /*
      2  * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
      3  * Copyright (C) 2008 Holger Hans Peter Freyther
      4  * Copyright (C) 2014 Google Inc. All rights reserved.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  *
     21  */
     22 
     23 #include "config.h"
     24 #include "platform/fonts/WidthIterator.h"
     25 
     26 #include "platform/fonts/Character.h"
     27 #include "platform/fonts/Font.h"
     28 #include "platform/fonts/FontPlatformFeatures.h"
     29 #include "platform/fonts/GlyphBuffer.h"
     30 #include "platform/fonts/Latin1TextIterator.h"
     31 #include "platform/fonts/SimpleFontData.h"
     32 #include "platform/text/SurrogatePairAwareTextIterator.h"
     33 #include "wtf/MathExtras.h"
     34 
     35 using namespace WTF;
     36 using namespace Unicode;
     37 
     38 namespace blink {
     39 
     40 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
     41     : m_font(font)
     42     , m_run(run)
     43     , m_currentCharacter(0)
     44     , m_runWidthSoFar(0)
     45     , m_isAfterExpansion(!run.allowsLeadingExpansion())
     46     , m_fallbackFonts(fallbackFonts)
     47     , m_maxGlyphBoundingBoxY(std::numeric_limits<float>::min())
     48     , m_minGlyphBoundingBoxY(std::numeric_limits<float>::max())
     49     , m_firstGlyphOverflow(0)
     50     , m_lastGlyphOverflow(0)
     51     , m_accountForGlyphBounds(accountForGlyphBounds)
     52     , m_forTextEmphasis(forTextEmphasis)
     53 {
     54     // If the padding is non-zero, count the number of spaces in the run
     55     // and divide that by the padding for per space addition.
     56     m_expansion = m_run.expansion();
     57     if (!m_expansion)
     58         m_expansionPerOpportunity = 0;
     59     else {
     60         bool isAfterExpansion = m_isAfterExpansion;
     61         unsigned expansionOpportunityCount = m_run.is8Bit() ? Character::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.direction(), isAfterExpansion) : Character::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.direction(), isAfterExpansion);
     62         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
     63             expansionOpportunityCount--;
     64 
     65         if (!expansionOpportunityCount)
     66             m_expansionPerOpportunity = 0;
     67         else
     68             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
     69     }
     70 }
     71 
     72 GlyphData WidthIterator::glyphDataForCharacter(CharacterData& charData, bool normalizeSpace)
     73 {
     74     ASSERT(m_font);
     75 
     76 #if ENABLE(SVG_FONTS)
     77     if (TextRun::RenderingContext* renderingContext = m_run.renderingContext()) {
     78         return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, charData.character,
     79             m_run.rtl(), charData.characterOffset, charData.clusterLength);
     80     }
     81 #endif
     82 
     83     return m_font->glyphDataForCharacter(charData.character, m_run.rtl(), normalizeSpace);
     84 }
     85 
     86 float WidthIterator::characterWidth(UChar32 character, const GlyphData& glyphData) const
     87 {
     88     const SimpleFontData* fontData = glyphData.fontData;
     89     ASSERT(fontData);
     90 
     91     if (UNLIKELY(character == '\t' && m_run.allowTabs()))
     92         return m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar);
     93 
     94     float width = fontData->widthForGlyph(glyphData.glyph);
     95 
     96     // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
     97     if (UNLIKELY(m_run.horizontalGlyphStretch() != 1))
     98         width *= m_run.horizontalGlyphStretch();
     99 
    100     return width;
    101 }
    102 
    103 void WidthIterator::cacheFallbackFont(const SimpleFontData* fontData,
    104     const SimpleFontData* primaryFont)
    105 {
    106     if (fontData == primaryFont)
    107         return;
    108 
    109     m_fallbackFonts->add(fontData);
    110 }
    111 
    112 float WidthIterator::adjustSpacing(float width, const CharacterData& charData,
    113     const SimpleFontData& fontData, GlyphBuffer* glyphBuffer)
    114 {
    115     // Account for letter-spacing.
    116     if (width)
    117         width += m_font->fontDescription().letterSpacing();
    118 
    119     static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
    120     bool treatAsSpace = Character::treatAsSpace(charData.character);
    121     if (treatAsSpace || (expandAroundIdeographs && Character::isCJKIdeographOrSymbol(charData.character))) {
    122         // Distribute the run's total expansion evenly over all expansion opportunities in the run.
    123         if (m_expansion) {
    124             if (!treatAsSpace && !m_isAfterExpansion) {
    125                 // Take the expansion opportunity before this ideograph.
    126                 m_expansion -= m_expansionPerOpportunity;
    127                 float expansionAtThisOpportunity = m_expansionPerOpportunity;
    128                 m_runWidthSoFar += expansionAtThisOpportunity;
    129                 if (glyphBuffer) {
    130                     if (glyphBuffer->isEmpty()) {
    131                         if (m_forTextEmphasis)
    132                             glyphBuffer->add(fontData.zeroWidthSpaceGlyph(), &fontData, m_expansionPerOpportunity);
    133                         else
    134                             glyphBuffer->add(fontData.spaceGlyph(), &fontData, expansionAtThisOpportunity);
    135                     } else {
    136                         glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
    137                     }
    138                 }
    139             }
    140             if (m_run.allowsTrailingExpansion()
    141                 || (m_run.ltr() && charData.characterOffset + charData.clusterLength < static_cast<size_t>(m_run.length()))
    142                 || (m_run.rtl() && charData.characterOffset)) {
    143                 m_expansion -= m_expansionPerOpportunity;
    144                 width += m_expansionPerOpportunity;
    145                 m_isAfterExpansion = true;
    146             }
    147         } else {
    148             m_isAfterExpansion = false;
    149         }
    150 
    151         // Account for word spacing.
    152         // We apply additional space between "words" by adding width to the space character.
    153         if (treatAsSpace && (charData.character != '\t' || !m_run.allowTabs())
    154             && (charData.characterOffset || charData.character == noBreakSpace)
    155             && m_font->fontDescription().wordSpacing()) {
    156             width += m_font->fontDescription().wordSpacing();
    157         }
    158     } else {
    159         m_isAfterExpansion = false;
    160     }
    161 
    162     return width;
    163 }
    164 
    165 void WidthIterator::updateGlyphBounds(const GlyphData& glyphData, float width, bool firstCharacter)
    166 {
    167     ASSERT(glyphData.fontData);
    168     FloatRect bounds = glyphData.fontData->boundsForGlyph(glyphData.glyph);
    169 
    170     if (firstCharacter)
    171         m_firstGlyphOverflow = std::max<float>(0, -bounds.x());
    172     m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
    173     m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY());
    174     m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
    175 }
    176 
    177 template <typename TextIterator>
    178 unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
    179 {
    180     bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion)
    181         && !m_run.spacingDisabled();
    182 
    183     const SimpleFontData* primaryFont = m_font->primaryFont();
    184     const SimpleFontData* lastFontData = primaryFont;
    185     bool normalizeSpace = m_run.normalizeSpace();
    186 
    187     CharacterData charData;
    188     while (textIterator.consume(charData.character, charData.clusterLength)) {
    189         charData.characterOffset = textIterator.currentCharacter();
    190 
    191         GlyphData glyphData = glyphDataForCharacter(charData, normalizeSpace);
    192 
    193         // Some fonts do not have a glyph for zero-width-space,
    194         // in that case use the space character and override the width.
    195         float width;
    196         if (!glyphData.glyph && Character::treatAsZeroWidthSpaceInComplexScript(charData.character)) {
    197             charData.character = space;
    198             glyphData = glyphDataForCharacter(charData);
    199             width = 0;
    200         } else {
    201             width = characterWidth(charData.character, glyphData);
    202         }
    203 
    204         Glyph glyph = glyphData.glyph;
    205         const SimpleFontData* fontData = glyphData.fontData;
    206         ASSERT(fontData);
    207 
    208         if (m_fallbackFonts && lastFontData != fontData && width) {
    209             lastFontData = fontData;
    210             cacheFallbackFont(fontData, primaryFont);
    211         }
    212 
    213         if (hasExtraSpacing)
    214             width = adjustSpacing(width, charData, *fontData, glyphBuffer);
    215 
    216         if (m_accountForGlyphBounds)
    217             updateGlyphBounds(glyphData, width, !charData.characterOffset);
    218 
    219         if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(charData.character))
    220             glyph = 0;
    221 
    222         // Advance past the character we just dealt with.
    223         textIterator.advance(charData.clusterLength);
    224         m_runWidthSoFar += width;
    225 
    226         if (glyphBuffer)
    227             glyphBuffer->add(glyph, fontData, width);
    228     }
    229 
    230     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
    231     m_currentCharacter = textIterator.currentCharacter();
    232 
    233     return consumedCharacters;
    234 }
    235 
    236 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
    237 {
    238     int length = m_run.length();
    239 
    240     if (offset > length)
    241         offset = length;
    242 
    243     if (m_currentCharacter >= static_cast<unsigned>(offset))
    244         return 0;
    245 
    246     if (m_run.is8Bit()) {
    247         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
    248         return advanceInternal(textIterator, glyphBuffer);
    249     }
    250 
    251     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
    252     return advanceInternal(textIterator, glyphBuffer);
    253 }
    254 
    255 bool WidthIterator::advanceOneCharacter(float& width)
    256 {
    257     float initialWidth = m_runWidthSoFar;
    258 
    259     if (!advance(m_currentCharacter + 1))
    260         return false;
    261 
    262     width = m_runWidthSoFar - initialWidth;
    263     return true;
    264 }
    265 
    266 } // namespace blink
    267