0b9a7402afb613606058ed98314a6b1dcb9056b4
[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, int 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, int& 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     int 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 (int i = lastGlyphCount; i < glyphBufferSize; ++i)
109         widthDifference -= advances[i].width();
110
111     if (!ltr)
112         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
113
114     font->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, m_enableKerning, m_requiresShaping);
115
116     if (!ltr)
117         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
118
119     for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) {
120         int spaceOffset = charactersTreatedAsSpace[i].first;
121         const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = charactersTreatedAsSpace[i].second;
122         if (spaceOffset && !originalAdvances.characterIsSpace)
123             glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.advanceBeforeCharacter);
124         glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtCharacter);
125     }
126     charactersTreatedAsSpace.clear();
127
128     for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
129         widthDifference += advances[i].width();
130
131     lastGlyphCount = glyphBufferSize;
132     return widthDifference;
133 }
134
135 static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
136 {
137     bool expandLeft = ideograph;
138     bool expandRight = ideograph;
139     if (treatAsSpace) {
140         if (ltr)
141             expandRight = true;
142         else
143             expandLeft = true;
144     }
145     if (isAfterExpansion) {
146         if (ltr)
147             expandLeft = false;
148         else
149             expandRight = false;
150     }
151     ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
152     ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
153     if (forbidLeadingExpansion)
154         expandLeft = false;
155     if (forbidTrailingExpansion)
156         expandRight = false;
157     if (forceLeadingExpansion)
158         expandLeft = true;
159     if (forceTrailingExpansion)
160         expandRight = true;
161     return std::make_pair(expandLeft, expandRight);
162 }
163
164 template <typename TextIterator>
165 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
166 {
167     bool rtl = m_run.rtl();
168     bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled();
169
170     bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
171     bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
172     bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
173     bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
174     float widthSinceLastRounding = m_runWidthSoFar;
175     float leftoverJustificationWidth = 0;
176     m_runWidthSoFar = floorf(m_runWidthSoFar);
177     widthSinceLastRounding -= m_runWidthSoFar;
178
179     float lastRoundingWidth = m_finalRoundingWidth;
180     FloatRect bounds;
181
182     const Font& primaryFont = m_font->primaryFont();
183     const Font* lastFontData = &primaryFont;
184     int lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
185
186     UChar32 character = 0;
187     UChar32 previousCharacter = 0;
188     unsigned clusterLength = 0;
189     CharactersTreatedAsSpace charactersTreatedAsSpace;
190     String normalizedSpacesStringCache;
191     // We are iterating in string order, not glyph order. Compare this to ComplexTextController::adjustGlyphsAndAdvances()
192     while (textIterator.consume(character, clusterLength)) {
193         unsigned advanceLength = clusterLength;
194         int currentCharacter = textIterator.currentCharacter();
195         const GlyphData& glyphData = m_font->glyphDataForCharacter(character, rtl);
196         Glyph glyph = glyphData.glyph;
197         if (!glyph) {
198             textIterator.advance(advanceLength);
199             continue;
200         }
201         const Font* font = glyphData.font;
202         ASSERT(font);
203
204         // Now that we have a glyph and font data, get its width.
205         float width;
206         if (character == '\t' && m_run.allowTabs())
207             width = m_font->tabWidth(*font, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
208         else {
209             width = font->widthForGlyph(glyph);
210
211             // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
212             width *= m_run.horizontalGlyphStretch();
213         }
214
215         if (font != lastFontData && width) {
216             auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
217             if (transformsType != TransformsType::None) {
218                 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
219                 if (glyphBuffer)
220                     glyphBuffer->shrink(lastGlyphCount);
221             }
222
223             lastFontData = font;
224             if (m_fallbackFonts && font != &primaryFont) {
225                 // FIXME: This does a little extra work that could be avoided if
226                 // glyphDataForCharacter() returned whether it chose to use a small caps font.
227                 if (!m_font->isSmallCaps() || character == u_toupper(character))
228                     m_fallbackFonts->add(font);
229                 else {
230                     const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(u_toupper(character), rtl);
231                     if (uppercaseGlyphData.font != &primaryFont)
232                         m_fallbackFonts->add(uppercaseGlyphData.font);
233                 }
234             }
235         }
236
237         if (hasExtraSpacing) {
238             // Account for letter-spacing.
239             if (width) {
240                 width += m_font->letterSpacing();
241                 width += leftoverJustificationWidth;
242                 leftoverJustificationWidth = 0;
243             }
244
245             static bool expandAroundIdeographs = FontCascade::canExpandAroundIdeographsInComplexText();
246             bool treatAsSpace = FontCascade::treatAsSpace(character);
247             bool currentIsLastCharacter = currentCharacter + advanceLength == static_cast<size_t>(m_run.length());
248             bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
249             bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
250             bool forbidLeadingExpansion = false;
251             bool forbidTrailingExpansion = false;
252             if (runForcesLeadingExpansion)
253                 forceLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
254             if (runForcesTrailingExpansion)
255                 forceTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
256             if (runForbidsLeadingExpansion)
257                 forbidLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
258             if (runForbidsTrailingExpansion)
259                 forbidTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
260             bool ideograph = (expandAroundIdeographs && FontCascade::isCJKIdeographOrSymbol(character));
261             if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
262                 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
263                 if (m_expansion) {
264                     bool expandLeft, expandRight;
265                     std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), m_isAfterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
266                     if (expandLeft) {
267                         if (m_run.ltr()) {
268                             // Increase previous width
269                             m_expansion -= m_expansionPerOpportunity;
270                             m_runWidthSoFar += m_expansionPerOpportunity;
271                             if (glyphBuffer) {
272                                 if (glyphBuffer->isEmpty()) {
273                                     if (m_forTextEmphasis)
274                                         glyphBuffer->add(font->zeroWidthSpaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
275                                     else
276                                         glyphBuffer->add(font->spaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
277                                 } else
278                                     glyphBuffer->expandLastAdvance(m_expansionPerOpportunity);
279                             }
280                         } else {
281                             // Increase next width
282                             leftoverJustificationWidth += m_expansionPerOpportunity;
283                             m_isAfterExpansion = true;
284                         }
285                     }
286                     if (expandRight) {
287                         m_expansion -= m_expansionPerOpportunity;
288                         width += m_expansionPerOpportunity;
289                         if (m_run.ltr())
290                             m_isAfterExpansion = true;
291                     }
292                 } else
293                     m_isAfterExpansion = false;
294
295                 // Account for word spacing.
296                 // We apply additional space between "words" by adding width to the space character.
297                 if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (currentCharacter || character == noBreakSpace) && m_font->wordSpacing())
298                     width += m_font->wordSpacing();
299             } else
300                 m_isAfterExpansion = false;
301         }
302
303         auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
304         if (transformsType != TransformsType::None && glyphBuffer && FontCascade::treatAsSpace(character)) {
305             charactersTreatedAsSpace.append(std::make_pair(glyphBuffer->size(),
306                 OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1).width() : 0, width)));
307         }
308
309         if (m_accountForGlyphBounds) {
310             bounds = font->boundsForGlyph(glyph);
311             if (!currentCharacter)
312                 m_firstGlyphOverflow = std::max<float>(0, -bounds.x());
313         }
314
315         if (m_forTextEmphasis && !FontCascade::canReceiveTextEmphasis(character))
316             glyph = 0;
317
318         // Advance past the character we just dealt with.
319         textIterator.advance(advanceLength);
320
321         float oldWidth = width;
322
323         widthSinceLastRounding += width;
324
325         if (glyphBuffer)
326             glyphBuffer->add(glyph, font, (rtl ? oldWidth + lastRoundingWidth : width), currentCharacter);
327
328         lastRoundingWidth = width - oldWidth;
329
330         if (m_accountForGlyphBounds) {
331             m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY());
332             m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
333             m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
334         }
335         previousCharacter = character;
336     }
337
338     if (leftoverJustificationWidth) {
339         if (m_forTextEmphasis)
340             glyphBuffer->add(lastFontData->zeroWidthSpaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1);
341         else
342             glyphBuffer->add(lastFontData->spaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1);
343     }
344
345     auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
346     if (transformsType != TransformsType::None) {
347         m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
348         if (glyphBuffer)
349             glyphBuffer->shrink(lastGlyphCount);
350     }
351
352     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
353     m_currentCharacter = textIterator.currentCharacter();
354     m_runWidthSoFar += widthSinceLastRounding;
355     m_finalRoundingWidth = lastRoundingWidth;
356     return consumedCharacters;
357 }
358
359 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
360 {
361     int length = m_run.length();
362
363     if (offset > length)
364         offset = length;
365
366     if (m_currentCharacter >= static_cast<unsigned>(offset))
367         return 0;
368
369     if (m_run.is8Bit()) {
370         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
371         return advanceInternal(textIterator, glyphBuffer);
372     }
373
374     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
375     return advanceInternal(textIterator, glyphBuffer);
376 }
377
378 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
379 {
380     int oldSize = glyphBuffer.size();
381     advance(m_currentCharacter + 1, &glyphBuffer);
382     float w = 0;
383     for (int i = oldSize; i < glyphBuffer.size(); ++i)
384         w += glyphBuffer.advanceAt(i).width();
385     width = w;
386     return glyphBuffer.size() > oldSize;
387 }
388
389 }