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