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 "WidthIterator.h"
26 #include "GlyphBuffer.h"
27 #include "SimpleFontData.h"
28 #include "SurrogatePairAwareTextIterator.h"
30 #include <wtf/MathExtras.h>
33 using namespace Unicode;
38 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
41 , m_currentCharacter(0)
43 , m_isAfterExpansion(!run.allowsLeadingExpansion())
44 , m_finalRoundingWidth(0)
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)
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();
57 m_expansionPerOpportunity = 0;
59 bool isAfterExpansion = m_isAfterExpansion;
60 unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion);
61 if (isAfterExpansion && !m_run.allowsTrailingExpansion())
62 expansionOpportunityCount--;
64 if (!expansionOpportunityCount)
65 m_expansionPerOpportunity = 0;
67 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
71 GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
76 if (TextRun::RenderingContext* renderingContext = m_run.renderingContext())
77 return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, character, mirror, currentCharacter, advanceLength);
79 UNUSED_PARAM(currentCharacter);
80 UNUSED_PARAM(advanceLength);
83 return m_font->glyphDataForCharacter(character, mirror);
86 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
88 if (offset > m_run.length())
89 offset = m_run.length();
91 if (int(m_currentCharacter) >= offset)
94 bool rtl = m_run.rtl();
95 bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled();
97 float widthSinceLastRounding = m_runWidthSoFar;
98 m_runWidthSoFar = floorf(m_runWidthSoFar);
99 widthSinceLastRounding -= m_runWidthSoFar;
101 float lastRoundingWidth = m_finalRoundingWidth;
104 const SimpleFontData* primaryFont = m_font->primaryFont();
105 const SimpleFontData* lastFontData = primaryFont;
107 UChar32 character = 0;
108 unsigned clusterLength = 0;
109 SurrogatePairAwareTextIterator textIterator(m_run.data(m_currentCharacter), m_currentCharacter, offset, m_run.length());
110 while (textIterator.consume(character, clusterLength)) {
111 unsigned advanceLength = clusterLength;
112 const GlyphData& glyphData = glyphDataForCharacter(character, rtl, textIterator.currentCharacter(), advanceLength);
113 Glyph glyph = glyphData.glyph;
114 const SimpleFontData* fontData = glyphData.fontData;
118 // Now that we have a glyph and font data, get its width.
120 if (character == '\t' && m_run.allowTabs()) {
121 float tabWidth = m_font->tabWidth(*fontData);
122 width = tabWidth - fmodf(m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding, tabWidth);
124 width = fontData->widthForGlyph(glyph);
127 // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
128 width *= m_run.horizontalGlyphStretch();
131 // We special case spaces in two ways when applying word rounding.
132 // First, we round spaces to an adjusted width in all fonts.
133 // Second, in fixed-pitch fonts we ensure that all characters that
134 // match the width of the space character have the same width as the space character.
135 if (m_run.applyWordRounding() && width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
136 width = fontData->adjustedSpaceWidth();
139 if (fontData != lastFontData && width) {
140 lastFontData = fontData;
141 if (m_fallbackFonts && fontData != primaryFont) {
142 // FIXME: This does a little extra work that could be avoided if
143 // glyphDataForCharacter() returned whether it chose to use a small caps font.
144 if (!m_font->isSmallCaps() || character == toUpper(character))
145 m_fallbackFonts->add(fontData);
147 const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character), rtl);
148 if (uppercaseGlyphData.fontData != primaryFont)
149 m_fallbackFonts->add(uppercaseGlyphData.fontData);
154 if (hasExtraSpacing) {
155 // Account for letter-spacing.
156 if (width && m_font->letterSpacing())
157 width += m_font->letterSpacing();
159 static bool expandAroundIdeographs = Font::canExpandAroundIdeographsInComplexText();
160 bool treatAsSpace = Font::treatAsSpace(character);
161 if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(character))) {
162 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
164 if (!treatAsSpace && !m_isAfterExpansion) {
165 // Take the expansion opportunity before this ideograph.
166 m_expansion -= m_expansionPerOpportunity;
167 m_runWidthSoFar += m_expansionPerOpportunity;
169 if (glyphBuffer->isEmpty())
170 glyphBuffer->add(fontData->spaceGlyph(), fontData, m_expansionPerOpportunity);
172 glyphBuffer->expandLastAdvance(m_expansionPerOpportunity);
175 if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textIterator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length()))
176 || (m_run.rtl() && textIterator.currentCharacter())) {
177 m_expansion -= m_expansionPerOpportunity;
178 width += m_expansionPerOpportunity;
179 m_isAfterExpansion = true;
182 m_isAfterExpansion = false;
184 // Account for word spacing.
185 // We apply additional space between "words" by adding width to the space character.
186 if (treatAsSpace && textIterator.currentCharacter() && !Font::treatAsSpace(textIterator.characters()[-1]) && m_font->wordSpacing())
187 width += m_font->wordSpacing();
189 m_isAfterExpansion = false;
192 if (m_accountForGlyphBounds) {
193 bounds = fontData->boundsForGlyph(glyph);
194 if (!textIterator.currentCharacter())
195 m_firstGlyphOverflow = max<float>(0, -bounds.x());
198 if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character))
201 // Advance past the character we just dealt with.
202 textIterator.advance(advanceLength);
204 float oldWidth = width;
206 // Force characters that are used to determine word boundaries for the rounding hack
207 // to be integer width, so following words will start on an integer boundary.
208 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character)) {
209 width = ceilf(width);
211 // Since widthSinceLastRounding can lose precision if we include measurements for
212 // preceding whitespace, we bypass it here.
213 m_runWidthSoFar += width;
215 // Since this is a rounding hack character, we should have reset this sum on the previous
217 ASSERT(!widthSinceLastRounding);
219 // Check to see if the next character is a "rounding hack character", if so, adjust
220 // width so that the total run width will be on an integer boundary.
221 if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters())))
222 || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) {
223 float totalWidth = widthSinceLastRounding + width;
224 widthSinceLastRounding = ceilf(totalWidth);
225 width += widthSinceLastRounding - totalWidth;
226 m_runWidthSoFar += widthSinceLastRounding;
227 widthSinceLastRounding = 0;
229 widthSinceLastRounding += width;
233 glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
235 lastRoundingWidth = width - oldWidth;
237 if (m_accountForGlyphBounds) {
238 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY());
239 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y());
240 m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width);
244 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
245 m_currentCharacter = textIterator.currentCharacter();
246 m_runWidthSoFar += widthSinceLastRounding;
247 m_finalRoundingWidth = lastRoundingWidth;
248 return consumedCharacters;
251 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer)
253 int oldSize = glyphBuffer->size();
254 advance(m_currentCharacter + 1, glyphBuffer);
256 for (int i = oldSize; i < glyphBuffer->size(); ++i)
257 w += glyphBuffer->advanceAt(i);
259 return glyphBuffer->size() > oldSize;