| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv
ed. | |
| 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 "core/platform/graphics/WidthIterator.h" | |
| 24 | |
| 25 #include "core/platform/graphics/SimpleFontData.h" | |
| 26 #include "platform/fonts/GlyphBuffer.h" | |
| 27 #include "platform/fonts/Latin1TextIterator.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 forTextEmphas
is) | |
| 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::expansionOpp
ortunityCount(m_run.characters8(), m_run.length(), m_run.ltr() ? LTR : RTL, isAf
terExpansion) : Font::expansionOpportunityCount(m_run.characters16(), m_run.leng
th(), 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, i
nt 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, ch
aracter, mirror, currentCharacter, advanceLength); | |
| 78 #else | |
| 79 UNUSED_PARAM(currentCharacter); | |
| 80 UNUSED_PARAM(advanceLength); | |
| 81 #endif | |
| 82 | |
| 83 return m_font->glyphDataForCharacter(character, mirror); | |
| 84 } | |
| 85 | |
| 86 struct OriginalAdvancesForCharacterTreatedAsSpace { | |
| 87 public: | |
| 88 OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore
, float advanceAt) | |
| 89 : characterIsSpace(isSpace) | |
| 90 , advanceBeforeCharacter(advanceBefore) | |
| 91 , advanceAtCharacter(advanceAt) | |
| 92 { | |
| 93 } | |
| 94 | |
| 95 bool characterIsSpace; | |
| 96 float advanceBeforeCharacter; | |
| 97 float advanceAtCharacter; | |
| 98 }; | |
| 99 | |
| 100 typedef Vector<pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> Charac
tersTreatedAsSpace; | |
| 101 | |
| 102 static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, unsi
gned& lastGlyphCount, const SimpleFontData* fontData, TypesettingFeatures typese
ttingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace) | |
| 103 { | |
| 104 ASSERT(typesettingFeatures & (Kerning | Ligatures)); | |
| 105 | |
| 106 if (!glyphBuffer) | |
| 107 return 0; | |
| 108 | |
| 109 unsigned glyphBufferSize = glyphBuffer->size(); | |
| 110 if (glyphBuffer->size() <= lastGlyphCount + 1) | |
| 111 return 0; | |
| 112 | |
| 113 GlyphBufferAdvance* advances = glyphBuffer->advances(0); | |
| 114 float widthDifference = 0; | |
| 115 for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i) | |
| 116 widthDifference -= advances[i].width(); | |
| 117 | |
| 118 if (!ltr) | |
| 119 glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount); | |
| 120 | |
| 121 fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + la
stGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures); | |
| 122 | |
| 123 if (!ltr) | |
| 124 glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount); | |
| 125 | |
| 126 for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) { | |
| 127 int spaceOffset = charactersTreatedAsSpace[i].first; | |
| 128 const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = cha
ractersTreatedAsSpace[i].second; | |
| 129 if (spaceOffset && !originalAdvances.characterIsSpace) | |
| 130 glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.ad
vanceBeforeCharacter); | |
| 131 glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtC
haracter); | |
| 132 } | |
| 133 charactersTreatedAsSpace.clear(); | |
| 134 | |
| 135 for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i) | |
| 136 widthDifference += advances[i].width(); | |
| 137 | |
| 138 lastGlyphCount = glyphBufferSize; | |
| 139 return widthDifference; | |
| 140 } | |
| 141 | |
| 142 template <typename TextIterator> | |
| 143 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, Glyph
Buffer* glyphBuffer) | |
| 144 { | |
| 145 bool rtl = m_run.rtl(); | |
| 146 bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() ||
m_expansion) && !m_run.spacingDisabled(); | |
| 147 | |
| 148 float widthSinceLastRounding = m_runWidthSoFar; | |
| 149 m_runWidthSoFar = floorf(m_runWidthSoFar); | |
| 150 widthSinceLastRounding -= m_runWidthSoFar; | |
| 151 | |
| 152 float lastRoundingWidth = m_finalRoundingWidth; | |
| 153 FloatRect bounds; | |
| 154 | |
| 155 const SimpleFontData* primaryFont = m_font->primaryFont(); | |
| 156 const SimpleFontData* lastFontData = primaryFont; | |
| 157 unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0; | |
| 158 | |
| 159 UChar32 character = 0; | |
| 160 unsigned clusterLength = 0; | |
| 161 CharactersTreatedAsSpace charactersTreatedAsSpace; | |
| 162 while (textIterator.consume(character, clusterLength)) { | |
| 163 unsigned advanceLength = clusterLength; | |
| 164 const GlyphData& glyphData = glyphDataForCharacter(character, rtl, textI
terator.currentCharacter(), advanceLength); | |
| 165 Glyph glyph = glyphData.glyph; | |
| 166 const SimpleFontData* fontData = glyphData.fontData; | |
| 167 | |
| 168 ASSERT(fontData); | |
| 169 | |
| 170 // Now that we have a glyph and font data, get its width. | |
| 171 float width; | |
| 172 if (character == '\t' && m_run.allowTabs()) | |
| 173 width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() +
m_runWidthSoFar + widthSinceLastRounding); | |
| 174 else { | |
| 175 width = fontData->widthForGlyph(glyph); | |
| 176 | |
| 177 // SVG uses horizontalGlyphStretch(), when textLength is used to str
etch/squeeze text. | |
| 178 width *= m_run.horizontalGlyphStretch(); | |
| 179 | |
| 180 // We special case spaces in two ways when applying word rounding. | |
| 181 // First, we round spaces to an adjusted width in all fonts. | |
| 182 // Second, in fixed-pitch fonts we ensure that all characters that | |
| 183 // match the width of the space character have the same width as the
space character. | |
| 184 if (m_run.applyWordRounding() && width == fontData->spaceWidth() &&
(fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph())) | |
| 185 width = fontData->adjustedSpaceWidth(); | |
| 186 } | |
| 187 | |
| 188 if (fontData != lastFontData && width) { | |
| 189 if (shouldApplyFontTransforms()) | |
| 190 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(),
lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace); | |
| 191 | |
| 192 lastFontData = fontData; | |
| 193 if (m_fallbackFonts && fontData != primaryFont) { | |
| 194 // FIXME: This does a little extra work that could be avoided if | |
| 195 // glyphDataForCharacter() returned whether it chose to use a sm
all caps font. | |
| 196 if (!m_font->isSmallCaps() || character == toUpper(character)) | |
| 197 m_fallbackFonts->add(fontData); | |
| 198 else { | |
| 199 const GlyphData& uppercaseGlyphData = m_font->glyphDataForCh
aracter(toUpper(character), rtl); | |
| 200 if (uppercaseGlyphData.fontData != primaryFont) | |
| 201 m_fallbackFonts->add(uppercaseGlyphData.fontData); | |
| 202 } | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 if (hasExtraSpacing) { | |
| 207 // Account for letter-spacing. | |
| 208 if (width && m_font->letterSpacing()) | |
| 209 width += m_font->letterSpacing(); | |
| 210 | |
| 211 static bool expandAroundIdeographs = Font::canExpandAroundIdeographs
InComplexText(); | |
| 212 bool treatAsSpace = Font::treatAsSpace(character); | |
| 213 if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographO
rSymbol(character))) { | |
| 214 // Distribute the run's total expansion evenly over all expansio
n opportunities in the run. | |
| 215 if (m_expansion) { | |
| 216 float previousExpansion = m_expansion; | |
| 217 if (!treatAsSpace && !m_isAfterExpansion) { | |
| 218 // Take the expansion opportunity before this ideograph. | |
| 219 m_expansion -= m_expansionPerOpportunity; | |
| 220 float expansionAtThisOpportunity = !m_run.applyWordRound
ing() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansi
on); | |
| 221 m_runWidthSoFar += expansionAtThisOpportunity; | |
| 222 if (glyphBuffer) { | |
| 223 if (glyphBuffer->isEmpty()) { | |
| 224 if (m_forTextEmphasis) | |
| 225 glyphBuffer->add(fontData->zeroWidthSpaceGly
ph(), fontData, m_expansionPerOpportunity); | |
| 226 else | |
| 227 glyphBuffer->add(fontData->spaceGlyph(), fon
tData, expansionAtThisOpportunity); | |
| 228 } else | |
| 229 glyphBuffer->expandLastAdvance(expansionAtThisOp
portunity); | |
| 230 } | |
| 231 previousExpansion = m_expansion; | |
| 232 } | |
| 233 if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textI
terator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length())
) | |
| 234 || (m_run.rtl() && textIterator.currentCharacter())) { | |
| 235 m_expansion -= m_expansionPerOpportunity; | |
| 236 width += !m_run.applyWordRounding() ? m_expansionPerOppo
rtunity : roundf(previousExpansion) - roundf(m_expansion); | |
| 237 m_isAfterExpansion = true; | |
| 238 } | |
| 239 } else | |
| 240 m_isAfterExpansion = false; | |
| 241 | |
| 242 // Account for word spacing. | |
| 243 // We apply additional space between "words" by adding width to
the space character. | |
| 244 if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) &&
(textIterator.currentCharacter() || character == noBreakSpace) && m_font->wordS
pacing()) | |
| 245 width += m_font->wordSpacing(); | |
| 246 } else | |
| 247 m_isAfterExpansion = false; | |
| 248 } | |
| 249 | |
| 250 if (shouldApplyFontTransforms() && glyphBuffer && Font::treatAsSpace(cha
racter)) | |
| 251 charactersTreatedAsSpace.append(make_pair(glyphBuffer->size(), | |
| 252 OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', gly
phBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1) : 0, width)))
; | |
| 253 | |
| 254 if (m_accountForGlyphBounds) { | |
| 255 bounds = fontData->boundsForGlyph(glyph); | |
| 256 if (!textIterator.currentCharacter()) | |
| 257 m_firstGlyphOverflow = max<float>(0, -bounds.x()); | |
| 258 } | |
| 259 | |
| 260 if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character)) | |
| 261 glyph = 0; | |
| 262 | |
| 263 // Advance past the character we just dealt with. | |
| 264 textIterator.advance(advanceLength); | |
| 265 | |
| 266 float oldWidth = width; | |
| 267 | |
| 268 // Force characters that are used to determine word boundaries for the r
ounding hack | |
| 269 // to be integer width, so following words will start on an integer boun
dary. | |
| 270 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character
)) { | |
| 271 width = ceilf(width); | |
| 272 | |
| 273 // Since widthSinceLastRounding can lose precision if we include mea
surements for | |
| 274 // preceding whitespace, we bypass it here. | |
| 275 m_runWidthSoFar += width; | |
| 276 | |
| 277 // Since this is a rounding hack character, we should have reset thi
s sum on the previous | |
| 278 // iteration. | |
| 279 ASSERT(!widthSinceLastRounding); | |
| 280 } else { | |
| 281 // Check to see if the next character is a "rounding hack character"
, if so, adjust | |
| 282 // width so that the total run width will be on an integer boundary. | |
| 283 if ((m_run.applyWordRounding() && textIterator.currentCharacter() <
m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters()))) | |
| 284 || (m_run.applyRunRounding() && textIterator.currentCharacter()
>= m_run.length())) { | |
| 285 float totalWidth = widthSinceLastRounding + width; | |
| 286 widthSinceLastRounding = ceilf(totalWidth); | |
| 287 width += widthSinceLastRounding - totalWidth; | |
| 288 m_runWidthSoFar += widthSinceLastRounding; | |
| 289 widthSinceLastRounding = 0; | |
| 290 } else | |
| 291 widthSinceLastRounding += width; | |
| 292 } | |
| 293 | |
| 294 if (glyphBuffer) | |
| 295 glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidt
h : width)); | |
| 296 | |
| 297 lastRoundingWidth = width - oldWidth; | |
| 298 | |
| 299 if (m_accountForGlyphBounds) { | |
| 300 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY()); | |
| 301 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y()); | |
| 302 m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width); | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 if (shouldApplyFontTransforms()) | |
| 307 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGly
phCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace); | |
| 308 | |
| 309 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCha
racter; | |
| 310 m_currentCharacter = textIterator.currentCharacter(); | |
| 311 m_runWidthSoFar += widthSinceLastRounding; | |
| 312 m_finalRoundingWidth = lastRoundingWidth; | |
| 313 return consumedCharacters; | |
| 314 } | |
| 315 | |
| 316 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) | |
| 317 { | |
| 318 int length = m_run.length(); | |
| 319 | |
| 320 if (offset > length) | |
| 321 offset = length; | |
| 322 | |
| 323 if (m_currentCharacter >= static_cast<unsigned>(offset)) | |
| 324 return 0; | |
| 325 | |
| 326 if (m_run.is8Bit()) { | |
| 327 Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_curre
ntCharacter, offset, length); | |
| 328 return advanceInternal(textIterator, glyphBuffer); | |
| 329 } | |
| 330 | |
| 331 SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter)
, m_currentCharacter, offset, length); | |
| 332 return advanceInternal(textIterator, glyphBuffer); | |
| 333 } | |
| 334 | |
| 335 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer) | |
| 336 { | |
| 337 unsigned oldSize = glyphBuffer.size(); | |
| 338 advance(m_currentCharacter + 1, &glyphBuffer); | |
| 339 float w = 0; | |
| 340 for (unsigned i = oldSize; i < glyphBuffer.size(); ++i) | |
| 341 w += glyphBuffer.advanceAt(i); | |
| 342 width = w; | |
| 343 return glyphBuffer.size() > oldSize; | |
| 344 } | |
| 345 | |
| 346 } | |
| OLD | NEW |