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 "WidthIterator.h" 24 25 #include "Font.h" 26 #include "GlyphBuffer.h" 27 #include "SimpleFontData.h" 28 #include "TextRun.h" 29 #include <wtf/MathExtras.h> 30 31 #if USE(ICU_UNICODE) 32 #include <unicode/unorm.h> 33 #endif 34 35 using namespace WTF; 36 using namespace Unicode; 37 using namespace std; 38 39 namespace WebCore { 40 41 // According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values 42 static const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8; 43 44 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis) 45 : m_font(font) 46 , m_run(run) 47 , m_end(run.length()) 48 , m_currentCharacter(0) 49 , m_runWidthSoFar(0) 50 , m_isAfterExpansion(!run.allowsLeadingExpansion()) 51 , m_fallbackFonts(fallbackFonts) 52 , m_accountForGlyphBounds(accountForGlyphBounds) 53 , m_maxGlyphBoundingBoxY(numeric_limits<float>::min()) 54 , m_minGlyphBoundingBoxY(numeric_limits<float>::max()) 55 , m_firstGlyphOverflow(0) 56 , m_lastGlyphOverflow(0) 57 , m_forTextEmphasis(forTextEmphasis) 58 { 59 // If the padding is non-zero, count the number of spaces in the run 60 // and divide that by the padding for per space addition. 61 m_expansion = m_run.expansion(); 62 if (!m_expansion) 63 m_expansionPerOpportunity = 0; 64 else { 65 bool isAfterExpansion = m_isAfterExpansion; 66 unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion); 67 if (isAfterExpansion && !m_run.allowsTrailingExpansion()) 68 expansionOpportunityCount--; 69 70 if (!expansionOpportunityCount) 71 m_expansionPerOpportunity = 0; 72 else 73 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; 74 } 75 } 76 77 void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) 78 { 79 if (offset > m_end) 80 offset = m_end; 81 82 int currentCharacter = m_currentCharacter; 83 const UChar* cp = m_run.data(currentCharacter); 84 85 bool rtl = m_run.rtl(); 86 bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled(); 87 88 FloatRect bounds; 89 90 const SimpleFontData* primaryFont = m_font->primaryFont(); 91 const SimpleFontData* lastFontData = primaryFont; 92 93 while (currentCharacter < offset) { 94 UChar32 c = *cp; 95 unsigned clusterLength = 1; 96 if (c >= 0x3041) { 97 if (c <= 0x30FE) { 98 // Deal with Hiragana and Katakana voiced and semi-voiced syllables. 99 // Normalize into composed form, and then look for glyph with base + combined mark. 100 // Check above for character range to minimize performance impact. 101 UChar32 normalized = normalizeVoicingMarks(currentCharacter); 102 if (normalized) { 103 c = normalized; 104 clusterLength = 2; 105 } 106 } else if (U16_IS_SURROGATE(c)) { 107 if (!U16_IS_SURROGATE_LEAD(c)) 108 break; 109 110 // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) 111 // code point before glyph lookup. 112 // Make sure we have another character and it's a low surrogate. 113 if (currentCharacter + 1 >= m_run.length()) 114 break; 115 UChar low = cp[1]; 116 if (!U16_IS_TRAIL(low)) 117 break; 118 c = U16_GET_SUPPLEMENTARY(c, low); 119 clusterLength = 2; 120 } 121 } 122 123 const GlyphData& glyphData = m_font->glyphDataForCharacter(c, rtl); 124 Glyph glyph = glyphData.glyph; 125 const SimpleFontData* fontData = glyphData.fontData; 126 127 ASSERT(fontData); 128 129 // Now that we have a glyph and font data, get its width. 130 float width; 131 if (c == '\t' && m_run.allowTabs()) { 132 float tabWidth = m_font->tabWidth(*fontData); 133 width = tabWidth - fmodf(m_run.xPos() + m_runWidthSoFar, tabWidth); 134 } else { 135 width = fontData->widthForGlyph(glyph); 136 137 #if ENABLE(SVG) 138 // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text. 139 width *= m_run.horizontalGlyphStretch(); 140 #endif 141 } 142 143 if (fontData != lastFontData && width) { 144 lastFontData = fontData; 145 if (m_fallbackFonts && fontData != primaryFont) { 146 // FIXME: This does a little extra work that could be avoided if 147 // glyphDataForCharacter() returned whether it chose to use a small caps font. 148 if (!m_font->isSmallCaps() || c == toUpper(c)) 149 m_fallbackFonts->add(fontData); 150 else { 151 const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(c), rtl); 152 if (uppercaseGlyphData.fontData != primaryFont) 153 m_fallbackFonts->add(uppercaseGlyphData.fontData); 154 } 155 } 156 } 157 158 if (hasExtraSpacing) { 159 // Account for letter-spacing. 160 if (width && m_font->letterSpacing()) 161 width += m_font->letterSpacing(); 162 163 static bool expandAroundIdeographs = Font::canExpandAroundIdeographsInComplexText(); 164 bool treatAsSpace = Font::treatAsSpace(c); 165 if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(c))) { 166 // Distribute the run's total expansion evenly over all expansion opportunities in the run. 167 if (m_expansion) { 168 if (!treatAsSpace && !m_isAfterExpansion) { 169 // Take the expansion opportunity before this ideograph. 170 m_expansion -= m_expansionPerOpportunity; 171 m_runWidthSoFar += m_expansionPerOpportunity; 172 if (glyphBuffer) { 173 if (glyphBuffer->isEmpty()) 174 glyphBuffer->add(fontData->spaceGlyph(), fontData, m_expansionPerOpportunity); 175 else 176 glyphBuffer->expandLastAdvance(m_expansionPerOpportunity); 177 } 178 } 179 if (m_run.allowsTrailingExpansion() || (m_run.ltr() && currentCharacter + clusterLength < static_cast<size_t>(m_run.length())) 180 || (m_run.rtl() && currentCharacter)) { 181 m_expansion -= m_expansionPerOpportunity; 182 width += m_expansionPerOpportunity; 183 m_isAfterExpansion = true; 184 } 185 } else 186 m_isAfterExpansion = false; 187 188 // Account for word spacing. 189 // We apply additional space between "words" by adding width to the space character. 190 if (treatAsSpace && currentCharacter && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) 191 width += m_font->wordSpacing(); 192 } else 193 m_isAfterExpansion = false; 194 } 195 196 if (m_accountForGlyphBounds) { 197 bounds = fontData->boundsForGlyph(glyph); 198 if (!currentCharacter) 199 m_firstGlyphOverflow = max<float>(0, -bounds.x()); 200 } 201 202 if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(c)) 203 glyph = 0; 204 205 // Advance past the character we just dealt with. 206 cp += clusterLength; 207 currentCharacter += clusterLength; 208 209 m_runWidthSoFar += width; 210 211 if (glyphBuffer) 212 glyphBuffer->add(glyph, fontData, width); 213 214 if (m_accountForGlyphBounds) { 215 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY()); 216 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y()); 217 m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width); 218 } 219 } 220 221 m_currentCharacter = currentCharacter; 222 } 223 224 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer) 225 { 226 int oldSize = glyphBuffer->size(); 227 advance(m_currentCharacter + 1, glyphBuffer); 228 float w = 0; 229 for (int i = oldSize; i < glyphBuffer->size(); ++i) 230 w += glyphBuffer->advanceAt(i); 231 width = w; 232 return glyphBuffer->size() > oldSize; 233 } 234 235 UChar32 WidthIterator::normalizeVoicingMarks(int currentCharacter) 236 { 237 if (currentCharacter + 1 < m_end) { 238 if (combiningClass(m_run[currentCharacter + 1]) == hiraganaKatakanaVoicingMarksCombiningClass) { 239 #if USE(ICU_UNICODE) 240 // Normalize into composed form using 3.2 rules. 241 UChar normalizedCharacters[2] = { 0, 0 }; 242 UErrorCode uStatus = U_ZERO_ERROR; 243 int32_t resultLength = unorm_normalize(m_run.data(currentCharacter), 2, 244 UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uStatus); 245 if (resultLength == 1 && uStatus == 0) 246 return normalizedCharacters[0]; 247 #elif USE(QT4_UNICODE) 248 QString tmp(reinterpret_cast<const QChar*>(m_run.data(currentCharacter)), 2); 249 QString res = tmp.normalized(QString::NormalizationForm_C, QChar::Unicode_3_2); 250 if (res.length() == 1) 251 return res.at(0).unicode(); 252 #endif 253 } 254 } 255 return 0; 256 } 257 258 } 259