• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "platform/fonts/mac/ComplexTextController.h"
27 
28 #include <ApplicationServices/ApplicationServices.h>
29 #include "platform/fonts/Font.h"
30 #include "platform/geometry/FloatSize.h"
31 #include "platform/text/TextBreakIterator.h"
32 #include "platform/text/TextRun.h"
33 #include "wtf/StdLibExtras.h"
34 #include "wtf/unicode/CharacterNames.h"
35 
36 using namespace std;
37 
38 namespace WebCore {
39 
40 class TextLayout {
41 public:
isNeeded(const TextRun & run,const Font & font)42     static bool isNeeded(const TextRun& run, const Font& font)
43     {
44         return font.codePath(run) == Font::Complex;
45     }
46 
TextLayout(const TextRun & run,unsigned textLength,const Font & font,float xPos)47     TextLayout(const TextRun& run, unsigned textLength, const Font& font, float xPos)
48         : m_font(font)
49         , m_run(constructTextRun(run, textLength, font, xPos))
50         , m_controller(adoptPtr(new ComplexTextController(&m_font, m_run, true)))
51     {
52     }
53 
width(unsigned from,unsigned len,HashSet<const SimpleFontData * > * fallbackFonts)54     float width(unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts)
55     {
56         m_controller->advance(from, 0, ByWholeGlyphs, fallbackFonts);
57         float beforeWidth = m_controller->runWidthSoFar();
58         if (m_font.wordSpacing() && from && Font::treatAsSpace(m_run[from]))
59             beforeWidth += m_font.wordSpacing();
60         m_controller->advance(from + len, 0, ByWholeGlyphs, fallbackFonts);
61         float afterWidth = m_controller->runWidthSoFar();
62         return afterWidth - beforeWidth;
63     }
64 
65 private:
constructTextRun(const TextRun & textRun,unsigned textLength,const Font & font,float xPos)66     static TextRun constructTextRun(const TextRun& textRun, unsigned textLength, const Font& font, float xPos)
67     {
68         TextRun run = textRun;
69         run.setCharactersLength(textLength);
70         ASSERT(run.charactersLength() >= run.length());
71 
72         run.setXPos(xPos);
73         return run;
74     }
75 
76     // ComplexTextController has only references to its Font and TextRun so they must be kept alive here.
77     Font m_font;
78     TextRun m_run;
79     OwnPtr<ComplexTextController> m_controller;
80 };
81 
createLayoutForMacComplexText(const TextRun & run,unsigned textLength,float xPos,bool collapseWhiteSpace) const82 PassOwnPtr<TextLayout> Font::createLayoutForMacComplexText(const TextRun& run, unsigned textLength, float xPos, bool collapseWhiteSpace) const
83 {
84     if (!collapseWhiteSpace || !TextLayout::isNeeded(run, *this))
85         return nullptr;
86     return adoptPtr(new TextLayout(run, textLength, *this, xPos));
87 }
88 
deleteLayout(TextLayout * layout)89 void Font::deleteLayout(TextLayout* layout)
90 {
91     delete layout;
92 }
93 
width(TextLayout & layout,unsigned from,unsigned len,HashSet<const SimpleFontData * > * fallbackFonts)94 float Font::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts)
95 {
96     return layout.width(from, len, fallbackFonts);
97 }
98 
roundCGFloat(CGFloat f)99 static inline CGFloat roundCGFloat(CGFloat f)
100 {
101     if (sizeof(CGFloat) == sizeof(float))
102         return roundf(static_cast<float>(f));
103     return static_cast<CGFloat>(round(f));
104 }
105 
ceilCGFloat(CGFloat f)106 static inline CGFloat ceilCGFloat(CGFloat f)
107 {
108     if (sizeof(CGFloat) == sizeof(float))
109         return ceilf(static_cast<float>(f));
110     return static_cast<CGFloat>(ceil(f));
111 }
112 
ComplexTextController(const Font * font,const TextRun & run,bool mayUseNaturalWritingDirection,HashSet<const SimpleFontData * > * fallbackFonts,bool forTextEmphasis)113 ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
114     : m_font(*font)
115     , m_run(run)
116     , m_isLTROnly(true)
117     , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
118     , m_forTextEmphasis(forTextEmphasis)
119     , m_currentCharacter(0)
120     , m_end(run.length())
121     , m_totalWidth(0)
122     , m_runWidthSoFar(0)
123     , m_numGlyphsSoFar(0)
124     , m_currentRun(0)
125     , m_glyphInCurrentRun(0)
126     , m_characterInCurrentGlyph(0)
127     , m_finalRoundingWidth(0)
128     , m_expansion(run.expansion())
129     , m_leadingExpansion(0)
130     , m_afterExpansion(!run.allowsLeadingExpansion())
131     , m_fallbackFonts(fallbackFonts)
132     , m_minGlyphBoundingBoxX(numeric_limits<float>::max())
133     , m_maxGlyphBoundingBoxX(numeric_limits<float>::min())
134     , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
135     , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
136     , m_lastRoundingGlyph(0)
137 {
138     if (!m_expansion)
139         m_expansionPerOpportunity = 0;
140     else {
141         bool isAfterExpansion = m_afterExpansion;
142         unsigned expansionOpportunityCount;
143         if (m_run.is8Bit())
144             expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters8(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
145          else
146              expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters16(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
147         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
148             expansionOpportunityCount--;
149 
150         if (!expansionOpportunityCount)
151             m_expansionPerOpportunity = 0;
152         else
153             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
154     }
155 
156     collectComplexTextRuns();
157     adjustGlyphsAndAdvances();
158 
159     if (!m_isLTROnly) {
160         m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
161 
162         m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
163         unsigned glyphCountSoFar = 0;
164         for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
165             m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
166             glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
167         }
168     }
169 
170     m_runWidthSoFar = m_leadingExpansion;
171 }
172 
offsetForPosition(float h,bool includePartialGlyphs)173 int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
174 {
175     if (h >= m_totalWidth)
176         return m_run.ltr() ? m_end : 0;
177 
178     h -= m_leadingExpansion;
179     if (h < 0)
180         return m_run.ltr() ? 0 : m_end;
181 
182     CGFloat x = h;
183 
184     size_t runCount = m_complexTextRuns.size();
185     size_t offsetIntoAdjustedGlyphs = 0;
186 
187     for (size_t r = 0; r < runCount; ++r) {
188         const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
189         for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
190             CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
191             if (x < adjustedAdvance) {
192                 CFIndex hitGlyphStart = complexTextRun.indexAt(j);
193                 CFIndex hitGlyphEnd;
194                 if (m_run.ltr())
195                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
196                 else
197                     hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
198 
199                 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
200                 // could use the glyph's "ligature carets". However, there is no Core Text API to get the
201                 // ligature carets.
202                 CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
203                 int stringLength = complexTextRun.stringLength();
204                 TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
205                 int clusterStart;
206                 if (cursorPositionIterator->isBoundary(hitIndex))
207                     clusterStart = hitIndex;
208                 else {
209                     clusterStart = cursorPositionIterator->preceding(hitIndex);
210                     if (clusterStart == TextBreakDone)
211                         clusterStart = 0;
212                 }
213 
214                 if (!includePartialGlyphs)
215                     return complexTextRun.stringLocation() + clusterStart;
216 
217                 int clusterEnd = cursorPositionIterator->following(hitIndex);
218                 if (clusterEnd == TextBreakDone)
219                     clusterEnd = stringLength;
220 
221                 CGFloat clusterWidth;
222                 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
223                 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
224                 // reordering and no font fallback should occur within a CTLine.
225                 if (clusterEnd - clusterStart > 1) {
226                     clusterWidth = adjustedAdvance;
227                     int firstGlyphBeforeCluster = j - 1;
228                     while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
229                         CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
230                         clusterWidth += width;
231                         x += width;
232                         firstGlyphBeforeCluster--;
233                     }
234                     unsigned firstGlyphAfterCluster = j + 1;
235                     while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
236                         clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
237                         firstGlyphAfterCluster++;
238                     }
239                 } else {
240                     clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
241                     x -=  clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
242                 }
243                 if (x <= clusterWidth / 2)
244                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
245                 else
246                     return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
247             }
248             x -= adjustedAdvance;
249         }
250         offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
251     }
252 
253     ASSERT_NOT_REACHED();
254     return 0;
255 }
256 
advanceByCombiningCharacterSequence(const UChar * & iterator,const UChar * end,UChar32 & baseCharacter,unsigned & markCount)257 static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
258 {
259     ASSERT(iterator < end);
260 
261     markCount = 0;
262 
263     baseCharacter = *iterator++;
264 
265     if (U16_IS_SURROGATE(baseCharacter)) {
266         if (!U16_IS_LEAD(baseCharacter))
267             return false;
268         if (iterator == end)
269             return false;
270         UChar trail = *iterator++;
271         if (!U16_IS_TRAIL(trail))
272             return false;
273         baseCharacter = U16_GET_SUPPLEMENTARY(baseCharacter, trail);
274     }
275 
276     // Consume marks.
277     while (iterator < end) {
278         UChar32 nextCharacter;
279         int markLength = 0;
280         U16_NEXT(iterator, markLength, end - iterator, nextCharacter);
281         if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
282             break;
283         markCount += markLength;
284         iterator += markLength;
285     }
286 
287     return true;
288 }
289 
collectComplexTextRuns()290 void ComplexTextController::collectComplexTextRuns()
291 {
292     if (!m_end)
293         return;
294 
295     // We break up glyph run generation for the string by FontData.
296     const UChar* cp;
297 
298     if (m_run.is8Bit()) {
299         String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
300         cp = stringFor8BitRun.characters16();
301         m_stringsFor8BitRuns.append(stringFor8BitRun);
302     } else
303         cp = m_run.characters16();
304 
305     if (m_font.isSmallCaps())
306         m_smallCapsBuffer.resize(m_end);
307 
308     unsigned indexOfFontTransition = 0;
309     const UChar* curr = cp;
310     const UChar* end = cp + m_end;
311 
312     const SimpleFontData* fontData;
313     bool isMissingGlyph;
314     const SimpleFontData* nextFontData;
315     bool nextIsMissingGlyph;
316 
317     unsigned markCount;
318     const UChar* sequenceStart = curr;
319     UChar32 baseCharacter;
320     if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
321         return;
322 
323     UChar uppercaseCharacter = 0;
324 
325     bool isSmallCaps;
326     bool nextIsSmallCaps = m_font.isSmallCaps() && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
327 
328     if (nextIsSmallCaps) {
329         m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter;
330         for (unsigned i = 0; i < markCount; ++i)
331             m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1];
332     }
333 
334     nextIsMissingGlyph = false;
335     nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
336     if (!nextFontData)
337         nextIsMissingGlyph = true;
338 
339     while (curr < end) {
340         fontData = nextFontData;
341         isMissingGlyph = nextIsMissingGlyph;
342         isSmallCaps = nextIsSmallCaps;
343         int index = curr - cp;
344 
345         if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
346             return;
347 
348         if (m_font.isSmallCaps()) {
349             nextIsSmallCaps = (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
350             if (nextIsSmallCaps) {
351                 m_smallCapsBuffer[index] = uppercaseCharacter;
352                 for (unsigned i = 0; i < markCount; ++i)
353                     m_smallCapsBuffer[index + i + 1] = cp[index + i + 1];
354             }
355         }
356 
357         nextIsMissingGlyph = false;
358         if (baseCharacter == zeroWidthJoiner)
359             nextFontData = fontData;
360         else {
361             nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
362             if (!nextFontData)
363                 nextIsMissingGlyph = true;
364         }
365 
366         if (nextFontData != fontData || nextIsMissingGlyph != isMissingGlyph) {
367             int itemStart = static_cast<int>(indexOfFontTransition);
368             int itemLength = index - indexOfFontTransition;
369             collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !isMissingGlyph ? fontData : 0);
370             indexOfFontTransition = index;
371         }
372     }
373 
374     int itemLength = m_end - indexOfFontTransition;
375     if (itemLength) {
376         int itemStart = indexOfFontTransition;
377         collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0);
378     }
379 
380     if (!m_run.ltr())
381         m_complexTextRuns.reverse();
382 }
383 
indexAt(size_t i) const384 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
385 {
386     return m_coreTextIndices[i];
387 }
388 
setIsNonMonotonic()389 void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
390 {
391     ASSERT(m_isMonotonic);
392     m_isMonotonic = false;
393 
394     Vector<bool, 64> mappedIndices(m_stringLength);
395     for (size_t i = 0; i < m_glyphCount; ++i) {
396         ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
397         mappedIndices[indexAt(i)] = true;
398     }
399 
400     m_glyphEndOffsets.grow(m_glyphCount);
401     for (size_t i = 0; i < m_glyphCount; ++i) {
402         CFIndex nextMappedIndex = m_indexEnd;
403         for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
404             if (mappedIndices[j]) {
405                 nextMappedIndex = j;
406                 break;
407             }
408         }
409         m_glyphEndOffsets[i] = nextMappedIndex;
410     }
411 }
412 
findNextRunIndex(unsigned runIndex) const413 unsigned ComplexTextController::findNextRunIndex(unsigned runIndex) const
414 {
415     const unsigned runOffset = stringEnd(*m_complexTextRuns[runIndex]);
416 
417     // Finds the run with the lowest stringBegin() offset that starts at or
418     // after |runOffset|.
419     //
420     // Note that this can't just find a run whose stringBegin() equals the
421     // stringEnd() of the previous run because CoreText on Mac OS X 10.6 does
422     // not return runs covering BiDi control chars, so this has to handle the
423     // resulting gaps.
424     unsigned result = 0;
425     unsigned lowestOffset = UINT_MAX;
426     for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
427         unsigned offset = stringBegin(*m_complexTextRuns[i]);
428         if (i != runIndex && offset >= runOffset && offset < lowestOffset) {
429             lowestOffset = offset;
430             result = i;
431         }
432     }
433 
434     ASSERT(lowestOffset != UINT_MAX);
435     return result;
436 }
437 
indexOfCurrentRun(unsigned & leftmostGlyph)438 unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
439 {
440     leftmostGlyph = 0;
441 
442     size_t runCount = m_complexTextRuns.size();
443     if (m_currentRun >= runCount)
444         return runCount;
445 
446     if (m_isLTROnly) {
447         for (unsigned i = 0; i < m_currentRun; ++i)
448             leftmostGlyph += m_complexTextRuns[i]->glyphCount();
449         return m_currentRun;
450     }
451 
452     if (m_runIndices.isEmpty()) {
453         unsigned firstRun = 0;
454         unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
455         for (unsigned i = 1; i < runCount; ++i) {
456             unsigned offset = stringBegin(*m_complexTextRuns[i]);
457             if (offset < firstRunOffset) {
458                 firstRun = i;
459                 firstRunOffset = offset;
460             }
461         }
462         m_runIndices.uncheckedAppend(firstRun);
463     }
464 
465     while (m_runIndices.size() <= m_currentRun) {
466         m_runIndices.uncheckedAppend(findNextRunIndex(m_runIndices.last()));
467     }
468 
469     unsigned currentRunIndex = m_runIndices[m_currentRun];
470     leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
471     return currentRunIndex;
472 }
473 
incrementCurrentRun(unsigned & leftmostGlyph)474 unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
475 {
476     if (m_isLTROnly) {
477         leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
478         return m_currentRun;
479     }
480 
481     m_currentRun++;
482     leftmostGlyph = 0;
483     return indexOfCurrentRun(leftmostGlyph);
484 }
485 
advance(unsigned offset,GlyphBuffer * glyphBuffer,GlyphIterationStyle iterationStyle,HashSet<const SimpleFontData * > * fallbackFonts)486 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const SimpleFontData*>* fallbackFonts)
487 {
488     if (static_cast<int>(offset) > m_end)
489         offset = m_end;
490 
491     if (offset <= m_currentCharacter) {
492         m_runWidthSoFar = m_leadingExpansion;
493         m_numGlyphsSoFar = 0;
494         m_currentRun = 0;
495         m_glyphInCurrentRun = 0;
496         m_characterInCurrentGlyph = 0;
497     }
498 
499     m_currentCharacter = offset;
500 
501     size_t runCount = m_complexTextRuns.size();
502 
503     unsigned leftmostGlyph = 0;
504     unsigned currentRunIndex = indexOfCurrentRun(leftmostGlyph);
505     while (m_currentRun < runCount) {
506         const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
507         bool ltr = complexTextRun.isLTR();
508         size_t glyphCount = complexTextRun.glyphCount();
509         unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
510         unsigned k = leftmostGlyph + g;
511         if (fallbackFonts && complexTextRun.fontData() != m_font.primaryFont())
512             fallbackFonts->add(complexTextRun.fontData());
513 
514         while (m_glyphInCurrentRun < glyphCount) {
515             unsigned glyphStartOffset = complexTextRun.indexAt(g);
516             unsigned glyphEndOffset;
517             if (complexTextRun.isMonotonic()) {
518                 if (ltr)
519                     glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
520                 else
521                     glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
522             } else
523                 glyphEndOffset = complexTextRun.endOffsetAt(g);
524 
525             CGSize adjustedAdvance = m_adjustedAdvances[k];
526 
527             if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
528                 return;
529 
530             if (glyphBuffer && !m_characterInCurrentGlyph)
531                 glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), adjustedAdvance);
532 
533             unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
534             m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
535             // FIXME: Instead of dividing the glyph's advance equally between the characters, this
536             // could use the glyph's "ligature carets". However, there is no Core Text API to get the
537             // ligature carets.
538             if (glyphStartOffset == glyphEndOffset) {
539                 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
540                 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
541                 m_runWidthSoFar += adjustedAdvance.width;
542             } else if (iterationStyle == ByWholeGlyphs) {
543                 if (!oldCharacterInCurrentGlyph)
544                     m_runWidthSoFar += adjustedAdvance.width;
545             } else
546                 m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
547 
548             if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
549                 return;
550 
551             m_numGlyphsSoFar++;
552             m_glyphInCurrentRun++;
553             m_characterInCurrentGlyph = 0;
554             if (ltr) {
555                 g++;
556                 k++;
557             } else {
558                 g--;
559                 k--;
560             }
561         }
562         currentRunIndex = incrementCurrentRun(leftmostGlyph);
563         m_glyphInCurrentRun = 0;
564     }
565     if (!m_run.ltr() && m_numGlyphsSoFar == m_adjustedAdvances.size())
566         m_runWidthSoFar += m_finalRoundingWidth;
567 }
568 
adjustGlyphsAndAdvances()569 void ComplexTextController::adjustGlyphsAndAdvances()
570 {
571     CGFloat widthSinceLastCommit = 0;
572     size_t runCount = m_complexTextRuns.size();
573     bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
574     for (size_t r = 0; r < runCount; ++r) {
575         ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
576         unsigned glyphCount = complexTextRun.glyphCount();
577         const SimpleFontData* fontData = complexTextRun.fontData();
578 
579         if (!complexTextRun.isLTR())
580             m_isLTROnly = false;
581 
582         const CGGlyph* glyphs = complexTextRun.glyphs();
583         const CGSize* advances = complexTextRun.advances();
584 
585         bool lastRun = r + 1 == runCount;
586         bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances();
587         float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset();
588         CGFloat roundedSpaceWidth = roundCGFloat(spaceWidth);
589         const UChar* cp = complexTextRun.characters();
590         CGPoint glyphOrigin = CGPointZero;
591         CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max();
592         bool isMonotonic = true;
593 
594         for (unsigned i = 0; i < glyphCount; i++) {
595             CFIndex characterIndex = complexTextRun.indexAt(i);
596             if (m_run.ltr()) {
597                 if (characterIndex < lastCharacterIndex)
598                     isMonotonic = false;
599             } else {
600                 if (characterIndex > lastCharacterIndex)
601                     isMonotonic = false;
602             }
603             UChar ch = *(cp + characterIndex);
604             bool lastGlyph = lastRun && i + 1 == glyphCount;
605             UChar nextCh;
606             if (lastGlyph)
607                 nextCh = ' ';
608             else if (i + 1 < glyphCount)
609                 nextCh = *(cp + complexTextRun.indexAt(i + 1));
610             else
611                 nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
612 
613             bool treatAsSpace = Font::treatAsSpace(ch);
614             CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
615             CGSize advance = treatAsSpace ? CGSizeMake(spaceWidth, advances[i].height) : advances[i];
616 
617             if (ch == '\t' && m_run.allowTabs())
618                 advance.width = m_font.tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_totalWidth + widthSinceLastCommit);
619             else if (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
620                 advance.width = 0;
621                 glyph = fontData->spaceGlyph();
622             }
623 
624             float roundedAdvanceWidth = roundf(advance.width);
625             if (roundsAdvances)
626                 advance.width = roundedAdvanceWidth;
627 
628             advance.width += fontData->syntheticBoldOffset();
629 
630 
631             // We special case spaces in two ways when applying word rounding.
632             // First, we round spaces to an adjusted width in all fonts.
633             // Second, in fixed-pitch fonts we ensure that all glyphs that
634             // match the width of the space glyph have the same width as the space glyph.
635             if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
636                 advance.width = fontData->adjustedSpaceWidth();
637 
638             if (hasExtraSpacing) {
639                 // If we're a glyph with an advance, go ahead and add in letter-spacing.
640                 // That way we weed out zero width lurkers.  This behavior matches the fast text code path.
641                 if (advance.width && m_font.letterSpacing())
642                     advance.width += m_font.letterSpacing();
643 
644                 // Handle justification and word-spacing.
645                 if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) {
646                     // Distribute the run's total expansion evenly over all expansion opportunities in the run.
647                     if (m_expansion) {
648                         float previousExpansion = m_expansion;
649                         if (!treatAsSpace && !m_afterExpansion) {
650                             // Take the expansion opportunity before this ideograph.
651                             m_expansion -= m_expansionPerOpportunity;
652                             float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
653                             m_totalWidth += expansionAtThisOpportunity;
654                             if (m_adjustedAdvances.isEmpty())
655                                 m_leadingExpansion = expansionAtThisOpportunity;
656                             else
657                                 m_adjustedAdvances.last().width += expansionAtThisOpportunity;
658                             previousExpansion = m_expansion;
659                         }
660                         if (!lastGlyph || m_run.allowsTrailingExpansion()) {
661                             m_expansion -= m_expansionPerOpportunity;
662                             advance.width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
663                             m_afterExpansion = true;
664                         }
665                     } else
666                         m_afterExpansion = false;
667 
668                     // Account for word-spacing.
669                     if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || r > 0) && m_font.wordSpacing())
670                         advance.width += m_font.wordSpacing();
671                 } else
672                     m_afterExpansion = false;
673             }
674 
675             // Apply rounding hacks if needed.
676             // We adjust the width of the last character of a "word" to ensure an integer width.
677             // Force characters that are used to determine word boundaries for the rounding hack
678             // to be integer width, so the following words will start on an integer boundary.
679             if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch))
680                 advance.width = ceilCGFloat(advance.width);
681 
682             // Check to see if the next character is a "rounding hack character", if so, adjust the
683             // width so that the total run width will be on an integer boundary.
684             if ((m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) {
685                 CGFloat totalWidth = widthSinceLastCommit + advance.width;
686                 widthSinceLastCommit = ceilCGFloat(totalWidth);
687                 CGFloat extraWidth = widthSinceLastCommit - totalWidth;
688                 if (m_run.ltr())
689                     advance.width += extraWidth;
690                 else {
691                     if (m_lastRoundingGlyph)
692                         m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth;
693                     else
694                         m_finalRoundingWidth = extraWidth;
695                     m_lastRoundingGlyph = m_adjustedAdvances.size() + 1;
696                 }
697                 m_totalWidth += widthSinceLastCommit;
698                 widthSinceLastCommit = 0;
699             } else
700                 widthSinceLastCommit += advance.width;
701 
702             // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
703             if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
704                 glyph = 0;
705 
706             advance.height *= -1;
707             m_adjustedAdvances.append(advance);
708             m_adjustedGlyphs.append(glyph);
709 
710             FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
711             glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
712             m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x());
713             m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
714             m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y());
715             m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
716             glyphOrigin.x += advance.width;
717             glyphOrigin.y += advance.height;
718 
719             lastCharacterIndex = characterIndex;
720         }
721         if (!isMonotonic)
722             complexTextRun.setIsNonMonotonic();
723     }
724     m_totalWidth += widthSinceLastCommit;
725 }
726 
727 } // namespace WebCore
728