2 * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Holger Hans Peter Freyther
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.
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.
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.
23 #include "platform/fonts/WidthIterator.h"
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"
34 using namespace Unicode;
39 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
42 , m_currentCharacter(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)
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();
59 m_expansionPerOpportunity = 0;
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--;
66 if (!expansionOpportunityCount)
67 m_expansionPerOpportunity = 0;
69 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
73 GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
78 if (TextRun::RenderingContext* renderingContext = m_run.renderingContext())
79 return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, character, mirror, currentCharacter, advanceLength);
82 return m_font->glyphDataForCharacter(character, mirror);
85 struct OriginalAdvancesForCharacterTreatedAsSpace {
87 OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore, float advanceAt)
88 : characterIsSpace(isSpace)
89 , advanceBeforeCharacter(advanceBefore)
90 , advanceAtCharacter(advanceAt)
94 bool characterIsSpace;
95 float advanceBeforeCharacter;
96 float advanceAtCharacter;
99 typedef Vector<pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace;
101 static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, unsigned& lastGlyphCount, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace)
103 ASSERT(typesettingFeatures & (Kerning | Ligatures));
108 unsigned glyphBufferSize = glyphBuffer->size();
109 if (glyphBuffer->size() <= lastGlyphCount + 1)
112 FloatSize* advances = glyphBuffer->advances(0);
113 float widthDifference = 0;
114 for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
115 widthDifference -= advances[i].width();
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);
124 charactersTreatedAsSpace.clear();
126 for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
127 widthDifference += advances[i].width();
129 lastGlyphCount = glyphBufferSize;
130 return widthDifference;
133 template <typename TextIterator>
134 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
136 bool rtl = m_run.rtl();
137 bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion) && !m_run.spacingDisabled();
139 float widthSinceLastRounding = m_runWidthSoFar;
140 m_runWidthSoFar = floorf(m_runWidthSoFar);
141 widthSinceLastRounding -= m_runWidthSoFar;
143 float lastRoundingWidth = m_finalRoundingWidth;
146 const SimpleFontData* primaryFont = m_font->primaryFont();
147 const SimpleFontData* lastFontData = primaryFont;
148 unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
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;
161 // Now that we have a glyph and font data, get its width.
163 if (character == '\t' && m_run.allowTabs())
164 width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
166 width = fontData->widthForGlyph(glyph);
168 // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
169 width *= m_run.horizontalGlyphStretch();
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();
179 if (fontData != lastFontData && width) {
180 if (shouldApplyFontTransforms())
181 m_runWidthSoFar += applyFontTransforms(glyphBuffer, lastGlyphCount, m_typesettingFeatures, charactersTreatedAsSpace);
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);
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);
198 if (hasExtraSpacing) {
199 // Account for letter-spacing.
200 if (width && m_font->fontDescription().letterSpacing())
201 width += m_font->fontDescription().letterSpacing();
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.
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;
215 if (glyphBuffer->isEmpty()) {
216 if (m_forTextEmphasis)
217 glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity);
219 glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
221 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
223 previousExpansion = m_expansion;
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;
232 m_isAfterExpansion = false;
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();
239 m_isAfterExpansion = false;
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,
249 if (m_accountForGlyphBounds) {
250 bounds = fontData->boundsForGlyph(glyph);
251 if (!textIterator.currentCharacter())
252 m_firstGlyphOverflow = max<float>(0, -bounds.x());
255 if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(character))
258 // Advance past the character we just dealt with.
259 textIterator.advance(advanceLength);
261 float oldWidth = width;
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);
268 // Since widthSinceLastRounding can lose precision if we include measurements for
269 // preceding whitespace, we bypass it here.
270 m_runWidthSoFar += width;
272 // Since this is a rounding hack character, we should have reset this sum on the previous
274 ASSERT(!widthSinceLastRounding);
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;
286 widthSinceLastRounding += width;
290 glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
292 lastRoundingWidth = width - oldWidth;
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);
301 if (shouldApplyFontTransforms())
302 m_runWidthSoFar += applyFontTransforms(glyphBuffer, lastGlyphCount, m_typesettingFeatures, charactersTreatedAsSpace);
304 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
305 m_currentCharacter = textIterator.currentCharacter();
306 m_runWidthSoFar += widthSinceLastRounding;
307 m_finalRoundingWidth = lastRoundingWidth;
308 return consumedCharacters;
311 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
313 int length = m_run.length();
318 if (m_currentCharacter >= static_cast<unsigned>(offset))
321 if (m_run.is8Bit()) {
322 Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
323 return advanceInternal(textIterator, glyphBuffer);
326 SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
327 return advanceInternal(textIterator, glyphBuffer);
330 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
332 unsigned oldSize = glyphBuffer.size();
333 advance(m_currentCharacter + 1, &glyphBuffer);
335 for (unsigned i = oldSize; i < glyphBuffer.size(); ++i)
336 w += glyphBuffer.advanceAt(i).width();
338 return glyphBuffer.size() > oldSize;