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