Fix by Graham Dennis, reviewed by Darin.
[WebKit-https.git] / WebCore / platform / Font.cpp
1 /**
2  * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2000 Dirk Mueller (mueller@kde.org)
7  * Copyright (C) 2003, 2006 Apple Computer, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  *
24  */
25
26 #include "config.h"
27 #include "Font.h"
28 #include "FontData.h"
29
30 #include "FontFallbackList.h"
31 #include "GraphicsContext.h"
32 #include "Settings.h"
33
34 #include "GlyphBuffer.h"
35
36 #include <unicode/umachine.h>
37 #include <unicode/unorm.h>
38
39 #include <wtf/MathExtras.h>
40
41 namespace WebCore {
42
43 // According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values
44 #define HIRAGANA_KATAKANA_VOICING_MARKS 8
45
46 const uint8_t Font::gRoundingHackCharacterTable[256] = {
47     0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48     1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/,
49     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52     1 /*no-break space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
55 };
56
57 struct WidthIterator {
58     WidthIterator(const Font* font, const TextRun& run, const TextStyle& style, const FontData* substituteFontData = 0);
59
60     void advance(int to, GlyphBuffer* glyphBuffer = 0);
61     bool advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer = 0);
62     
63     const Font* m_font;
64
65     const TextRun& m_run;
66     int m_end;
67
68     const TextStyle& m_style;
69     
70     const FontData* m_substituteFontData;
71
72     unsigned m_currentCharacter;
73     float m_runWidthSoFar;
74     float m_widthToStart;
75     float m_padding;
76     float m_padPerSpace;
77     float m_finalRoundingWidth;
78     
79 private:
80     UChar32 normalizeVoicingMarks(int currentCharacter);
81 };
82
83 WidthIterator::WidthIterator(const Font* font, const TextRun& run, const TextStyle& style, const FontData* substituteFontData)
84 :m_font(font), m_run(run), m_end(style.rtl() ? run.length() : run.to()), m_style(style), m_substituteFontData(substituteFontData),
85  m_currentCharacter(run.from()), m_runWidthSoFar(0), m_finalRoundingWidth(0)
86 {
87     // If the padding is non-zero, count the number of spaces in the run
88     // and divide that by the padding for per space addition.
89     m_padding = m_style.padding();
90     if (!m_padding)
91         m_padPerSpace = 0;
92     else {
93         float numSpaces = 0;
94         for (int i = run.from(); i < m_end; i++)
95             if (Font::treatAsSpace(m_run[i]))
96                 numSpaces++;
97
98         if (numSpaces == 0)
99             m_padPerSpace = 0;
100         else
101             m_padPerSpace = ceilf(m_style.padding() / numSpaces);
102     }
103     
104     // Calculate width up to starting position of the run.  This is
105     // necessary to ensure that our rounding hacks are always consistently
106     // applied.
107     if (run.from() == 0)
108         m_widthToStart = 0;
109     else {
110         TextRun completeRun(run);
111         completeRun.makeComplete();
112         WidthIterator startPositionIterator(font, completeRun, style, m_substituteFontData);
113         startPositionIterator.advance(run.from());
114         m_widthToStart = startPositionIterator.m_runWidthSoFar;
115     }
116 }
117
118 void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
119 {
120     if (offset > m_end)
121         offset = m_end;
122
123     int currentCharacter = m_currentCharacter;
124     const UChar* cp = m_run.data(currentCharacter);
125
126     bool rtl = m_style.rtl();
127     bool needCharTransform = rtl || m_font->isSmallCaps();
128     bool hasExtraSpacing = m_font->letterSpacing() || m_font->wordSpacing() || m_padding;
129
130     float runWidthSoFar = m_runWidthSoFar;
131     float lastRoundingWidth = m_finalRoundingWidth;
132     
133     const FontData* primaryFont = m_font->primaryFont();
134
135     while (currentCharacter < offset) {
136         UChar32 c = *cp;
137         unsigned clusterLength = 1;
138         if (c >= 0x3041) {
139             if (c <= 0x30FE) {
140                 // Deal with Hiragana and Katakana voiced and semi-voiced syllables.
141                 // Normalize into composed form, and then look for glyph with base + combined mark.
142                 // Check above for character range to minimize performance impact.
143                 UChar32 normalized = normalizeVoicingMarks(currentCharacter);
144                 if (normalized) {
145                     c = normalized;
146                     clusterLength = 2;
147                 }
148             } else if (U16_IS_SURROGATE(c)) {
149                 if (!U16_IS_SURROGATE_LEAD(c))
150                     break;
151
152                 // Do we have a surrogate pair?  If so, determine the full Unicode (32 bit)
153                 // code point before glyph lookup.
154                 // Make sure we have another character and it's a low surrogate.
155                 if (currentCharacter + 1 >= m_run.length())
156                     break;
157                 UChar low = cp[1];
158                 if (!U16_IS_TRAIL(low))
159                     break;
160                 c = U16_GET_SUPPLEMENTARY(c, low);
161                 clusterLength = 2;
162             }
163         }
164
165         const FontData* fontData = m_substituteFontData ? m_substituteFontData : primaryFont;
166
167         if (needCharTransform) {
168             if (rtl)
169                 c = u_charMirror(c);
170
171             // If small-caps, convert lowercase to upper.
172             if (m_font->isSmallCaps() && !u_isUUppercase(c)) {
173                 UChar32 upperC = u_toupper(c);
174                 if (upperC != c) {
175                     c = upperC;
176                     fontData = fontData->smallCapsFontData(m_font->fontDescription());
177                 }
178             }
179         }
180
181         // FIXME: Should go through fallback list eventually when we rework the glyph map.
182         const GlyphData& glyphData = fontData->glyphDataForCharacter(c);
183         Glyph glyph = glyphData.glyph;
184         fontData = glyphData.fontData;
185
186         // Try to find a substitute font if this font didn't have a glyph for a character in the
187         // string. If one isn't found we end up drawing and measuring the 0 glyph, usually a box.
188         if (glyph == 0 && !m_substituteFontData && m_style.attemptFontSubstitution()) {
189             const FontData* substituteFontData = m_font->fontDataForCharacters(cp, clusterLength);
190             if (substituteFontData) {
191                 GlyphBuffer localGlyphBuffer;
192                 m_font->floatWidthForSimpleText(TextRun((UChar*)cp, clusterLength), TextStyle(0, 0, 0, m_style.rtl(), m_style.directionalOverride(), 
193                                                                                               false, m_style.applyWordRounding()), 
194                                                                                               substituteFontData, 0, &localGlyphBuffer);
195                 if (localGlyphBuffer.size() == 1) {
196                     assert(substituteFontData == localGlyphBuffer.fontDataAt(0));
197                     glyph = localGlyphBuffer.glyphAt(0);
198                     fontData->setGlyphDataForCharacter(c, glyph, substituteFontData);
199                     fontData = substituteFontData;
200                 }
201             }
202         }
203
204         // Now that we have a glyph and font data, get its width.
205         float width;
206         if (c == '\t' && m_style.tabWidth())
207             width = m_style.tabWidth() - fmodf(m_style.xPos() + runWidthSoFar, m_style.tabWidth());
208         else {
209             width = fontData->widthForGlyph(glyph);
210             // We special case spaces in two ways when applying word rounding.
211             // First, we round spaces to an adjusted width in all fonts.
212             // Second, in fixed-pitch fonts we ensure that all characters that
213             // match the width of the space character have the same width as the space character.
214             if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_style.applyWordRounding())
215                 width = fontData->m_adjustedSpaceWidth;
216         }
217
218         if (hasExtraSpacing) {
219             // Account for letter-spacing.
220             if (width && m_font->letterSpacing())
221                 width += m_font->letterSpacing();
222
223             if (Font::treatAsSpace(c)) {
224                 // Account for padding. WebCore uses space padding to justify text.
225                 // We distribute the specified padding over the available spaces in the run.
226                 if (m_padding) {
227                     // Use left over padding if not evenly divisible by number of spaces.
228                     if (m_padding < m_padPerSpace) {
229                         width += m_padding;
230                         m_padding = 0;
231                     } else {
232                         width += m_padPerSpace;
233                         m_padding -= m_padPerSpace;
234                     }
235                 }
236
237                 // Account for word spacing.
238                 // We apply additional space between "words" by adding width to the space character.
239                 if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing())
240                     width += m_font->wordSpacing();
241             }
242         }
243
244         // Advance past the character we just dealt with.
245         cp += clusterLength;
246         currentCharacter += clusterLength;
247
248         // Account for float/integer impedance mismatch between CG and KHTML. "Words" (characters 
249         // followed by a character defined by isRoundingHackCharacter()) are always an integer width.
250         // We adjust the width of the last character of a "word" to ensure an integer width.
251         // If we move KHTML to floats we can remove this (and related) hacks.
252
253         float oldWidth = width;
254
255         // Force characters that are used to determine word boundaries for the rounding hack
256         // to be integer width, so following words will start on an integer boundary.
257         if (m_style.applyWordRounding() && Font::isRoundingHackCharacter(c))
258             width = ceilf(width);
259
260         // Check to see if the next character is a "rounding hack character", if so, adjust
261         // width so that the total run width will be on an integer boundary.
262         if ((m_style.applyWordRounding() && currentCharacter < m_run.length() && Font::isRoundingHackCharacter(*cp))
263                 || (m_style.applyRunRounding() && currentCharacter >= m_end)) {
264             float totalWidth = m_widthToStart + runWidthSoFar + width;
265             width += ceilf(totalWidth) - totalWidth;
266         }
267
268         runWidthSoFar += width;
269
270         if (glyphBuffer)
271             glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
272
273         lastRoundingWidth = width - oldWidth;
274     }
275
276     m_currentCharacter = currentCharacter;
277     m_runWidthSoFar = runWidthSoFar;
278     m_finalRoundingWidth = lastRoundingWidth;
279 }
280
281 bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer)
282 {
283     glyphBuffer->clear();
284     advance(m_currentCharacter + 1, glyphBuffer);
285     float w = 0;
286     for (int i = 0; i < glyphBuffer->size(); ++i)
287         w += glyphBuffer->advanceAt(i);
288     width = w;
289     return !glyphBuffer->isEmpty();
290 }
291
292 UChar32 WidthIterator::normalizeVoicingMarks(int currentCharacter)
293 {
294     if (currentCharacter + 1 < m_end) {
295         if (u_getCombiningClass(m_run[currentCharacter + 1]) == HIRAGANA_KATAKANA_VOICING_MARKS) {
296             // Normalize into composed form using 3.2 rules.
297             UChar normalizedCharacters[2] = { 0, 0 };
298             UErrorCode uStatus = (UErrorCode)0;  
299             int32_t resultLength = unorm_normalize(m_run.data(currentCharacter), 2,
300                 UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uStatus);
301             if (resultLength == 1 && uStatus == 0)
302                 return normalizedCharacters[0];
303         }
304     }
305     return 0;
306 }
307
308 // ============================================================================================
309 // Font Implementation (Cross-Platform Portion)
310 // ============================================================================================
311
312 Font::Font() :m_fontList(0), m_letterSpacing(0), m_wordSpacing(0) {}
313 Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) 
314 : m_fontDescription(fd),
315   m_fontList(0),
316   m_letterSpacing(letterSpacing),
317   m_wordSpacing(wordSpacing)
318 {}
319
320 Font::Font(const Font& other)
321 {
322     m_fontDescription = other.m_fontDescription;
323     m_fontList = other.m_fontList;
324     m_letterSpacing = other.m_letterSpacing;
325     m_wordSpacing = other.m_wordSpacing;
326 }
327
328 Font& Font::operator=(const Font& other)
329 {
330     if (&other != this) {
331         m_fontDescription = other.m_fontDescription;
332         m_fontList = other.m_fontList;
333         m_letterSpacing = other.m_letterSpacing;
334         m_wordSpacing = other.m_wordSpacing;
335     }
336     return *this;
337 }
338
339 Font::~Font()
340 {
341 }
342
343 const FontData* Font::primaryFont() const
344 {
345     assert(m_fontList);
346     return m_fontList->primaryFont(this);
347 }
348
349 const FontData* Font::fontDataAt(unsigned index) const
350 {
351     assert(m_fontList);
352     return m_fontList->fontDataAt(this, index);
353 }
354
355 const FontData* Font::fontDataForCharacters(const UChar* characters, int length) const
356 {
357     assert(m_fontList);
358     return m_fontList->fontDataForCharacters(this, characters, length);
359 }
360
361 void Font::update() const
362 {
363     // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr, but it ends up 
364     // being reasonably safe (because inherited fonts in the render tree pick up the new
365     // style anyway.  Other copies are transient, e.g., the state in the GraphicsContext, and
366     // won't stick around long enough to get you in trouble).  Still, this is pretty disgusting,
367     // and could eventually be rectified by using RefPtrs for Fonts themselves.
368     if (!m_fontList)
369         m_fontList = new FontFallbackList();
370     m_fontList->invalidate();
371 }
372
373 int Font::width(const TextRun& run, const TextStyle& style) const
374 {
375     return lroundf(floatWidth(run, style));
376 }
377
378 int Font::ascent() const
379 {
380     return primaryFont()->ascent();
381 }
382
383 int Font::descent() const
384 {
385     return primaryFont()->descent();
386 }
387
388 int Font::lineSpacing() const
389 {
390     return primaryFont()->lineSpacing();
391 }
392
393 float Font::xHeight() const
394 {
395     return primaryFont()->xHeight();
396 }
397
398 bool Font::isFixedPitch() const
399 {
400     assert(m_fontList);
401     return m_fontList->isFixedPitch(this);
402 }
403
404 // FIXME: These methods will eventually be cross-platform, but to keep Windows compiling we'll make this Apple-only for now.
405 bool Font::gAlwaysUseComplexPath = false;
406 void Font::setAlwaysUseComplexPath(bool alwaysUse)
407 {
408     gAlwaysUseComplexPath = alwaysUse;
409 }
410
411 bool Font::canUseGlyphCache(const TextRun& run) const
412 {
413     if (gAlwaysUseComplexPath)
414         return false;
415     
416     // Start from 0 since drawing and highlighting also measure the characters before run->from
417     for (int i = 0; i < run.to(); i++) {
418         const UChar c = run[i];
419         if (c < 0x300)      // U+0300 through U+036F Combining diacritical marks
420             continue;
421         if (c <= 0x36F)
422             return false;
423
424         if (c < 0x0591 || c == 0x05BE)     // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
425             continue;
426         if (c <= 0x05CF)
427             return false;
428
429         if (c < 0x0600)     // U+0600 through U+1059 Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
430             continue;
431         if (c <= 0x1059)
432             return false;
433
434         if (c < 0x1100)     // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; Modern Korean will be precomposed as a result of step A)
435             continue;
436         if (c <= 0x11FF)
437             return false;
438
439         if (c < 0x1780)     // U+1780 through U+18AF Khmer, Mongolian
440             continue;
441         if (c <= 0x18AF)
442             return false;
443
444         if (c < 0x1900)     // U+1900 through U+194F Limbu (Unicode 4.0)
445             continue;
446         if (c <= 0x194F)
447             return false;
448
449         if (c < 0x20D0)     // U+20D0 through U+20FF Combining marks for symbols
450             continue;
451         if (c <= 0x20FF)
452             return false;
453
454         if (c < 0xFE20)     // U+FE20 through U+FE2F Combining half marks
455             continue;
456         if (c <= 0xFE2F)
457             return false;
458     }
459
460     return true;
461
462 }
463
464 void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const TextStyle& style, const FloatPoint& point) const
465 {
466     // This glyph buffer holds our glyphs+advances+font data for each glyph.
467     GlyphBuffer glyphBuffer;
468
469     // Our measuring code will generate glyphs and advances for us.
470     float startX;
471     floatWidthForSimpleText(run, style, 0, &startX, &glyphBuffer);
472     
473     // We couldn't generate any glyphs for the run.  Give up.
474     if (glyphBuffer.isEmpty())
475         return;
476     
477     // Calculate the starting point of the glyphs to be displayed by adding
478     // all the advances up to the first glyph.
479     startX += point.x();
480     FloatPoint startPoint(startX, point.y());
481
482     // Swap the order of the glyphs if right-to-left.
483     if (style.rtl())
484         for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end)
485             glyphBuffer.swap(i, end);
486
487     // Draw each contiguous run of glyphs that use the same font data.
488     const FontData* fontData = glyphBuffer.fontDataAt(0);
489     float nextX = startX;
490     int lastFrom = 0;
491     int nextGlyph = 0;
492     while (nextGlyph < glyphBuffer.size()) {
493         const FontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph);
494         if (nextFontData != fontData) {
495             drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
496             lastFrom = nextGlyph;
497             fontData = nextFontData;
498             startPoint.setX(nextX);
499         }
500         nextX += glyphBuffer.advanceAt(nextGlyph);
501         nextGlyph++;
502     }
503     drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
504 }
505
506 void Font::drawText(GraphicsContext* context, const TextRun& run, const TextStyle& style, const FloatPoint& point) const
507 {
508     if (canUseGlyphCache(run))
509         drawSimpleText(context, run, style, point);
510     else
511         drawComplexText(context, run, style, point);
512 }
513
514 float Font::floatWidth(const TextRun& run, const TextStyle& style) const
515 {
516     if (canUseGlyphCache(run))
517         return floatWidthForSimpleText(run, style, 0, 0, 0);
518     else
519         return floatWidthForComplexText(run, style);
520 }
521
522 float Font::floatWidthForSimpleText(const TextRun& run, const TextStyle& style,
523                                     const FontData* substituteFont, float* startPosition, GlyphBuffer* glyphBuffer) const
524 {
525     
526     WidthIterator it(this, run, style, substituteFont);
527     it.advance(run.to(), glyphBuffer);
528     float runWidth = it.m_runWidthSoFar;
529     if (startPosition) {
530         if (style.ltr())
531             *startPosition = it.m_widthToStart;
532         else {
533             float finalRoundingWidth = it.m_finalRoundingWidth;
534             it.advance(run.length());
535             *startPosition = it.m_runWidthSoFar - runWidth + finalRoundingWidth;
536         }
537     }
538     return runWidth;
539 }
540
541 FloatRect Font::selectionRectForText(const TextRun& run, const TextStyle& style, const IntPoint& point, int h) const
542 {
543     if (canUseGlyphCache(run))
544         return selectionRectForSimpleText(run, style, point, h);
545     return selectionRectForComplexText(run, style, point, h);
546 }
547
548 FloatRect Font::selectionRectForSimpleText(const TextRun& run, const TextStyle& style, const IntPoint& point, int h) const
549 {
550     TextRun completeRun(run);
551     completeRun.makeComplete();
552
553     WidthIterator it(this, completeRun, style);
554     it.advance(run.from());
555     float beforeWidth = it.m_runWidthSoFar;
556     it.advance(run.to());
557     float afterWidth = it.m_runWidthSoFar;
558
559     // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning
560     if (style.rtl()) {
561         it.advance(run.length());
562         float totalWidth = it.m_runWidthSoFar;
563         return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h);
564     } else {
565         return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
566     }
567 }
568
569 int Font::offsetForPosition(const TextRun& run, const TextStyle& style, int x, bool includePartialGlyphs) const
570 {
571     if (canUseGlyphCache(run))
572         return offsetForPositionForSimpleText(run, style, x, includePartialGlyphs);
573     return offsetForPositionForComplexText(run, style, x, includePartialGlyphs);
574 }
575
576 int Font::offsetForPositionForSimpleText(const TextRun& run, const TextStyle& style, int x, bool includePartialGlyphs) const
577 {
578     float delta = (float)x;
579
580     WidthIterator it(this, run, style);    
581     GlyphBuffer localGlyphBuffer;
582     unsigned offset;
583     if (style.rtl()) {
584         delta -= floatWidthForSimpleText(run, style, 0, 0, 0);
585         while (1) {
586             offset = it.m_currentCharacter;
587             float w;
588             if (!it.advanceOneCharacter(w, &localGlyphBuffer))
589                 break;
590             delta += w;
591             if (includePartialGlyphs) {
592                 if (delta - w / 2 >= 0)
593                     break;
594             } else {
595                 if (delta >= 0)
596                     break;
597             }
598         }
599     } else {
600         while (1) {
601             offset = it.m_currentCharacter;
602             float w;
603             if (!it.advanceOneCharacter(w, &localGlyphBuffer))
604                 break;
605             delta -= w;
606             if (includePartialGlyphs) {
607                 if (delta + w / 2 <= 0)
608                     break;
609             } else {
610                 if (delta <= 0)
611                     break;
612             }
613         }
614     }
615
616     return offset - run.from();
617 }
618
619 }