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

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

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

Powered by Google App Engine
This is Rietveld 408576698