1 /*
2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25 #include "config.h"
26 #include "ComplexTextController.h"
27
28 #include "FloatSize.h"
29 #include "Font.h"
30 #include "TextBreakIterator.h"
31 #include "TextRun.h"
32 #include <ApplicationServices/ApplicationServices.h>
33 #include <wtf/StdLibExtras.h>
34 #include <wtf/unicode/CharacterNames.h>
35
36 #if defined(BUILDING_ON_LEOPARD)
37 // Undefined when compiling agains the 10.5 SDK.
38 #define kCTVersionNumber10_6 0x00030000
39 #endif
40
41 using namespace std;
42
43 namespace WebCore {
44
roundCGFloat(CGFloat f)45 static inline CGFloat roundCGFloat(CGFloat f)
46 {
47 if (sizeof(CGFloat) == sizeof(float))
48 return roundf(static_cast<float>(f));
49 return static_cast<CGFloat>(round(f));
50 }
51
ComplexTextController(const Font * font,const TextRun & run,bool mayUseNaturalWritingDirection,HashSet<const SimpleFontData * > * fallbackFonts,bool forTextEmphasis)52 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
53 : m_font(*font)
54 , m_run(run)
55 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
56 , m_forTextEmphasis(forTextEmphasis)
57 , m_currentCharacter(0)
58 , m_end(run.length())
59 , m_totalWidth(0)
60 , m_runWidthSoFar(0)
61 , m_numGlyphsSoFar(0)
62 , m_currentRun(0)
63 , m_glyphInCurrentRun(0)
64 , m_characterInCurrentGlyph(0)
65 , m_expansion(run.expansion())
66 , m_leadingExpansion(0)
67 , m_afterExpansion(!run.allowsLeadingExpansion())
68 , m_fallbackFonts(fallbackFonts)
69 , m_minGlyphBoundingBoxX(numeric_limits<float>::max())
70 , m_maxGlyphBoundingBoxX(numeric_limits<float>::min())
71 , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
72 , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
73 {
74 if (!m_expansion)
75 m_expansionPerOpportunity = 0;
76 else {
77 bool isAfterExpansion = m_afterExpansion;
78 unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
79 if (isAfterExpansion && !m_run.allowsTrailingExpansion())
80 expansionOpportunityCount--;
81
82 if (!expansionOpportunityCount)
83 m_expansionPerOpportunity = 0;
84 else
85 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
86 }
87
88 collectComplexTextRuns();
89 adjustGlyphsAndAdvances();
90
91 m_runWidthSoFar = m_leadingExpansion;
92 }
93
offsetForPosition(float h,bool includePartialGlyphs)94 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
95 {
96 if (h >= m_totalWidth)
97 return m_run.ltr() ? m_end : 0;
98
99 h -= m_leadingExpansion;
100 if (h < 0)
101 return m_run.ltr() ? 0 : m_end;
102
103 CGFloat x = h;
104
105 size_t runCount = m_complexTextRuns.size();
106 size_t offsetIntoAdjustedGlyphs = 0;
107
108 for (size_t r = 0; r < runCount; ++r) {
109 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
110 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
111 CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
112 if (x < adjustedAdvance) {
113 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
114 CFIndex hitGlyphEnd;
115 if (m_run.ltr())
116 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
117 else
118 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
119
120 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
121 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
122 // ligature carets.
123 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
124 int stringLength = complexTextRun.stringLength();
125 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
126 int clusterStart;
127 if (isTextBreak(cursorPositionIterator, hitIndex))
128 clusterStart = hitIndex;
129 else {
130 clusterStart = textBreakPreceding(cursorPositionIterator, hitIndex);
131 if (clusterStart == TextBreakDone)
132 clusterStart = 0;
133 }
134
135 if (!includePartialGlyphs)
136 return complexTextRun.stringLocation() + clusterStart;
137
138 int clusterEnd = textBreakFollowing(cursorPositionIterator, hitIndex);
139 if (clusterEnd == TextBreakDone)
140 clusterEnd = stringLength;
141
142 CGFloat clusterWidth;
143 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
144 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
145 // reordering and on font fallback should occur within a CTLine.
146 if (clusterEnd - clusterStart > 1) {
147 clusterWidth = adjustedAdvance;
148 int firstGlyphBeforeCluster = j - 1;
149 while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
150 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
151 clusterWidth += width;
152 x += width;
153 firstGlyphBeforeCluster--;
154 }
155 unsigned firstGlyphAfterCluster = j + 1;
156 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
157 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
158 firstGlyphAfterCluster++;
159 }
160 } else {
161 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
162 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
163 }
164 if (x <= clusterWidth / 2)
165 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
166 else
167 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
168 }
169 x -= adjustedAdvance;
170 }
171 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
172 }
173
174 ASSERT_NOT_REACHED();
175 return 0;
176 }
177
collectComplexTextRuns()178 void ComplexTextController::collectComplexTextRuns()
179 {
180 if (!m_end)
181 return;
182
183 // We break up glyph run generation for the string by FontData and (if needed) the use of small caps.
184 const UChar* cp = m_run.characters();
185
186 if (m_font.isSmallCaps())
187 m_smallCapsBuffer.resize(m_end);
188
189 unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0;
190 const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp;
191 const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end;
192
193 GlyphData glyphData;
194 GlyphData nextGlyphData;
195
196 bool isSurrogate = U16_IS_SURROGATE(*curr);
197 if (isSurrogate) {
198 if (m_run.ltr()) {
199 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
200 return;
201 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
202 } else {
203 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
204 return;
205 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
206 }
207 } else
208 nextGlyphData = m_font.glyphDataForCharacter(*curr, false);
209
210 UChar newC = 0;
211
212 bool isSmallCaps;
213 bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
214
215 if (nextIsSmallCaps)
216 m_smallCapsBuffer[curr - cp] = newC;
217
218 while (true) {
219 curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1);
220 if (curr == end)
221 break;
222
223 glyphData = nextGlyphData;
224 isSmallCaps = nextIsSmallCaps;
225 int index = curr - cp;
226 isSurrogate = U16_IS_SURROGATE(*curr);
227 UChar c = *curr;
228 bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
229 if (isSurrogate) {
230 if (m_run.ltr()) {
231 if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1]))
232 return;
233 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false);
234 } else {
235 if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1]))
236 return;
237 nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false);
238 }
239 } else
240 nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps ? SmallCapsVariant : AutoVariant);
241
242 if (!isSurrogate && m_font.isSmallCaps()) {
243 nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
244 if (nextIsSmallCaps)
245 m_smallCapsBuffer[index] = forceSmallCaps ? c : newC;
246 }
247
248 if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) {
249 int itemStart = m_run.rtl() ? index + 1 : static_cast<int>(indexOfFontTransition);
250 int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition;
251 collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0);
252 indexOfFontTransition = index;
253 }
254 }
255
256 int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition;
257 if (itemLength) {
258 int itemStart = m_run.rtl() ? 0 : indexOfFontTransition;
259 collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0);
260 }
261 }
262
263 #if USE(CORE_TEXT) && USE(ATSUI)
shouldUseATSUIAPI()264 static inline bool shouldUseATSUIAPI()
265 {
266 enum TypeRenderingAPIToUse { UnInitialized, UseATSUI, UseCoreText };
267 static TypeRenderingAPIToUse apiToUse = UnInitialized;
268
269 if (UNLIKELY(apiToUse == UnInitialized)) {
270 if (&CTGetCoreTextVersion != 0 && CTGetCoreTextVersion() >= kCTVersionNumber10_6)
271 apiToUse = UseCoreText;
272 else
273 apiToUse = UseATSUI;
274 }
275
276 return apiToUse == UseATSUI;
277 }
278 #endif
279
indexAt(size_t i) const280 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
281 {
282 #if USE(CORE_TEXT) && USE(ATSUI)
283 return shouldUseATSUIAPI() ? m_atsuiIndices[i] : m_coreTextIndices[i];
284 #elif USE(ATSUI)
285 return m_atsuiIndices[i];
286 #elif USE(CORE_TEXT)
287 return m_coreTextIndices[i];
288 #endif
289 }
290
collectComplexTextRunsForCharacters(const UChar * cp,unsigned length,unsigned stringLocation,const SimpleFontData * fontData)291 void ComplexTextController::collectComplexTextRunsForCharacters(const UChar* cp, unsigned length, unsigned stringLocation, const SimpleFontData* fontData)
292 {
293 #if USE(CORE_TEXT) && USE(ATSUI)
294 if (shouldUseATSUIAPI())
295 return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
296 return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
297 #elif USE(ATSUI)
298 return collectComplexTextRunsForCharactersATSUI(cp, length, stringLocation, fontData);
299 #elif USE(CORE_TEXT)
300 return collectComplexTextRunsForCharactersCoreText(cp, length, stringLocation, fontData);
301 #endif
302 }
303
ComplexTextRun(const SimpleFontData * fontData,const UChar * characters,unsigned stringLocation,size_t stringLength,bool ltr)304 ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr)
305 : m_fontData(fontData)
306 , m_characters(characters)
307 , m_stringLocation(stringLocation)
308 , m_stringLength(stringLength)
309 , m_indexEnd(stringLength)
310 , m_isMonotonic(true)
311 {
312 #if USE(CORE_TEXT) && USE(ATSUI)
313 shouldUseATSUIAPI() ? createTextRunFromFontDataATSUI(ltr) : createTextRunFromFontDataCoreText(ltr);
314 #elif USE(ATSUI)
315 createTextRunFromFontDataATSUI(ltr);
316 #elif USE(CORE_TEXT)
317 createTextRunFromFontDataCoreText(ltr);
318 #endif
319 }
320
setIsNonMonotonic()321 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
322 {
323 ASSERT(m_isMonotonic);
324 m_isMonotonic = false;
325
326 Vector<bool, 64> mappedIndices(m_stringLength);
327 for (size_t i = 0; i < m_glyphCount; ++i) {
328 ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
329 mappedIndices[indexAt(i)] = true;
330 }
331
332 m_glyphEndOffsets.grow(m_glyphCount);
333 for (size_t i = 0; i < m_glyphCount; ++i) {
334 CFIndex nextMappedIndex = m_indexEnd;
335 for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
336 if (mappedIndices[j]) {
337 nextMappedIndex = j;
338 break;
339 }
340 }
341 m_glyphEndOffsets[i] = nextMappedIndex;
342 }
343 }
344
advance(unsigned offset,GlyphBuffer * glyphBuffer)345 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer)
346 {
347 if (static_cast<int>(offset) > m_end)
348 offset = m_end;
349
350 if (offset <= m_currentCharacter)
351 return;
352
353 m_currentCharacter = offset;
354
355 size_t runCount = m_complexTextRuns.size();
356
357 bool ltr = m_run.ltr();
358
359 unsigned k = ltr ? m_numGlyphsSoFar : m_adjustedGlyphs.size() - 1 - m_numGlyphsSoFar;
360 while (m_currentRun < runCount) {
361 const ComplexTextRun& complexTextRun = *m_complexTextRuns[ltr ? m_currentRun : runCount - 1 - m_currentRun];
362 size_t glyphCount = complexTextRun.glyphCount();
363 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
364 while (m_glyphInCurrentRun < glyphCount) {
365 unsigned glyphStartOffset = complexTextRun.indexAt(g);
366 unsigned glyphEndOffset;
367 if (complexTextRun.isMonotonic()) {
368 if (ltr)
369 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
370 else
371 glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
372 } else
373 glyphEndOffset = complexTextRun.endOffsetAt(g);
374
375 CGSize adjustedAdvance = m_adjustedAdvances[k];
376
377 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
378 return;
379
380 if (glyphBuffer && !m_characterInCurrentGlyph)
381 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance);
382
383 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
384 m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
385 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
386 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
387 // ligature carets.
388 if (glyphStartOffset == glyphEndOffset) {
389 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
390 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
391 m_runWidthSoFar += adjustedAdvance.width;
392 } else
393 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
394
395 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
396 return;
397
398 m_numGlyphsSoFar++;
399 m_glyphInCurrentRun++;
400 m_characterInCurrentGlyph = 0;
401 if (ltr) {
402 g++;
403 k++;
404 } else {
405 g--;
406 k--;
407 }
408 }
409 m_currentRun++;
410 m_glyphInCurrentRun = 0;
411 }
412 }
413
adjustGlyphsAndAdvances()414 void ComplexTextController::adjustGlyphsAndAdvances()
415 {
416 CGFloat widthSinceLastCommit = 0;
417 size_t runCount = m_complexTextRuns.size();
418 bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
419 for (size_t r = 0; r < runCount; ++r) {
420 ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
421 unsigned glyphCount = complexTextRun.glyphCount();
422 const SimpleFontData* fontData = complexTextRun.fontData();
423
424 const CGGlyph* glyphs = complexTextRun.glyphs();
425 const CGSize* advances = complexTextRun.advances();
426
427 bool lastRun = r + 1 == runCount;
428 bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
429 const UChar* cp = complexTextRun.characters();
430 CGPoint glyphOrigin = CGPointZero;
431 CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
432 bool isMonotonic = true;
433
434 for (unsigned i = 0; i < glyphCount; i++) {
435 CFIndex characterIndex = complexTextRun.indexAt(i);
436 if (m_run.ltr()) {
437 if (characterIndex < lastCharacterIndex)
438 isMonotonic = false;
439 } else {
440 if (characterIndex > lastCharacterIndex)
441 isMonotonic = false;
442 }
443 UChar ch = *(cp + characterIndex);
444 bool lastGlyph = lastRun && i + 1 == glyphCount;
445 UChar nextCh;
446 if (lastGlyph)
447 nextCh = ' ';
448 else if (i + 1 < glyphCount)
449 nextCh = *(cp + complexTextRun.indexAt(i + 1));
450 else
451 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
452
453 bool treatAsSpace = Font::treatAsSpace(ch);
454 CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
455 CGSize advance = treatAsSpace ? CGSizeMake(fontData->spaceWidth(), advances[i].height) : advances[i];
456
457 if (ch == '\t' && m_run.allowTabs()) {
458 float tabWidth = m_font.tabWidth(*fontData);
459 advance.width = tabWidth - fmodf(m_run.xPos() + m_totalWidth + widthSinceLastCommit, tabWidth);
460 } else if (ch == zeroWidthSpace || (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace)) {
461 advance.width = 0;
462 glyph = fontData->spaceGlyph();
463 }
464
465 float roundedAdvanceWidth = roundf(advance.width);
466 if (roundsAdvances)
467 advance.width = roundedAdvanceWidth;
468
469 advance.width += fontData->syntheticBoldOffset();
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 (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
479 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
480 if (m_expansion) {
481 if (!treatAsSpace && !m_afterExpansion) {
482 // Take the expansion opportunity before this ideograph.
483 m_expansion -= m_expansionPerOpportunity;
484 m_totalWidth += m_expansionPerOpportunity;
485 if (m_adjustedAdvances.isEmpty())
486 m_leadingExpansion = m_expansionPerOpportunity;
487 else
488 m_adjustedAdvances.last().width += m_expansionPerOpportunity;
489 }
490 if (!lastGlyph || m_run.allowsTrailingExpansion()) {
491 m_expansion -= m_expansionPerOpportunity;
492 advance.width += m_expansionPerOpportunity;
493 m_afterExpansion = true;
494 }
495 } else
496 m_afterExpansion = false;
497
498 // Account for word-spacing.
499 if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
500 advance.width += m_font.wordSpacing();
501 } else
502 m_afterExpansion = false;
503 }
504
505 widthSinceLastCommit += advance.width;
506
507 // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
508 if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
509 glyph = 0;
510
511 advance.height *= -1;
512 m_adjustedAdvances.append(advance);
513 m_adjustedGlyphs.append(glyph);
514
515 FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
516 glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
517 m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
518 m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
519 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
520 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
521 glyphOrigin.x += advance.width;
522 glyphOrigin.y += advance.height;
523
524 lastCharacterIndex = characterIndex;
525 }
526 if (!isMonotonic)
527 complexTextRun.setIsNonMonotonic();
528 }
529 m_totalWidth += widthSinceLastCommit;
530 }
531
532 } // namespace WebCore
533