Upstream version 5.34.104.0
[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.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--;
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, bool ltr, unsigned& lastGlyphCount, const SimpleFontData* fontData, 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     GlyphBufferAdvance* advances = glyphBuffer->advances(0);
113     float widthDifference = 0;
114     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
115         widthDifference -= advances[i].width();
116
117     if (!ltr)
118         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
119
120     fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures);
121
122     if (!ltr)
123         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
124
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);
131     }
132     charactersTreatedAsSpace.clear();
133
134     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
135         widthDifference += advances[i].width();
136
137     lastGlyphCount = glyphBufferSize;
138     return widthDifference;
139 }
140
141 template <typename TextIterator>
142 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
143 {
144     bool rtl = m_run.rtl();
145     bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion) && !m_run.spacingDisabled();
146
147     float widthSinceLastRounding = m_runWidthSoFar;
148     m_runWidthSoFar = floorf(m_runWidthSoFar);
149     widthSinceLastRounding -= m_runWidthSoFar;
150
151     float lastRoundingWidth = m_finalRoundingWidth;
152     FloatRect bounds;
153
154     const SimpleFontData* primaryFont = m_font->primaryFont();
155     const SimpleFontData* lastFontData = primaryFont;
156     unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
157
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;
166
167         ASSERT(fontData);
168
169         // Now that we have a glyph and font data, get its width.
170         float width;
171         if (character == '\t' && m_run.allowTabs())
172             width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
173         else {
174             width = fontData->widthForGlyph(glyph);
175
176             // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
177             width *= m_run.horizontalGlyphStretch();
178
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();
185         }
186
187         if (fontData != lastFontData && width) {
188             if (shouldApplyFontTransforms())
189                 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);
190
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);
197                 else {
198                     const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character), rtl);
199                     if (uppercaseGlyphData.fontData != primaryFont)
200                         m_fallbackFonts->add(uppercaseGlyphData.fontData);
201                 }
202             }
203         }
204
205         if (hasExtraSpacing) {
206             // Account for letter-spacing.
207             if (width && m_font->fontDescription().letterSpacing())
208                 width += m_font->fontDescription().letterSpacing();
209
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.
214                 if (m_expansion) {
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;
221                         if (glyphBuffer) {
222                             if (glyphBuffer->isEmpty()) {
223                                 if (m_forTextEmphasis)
224                                     glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity);
225                                 else
226                                     glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
227                             } else
228                                 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
229                         }
230                         previousExpansion = m_expansion;
231                     }
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;
237                     }
238                 } else
239                     m_isAfterExpansion = false;
240
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();
245             } else
246                 m_isAfterExpansion = false;
247         }
248
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)));
252
253         if (m_accountForGlyphBounds) {
254             bounds = fontData->boundsForGlyph(glyph);
255             if (!textIterator.currentCharacter())
256                 m_firstGlyphOverflow = max<float>(0, -bounds.x());
257         }
258
259         if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(character))
260             glyph = 0;
261
262         // Advance past the character we just dealt with.
263         textIterator.advance(advanceLength);
264
265         float oldWidth = width;
266
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);
271
272             // Since widthSinceLastRounding can lose precision if we include measurements for
273             // preceding whitespace, we bypass it here.
274             m_runWidthSoFar += width;
275
276             // Since this is a rounding hack character, we should have reset this sum on the previous
277             // iteration.
278             ASSERT(!widthSinceLastRounding);
279         } else {
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;
289             } else
290                 widthSinceLastRounding += width;
291         }
292
293         if (glyphBuffer)
294             glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
295
296         lastRoundingWidth = width - oldWidth;
297
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);
302         }
303     }
304
305     if (shouldApplyFontTransforms())
306         m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);
307
308     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
309     m_currentCharacter = textIterator.currentCharacter();
310     m_runWidthSoFar += widthSinceLastRounding;
311     m_finalRoundingWidth = lastRoundingWidth;
312     return consumedCharacters;
313 }
314
315 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
316 {
317     int length = m_run.length();
318
319     if (offset > length)
320         offset = length;
321
322     if (m_currentCharacter >= static_cast<unsigned>(offset))
323         return 0;
324
325     if (m_run.is8Bit()) {
326         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
327         return advanceInternal(textIterator, glyphBuffer);
328     }
329
330     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
331     return advanceInternal(textIterator, glyphBuffer);
332 }
333
334 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
335 {
336     unsigned oldSize = glyphBuffer.size();
337     advance(m_currentCharacter + 1, &glyphBuffer);
338     float w = 0;
339     for (unsigned i = oldSize; i < glyphBuffer.size(); ++i)
340         w += glyphBuffer.advanceAt(i);
341     width = w;
342     return glyphBuffer.size() > oldSize;
343 }
344
345 }