2 * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Holger Hans Peter Freyther
4 * Copyright (C) 2014 Google Inc. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
24 #include "platform/fonts/WidthIterator.h"
26 #include "platform/fonts/Character.h"
27 #include "platform/fonts/Font.h"
28 #include "platform/fonts/FontPlatformFeatures.h"
29 #include "platform/fonts/GlyphBuffer.h"
30 #include "platform/fonts/Latin1TextIterator.h"
31 #include "platform/fonts/SimpleFontData.h"
32 #include "platform/text/SurrogatePairAwareTextIterator.h"
33 #include "wtf/MathExtras.h"
36 using namespace Unicode;
41 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
44 , m_currentCharacter(0)
46 , m_isAfterExpansion(!run.allowsLeadingExpansion())
47 , m_fallbackFonts(fallbackFonts)
48 , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
49 , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
50 , m_firstGlyphOverflow(0)
51 , m_lastGlyphOverflow(0)
52 , m_accountForGlyphBounds(accountForGlyphBounds)
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(CharacterData& charData)
78 if (TextRun::RenderingContext* renderingContext = m_run.renderingContext()) {
79 return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, charData.character,
80 m_run.rtl(), charData.characterOffset, charData.clusterLength);
84 return m_font->glyphDataForCharacter(charData.character, m_run.rtl());
87 float WidthIterator::characterWidth(UChar32 character, const GlyphData& glyphData) const
89 const SimpleFontData* fontData = glyphData.fontData;
92 if (UNLIKELY(character == '\t' && m_run.allowTabs()))
93 return m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar);
95 float width = fontData->widthForGlyph(glyphData.glyph);
97 // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
98 if (UNLIKELY(m_run.horizontalGlyphStretch() != 1))
99 width *= m_run.horizontalGlyphStretch();
104 void WidthIterator::cacheFallbackFont(UChar32 character, const SimpleFontData* fontData,
105 const SimpleFontData* primaryFont)
107 if (fontData == primaryFont)
110 // FIXME: This does a little extra work that could be avoided if
111 // glyphDataForCharacter() returned whether it chose to use a small caps font.
112 if (m_font->fontDescription().variant() == FontVariantNormal || character == toUpper(character)) {
113 m_fallbackFonts->add(fontData);
115 ASSERT(m_font->fontDescription().variant() == FontVariantSmallCaps);
116 const GlyphData uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character),
118 if (uppercaseGlyphData.fontData != primaryFont)
119 m_fallbackFonts->add(uppercaseGlyphData.fontData);
123 float WidthIterator::adjustSpacing(float width, const CharacterData& charData,
124 const SimpleFontData& fontData, GlyphBuffer* glyphBuffer)
126 // Account for letter-spacing.
128 width += m_font->fontDescription().letterSpacing();
130 static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
131 bool treatAsSpace = Character::treatAsSpace(charData.character);
132 if (treatAsSpace || (expandAroundIdeographs && Character::isCJKIdeographOrSymbol(charData.character))) {
133 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
135 if (!treatAsSpace && !m_isAfterExpansion) {
136 // Take the expansion opportunity before this ideograph.
137 m_expansion -= m_expansionPerOpportunity;
138 float expansionAtThisOpportunity = m_expansionPerOpportunity;
139 m_runWidthSoFar += expansionAtThisOpportunity;
141 if (glyphBuffer->isEmpty()) {
142 if (m_forTextEmphasis)
143 glyphBuffer->add(fontData.zeroWidthSpaceGlyph(), &fontData, m_expansionPerOpportunity);
145 glyphBuffer->add(fontData.spaceGlyph(), &fontData, expansionAtThisOpportunity);
147 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
151 if (m_run.allowsTrailingExpansion()
152 || (m_run.ltr() && charData.characterOffset + charData.clusterLength < static_cast<size_t>(m_run.length()))
153 || (m_run.rtl() && charData.characterOffset)) {
154 m_expansion -= m_expansionPerOpportunity;
155 width += m_expansionPerOpportunity;
156 m_isAfterExpansion = true;
159 m_isAfterExpansion = false;
162 // Account for word spacing.
163 // We apply additional space between "words" by adding width to the space character.
164 if (treatAsSpace && (charData.character != '\t' || !m_run.allowTabs())
165 && (charData.characterOffset || charData.character == noBreakSpace)
166 && m_font->fontDescription().wordSpacing()) {
167 width += m_font->fontDescription().wordSpacing();
170 m_isAfterExpansion = false;
176 void WidthIterator::updateGlyphBounds(const GlyphData& glyphData, float width, bool firstCharacter)
178 ASSERT(glyphData.fontData);
179 FloatRect bounds = glyphData.fontData->boundsForGlyph(glyphData.glyph);
182 m_firstGlyphOverflow = max<float>(0, -bounds.x());
183 m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width);
184 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY());
185 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y());
188 template <typename TextIterator>
189 unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
191 bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion)
192 && !m_run.spacingDisabled();
194 const SimpleFontData* primaryFont = m_font->primaryFont();
195 const SimpleFontData* lastFontData = primaryFont;
197 CharacterData charData;
198 while (textIterator.consume(charData.character, charData.clusterLength)) {
199 charData.characterOffset = textIterator.currentCharacter();
201 const GlyphData glyphData = glyphDataForCharacter(charData);
202 Glyph glyph = glyphData.glyph;
203 const SimpleFontData* fontData = glyphData.fontData;
206 // Now that we have a glyph and font data, get its width.
207 float width = characterWidth(charData.character, glyphData);
209 if (m_fallbackFonts && lastFontData != fontData && width) {
210 lastFontData = fontData;
211 cacheFallbackFont(charData.character, fontData, primaryFont);
215 width = adjustSpacing(width, charData, *fontData, glyphBuffer);
217 if (m_accountForGlyphBounds)
218 updateGlyphBounds(glyphData, width, !charData.characterOffset);
220 if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(charData.character))
223 // Advance past the character we just dealt with.
224 textIterator.advance(charData.clusterLength);
225 m_runWidthSoFar += width;
228 glyphBuffer->add(glyph, fontData, width);
231 unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
232 m_currentCharacter = textIterator.currentCharacter();
234 return consumedCharacters;
237 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
239 int length = m_run.length();
244 if (m_currentCharacter >= static_cast<unsigned>(offset))
247 if (m_run.is8Bit()) {
248 Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
249 return advanceInternal(textIterator, glyphBuffer);
252 SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
253 return advanceInternal(textIterator, glyphBuffer);
256 bool WidthIterator::advanceOneCharacter(float& width)
258 float initialWidth = m_runWidthSoFar;
260 if (!advance(m_currentCharacter + 1))
263 width = m_runWidthSoFar - initialWidth;