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 float previousExpansion = m_expansion;
165 if (!treatAsSpace && !m_isAfterExpansion) {
166 // Take the expansion opportunity before this ideograph.
167 m_expansion -= m_expansionPerOpportunity;
168 float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
169 m_runWidthSoFar += expansionAtThisOpportunity;
171 if (glyphBuffer->isEmpty())
172 glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
174 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
176 previousExpansion = m_expansion;
178 if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textIterator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length()))
179 || (m_run.rtl() && textIterator.currentCharacter())) {
180 m_expansion -= m_expansionPerOpportunity;
181 width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
182 m_isAfterExpansion = true;
185 m_isAfterExpansion = false;
187 // Account for word spacing.
188 // We apply additional space between "words" by adding width to the space character.
189 if (treatAsSpace && textIterator.currentCharacter() && !Font::treatAsSpace(textIterator.characters()[-1]) && m_font->wordSpacing())
190 width += m_font->wordSpacing();
192 m_isAfterExpansion = false;
195 if (m_accountForGlyphBounds) {
196 bounds = fontData->boundsForGlyph(glyph);
197 if (!textIterator.currentCharacter())
198 m_firstGlyphOverflow = max<float>(0, -bounds.x());
201 if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character))
204 // Advance past the character we just dealt with.
205 textIterator.advance(advanceLength);
207 float oldWidth = width;
209 // Force characters that are used to determine word boundaries for the rounding hack
210 // to be integer width, so following words will start on an integer boundary.
211 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character)) {
212 width = ceilf(width);
214 // Since widthSinceLastRounding can lose precision if we include measurements for
215 // preceding whitespace, we bypass it here.
216 m_runWidthSoFar += width;
218 // Since this is a rounding hack character, we should have reset this sum on the previous
220 ASSERT(!widthSinceLastRounding);
222 // Check to see if the next character is a "rounding hack character", if so, adjust
223 // width so that the total run width will be on an integer boundary.
224 if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters())))
225 || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) {
226 float totalWidth = widthSinceLastRounding + width;
227 widthSinceLastRounding = ceilf(totalWidth);
228 width += widthSinceLastRounding - totalWidth;
229 m_runWidthSoFar += widthSinceLastRounding;
230 widthSinceLastRounding = 0;
232 widthSinceLastRounding += width;
236 glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
238 lastRoundingWidth = width - oldWidth;
240 if (m_accountForGlyphBounds) {
241 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY());
242 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y());
243 m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width);
247 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
248 m_currentCharacter = textIterator.currentCharacter();
249 m_runWidthSoFar += widthSinceLastRounding;
250 m_finalRoundingWidth = lastRoundingWidth;
251 return consumedCharacters;
254 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer)
256 int oldSize = glyphBuffer->size();
257 advance(m_currentCharacter + 1, glyphBuffer);
259 for (int i = oldSize; i < glyphBuffer->size(); ++i)
260 w += glyphBuffer->advanceAt(i);
262 return glyphBuffer->size() > oldSize;