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  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  *
     20  */
     21 
     22 #include "config.h"
     23 #include "platform/fonts/WidthIterator.h"
     24 
     25 #include "platform/fonts/Character.h"
     26 #include "platform/fonts/FontPlatformFeatures.h"
     27 #include "platform/fonts/GlyphBuffer.h"
     28 #include "platform/fonts/Latin1TextIterator.h"
     29 #include "platform/fonts/SimpleFontData.h"
     30 #include "platform/text/SurrogatePairAwareTextIterator.h"
     31 #include "wtf/MathExtras.h"
     32 
     33 using namespace WTF;
     34 using namespace Unicode;
     35 using namespace std;
     36 
     37 namespace WebCore {
     38 
     39 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
     40     : m_font(font)
     41     , m_run(run)
     42     , m_currentCharacter(0)
     43     , m_runWidthSoFar(0)
     44     , m_isAfterExpansion(!run.allowsLeadingExpansion())
     45     , m_finalRoundingWidth(0)
     46     , m_typesettingFeatures(font->fontDescription().typesettingFeatures())
     47     , m_fallbackFonts(fallbackFonts)
     48     , m_accountForGlyphBounds(accountForGlyphBounds)
     49     , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
     50     , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
     51     , m_firstGlyphOverflow(0)
     52     , m_lastGlyphOverflow(0)
     53     , m_forTextEmphasis(forTextEmphasis)
     54 {
     55     // If the padding is non-zero, count the number of spaces in the run
     56     // and divide that by the padding for per space addition.
     57     m_expansion = m_run.expansion();
     58     if (!m_expansion)
     59         m_expansionPerOpportunity = 0;
     60     else {
     61         bool isAfterExpansion = m_isAfterExpansion;
     62         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);
     63         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
     64             expansionOpportunityCount--;
     65 
     66         if (!expansionOpportunityCount)
     67             m_expansionPerOpportunity = 0;
     68         else
     69             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
     70     }
     71 }
     72 
     73 GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
     74 {
     75     ASSERT(m_font);
     76 
     77 #if ENABLE(SVG_FONTS)
     78     if (TextRun::RenderingContext* renderingContext = m_run.renderingContext())
     79         return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, character, mirror, currentCharacter, advanceLength);
     80 #endif
     81 
     82     return m_font->glyphDataForCharacter(character, mirror);
     83 }
     84 
     85 struct OriginalAdvancesForCharacterTreatedAsSpace {
     86 public:
     87     OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore, float advanceAt)
     88         : characterIsSpace(isSpace)
     89         , advanceBeforeCharacter(advanceBefore)
     90         , advanceAtCharacter(advanceAt)
     91     {
     92     }
     93 
     94     bool characterIsSpace;
     95     float advanceBeforeCharacter;
     96     float advanceAtCharacter;
     97 };
     98 
     99 typedef Vector<pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace;
    100 
    101 static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, unsigned& lastGlyphCount, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace)
    102 {
    103     ASSERT(typesettingFeatures & (Kerning | Ligatures));
    104 
    105     if (!glyphBuffer)
    106         return 0;
    107 
    108     unsigned glyphBufferSize = glyphBuffer->size();
    109     if (glyphBuffer->size() <= lastGlyphCount + 1)
    110         return 0;
    111 
    112     FloatSize* advances = glyphBuffer->advances(0);
    113     float widthDifference = 0;
    114     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
    115         widthDifference -= advances[i].width();
    116 
    117     for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) {
    118         int spaceOffset = charactersTreatedAsSpace[i].first;
    119         const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = charactersTreatedAsSpace[i].second;
    120         if (spaceOffset && !originalAdvances.characterIsSpace)
    121             glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.advanceBeforeCharacter);
    122         glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtCharacter);
    123     }
    124     charactersTreatedAsSpace.clear();
    125 
    126     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
    127         widthDifference += advances[i].width();
    128 
    129     lastGlyphCount = glyphBufferSize;
    130     return widthDifference;
    131 }
    132 
    133 template <typename TextIterator>
    134 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
    135 {
    136     bool rtl = m_run.rtl();
    137     bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion) && !m_run.spacingDisabled();
    138 
    139     float widthSinceLastRounding = m_runWidthSoFar;
    140     m_runWidthSoFar = floorf(m_runWidthSoFar);
    141     widthSinceLastRounding -= m_runWidthSoFar;
    142 
    143     float lastRoundingWidth = m_finalRoundingWidth;
    144     FloatRect bounds;
    145 
    146     const SimpleFontData* primaryFont = m_font->primaryFont();
    147     const SimpleFontData* lastFontData = primaryFont;
    148     unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
    149 
    150     UChar32 character = 0;
    151     unsigned clusterLength = 0;
    152     CharactersTreatedAsSpace charactersTreatedAsSpace;
    153     while (textIterator.consume(character, clusterLength)) {
    154         unsigned advanceLength = clusterLength;
    155         const GlyphData& glyphData = glyphDataForCharacter(character, rtl, textIterator.currentCharacter(), advanceLength);
    156         Glyph glyph = glyphData.glyph;
    157         const SimpleFontData* fontData = glyphData.fontData;
    158 
    159         ASSERT(fontData);
    160 
    161         // Now that we have a glyph and font data, get its width.
    162         float width;
    163         if (character == '\t' && m_run.allowTabs())
    164             width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
    165         else {
    166             width = fontData->widthForGlyph(glyph);
    167 
    168             // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
    169             width *= m_run.horizontalGlyphStretch();
    170 
    171             // We special case spaces in two ways when applying word rounding.
    172             // First, we round spaces to an adjusted width in all fonts.
    173             // Second, in fixed-pitch fonts we ensure that all characters that
    174             // match the width of the space character have the same width as the space character.
    175             if (m_run.applyWordRounding() && width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
    176                 width = fontData->adjustedSpaceWidth();
    177         }
    178 
    179         if (fontData != lastFontData && width) {
    180             if (shouldApplyFontTransforms())
    181                 m_runWidthSoFar += applyFontTransforms(glyphBuffer, lastGlyphCount, m_typesettingFeatures, charactersTreatedAsSpace);
    182 
    183             lastFontData = fontData;
    184             if (m_fallbackFonts && fontData != primaryFont) {
    185                 // FIXME: This does a little extra work that could be avoided if
    186                 // glyphDataForCharacter() returned whether it chose to use a small caps font.
    187                 if (m_font->fontDescription().variant() == FontVariantNormal || character == toUpper(character))
    188                     m_fallbackFonts->add(fontData);
    189                 else {
    190                     ASSERT(m_font->fontDescription().variant() == FontVariantSmallCaps);
    191                     const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character), rtl);
    192                     if (uppercaseGlyphData.fontData != primaryFont)
    193                         m_fallbackFonts->add(uppercaseGlyphData.fontData);
    194                 }
    195             }
    196         }
    197 
    198         if (hasExtraSpacing) {
    199             // Account for letter-spacing.
    200             if (width && m_font->fontDescription().letterSpacing())
    201                 width += m_font->fontDescription().letterSpacing();
    202 
    203             static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
    204             bool treatAsSpace = Character::treatAsSpace(character);
    205             if (treatAsSpace || (expandAroundIdeographs && Character::isCJKIdeographOrSymbol(character))) {
    206                 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
    207                 if (m_expansion) {
    208                     float previousExpansion = m_expansion;
    209                     if (!treatAsSpace && !m_isAfterExpansion) {
    210                         // Take the expansion opportunity before this ideograph.
    211                         m_expansion -= m_expansionPerOpportunity;
    212                         float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
    213                         m_runWidthSoFar += expansionAtThisOpportunity;
    214                         if (glyphBuffer) {
    215                             if (glyphBuffer->isEmpty()) {
    216                                 if (m_forTextEmphasis)
    217                                     glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity);
    218                                 else
    219                                     glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
    220                             } else
    221                                 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
    222                         }
    223                         previousExpansion = m_expansion;
    224                     }
    225                     if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textIterator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length()))
    226                         || (m_run.rtl() && textIterator.currentCharacter())) {
    227                         m_expansion -= m_expansionPerOpportunity;
    228                         width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
    229                         m_isAfterExpansion = true;
    230                     }
    231                 } else
    232                     m_isAfterExpansion = false;
    233 
    234                 // Account for word spacing.
    235                 // We apply additional space between "words" by adding width to the space character.
    236                 if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (textIterator.currentCharacter() || character == noBreakSpace) && m_font->fontDescription().wordSpacing())
    237                     width += m_font->fontDescription().wordSpacing();
    238             } else
    239                 m_isAfterExpansion = false;
    240         }
    241 
    242         if (shouldApplyFontTransforms() && glyphBuffer && Character::treatAsSpace(character)) {
    243             charactersTreatedAsSpace.append(make_pair(glyphBuffer->size(),
    244                 OriginalAdvancesForCharacterTreatedAsSpace(character == ' ',
    245                     glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1).width() : 0,
    246                     width)));
    247         }
    248 
    249         if (m_accountForGlyphBounds) {
    250             bounds = fontData->boundsForGlyph(glyph);
    251             if (!textIterator.currentCharacter())
    252                 m_firstGlyphOverflow = max<float>(0, -bounds.x());
    253         }
    254 
    255         if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(character))
    256             glyph = 0;
    257 
    258         // Advance past the character we just dealt with.
    259         textIterator.advance(advanceLength);
    260 
    261         float oldWidth = width;
    262 
    263         // Force characters that are used to determine word boundaries for the rounding hack
    264         // to be integer width, so following words will start on an integer boundary.
    265         if (m_run.applyWordRounding() && Character::isRoundingHackCharacter(character)) {
    266             width = ceilf(width);
    267 
    268             // Since widthSinceLastRounding can lose precision if we include measurements for
    269             // preceding whitespace, we bypass it here.
    270             m_runWidthSoFar += width;
    271 
    272             // Since this is a rounding hack character, we should have reset this sum on the previous
    273             // iteration.
    274             ASSERT(!widthSinceLastRounding);
    275         } else {
    276             // Check to see if the next character is a "rounding hack character", if so, adjust
    277             // width so that the total run width will be on an integer boundary.
    278             if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Character::isRoundingHackCharacter(*(textIterator.characters())))
    279                 || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) {
    280                 float totalWidth = widthSinceLastRounding + width;
    281                 widthSinceLastRounding = ceilf(totalWidth);
    282                 width += widthSinceLastRounding - totalWidth;
    283                 m_runWidthSoFar += widthSinceLastRounding;
    284                 widthSinceLastRounding = 0;
    285             } else
    286                 widthSinceLastRounding += width;
    287         }
    288 
    289         if (glyphBuffer)
    290             glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
    291 
    292         lastRoundingWidth = width - oldWidth;
    293 
    294         if (m_accountForGlyphBounds) {
    295             m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY());
    296             m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y());
    297             m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width);
    298         }
    299     }
    300 
    301     if (shouldApplyFontTransforms())
    302         m_runWidthSoFar += applyFontTransforms(glyphBuffer, lastGlyphCount, m_typesettingFeatures, charactersTreatedAsSpace);
    303 
    304     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
    305     m_currentCharacter = textIterator.currentCharacter();
    306     m_runWidthSoFar += widthSinceLastRounding;
    307     m_finalRoundingWidth = lastRoundingWidth;
    308     return consumedCharacters;
    309 }
    310 
    311 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
    312 {
    313     int length = m_run.length();
    314 
    315     if (offset > length)
    316         offset = length;
    317 
    318     if (m_currentCharacter >= static_cast<unsigned>(offset))
    319         return 0;
    320 
    321     if (m_run.is8Bit()) {
    322         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
    323         return advanceInternal(textIterator, glyphBuffer);
    324     }
    325 
    326     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
    327     return advanceInternal(textIterator, glyphBuffer);
    328 }
    329 
    330 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
    331 {
    332     unsigned oldSize = glyphBuffer.size();
    333     advance(m_currentCharacter + 1, &glyphBuffer);
    334     float w = 0;
    335     for (unsigned i = oldSize; i < glyphBuffer.size(); ++i)
    336         w += glyphBuffer.advanceAt(i).width();
    337     width = w;
    338     return glyphBuffer.size() > oldSize;
    339 }
    340 
    341 }
    342