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.ltr() ? LTR : RTL, isAfterExpansion) : Character::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.ltr() ? LTR : RTL, 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, bool ltr, unsigned& lastGlyphCount, const SimpleFontData* fontData, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace)
103 ASSERT(typesettingFeatures & (Kerning | Ligatures));
108 unsigned glyphBufferSize = glyphBuffer->size();
109 if (glyphBuffer->size() <= lastGlyphCount + 1)
112 GlyphBufferAdvance* advances = glyphBuffer->advances(0);
113 float widthDifference = 0;
114 for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
115 widthDifference -= advances[i].width();
118 glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
120 fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures);
123 glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
125 for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) {
126 int spaceOffset = charactersTreatedAsSpace[i].first;
127 const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = charactersTreatedAsSpace[i].second;
128 if (spaceOffset && !originalAdvances.characterIsSpace)
129 glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.advanceBeforeCharacter);
130 glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtCharacter);
132 charactersTreatedAsSpace.clear();
134 for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
135 widthDifference += advances[i].width();
137 lastGlyphCount = glyphBufferSize;
138 return widthDifference;
141 template <typename TextIterator>
142 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
144 bool rtl = m_run.rtl();
145 bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion) && !m_run.spacingDisabled();
147 float widthSinceLastRounding = m_runWidthSoFar;
148 m_runWidthSoFar = floorf(m_runWidthSoFar);
149 widthSinceLastRounding -= m_runWidthSoFar;
151 float lastRoundingWidth = m_finalRoundingWidth;
154 const SimpleFontData* primaryFont = m_font->primaryFont();
155 const SimpleFontData* lastFontData = primaryFont;
156 unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
158 UChar32 character = 0;
159 unsigned clusterLength = 0;
160 CharactersTreatedAsSpace charactersTreatedAsSpace;
161 while (textIterator.consume(character, clusterLength)) {
162 unsigned advanceLength = clusterLength;
163 const GlyphData& glyphData = glyphDataForCharacter(character, rtl, textIterator.currentCharacter(), advanceLength);
164 Glyph glyph = glyphData.glyph;
165 const SimpleFontData* fontData = glyphData.fontData;
169 // Now that we have a glyph and font data, get its width.
171 if (character == '\t' && m_run.allowTabs())
172 width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
174 width = fontData->widthForGlyph(glyph);
176 // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
177 width *= m_run.horizontalGlyphStretch();
179 // We special case spaces in two ways when applying word rounding.
180 // First, we round spaces to an adjusted width in all fonts.
181 // Second, in fixed-pitch fonts we ensure that all characters that
182 // match the width of the space character have the same width as the space character.
183 if (m_run.applyWordRounding() && width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
184 width = fontData->adjustedSpaceWidth();
187 if (fontData != lastFontData && width) {
188 if (shouldApplyFontTransforms())
189 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);
191 lastFontData = fontData;
192 if (m_fallbackFonts && fontData != primaryFont) {
193 // FIXME: This does a little extra work that could be avoided if
194 // glyphDataForCharacter() returned whether it chose to use a small caps font.
195 if (!m_font->fontDescription().smallCaps() || character == toUpper(character))
196 m_fallbackFonts->add(fontData);
198 const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character), rtl);
199 if (uppercaseGlyphData.fontData != primaryFont)
200 m_fallbackFonts->add(uppercaseGlyphData.fontData);
205 if (hasExtraSpacing) {
206 // Account for letter-spacing.
207 if (width && m_font->fontDescription().letterSpacing())
208 width += m_font->fontDescription().letterSpacing();
210 static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
211 bool treatAsSpace = Character::treatAsSpace(character);
212 if (treatAsSpace || (expandAroundIdeographs && Character::isCJKIdeographOrSymbol(character))) {
213 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
215 float previousExpansion = m_expansion;
216 if (!treatAsSpace && !m_isAfterExpansion) {
217 // Take the expansion opportunity before this ideograph.
218 m_expansion -= m_expansionPerOpportunity;
219 float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
220 m_runWidthSoFar += expansionAtThisOpportunity;
222 if (glyphBuffer->isEmpty()) {
223 if (m_forTextEmphasis)
224 glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity);
226 glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
228 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
230 previousExpansion = m_expansion;
232 if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textIterator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length()))
233 || (m_run.rtl() && textIterator.currentCharacter())) {
234 m_expansion -= m_expansionPerOpportunity;
235 width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
236 m_isAfterExpansion = true;
239 m_isAfterExpansion = false;
241 // Account for word spacing.
242 // We apply additional space between "words" by adding width to the space character.
243 if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (textIterator.currentCharacter() || character == noBreakSpace) && m_font->fontDescription().wordSpacing())
244 width += m_font->fontDescription().wordSpacing();
246 m_isAfterExpansion = false;
249 if (shouldApplyFontTransforms() && glyphBuffer && Character::treatAsSpace(character))
250 charactersTreatedAsSpace.append(make_pair(glyphBuffer->size(),
251 OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1) : 0, width)));
253 if (m_accountForGlyphBounds) {
254 bounds = fontData->boundsForGlyph(glyph);
255 if (!textIterator.currentCharacter())
256 m_firstGlyphOverflow = max<float>(0, -bounds.x());
259 if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(character))
262 // Advance past the character we just dealt with.
263 textIterator.advance(advanceLength);
265 float oldWidth = width;
267 // Force characters that are used to determine word boundaries for the rounding hack
268 // to be integer width, so following words will start on an integer boundary.
269 if (m_run.applyWordRounding() && Character::isRoundingHackCharacter(character)) {
270 width = ceilf(width);
272 // Since widthSinceLastRounding can lose precision if we include measurements for
273 // preceding whitespace, we bypass it here.
274 m_runWidthSoFar += width;
276 // Since this is a rounding hack character, we should have reset this sum on the previous
278 ASSERT(!widthSinceLastRounding);
280 // Check to see if the next character is a "rounding hack character", if so, adjust
281 // width so that the total run width will be on an integer boundary.
282 if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Character::isRoundingHackCharacter(*(textIterator.characters())))
283 || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) {
284 float totalWidth = widthSinceLastRounding + width;
285 widthSinceLastRounding = ceilf(totalWidth);
286 width += widthSinceLastRounding - totalWidth;
287 m_runWidthSoFar += widthSinceLastRounding;
288 widthSinceLastRounding = 0;
290 widthSinceLastRounding += width;
294 glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
296 lastRoundingWidth = width - oldWidth;
298 if (m_accountForGlyphBounds) {
299 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY());
300 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y());
301 m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width);
305 if (shouldApplyFontTransforms())
306 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);
308 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
309 m_currentCharacter = textIterator.currentCharacter();
310 m_runWidthSoFar += widthSinceLastRounding;
311 m_finalRoundingWidth = lastRoundingWidth;
312 return consumedCharacters;
315 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
317 int length = m_run.length();
322 if (m_currentCharacter >= static_cast<unsigned>(offset))
325 if (m_run.is8Bit()) {
326 Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
327 return advanceInternal(textIterator, glyphBuffer);
330 SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
331 return advanceInternal(textIterator, glyphBuffer);
334 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
336 unsigned oldSize = glyphBuffer.size();
337 advance(m_currentCharacter + 1, &glyphBuffer);
339 for (unsigned i = oldSize; i < glyphBuffer.size(); ++i)
340 w += glyphBuffer.advanceAt(i);
342 return glyphBuffer.size() > oldSize;