[Mac] Some ligatures are applied across different fronts
[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 "GlyphBuffer.h"
27 #include "Latin1TextIterator.h"
28 #include "SimpleFontData.h"
29 #include "SurrogatePairAwareTextIterator.h"
30 #include <wtf/MathExtras.h>
31
32 using namespace WTF;
33 using namespace Unicode;
34 using namespace std;
35
36 namespace WebCore {
37
38 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* 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.allowsLeadingExpansion())
44     , m_finalRoundingWidth(0)
45     , m_typesettingFeatures(font->typesettingFeatures())
46     , m_fallbackFonts(fallbackFonts)
47     , m_accountForGlyphBounds(accountForGlyphBounds)
48     , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
49     , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
50     , m_firstGlyphOverflow(0)
51     , m_lastGlyphOverflow(0)
52     , m_forTextEmphasis(forTextEmphasis)
53 {
54     // If the padding is non-zero, count the number of spaces in the run
55     // and divide that by the padding for per space addition.
56     m_expansion = m_run.expansion();
57     if (!m_expansion)
58         m_expansionPerOpportunity = 0;
59     else {
60         bool isAfterExpansion = m_isAfterExpansion;
61         unsigned expansionOpportunityCount = m_run.is8Bit() ? Font::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion) : Font::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion);
62         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
63             expansionOpportunityCount--;
64
65         if (!expansionOpportunityCount)
66             m_expansionPerOpportunity = 0;
67         else
68             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
69     }
70 }
71
72 GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
73 {
74     ASSERT(m_font);
75
76 #if ENABLE(SVG_FONTS)
77     if (TextRun::RenderingContext* renderingContext = m_run.renderingContext())
78         return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, character, mirror, currentCharacter, advanceLength);
79 #else
80     UNUSED_PARAM(currentCharacter);
81     UNUSED_PARAM(advanceLength);
82 #endif
83
84     return m_font->glyphDataForCharacter(character, mirror);
85 }
86
87 struct OriginalAdvancesForCharacterTreatedAsSpace {
88 public:
89     OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore, float advanceAt)
90         : characterIsSpace(isSpace)
91         , advanceBeforeCharacter(advanceBefore)
92         , advanceAtCharacter(advanceAt)
93     {
94     }
95
96     bool characterIsSpace;
97     float advanceBeforeCharacter;
98     float advanceAtCharacter;
99 };
100
101 typedef Vector<pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace;
102
103 static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int& lastGlyphCount, const SimpleFontData* fontData, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace)
104 {
105     ASSERT(typesettingFeatures & (Kerning | Ligatures));
106
107     if (!glyphBuffer)
108         return 0;
109
110     int glyphBufferSize = glyphBuffer->size();
111     if (glyphBuffer->size() <= lastGlyphCount + 1)
112         return 0;
113
114     GlyphBufferAdvance* advances = glyphBuffer->advances(0);
115     float widthDifference = 0;
116     for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
117         widthDifference -= advances[i].width();
118
119     if (!ltr)
120         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
121
122     fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures);
123
124     if (!ltr)
125         glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
126
127     for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) {
128         int spaceOffset = charactersTreatedAsSpace[i].first;
129         const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = charactersTreatedAsSpace[i].second;
130         if (spaceOffset && !originalAdvances.characterIsSpace)
131             glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.advanceBeforeCharacter);
132         glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtCharacter);
133     }
134     charactersTreatedAsSpace.clear();
135
136     for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
137         widthDifference += advances[i].width();
138
139     lastGlyphCount = glyphBufferSize;
140     return widthDifference;
141 }
142
143 template <typename TextIterator>
144 inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
145 {
146     bool rtl = m_run.rtl();
147     bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled();
148
149     float widthSinceLastRounding = m_runWidthSoFar;
150     m_runWidthSoFar = floorf(m_runWidthSoFar);
151     widthSinceLastRounding -= m_runWidthSoFar;
152
153     float lastRoundingWidth = m_finalRoundingWidth;
154     FloatRect bounds;
155
156     const SimpleFontData* primaryFont = m_font->primaryFont();
157     const SimpleFontData* lastFontData = primaryFont;
158     int lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
159
160     UChar32 character = 0;
161     unsigned clusterLength = 0;
162     CharactersTreatedAsSpace charactersTreatedAsSpace;
163     while (textIterator.consume(character, clusterLength)) {
164         unsigned advanceLength = clusterLength;
165         const GlyphData& glyphData = glyphDataForCharacter(character, rtl, textIterator.currentCharacter(), advanceLength);
166         Glyph glyph = glyphData.glyph;
167         const SimpleFontData* fontData = glyphData.fontData;
168
169         ASSERT(fontData);
170
171         // Now that we have a glyph and font data, get its width.
172         float width;
173         if (character == '\t' && m_run.allowTabs())
174             width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
175         else {
176             width = fontData->widthForGlyph(glyph);
177
178 #if ENABLE(SVG)
179             // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
180             width *= m_run.horizontalGlyphStretch();
181 #endif
182
183             // We special case spaces in two ways when applying word rounding.
184             // First, we round spaces to an adjusted width in all fonts.
185             // Second, in fixed-pitch fonts we ensure that all characters that
186             // match the width of the space character have the same width as the space character.
187             if (m_run.applyWordRounding() && width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
188                 width = fontData->adjustedSpaceWidth();
189         }
190
191         if (fontData != lastFontData && width) {
192             if (shouldApplyFontTransforms()) {
193                 m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);
194                 lastGlyphCount = glyphBuffer->size(); // applyFontTransforms doesn't update when there had been only one glyph.
195             }
196
197             lastFontData = fontData;
198             if (m_fallbackFonts && fontData != primaryFont) {
199                 // FIXME: This does a little extra work that could be avoided if
200                 // glyphDataForCharacter() returned whether it chose to use a small caps font.
201                 if (!m_font->isSmallCaps() || character == toUpper(character))
202                     m_fallbackFonts->add(fontData);
203                 else {
204                     const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character), rtl);
205                     if (uppercaseGlyphData.fontData != primaryFont)
206                         m_fallbackFonts->add(uppercaseGlyphData.fontData);
207                 }
208             }
209         }
210
211         if (hasExtraSpacing) {
212             // Account for letter-spacing.
213             if (width && m_font->letterSpacing())
214                 width += m_font->letterSpacing();
215
216             static bool expandAroundIdeographs = Font::canExpandAroundIdeographsInComplexText();
217             bool treatAsSpace = Font::treatAsSpace(character);
218             if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(character))) {
219                 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
220                 if (m_expansion) {
221                     float previousExpansion = m_expansion;
222                     if (!treatAsSpace && !m_isAfterExpansion) {
223                         // Take the expansion opportunity before this ideograph.
224                         m_expansion -= m_expansionPerOpportunity;
225                         float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
226                         m_runWidthSoFar += expansionAtThisOpportunity;
227                         if (glyphBuffer) {
228                             if (glyphBuffer->isEmpty()) {
229                                 if (m_forTextEmphasis)
230                                     glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity);
231                                 else
232                                     glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
233                             } else
234                                 glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
235                         }
236                         previousExpansion = m_expansion;
237                     }
238                     if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textIterator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length()))
239                         || (m_run.rtl() && textIterator.currentCharacter())) {
240                         m_expansion -= m_expansionPerOpportunity;
241                         width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
242                         m_isAfterExpansion = true;
243                     }
244                 } else
245                     m_isAfterExpansion = false;
246
247                 // Account for word spacing.
248                 // We apply additional space between "words" by adding width to the space character.
249                 if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (textIterator.currentCharacter() || character == noBreakSpace) && m_font->wordSpacing())
250                     width += m_font->wordSpacing();
251             } else
252                 m_isAfterExpansion = false;
253         }
254
255         if (shouldApplyFontTransforms() && glyphBuffer && Font::treatAsSpace(character))
256             charactersTreatedAsSpace.append(make_pair(glyphBuffer->size(),
257                 OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1) : 0, width)));
258
259         if (m_accountForGlyphBounds) {
260             bounds = fontData->boundsForGlyph(glyph);
261             if (!textIterator.currentCharacter())
262                 m_firstGlyphOverflow = max<float>(0, -bounds.x());
263         }
264
265         if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character))
266             glyph = 0;
267
268         // Advance past the character we just dealt with.
269         textIterator.advance(advanceLength);
270
271         float oldWidth = width;
272
273         // Force characters that are used to determine word boundaries for the rounding hack
274         // to be integer width, so following words will start on an integer boundary.
275         if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character)) {
276             width = ceilf(width);
277
278             // Since widthSinceLastRounding can lose precision if we include measurements for
279             // preceding whitespace, we bypass it here.
280             m_runWidthSoFar += width;
281
282             // Since this is a rounding hack character, we should have reset this sum on the previous
283             // iteration.
284             ASSERT(!widthSinceLastRounding);
285         } else {
286             // Check to see if the next character is a "rounding hack character", if so, adjust
287             // width so that the total run width will be on an integer boundary.
288             if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters())))
289                 || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) {
290                 float totalWidth = widthSinceLastRounding + width;
291                 widthSinceLastRounding = ceilf(totalWidth);
292                 width += widthSinceLastRounding - totalWidth;
293                 m_runWidthSoFar += widthSinceLastRounding;
294                 widthSinceLastRounding = 0;
295             } else
296                 widthSinceLastRounding += width;
297         }
298
299         if (glyphBuffer)
300             glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
301
302         lastRoundingWidth = width - oldWidth;
303
304         if (m_accountForGlyphBounds) {
305             m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY());
306             m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y());
307             m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width);
308         }
309     }
310
311     if (shouldApplyFontTransforms())
312         m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);
313
314     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
315     m_currentCharacter = textIterator.currentCharacter();
316     m_runWidthSoFar += widthSinceLastRounding;
317     m_finalRoundingWidth = lastRoundingWidth;
318     return consumedCharacters;
319 }
320
321 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
322 {
323     int length = m_run.length();
324
325     if (offset > length)
326         offset = length;
327
328     if (m_currentCharacter >= static_cast<unsigned>(offset))
329         return 0;
330
331     if (m_run.is8Bit()) {
332         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
333         return advanceInternal(textIterator, glyphBuffer);
334     }
335
336     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
337     return advanceInternal(textIterator, glyphBuffer);
338 }
339
340 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
341 {
342     int oldSize = glyphBuffer.size();
343     advance(m_currentCharacter + 1, &glyphBuffer);
344     float w = 0;
345     for (int i = oldSize; i < glyphBuffer.size(); ++i)
346         w += glyphBuffer.advanceAt(i);
347     width = w;
348     return glyphBuffer.size() > oldSize;
349 }
350
351 }