Upstream version 9.38.198.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  * Copyright (C) 2014 Google Inc. All rights reserved.
5  *
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.
10  *
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.
15  *
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.
20  *
21  */
22
23 #include "config.h"
24 #include "platform/fonts/WidthIterator.h"
25
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"
34
35 using namespace WTF;
36 using namespace Unicode;
37 using namespace std;
38
39 namespace blink {
40
41 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
42     : m_font(font)
43     , m_run(run)
44     , m_currentCharacter(0)
45     , m_runWidthSoFar(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)
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(CharacterData& charData)
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, charData.character,
80             m_run.rtl(), charData.characterOffset, charData.clusterLength);
81     }
82 #endif
83
84     return m_font->glyphDataForCharacter(charData.character, m_run.rtl());
85 }
86
87 float WidthIterator::characterWidth(UChar32 character, const GlyphData& glyphData) const
88 {
89     const SimpleFontData* fontData = glyphData.fontData;
90     ASSERT(fontData);
91
92     if (UNLIKELY(character == '\t' && m_run.allowTabs()))
93         return m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar);
94
95     float width = fontData->widthForGlyph(glyphData.glyph);
96
97     // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
98     if (UNLIKELY(m_run.horizontalGlyphStretch() != 1))
99         width *= m_run.horizontalGlyphStretch();
100
101     return width;
102 }
103
104 void WidthIterator::cacheFallbackFont(UChar32 character, const SimpleFontData* fontData,
105     const SimpleFontData* primaryFont)
106 {
107     if (fontData == primaryFont)
108         return;
109
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);
114     } else {
115         ASSERT(m_font->fontDescription().variant() == FontVariantSmallCaps);
116         const GlyphData uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character),
117             m_run.rtl());
118         if (uppercaseGlyphData.fontData != primaryFont)
119             m_fallbackFonts->add(uppercaseGlyphData.fontData);
120     }
121 }
122
123 float WidthIterator::adjustSpacing(float width, const CharacterData& charData,
124     const SimpleFontData& fontData, GlyphBuffer* glyphBuffer)
125 {
126     // Account for letter-spacing.
127     if (width)
128         width += m_font->fontDescription().letterSpacing();
129
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.
134         if (m_expansion) {
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;
140                 if (glyphBuffer) {
141                     if (glyphBuffer->isEmpty()) {
142                         if (m_forTextEmphasis)
143                             glyphBuffer->add(fontData.zeroWidthSpaceGlyph(), &fontData, m_expansionPerOpportunity);
144                         else
145                             glyphBuffer->add(fontData.spaceGlyph(), &fontData, expansionAtThisOpportunity);
146                     } else {
147                         glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
148                     }
149                 }
150             }
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;
157             }
158         } else {
159             m_isAfterExpansion = false;
160         }
161
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();
168         }
169     } else {
170         m_isAfterExpansion = false;
171     }
172
173     return width;
174 }
175
176 void WidthIterator::updateGlyphBounds(const GlyphData& glyphData, float width, bool firstCharacter)
177 {
178     ASSERT(glyphData.fontData);
179     FloatRect bounds = glyphData.fontData->boundsForGlyph(glyphData.glyph);
180
181     if (firstCharacter)
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());
186 }
187
188 template <typename TextIterator>
189 unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
190 {
191     bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion)
192         && !m_run.spacingDisabled();
193
194     const SimpleFontData* primaryFont = m_font->primaryFont();
195     const SimpleFontData* lastFontData = primaryFont;
196
197     CharacterData charData;
198     while (textIterator.consume(charData.character, charData.clusterLength)) {
199         charData.characterOffset = textIterator.currentCharacter();
200
201         const GlyphData glyphData = glyphDataForCharacter(charData);
202         Glyph glyph = glyphData.glyph;
203         const SimpleFontData* fontData = glyphData.fontData;
204         ASSERT(fontData);
205
206         // Now that we have a glyph and font data, get its width.
207         float width = characterWidth(charData.character, glyphData);
208
209         if (m_fallbackFonts && lastFontData != fontData && width) {
210             lastFontData = fontData;
211             cacheFallbackFont(charData.character, fontData, primaryFont);
212         }
213
214         if (hasExtraSpacing)
215             width = adjustSpacing(width, charData, *fontData, glyphBuffer);
216
217         if (m_accountForGlyphBounds)
218             updateGlyphBounds(glyphData, width, !charData.characterOffset);
219
220         if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(charData.character))
221             glyph = 0;
222
223         // Advance past the character we just dealt with.
224         textIterator.advance(charData.clusterLength);
225         m_runWidthSoFar += width;
226
227         if (glyphBuffer)
228             glyphBuffer->add(glyph, fontData, width);
229     }
230
231     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
232     m_currentCharacter = textIterator.currentCharacter();
233
234     return consumedCharacters;
235 }
236
237 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
238 {
239     int length = m_run.length();
240
241     if (offset > length)
242         offset = length;
243
244     if (m_currentCharacter >= static_cast<unsigned>(offset))
245         return 0;
246
247     if (m_run.is8Bit()) {
248         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
249         return advanceInternal(textIterator, glyphBuffer);
250     }
251
252     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
253     return advanceInternal(textIterator, glyphBuffer);
254 }
255
256 bool WidthIterator::advanceOneCharacter(float& width)
257 {
258     float initialWidth = m_runWidthSoFar;
259
260     if (!advance(m_currentCharacter + 1))
261         return false;
262
263     width = m_runWidthSoFar - initialWidth;
264     return true;
265 }
266
267 } // namespace blink