8816fd8158fde2c8bcb73270715bf0ecc6f6a7f5
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / fonts / WidthIterator.cpp
1 /*
2  * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Holger Hans Peter Freyther
4  *
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.
9  *
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.
14  *
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.
19  *
20  */
21
22 #include "config.h"
23 #include "platform/fonts/WidthIterator.h"
24
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"
32
33 using namespace WTF;
34 using namespace Unicode;
35 using namespace std;
36
37 namespace WebCore {
38
39 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
40     : m_font(font)
41     , m_run(run)
42     , m_currentCharacter(0)
43     , m_runWidthSoFar(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)
54 {
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();
58     if (!m_expansion)
59         m_expansionPerOpportunity = 0;
60     else {
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--;
65
66         if (!expansionOpportunityCount)
67             m_expansionPerOpportunity = 0;
68         else
69             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
70     }
71 }
72
73 GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
74 {
75     ASSERT(m_font);
76
77 #if ENABLE(SVG_FONTS)
78     if (TextRun::RenderingContext* renderingContext = m_run.renderingContext())
79         return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, character, mirror, currentCharacter, advanceLength);
80 #endif
81
82     return m_font->glyphDataForCharacter(character, mirror);
83 }
84
85 struct OriginalAdvancesForCharacterTreatedAsSpace {
86 public:
87     OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore, float advanceAt)
88         : characterIsSpace(isSpace)
89         , advanceBeforeCharacter(advanceBefore)
90         , advanceAtCharacter(advanceAt)
91     {
92     }
93
94     bool characterIsSpace;
95     float advanceBeforeCharacter;
96     float advanceAtCharacter;
97 };
98
99 typedef Vector<pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace;
100
101 static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, unsigned& lastGlyphCount, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace)
102 {
103     ASSERT(typesettingFeatures & (Kerning | Ligatures));
104
105     if (!glyphBuffer)
106         return 0;
107
108     unsigned glyphBufferSize = glyphBuffer->size();
109     if (glyphBuffer->size() <= lastGlyphCount + 1)
110         return 0;
111
112     FloatSize* advances = glyphBuffer->advances(0);
113     float widthDifference = 0;
114     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
115         widthDifference -= advances[i].width();
116
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);
123     }
124     charactersTreatedAsSpace.clear();
125
126     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
127         widthDifference += advances[i].width();
128
129     lastGlyphCount = glyphBufferSize;
130     return widthDifference;
131 }
132
133 template <typename TextIterator>
134 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
135 {
136     bool rtl = m_run.rtl();
137     bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion) && !m_run.spacingDisabled();
138
139     float widthSinceLastRounding = m_runWidthSoFar;
140     m_runWidthSoFar = floorf(m_runWidthSoFar);
141     widthSinceLastRounding -= m_runWidthSoFar;
142
143     float lastRoundingWidth = m_finalRoundingWidth;
144     FloatRect bounds;
145
146     const SimpleFontData* primaryFont = m_font->primaryFont();
147     const SimpleFontData* lastFontData = primaryFont;
148     unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
149
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;
158
159         ASSERT(fontData);
160
161         // Now that we have a glyph and font data, get its width.
162         float width;
163         if (character == '\t' && m_run.allowTabs())
164             width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
165         else {
166             width = fontData->widthForGlyph(glyph);
167
168             // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
169             width *= m_run.horizontalGlyphStretch();
170
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();
177         }
178
179         if (fontData != lastFontData && width) {
180             if (shouldApplyFontTransforms())
181                 m_runWidthSoFar += applyFontTransforms(glyphBuffer, lastGlyphCount, m_typesettingFeatures, charactersTreatedAsSpace);
182
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);
189                 else {
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);
194                 }
195             }
196         }
197
198         if (hasExtraSpacing) {
199             // Account for letter-spacing.
200             if (width && m_font->fontDescription().letterSpacing())
201                 width += m_font->fontDescription().letterSpacing();
202
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.
207                 if (m_expansion) {
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;
214                         if (glyphBuffer) {
215                             if (glyphBuffer->isEmpty()) {
216                                 if (m_forTextEmphasis)
217                                     glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity);
218                                 else
219                                     glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
220                             } else
221                                 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
222                         }
223                         previousExpansion = m_expansion;
224                     }
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;
230                     }
231                 } else
232                     m_isAfterExpansion = false;
233
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();
238             } else
239                 m_isAfterExpansion = false;
240         }
241
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,
246                     width)));
247         }
248
249         if (m_accountForGlyphBounds) {
250             bounds = fontData->boundsForGlyph(glyph);
251             if (!textIterator.currentCharacter())
252                 m_firstGlyphOverflow = max<float>(0, -bounds.x());
253         }
254
255         if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(character))
256             glyph = 0;
257
258         // Advance past the character we just dealt with.
259         textIterator.advance(advanceLength);
260
261         float oldWidth = width;
262
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);
267
268             // Since widthSinceLastRounding can lose precision if we include measurements for
269             // preceding whitespace, we bypass it here.
270             m_runWidthSoFar += width;
271
272             // Since this is a rounding hack character, we should have reset this sum on the previous
273             // iteration.
274             ASSERT(!widthSinceLastRounding);
275         } else {
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;
285             } else
286                 widthSinceLastRounding += width;
287         }
288
289         if (glyphBuffer)
290             glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
291
292         lastRoundingWidth = width - oldWidth;
293
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);
298         }
299     }
300
301     if (shouldApplyFontTransforms())
302         m_runWidthSoFar += applyFontTransforms(glyphBuffer, lastGlyphCount, m_typesettingFeatures, charactersTreatedAsSpace);
303
304     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
305     m_currentCharacter = textIterator.currentCharacter();
306     m_runWidthSoFar += widthSinceLastRounding;
307     m_finalRoundingWidth = lastRoundingWidth;
308     return consumedCharacters;
309 }
310
311 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
312 {
313     int length = m_run.length();
314
315     if (offset > length)
316         offset = length;
317
318     if (m_currentCharacter >= static_cast<unsigned>(offset))
319         return 0;
320
321     if (m_run.is8Bit()) {
322         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
323         return advanceInternal(textIterator, glyphBuffer);
324     }
325
326     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
327     return advanceInternal(textIterator, glyphBuffer);
328 }
329
330 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
331 {
332     unsigned oldSize = glyphBuffer.size();
333     advance(m_currentCharacter + 1, &glyphBuffer);
334     float w = 0;
335     for (unsigned i = oldSize; i < glyphBuffer.size(); ++i)
336         w += glyphBuffer.advanceAt(i).width();
337     width = w;
338     return glyphBuffer.size() > oldSize;
339 }
340
341 }