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