Migrate from ints to unsigneds when referring to indices into strings
[WebKit-https.git] / Source / WebCore / platform / graphics / 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 "WidthIterator.h"
24
25 #include "Font.h"
26 #include "FontCascade.h"
27 #include "GlyphBuffer.h"
28 #include "Latin1TextIterator.h"
29 #include "SurrogatePairAwareTextIterator.h"
30 #include <wtf/MathExtras.h>
31
32 using namespace WTF;
33 using namespace Unicode;
34
35 namespace WebCore {
36
37 WidthIterator::WidthIterator(const FontCascade* font, const TextRun& run, HashSet<const Font*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
38     : m_font(font)
39     , m_run(run)
40     , m_currentCharacter(0)
41     , m_runWidthSoFar(0)
42     , m_isAfterExpansion((run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion)
43     , m_finalRoundingWidth(0)
44     , m_fallbackFonts(fallbackFonts)
45     , m_accountForGlyphBounds(accountForGlyphBounds)
46     , m_enableKerning(font->enableKerning())
47     , m_requiresShaping(font->requiresShaping())
48     , m_forTextEmphasis(forTextEmphasis)
49 {
50     // If the padding is non-zero, count the number of spaces in the run
51     // and divide that by the padding for per space addition.
52     m_expansion = m_run.expansion();
53     if (!m_expansion)
54         m_expansionPerOpportunity = 0;
55     else {
56         unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, run.expansionBehavior()).first;
57
58         if (!expansionOpportunityCount)
59             m_expansionPerOpportunity = 0;
60         else
61             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
62     }
63 }
64
65 struct OriginalAdvancesForCharacterTreatedAsSpace {
66 public:
67     OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore, float advanceAt)
68         : characterIsSpace(isSpace)
69         , advanceBeforeCharacter(advanceBefore)
70         , advanceAtCharacter(advanceAt)
71     {
72     }
73
74     bool characterIsSpace;
75     float advanceBeforeCharacter;
76     float advanceAtCharacter;
77 };
78
79 static inline bool isSoftBankEmoji(UChar32 codepoint)
80 {
81     return codepoint >= 0xE001 && codepoint <= 0xE537;
82 }
83
84 inline auto WidthIterator::shouldApplyFontTransforms(const GlyphBuffer* glyphBuffer, unsigned lastGlyphCount, UChar32 previousCharacter) const -> TransformsType
85 {
86     if (glyphBuffer && glyphBuffer->size() == (lastGlyphCount + 1) && isSoftBankEmoji(previousCharacter))
87         return TransformsType::Forced;
88     if (m_run.length() <= 1 || !(m_enableKerning || m_requiresShaping))
89         return TransformsType::None;
90     return TransformsType::NotForced;
91 }
92
93 inline float WidthIterator::applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, unsigned& lastGlyphCount, const Font* font, UChar32 previousCharacter, bool force, CharactersTreatedAsSpace& charactersTreatedAsSpace)
94 {
95     ASSERT_UNUSED(previousCharacter, shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter) != WidthIterator::TransformsType::None);
96
97     if (!glyphBuffer)
98         return 0;
99
100     unsigned glyphBufferSize = glyphBuffer->size();
101     if (!force && glyphBufferSize <= lastGlyphCount + 1) {
102         lastGlyphCount = glyphBufferSize;
103         return 0;
104     }
105
106     GlyphBufferAdvance* advances = glyphBuffer->advances(0);
107     float widthDifference = 0;
108     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
109         widthDifference -= advances[i].width();
110
111     ASSERT(lastGlyphCount <= glyphBufferSize);
112     if (!ltr)
113         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
114
115     font->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, m_enableKerning, m_requiresShaping);
116
117     if (!ltr)
118         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
119
120     for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) {
121         int spaceOffset = charactersTreatedAsSpace[i].first;
122         const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = charactersTreatedAsSpace[i].second;
123         if (spaceOffset && !originalAdvances.characterIsSpace)
124             glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.advanceBeforeCharacter);
125         glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtCharacter);
126     }
127     charactersTreatedAsSpace.clear();
128
129     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
130         widthDifference += advances[i].width();
131
132     lastGlyphCount = glyphBufferSize;
133     return widthDifference;
134 }
135
136 static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
137 {
138     bool expandLeft = ideograph;
139     bool expandRight = ideograph;
140     if (treatAsSpace) {
141         if (ltr)
142             expandRight = true;
143         else
144             expandLeft = true;
145     }
146     if (isAfterExpansion) {
147         if (ltr)
148             expandLeft = false;
149         else
150             expandRight = false;
151     }
152     ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
153     ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
154     if (forbidLeadingExpansion)
155         expandLeft = false;
156     if (forbidTrailingExpansion)
157         expandRight = false;
158     if (forceLeadingExpansion)
159         expandLeft = true;
160     if (forceTrailingExpansion)
161         expandRight = true;
162     return std::make_pair(expandLeft, expandRight);
163 }
164
165 template <typename TextIterator>
166 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
167 {
168     bool rtl = m_run.rtl();
169     bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled();
170
171     bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
172     bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
173     bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
174     bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
175     float widthSinceLastRounding = m_runWidthSoFar;
176     float leftoverJustificationWidth = 0;
177     m_runWidthSoFar = floorf(m_runWidthSoFar);
178     widthSinceLastRounding -= m_runWidthSoFar;
179
180     float lastRoundingWidth = m_finalRoundingWidth;
181     FloatRect bounds;
182
183     const Font& primaryFont = m_font->primaryFont();
184     const Font* lastFontData = &primaryFont;
185     unsigned lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
186
187     UChar32 character = 0;
188     UChar32 previousCharacter = 0;
189     unsigned clusterLength = 0;
190     CharactersTreatedAsSpace charactersTreatedAsSpace;
191     String normalizedSpacesStringCache;
192     // We are iterating in string order, not glyph order. Compare this to ComplexTextController::adjustGlyphsAndAdvances()
193     while (textIterator.consume(character, clusterLength)) {
194         unsigned advanceLength = clusterLength;
195         int currentCharacter = textIterator.currentIndex();
196         const GlyphData& glyphData = m_font->glyphDataForCharacter(character, rtl);
197         Glyph glyph = glyphData.glyph;
198         if (!glyph) {
199             textIterator.advance(advanceLength);
200             continue;
201         }
202         const Font* font = glyphData.font;
203         ASSERT(font);
204
205         // Now that we have a glyph and font data, get its width.
206         float width;
207         if (character == '\t' && m_run.allowTabs())
208             width = m_font->tabWidth(*font, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
209         else {
210             width = font->widthForGlyph(glyph);
211
212             // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
213             width *= m_run.horizontalGlyphStretch();
214         }
215
216         if (font != lastFontData && width) {
217             auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
218             if (transformsType != TransformsType::None) {
219                 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
220                 if (glyphBuffer)
221                     glyphBuffer->shrink(lastGlyphCount);
222             }
223
224             lastFontData = font;
225             if (m_fallbackFonts && font != &primaryFont) {
226                 // FIXME: This does a little extra work that could be avoided if
227                 // glyphDataForCharacter() returned whether it chose to use a small caps font.
228                 if (!m_font->isSmallCaps() || character == u_toupper(character))
229                     m_fallbackFonts->add(font);
230                 else {
231                     const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(u_toupper(character), rtl);
232                     if (uppercaseGlyphData.font != &primaryFont)
233                         m_fallbackFonts->add(uppercaseGlyphData.font);
234                 }
235             }
236         }
237
238         if (hasExtraSpacing) {
239             // Account for letter-spacing.
240             if (width) {
241                 width += m_font->letterSpacing();
242                 width += leftoverJustificationWidth;
243                 leftoverJustificationWidth = 0;
244             }
245
246             static bool expandAroundIdeographs = FontCascade::canExpandAroundIdeographsInComplexText();
247             bool treatAsSpace = FontCascade::treatAsSpace(character);
248             bool currentIsLastCharacter = currentCharacter + advanceLength == static_cast<size_t>(m_run.length());
249             bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
250             bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
251             bool forbidLeadingExpansion = false;
252             bool forbidTrailingExpansion = false;
253             if (runForcesLeadingExpansion)
254                 forceLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
255             if (runForcesTrailingExpansion)
256                 forceTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
257             if (runForbidsLeadingExpansion)
258                 forbidLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
259             if (runForbidsTrailingExpansion)
260                 forbidTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
261             bool ideograph = (expandAroundIdeographs && FontCascade::isCJKIdeographOrSymbol(character));
262             if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
263                 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
264                 if (m_expansion) {
265                     bool expandLeft, expandRight;
266                     std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), m_isAfterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
267                     if (expandLeft) {
268                         if (m_run.ltr()) {
269                             // Increase previous width
270                             m_expansion -= m_expansionPerOpportunity;
271                             m_runWidthSoFar += m_expansionPerOpportunity;
272                             if (glyphBuffer) {
273                                 if (glyphBuffer->isEmpty()) {
274                                     if (m_forTextEmphasis)
275                                         glyphBuffer->add(font->zeroWidthSpaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
276                                     else
277                                         glyphBuffer->add(font->spaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
278                                 } else
279                                     glyphBuffer->expandLastAdvance(m_expansionPerOpportunity);
280                             }
281                         } else {
282                             // Increase next width
283                             leftoverJustificationWidth += m_expansionPerOpportunity;
284                             m_isAfterExpansion = true;
285                         }
286                     }
287                     if (expandRight) {
288                         m_expansion -= m_expansionPerOpportunity;
289                         width += m_expansionPerOpportunity;
290                         if (m_run.ltr())
291                             m_isAfterExpansion = true;
292                     }
293                 } else
294                     m_isAfterExpansion = false;
295
296                 // Account for word spacing.
297                 // We apply additional space between "words" by adding width to the space character.
298                 if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (currentCharacter || character == noBreakSpace) && m_font->wordSpacing())
299                     width += m_font->wordSpacing();
300             } else
301                 m_isAfterExpansion = false;
302         }
303
304         auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
305         if (transformsType != TransformsType::None && glyphBuffer && FontCascade::treatAsSpace(character)) {
306             charactersTreatedAsSpace.append(std::make_pair(glyphBuffer->size(),
307                 OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1).width() : 0, width)));
308         }
309
310         if (m_accountForGlyphBounds) {
311             bounds = font->boundsForGlyph(glyph);
312             if (!currentCharacter)
313                 m_firstGlyphOverflow = std::max<float>(0, -bounds.x());
314         }
315
316         if (m_forTextEmphasis && !FontCascade::canReceiveTextEmphasis(character))
317             glyph = 0;
318
319         // Advance past the character we just dealt with.
320         textIterator.advance(advanceLength);
321
322         float oldWidth = width;
323
324         widthSinceLastRounding += width;
325
326         if (glyphBuffer)
327             glyphBuffer->add(glyph, font, (rtl ? oldWidth + lastRoundingWidth : width), currentCharacter);
328
329         lastRoundingWidth = width - oldWidth;
330
331         if (m_accountForGlyphBounds) {
332             m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY());
333             m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
334             m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
335         }
336         previousCharacter = character;
337     }
338
339     if (leftoverJustificationWidth) {
340         if (m_forTextEmphasis)
341             glyphBuffer->add(lastFontData->zeroWidthSpaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1);
342         else
343             glyphBuffer->add(lastFontData->spaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1);
344     }
345
346     auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
347     if (transformsType != TransformsType::None) {
348         m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
349         if (glyphBuffer)
350             glyphBuffer->shrink(lastGlyphCount);
351     }
352
353     unsigned consumedCharacters = textIterator.currentIndex() - m_currentCharacter;
354     m_currentCharacter = textIterator.currentIndex();
355     m_runWidthSoFar += widthSinceLastRounding;
356     m_finalRoundingWidth = lastRoundingWidth;
357     return consumedCharacters;
358 }
359
360 unsigned WidthIterator::advance(unsigned offset, GlyphBuffer* glyphBuffer)
361 {
362     unsigned length = m_run.length();
363
364     if (offset > length)
365         offset = length;
366
367     if (m_currentCharacter >= offset)
368         return 0;
369
370     if (m_run.is8Bit()) {
371         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
372         return advanceInternal(textIterator, glyphBuffer);
373     }
374
375     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
376     return advanceInternal(textIterator, glyphBuffer);
377 }
378
379 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
380 {
381     unsigned oldSize = glyphBuffer.size();
382     advance(m_currentCharacter + 1, &glyphBuffer);
383     float w = 0;
384     for (unsigned i = oldSize; i < glyphBuffer.size(); ++i)
385         w += glyphBuffer.advanceAt(i).width();
386     width = w;
387     return glyphBuffer.size() > oldSize;
388 }
389
390 }