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