Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Side by Side Diff: Source/platform/fonts/mac/ComplexTextController.cpp

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

Powered by Google App Engine
This is Rietveld 408576698