1 /*
2 * Copyright (C) 2007, 2008 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "CoreTextController.h"
28
29 #if USE(CORE_TEXT)
30
31 #include "CharacterNames.h"
32 #include "Font.h"
33 #include "FontCache.h"
34 #include "SimpleFontData.h"
35 #include "TextBreakIterator.h"
36 #include <wtf/MathExtras.h>
37
38 using namespace std;
39
40 namespace WebCore {
41
roundCGFloat(CGFloat f)42 static inline CGFloat roundCGFloat(CGFloat f)
43 {
44 if (sizeof(CGFloat) == sizeof(float))
45 return roundf(static_cast<float>(f));
46 return static_cast<CGFloat>(round(f));
47 }
48
ceilCGFloat(CGFloat f)49 static inline CGFloat ceilCGFloat(CGFloat f)
50 {
51 if (sizeof(CGFloat) == sizeof(float))
52 return ceilf(static_cast<float>(f));
53 return static_cast<CGFloat>(ceil(f));
54 }
55
CoreTextRun(CTRunRef ctRun,const SimpleFontData * fontData,const UChar * characters,unsigned stringLocation,size_t stringLength)56 CoreTextController::CoreTextRun::CoreTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength)
57 : m_CTRun(ctRun)
58 , m_fontData(fontData)
59 , m_characters(characters)
60 , m_stringLocation(stringLocation)
61 , m_stringLength(stringLength)
62 {
63 m_glyphCount = CTRunGetGlyphCount(ctRun);
64 m_indices = CTRunGetStringIndicesPtr(ctRun);
65 if (!m_indices) {
66 m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex)));
67 CFDataIncreaseLength(m_indicesData.get(), m_glyphCount * sizeof(CFIndex));
68 m_indices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_indicesData.get()));
69 CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), const_cast<CFIndex*>(m_indices));
70 }
71 }
72
73 // Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on
74 // glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path.
CoreTextRun(const SimpleFontData * fontData,const UChar * characters,unsigned stringLocation,size_t stringLength,bool ltr)75 CoreTextController::CoreTextRun::CoreTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
76 : m_fontData(fontData)
77 , m_characters(characters)
78 , m_stringLocation(stringLocation)
79 , m_stringLength(stringLength)
80 {
81 Vector<CFIndex, 16> indices;
82 unsigned r = 0;
83 while (r < stringLength) {
84 indices.append(r);
85 if (U_IS_SURROGATE(characters[r])) {
86 ASSERT(r + 1 < stringLength);
87 ASSERT(U_IS_SURROGATE_LEAD(characters[r]));
88 ASSERT(U_IS_TRAIL(characters[r + 1]));
89 r += 2;
90 } else
91 r++;
92 }
93 m_glyphCount = indices.size();
94 if (!ltr) {
95 for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end)
96 std::swap(indices[r], indices[end]);
97 }
98 m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex)));
99 CFDataAppendBytes(m_indicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex));
100 m_indices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_indicesData.get()));
101 }
102
CoreTextController(const Font * font,const TextRun & run,bool mayUseNaturalWritingDirection)103 CoreTextController::CoreTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection)
104 : m_font(*font)
105 , m_run(run)
106 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
107 , m_currentCharacter(0)
108 , m_end(run.length())
109 , m_totalWidth(0)
110 , m_runWidthSoFar(0)
111 , m_numGlyphsSoFar(0)
112 , m_currentRun(0)
113 , m_glyphInCurrentRun(0)
114 , m_finalRoundingWidth(0)
115 , m_lastRoundingGlyph(0)
116 {
117 m_padding = m_run.padding();
118 if (!m_padding)
119 m_padPerSpace = 0;
120 else {
121 float numSpaces = 0;
122 for (int s = 0; s < m_run.length(); s++)
123 if (Font::treatAsSpace(m_run[s]))
124 numSpaces++;
125
126 if (numSpaces == 0)
127 m_padPerSpace = 0;
128 else
129 m_padPerSpace = ceilf(m_run.padding() / numSpaces);
130 }
131
132 collectCoreTextRuns();
133 adjustGlyphsAndAdvances();
134 }
135
offsetForPosition(int h,bool includePartialGlyphs)136 int CoreTextController::offsetForPosition(int h, bool includePartialGlyphs)
137 {
138 // FIXME: For positions occurring within a ligature, we should return the closest "ligature caret" or
139 // approximate it by dividing the width of the ligature by the number of characters it encompasses.
140 // However, Core Text does not expose a low-level API for directly finding
141 // out how many characters a ligature encompasses (the "attachment count").
142 if (h >= m_totalWidth)
143 return m_run.ltr() ? m_end : 0;
144 if (h < 0)
145 return m_run.ltr() ? 0 : m_end;
146
147 CGFloat x = h;
148
149 size_t runCount = m_coreTextRuns.size();
150 size_t offsetIntoAdjustedGlyphs = 0;
151
152 for (size_t r = 0; r < runCount; ++r) {
153 const CoreTextRun& coreTextRun = m_coreTextRuns[r];
154 for (unsigned j = 0; j < coreTextRun.glyphCount(); ++j) {
155 CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
156 if (x <= adjustedAdvance) {
157 CFIndex hitIndex = coreTextRun.indexAt(j);
158 int stringLength = coreTextRun.stringLength();
159 TextBreakIterator* characterIterator = characterBreakIterator(coreTextRun.characters(), stringLength);
160 int clusterStart;
161 if (isTextBreak(characterIterator, hitIndex))
162 clusterStart = hitIndex;
163 else {
164 clusterStart = textBreakPreceding(characterIterator, hitIndex);
165 if (clusterStart == TextBreakDone)
166 clusterStart = 0;
167 }
168
169 if (!includePartialGlyphs)
170 return coreTextRun.stringLocation() + clusterStart;
171
172 int clusterEnd = textBreakFollowing(characterIterator, hitIndex);
173 if (clusterEnd == TextBreakDone)
174 clusterEnd = stringLength;
175
176 CGFloat clusterWidth = adjustedAdvance;
177 // FIXME: The search stops at the boundaries of coreTextRun. In theory, it should go on into neighboring CoreTextRuns
178 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
179 // reordering and on font fallback should occur within a CTLine.
180 if (clusterEnd - clusterStart > 1) {
181 int firstGlyphBeforeCluster = j - 1;
182 while (firstGlyphBeforeCluster && coreTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
183 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
184 clusterWidth += width;
185 x += width;
186 firstGlyphBeforeCluster--;
187 }
188 unsigned firstGlyphAfterCluster = j + 1;
189 while (firstGlyphAfterCluster < coreTextRun.glyphCount() && coreTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
190 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
191 firstGlyphAfterCluster++;
192 }
193 }
194 if (x <= clusterWidth / 2)
195 return coreTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
196 else
197 return coreTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
198 }
199 x -= adjustedAdvance;
200 }
201 offsetIntoAdjustedGlyphs += coreTextRun.glyphCount();
202 }
203
204 ASSERT_NOT_REACHED();
205 return 0;
206 }
207
collectCoreTextRuns()208 void CoreTextController::collectCoreTextRuns()
209 {
210 if (!m_end)
211 return;
212
213 // We break up glyph run generation for the string by FontData and (if needed) the use of small caps.
214 const UChar* cp = m_run.characters();
215 bool hasTrailingSoftHyphen = m_run[m_end - 1] == softHyphen;
216
217 if (m_font.isSmallCaps() || hasTrailingSoftHyphen)
218 m_smallCapsBuffer.resize(m_end);
219
220 unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0;
221 const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp;
222 const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end;
223
224 // FIXME: Using HYPHEN-MINUS rather than HYPHEN because Times has a HYPHEN-MINUS glyph that looks like its
225 // SOFT-HYPHEN glyph, and has no HYPHEN glyph.
226 static const UChar hyphen = '-';
227
228 if (hasTrailingSoftHyphen && m_run.rtl()) {
229 collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData);
230 indexOfFontTransition--;
231 curr--;
232 }
233
234 GlyphData glyphData;
235 GlyphData nextGlyphData;
236
237 bool isSurrogate = U16_IS_SURROGATE(*curr);
238 if (isSurrogate) {
239 if (m_run.ltr()) {
240 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
241 return;
242 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
243 } else {
244 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
245 return;
246 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
247 }
248 } else
249 nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
250
251 UChar newC = 0;
252
253 bool isSmallCaps;
254 bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
255
256 if (nextIsSmallCaps)
257 m_smallCapsBuffer[curr - cp] = newC;
258
259 while (true) {
260 curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1);
261 if (curr == end)
262 break;
263
264 glyphData = nextGlyphData;
265 isSmallCaps = nextIsSmallCaps;
266 int index = curr - cp;
267 isSurrogate = U16_IS_SURROGATE(*curr);
268 UChar c = *curr;
269 bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
270 if (isSurrogate) {
271 if (m_run.ltr()) {
272 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
273 return;
274 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
275 } else {
276 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
277 return;
278 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
279 }
280 } else
281 nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps);
282
283 if (!isSurrogate && m_font.isSmallCaps()) {
284 nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
285 if (nextIsSmallCaps)
286 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
287 }
288
289 if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
290 int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition;
291 int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition;
292 collectCoreTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
293 indexOfFontTransition = index;
294 }
295 }
296
297 int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition - (hasTrailingSoftHyphen ? 1 : 0);
298 if (itemLength) {
299 int itemStart = m_run.rtl() ? 0 : indexOfFontTransition;
300 collectCoreTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
301 }
302
303 if (hasTrailingSoftHyphen && m_run.ltr())
304 collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData);
305 }
306
advance(unsigned offset,GlyphBuffer * glyphBuffer)307 void CoreTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
308 {
309 // FIXME: For offsets falling inside a ligature, we should advance only as far as the appropriate "ligature caret"
310 // or divide the width of the ligature by the number of offsets it encompasses and make an advance proportional
311 // to the offsets into the ligature. However, Core Text does not expose a low-level API for
312 // directly finding out how many characters a ligature encompasses (the "attachment count").
313 if (static_cast<int>(offset) > m_end)
314 offset = m_end;
315
316 if (offset <= m_currentCharacter)
317 return;
318
319 m_currentCharacter = offset;
320
321 size_t runCount = m_coreTextRuns.size();
322
323 bool ltr = m_run.ltr();
324
325 unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
326 while (m_currentRun < runCount) {
327 const CoreTextRun& coreTextRun = m_coreTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
328 size_t glyphCount = coreTextRun.glyphCount();
329 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
330 while (m_glyphInCurrentRun < glyphCount) {
331 if (coreTextRun.indexAt(g) + coreTextRun.stringLocation() >= m_currentCharacter)
332 return;
333 CGSize adjustedAdvance = m_adjustedAdvances[k];
334 if (glyphBuffer)
335 glyphBuffer->add(m_adjustedGlyphs[k], coreTextRun.fontData(), adjustedAdvance);
336 m_runWidthSoFar += adjustedAdvance.width;
337 m_numGlyphsSoFar++;
338 m_glyphInCurrentRun++;
339 if (ltr) {
340 g++;
341 k++;
342 } else {
343 g--;
344 k--;
345 }
346 }
347 m_currentRun++;
348 m_glyphInCurrentRun = 0;
349 }
350 if (!ltr && m_numGlyphsSoFar == m_adjustedAdvances.size())
351 m_runWidthSoFar += m_finalRoundingWidth;
352 }
353
collectCoreTextRunsForCharacters(const UChar * cp,unsigned length,unsigned stringLocation,const SimpleFontData * fontData)354 void CoreTextController::collectCoreTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
355 {
356 if (!fontData) {
357 // Create a run of missing glyphs from the primary font.
358 m_coreTextRuns.append(CoreTextRun(m_font.primaryFont(), cp, stringLocation, length, m_run.ltr()));
359 return;
360 }
361
362 RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(NULL, cp, length, kCFAllocatorNull));
363
364 RetainPtr<CFAttributedStringRef> attributedString(AdoptCF, CFAttributedStringCreate(NULL, string.get(), fontData->getCFStringAttributes()));
365
366 RetainPtr<CTTypesetterRef> typesetter;
367
368 if (!m_mayUseNaturalWritingDirection || m_run.directionalOverride()) {
369 static const void* optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel };
370 static const void* ltrOptionValues[] = { kCFBooleanFalse };
371 static const void* rtlOptionValues[] = { kCFBooleanTrue };
372 static CFDictionaryRef ltrTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, ltrOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
373 static CFDictionaryRef rtlTypesetterOptions = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, sizeof(optionKeys) / sizeof(*optionKeys), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
374 typesetter.adoptCF(CTTypesetterCreateWithAttributedStringAndOptions(attributedString.get(), m_run.ltr() ? ltrTypesetterOptions : rtlTypesetterOptions));
375 } else
376 typesetter.adoptCF(CTTypesetterCreateWithAttributedString(attributedString.get()));
377
378 RetainPtr<CTLineRef> line(AdoptCF, CTTypesetterCreateLine(typesetter.get(), CFRangeMake(0, 0)));
379
380 CFArrayRef runArray = CTLineGetGlyphRuns(line.get());
381
382 CFIndex runCount = CFArrayGetCount(runArray);
383
384 for (CFIndex r = 0; r < runCount; r++) {
385 CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r));
386 ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID());
387 m_coreTextRuns.append(CoreTextRun(ctRun, fontData, cp, stringLocation, length));
388 }
389 }
390
adjustGlyphsAndAdvances()391 void CoreTextController::adjustGlyphsAndAdvances()
392 {
393 size_t runCount = m_coreTextRuns.size();
394 for (size_t r = 0; r < runCount; ++r) {
395 const CoreTextRun& coreTextRun = m_coreTextRuns[r];
396 unsigned glyphCount = coreTextRun.glyphCount();
397 const SimpleFontData* fontData = coreTextRun.fontData();
398
399 Vector<CGGlyph, 256> glyphsVector;
400 const CGGlyph* glyphs;
401
402 Vector<CGSize, 256> advancesVector;
403 const CGSize* advances;
404
405 if (coreTextRun.ctRun()) {
406 glyphs = CTRunGetGlyphsPtr(coreTextRun.ctRun());
407 if (!glyphs) {
408 glyphsVector.grow(glyphCount);
409 CTRunGetGlyphs(coreTextRun.ctRun(), CFRangeMake(0, 0), glyphsVector.data());
410 glyphs = glyphsVector.data();
411 }
412
413 advances = CTRunGetAdvancesPtr(coreTextRun.ctRun());
414 if (!advances) {
415 advancesVector.grow(glyphCount);
416 CTRunGetAdvances(coreTextRun.ctRun(), CFRangeMake(0, 0), advancesVector.data());
417 advances = advancesVector.data();
418 }
419 } else {
420 // Synthesize a run of missing glyphs.
421 glyphsVector.fill(0, glyphCount);
422 glyphs = glyphsVector.data();
423 advancesVector.fill(CGSizeMake(fontData->widthForGlyph(0), 0), glyphCount);
424 advances = advancesVector.data();
425 }
426
427 bool lastRun = r + 1 == runCount;
428 const UChar* cp = coreTextRun.characters();
429 CGFloat roundedSpaceWidth = roundCGFloat(fontData->m_spaceWidth);
430 bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
431 bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled();
432
433
434 for (unsigned i = 0; i < glyphCount; i++) {
435 CFIndex characterIndex = coreTextRun.indexAt(i);
436 UChar ch = *(cp + characterIndex);
437 bool lastGlyph = lastRun && i + 1 == glyphCount;
438 UChar nextCh;
439 if (lastGlyph)
440 nextCh = ' ';
441 else if (i + 1 < glyphCount)
442 nextCh = *(cp + coreTextRun.indexAt(i + 1));
443 else
444 nextCh = *(m_coreTextRuns[r + 1].characters() + m_coreTextRuns[r + 1].indexAt(0));
445
446 bool treatAsSpace = Font::treatAsSpace(ch);
447 CGGlyph glyph = treatAsSpace ? fontData->m_spaceGlyph : glyphs[i];
448 CGSize advance = treatAsSpace ? CGSizeMake(fontData->m_spaceWidth, advances[i].height) : advances[i];
449
450 if (ch == '\t' && m_run.allowTabs()) {
451 float tabWidth = m_font.tabWidth();
452 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth, tabWidth);
453 } else if (ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
454 advance.width = 0;
455 glyph = fontData->m_spaceGlyph;
456 }
457
458 float roundedAdvanceWidth = roundf(advance.width);
459 if (roundsAdvances)
460 advance.width = roundedAdvanceWidth;
461
462 advance.width += fontData->m_syntheticBoldOffset;
463
464 // We special case spaces in two ways when applying word rounding.
465 // First, we round spaces to an adjusted width in all fonts.
466 // Second, in fixed-pitch fonts we ensure that all glyphs that
467 // match the width of the space glyph have the same width as the space glyph.
468 if (roundedAdvanceWidth == roundedSpaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding())
469 advance.width = fontData->m_adjustedSpaceWidth;
470
471 if (hasExtraSpacing) {
472 // If we're a glyph with an advance, go ahead and add in letter-spacing.
473 // That way we weed out zero width lurkers. This behavior matches the fast text code path.
474 if (advance.width && m_font.letterSpacing())
475 advance.width += m_font.letterSpacing();
476
477 // Handle justification and word-spacing.
478 if (glyph == fontData->m_spaceGlyph) {
479 // Account for padding. WebCore uses space padding to justify text.
480 // We distribute the specified padding over the available spaces in the run.
481 if (m_padding) {
482 // Use leftover padding if not evenly divisible by number of spaces.
483 if (m_padding < m_padPerSpace) {
484 advance.width += m_padding;
485 m_padding = 0;
486 } else {
487 advance.width += m_padPerSpace;
488 m_padding -= m_padPerSpace;
489 }
490 }
491
492 // Account for word-spacing.
493 if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
494 advance.width += m_font.wordSpacing();
495 }
496 }
497
498 // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters
499 // followed by a character defined by isRoundingHackCharacter()) are always an integer width.
500 // We adjust the width of the last character of a "word" to ensure an integer width.
501 // Force characters that are used to determine word boundaries for the rounding hack
502 // to be integer width, so the following words will start on an integer boundary.
503 if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
504 advance.width = ceilCGFloat(advance.width);
505
506 // Check to see if the next character is a "rounding hack character", if so, adjust the
507 // width so that the total run width will be on an integer boundary.
508 if (m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh) || m_run.applyRunRounding() && lastGlyph) {
509 CGFloat totalWidth = m_totalWidth + advance.width;
510 CGFloat extraWidth = ceilCGFloat(totalWidth) - totalWidth;
511 if (m_run.ltr())
512 advance.width += extraWidth;
513 else {
514 m_totalWidth += extraWidth;
515 if (m_lastRoundingGlyph)
516 m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
517 else
518 m_finalRoundingWidth = extraWidth;
519 m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
520 }
521 }
522
523 m_totalWidth += advance.width;
524 advance.height *= -1;
525 m_adjustedAdvances.append(advance);
526 m_adjustedGlyphs.append(glyph);
527 }
528 }
529 }
530
531 } // namespace WebCore
532
533 #endif // USE(CORE_TEXT)
534