Use NeverDestroyed instead of DEPRECATED_DEFINE_STATIC_LOCAL
[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 GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength, String& normalizedSpacesStringCache)
66 {
67     ASSERT(m_font);
68
69 #if ENABLE(SVG_FONTS)
70     if (TextRun::RenderingContext* renderingContext = m_run.renderingContext())
71         return renderingContext->glyphDataForCharacter(*m_font, *this, character, mirror, currentCharacter, advanceLength, normalizedSpacesStringCache);
72 #else
73     UNUSED_PARAM(currentCharacter);
74     UNUSED_PARAM(advanceLength);
75     UNUSED_PARAM(normalizedSpacesStringCache);
76 #endif
77
78     return m_font->glyphDataForCharacter(character, mirror);
79 }
80
81 struct OriginalAdvancesForCharacterTreatedAsSpace {
82 public:
83     OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore, float advanceAt)
84         : characterIsSpace(isSpace)
85         , advanceBeforeCharacter(advanceBefore)
86         , advanceAtCharacter(advanceAt)
87     {
88     }
89
90     bool characterIsSpace;
91     float advanceBeforeCharacter;
92     float advanceAtCharacter;
93 };
94
95 static inline bool isSoftBankEmoji(UChar32 codepoint)
96 {
97     return codepoint >= 0xE001 && codepoint <= 0xE537;
98 }
99
100 inline auto WidthIterator::shouldApplyFontTransforms(const GlyphBuffer* glyphBuffer, int lastGlyphCount, UChar32 previousCharacter) const -> TransformsType
101 {
102     if (glyphBuffer && glyphBuffer->size() == (lastGlyphCount + 1) && isSoftBankEmoji(previousCharacter))
103         return TransformsType::Forced;
104     if (m_run.length() <= 1 || !(m_enableKerning || m_requiresShaping))
105         return TransformsType::None;
106     return TransformsType::NotForced;
107 }
108
109 inline float WidthIterator::applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int& lastGlyphCount, const Font* font, UChar32 previousCharacter, bool force, CharactersTreatedAsSpace& charactersTreatedAsSpace)
110 {
111     ASSERT_UNUSED(previousCharacter, shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter) != WidthIterator::TransformsType::None);
112
113     if (!glyphBuffer)
114         return 0;
115
116     int glyphBufferSize = glyphBuffer->size();
117     if (!force && glyphBufferSize <= lastGlyphCount + 1) {
118         lastGlyphCount = glyphBufferSize;
119         return 0;
120     }
121
122     GlyphBufferAdvance* advances = glyphBuffer->advances(0);
123     float widthDifference = 0;
124     for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
125         widthDifference -= advances[i].width();
126
127     if (!ltr)
128         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
129
130 #if ENABLE(SVG_FONTS)
131     // We need to handle transforms on SVG fonts internally, since they are rendered internally.
132     if (font->isSVGFont()) {
133         // SVG font ligatures are handled during glyph selection, only kerning remaining.
134         if (run().renderingContext() && m_enableKerning) {
135             // FIXME: We could pass the necessary context down to this level so we can lazily create rendering contexts at this point.
136             // However, a larger refactoring of SVG fonts might necessary to sidestep this problem completely.
137             run().renderingContext()->applySVGKerning(font, *this, glyphBuffer, lastGlyphCount);
138         }
139     } else
140 #endif
141         font->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, m_enableKerning, m_requiresShaping);
142
143     if (!ltr)
144         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
145
146     for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) {
147         int spaceOffset = charactersTreatedAsSpace[i].first;
148         const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = charactersTreatedAsSpace[i].second;
149         if (spaceOffset && !originalAdvances.characterIsSpace)
150             glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.advanceBeforeCharacter);
151         glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtCharacter);
152     }
153     charactersTreatedAsSpace.clear();
154
155     for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
156         widthDifference += advances[i].width();
157
158     lastGlyphCount = glyphBufferSize;
159     return widthDifference;
160 }
161
162 static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
163 {
164     bool expandLeft = ideograph;
165     bool expandRight = ideograph;
166     if (treatAsSpace) {
167         if (ltr)
168             expandRight = true;
169         else
170             expandLeft = true;
171     }
172     if (isAfterExpansion) {
173         if (ltr)
174             expandLeft = false;
175         else
176             expandRight = false;
177     }
178     ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
179     ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
180     if (forbidLeadingExpansion)
181         expandLeft = false;
182     if (forbidTrailingExpansion)
183         expandRight = false;
184     if (forceLeadingExpansion)
185         expandLeft = true;
186     if (forceTrailingExpansion)
187         expandRight = true;
188     return std::make_pair(expandLeft, expandRight);
189 }
190
191 template <typename TextIterator>
192 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
193 {
194     bool rtl = m_run.rtl();
195     bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled();
196
197     bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
198     bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
199     bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
200     bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
201     float widthSinceLastRounding = m_runWidthSoFar;
202     float leftoverJustificationWidth = 0;
203     m_runWidthSoFar = floorf(m_runWidthSoFar);
204     widthSinceLastRounding -= m_runWidthSoFar;
205
206     float lastRoundingWidth = m_finalRoundingWidth;
207     FloatRect bounds;
208
209     const Font& primaryFont = m_font->primaryFont();
210     const Font* lastFontData = &primaryFont;
211     int lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
212
213     UChar32 character = 0;
214     UChar32 previousCharacter = 0;
215     unsigned clusterLength = 0;
216     CharactersTreatedAsSpace charactersTreatedAsSpace;
217     String normalizedSpacesStringCache;
218     // We are iterating in string order, not glyph order. Compare this to ComplexTextController::adjustGlyphsAndAdvances()
219     while (textIterator.consume(character, clusterLength)) {
220         unsigned advanceLength = clusterLength;
221         int currentCharacter = textIterator.currentCharacter();
222         const GlyphData& glyphData = glyphDataForCharacter(character, rtl, currentCharacter, advanceLength, normalizedSpacesStringCache);
223         Glyph glyph = glyphData.glyph;
224         if (!glyph) {
225             textIterator.advance(advanceLength);
226             continue;
227         }
228         const Font* font = glyphData.font;
229         ASSERT(font);
230
231         // Now that we have a glyph and font data, get its width.
232         float width;
233         if (character == '\t' && m_run.allowTabs())
234             width = m_font->tabWidth(*font, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
235         else {
236             width = font->widthForGlyph(glyph);
237
238             // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
239             width *= m_run.horizontalGlyphStretch();
240
241             // We special case spaces in two ways when applying word rounding.
242             // First, we round spaces to an adjusted width in all fonts.
243             // Second, in fixed-pitch fonts we ensure that all characters that
244             // match the width of the space character have the same width as the space character.
245             if (m_run.applyWordRounding() && width == font->spaceWidth() && (font->pitch() == FixedPitch || glyph == font->spaceGlyph()))
246                 width = font->adjustedSpaceWidth();
247         }
248
249         if (font != lastFontData && width) {
250             auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
251             if (transformsType != TransformsType::None) {
252                 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
253                 if (glyphBuffer)
254                     glyphBuffer->shrink(lastGlyphCount);
255             }
256
257             lastFontData = font;
258             if (m_fallbackFonts && font != &primaryFont) {
259                 // FIXME: This does a little extra work that could be avoided if
260                 // glyphDataForCharacter() returned whether it chose to use a small caps font.
261                 if (!m_font->isSmallCaps() || character == u_toupper(character))
262                     m_fallbackFonts->add(font);
263                 else {
264                     const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(u_toupper(character), rtl);
265                     if (uppercaseGlyphData.font != &primaryFont)
266                         m_fallbackFonts->add(uppercaseGlyphData.font);
267                 }
268             }
269         }
270
271         if (hasExtraSpacing) {
272             // Account for letter-spacing.
273             if (width) {
274                 width += m_font->letterSpacing();
275                 width += leftoverJustificationWidth;
276                 leftoverJustificationWidth = 0;
277             }
278
279             static bool expandAroundIdeographs = FontCascade::canExpandAroundIdeographsInComplexText();
280             bool treatAsSpace = FontCascade::treatAsSpace(character);
281             bool currentIsLastCharacter = currentCharacter + advanceLength == static_cast<size_t>(m_run.length());
282             bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
283             bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
284             bool forbidLeadingExpansion = false;
285             bool forbidTrailingExpansion = false;
286             if (runForcesLeadingExpansion)
287                 forceLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
288             if (runForcesTrailingExpansion)
289                 forceTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
290             if (runForbidsLeadingExpansion)
291                 forbidLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
292             if (runForbidsTrailingExpansion)
293                 forbidTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
294             bool ideograph = (expandAroundIdeographs && FontCascade::isCJKIdeographOrSymbol(character));
295             if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
296                 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
297                 if (m_expansion) {
298                     bool expandLeft, expandRight;
299                     std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), m_isAfterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
300                     float previousExpansion = m_expansion;
301                     if (expandLeft) {
302                         if (m_run.ltr()) {
303                             // Increase previous width
304                             m_expansion -= m_expansionPerOpportunity;
305                             float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
306                             m_runWidthSoFar += expansionAtThisOpportunity;
307                             if (glyphBuffer) {
308                                 if (glyphBuffer->isEmpty()) {
309                                     if (m_forTextEmphasis)
310                                         glyphBuffer->add(font->zeroWidthSpaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
311                                     else
312                                         glyphBuffer->add(font->spaceGlyph(), font, expansionAtThisOpportunity, currentCharacter);
313                                 } else
314                                     glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
315                             }
316                         } else {
317                             // Increase next width
318                             float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion - m_expansionPerOpportunity);
319                             leftoverJustificationWidth += expansionAtThisOpportunity;
320                             m_isAfterExpansion = true;
321                         }
322                         previousExpansion = m_expansion;
323                     }
324                     if (expandRight) {
325                         m_expansion -= m_expansionPerOpportunity;
326                         float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
327                         width += expansionAtThisOpportunity;
328                         if (m_run.ltr())
329                             m_isAfterExpansion = true;
330                     }
331                 } else
332                     m_isAfterExpansion = false;
333
334                 // Account for word spacing.
335                 // We apply additional space between "words" by adding width to the space character.
336                 if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (currentCharacter || character == noBreakSpace) && m_font->wordSpacing())
337                     width += m_font->wordSpacing();
338             } else
339                 m_isAfterExpansion = false;
340         }
341
342         auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
343         if (transformsType != TransformsType::None && glyphBuffer && FontCascade::treatAsSpace(character)) {
344             charactersTreatedAsSpace.append(std::make_pair(glyphBuffer->size(),
345                 OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1).width() : 0, width)));
346         }
347
348         if (m_accountForGlyphBounds) {
349             bounds = font->boundsForGlyph(glyph);
350             if (!currentCharacter)
351                 m_firstGlyphOverflow = std::max<float>(0, -bounds.x());
352         }
353
354         if (m_forTextEmphasis && !FontCascade::canReceiveTextEmphasis(character))
355             glyph = 0;
356
357         // Advance past the character we just dealt with.
358         textIterator.advance(advanceLength);
359
360         float oldWidth = width;
361
362         // Force characters that are used to determine word boundaries for the rounding hack
363         // to be integer width, so following words will start on an integer boundary.
364         if (m_run.applyWordRounding() && FontCascade::isRoundingHackCharacter(character)) {
365             width = ceilf(width);
366
367             // Since widthSinceLastRounding can lose precision if we include measurements for
368             // preceding whitespace, we bypass it here.
369             m_runWidthSoFar += width;
370
371             // Since this is a rounding hack character, we should have reset this sum on the previous
372             // iteration.
373             ASSERT(!widthSinceLastRounding);
374         } else {
375             // Check to see if the next character is a "rounding hack character", if so, adjust
376             // width so that the total run width will be on an integer boundary.
377             if ((m_run.applyWordRounding() && static_cast<unsigned>(textIterator.currentCharacter()) < m_run.length() && FontCascade::isRoundingHackCharacter(*(textIterator.characters())))
378                 || (m_run.applyRunRounding() && static_cast<unsigned>(textIterator.currentCharacter()) >= m_run.length())) {
379                 float totalWidth = widthSinceLastRounding + width;
380                 widthSinceLastRounding = ceilf(totalWidth);
381                 width += widthSinceLastRounding - totalWidth;
382                 m_runWidthSoFar += widthSinceLastRounding;
383                 widthSinceLastRounding = 0;
384             } else
385                 widthSinceLastRounding += width;
386         }
387
388         if (glyphBuffer)
389             glyphBuffer->add(glyph, font, (rtl ? oldWidth + lastRoundingWidth : width), currentCharacter);
390
391         lastRoundingWidth = width - oldWidth;
392
393         if (m_accountForGlyphBounds) {
394             m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY());
395             m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
396             m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
397         }
398         previousCharacter = character;
399     }
400
401     if (leftoverJustificationWidth) {
402         if (m_forTextEmphasis)
403             glyphBuffer->add(lastFontData->zeroWidthSpaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1);
404         else
405             glyphBuffer->add(lastFontData->spaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length() - 1);
406     }
407
408     auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
409     if (transformsType != TransformsType::None) {
410         m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
411         if (glyphBuffer)
412             glyphBuffer->shrink(lastGlyphCount);
413     }
414
415     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
416     m_currentCharacter = textIterator.currentCharacter();
417     m_runWidthSoFar += widthSinceLastRounding;
418     m_finalRoundingWidth = lastRoundingWidth;
419     return consumedCharacters;
420 }
421
422 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
423 {
424     int length = m_run.length();
425
426     if (offset > length)
427         offset = length;
428
429     if (m_currentCharacter >= static_cast<unsigned>(offset))
430         return 0;
431
432     if (m_run.is8Bit()) {
433         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
434         return advanceInternal(textIterator, glyphBuffer);
435     }
436
437     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
438     return advanceInternal(textIterator, glyphBuffer);
439 }
440
441 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
442 {
443     int oldSize = glyphBuffer.size();
444     advance(m_currentCharacter + 1, &glyphBuffer);
445     float w = 0;
446     for (int i = oldSize; i < glyphBuffer.size(); ++i)
447         w += glyphBuffer.advanceAt(i).width();
448     width = w;
449     return glyphBuffer.size() > oldSize;
450 }
451
452 }