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

Side by Side Diff: Source/core/platform/graphics/mac/ComplexTextController.cpp

Issue 99103006: Moving GraphicsContext and dependencies from core to platform. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Final patch - fixes Android Created 7 years 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 | Annotate | Revision Log
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 "core/platform/graphics/mac/ComplexTextController.h"
27
28 #include <ApplicationServices/ApplicationServices.h>
29 #include "core/platform/graphics/Font.h"
30 #include "platform/geometry/FloatSize.h"
31 #include "platform/graphics/TextRun.h"
32 #include "platform/text/TextBreakIterator.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:
42 static bool isNeeded(const TextRun& run, const Font& font)
43 {
44 return font.codePath(run) == Font::Complex;
45 }
46
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
54 float width(unsigned from, unsigned len, HashSet<const SimpleFontData*>* fal lbackFonts)
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:
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
82 PassOwnPtr<TextLayout> Font::createLayoutForMacComplexText(const TextRun& run, u nsigned 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
89 void Font::deleteLayout(TextLayout* layout)
90 {
91 delete layout;
92 }
93
94 float Font::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts)
95 {
96 return layout.width(from, len, fallbackFonts);
97 }
98
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
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
113 ComplexTextController::ComplexTextController(const Font* font, const TextRun& ru n, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackF onts, 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.ch aracters8(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion);
145 else
146 expansionOpportunityCount = Font::expansionOpportunityCount(m_run.c haracters16(), 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.si ze());
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
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[offsetIntoAdjustedGlyph s + 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 < complexTex tRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complex TextRun.indexEnd()));
196 else
197 hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTex tRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
198
199 // FIXME: Instead of dividing the glyph's advance equally betwee n 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 = cursorMovementIterat or(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 t here 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.indexA t(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphB eforeCluster) < clusterEnd) {
229 CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGly phs + 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 && complexText Run.indexAt(firstGlyphAfterCluster) < clusterEnd) {
236 clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGly phs + firstGlyphAfterCluster].width;
237 firstGlyphAfterCluster++;
238 }
239 } else {
240 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStar t);
241 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
242 }
243 if (x <= clusterWidth / 2)
244 return complexTextRun.stringLocation() + (m_run.ltr() ? clus terStart : clusterEnd);
245 else
246 return complexTextRun.stringLocation() + (m_run.ltr() ? clus terEnd : clusterStart);
247 }
248 x -= adjustedAdvance;
249 }
250 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
251 }
252
253 ASSERT_NOT_REACHED();
254 return 0;
255 }
256
257 static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UC har* 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
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.characte rs8(), 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)) != baseChara cter;
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, c urr - 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, markC ount))
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 + ind ex, 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 ? nextFont Data : 0);
378 }
379
380 if (!m_run.ltr())
381 m_complexTextRuns.reverse();
382 }
383
384 CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
385 {
386 return m_coreTextIndices[i];
387 }
388
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
413 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
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
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
486 void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, G lyphIterationStyle 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[currentRunInde x];
507 bool ltr = complexTextRun.isLTR();
508 size_t glyphCount = complexTextRun.glyphCount();
509 unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurre ntRun;
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.i ndexEnd()));
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_currentC haracter)
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 th e characters, this
536 // could use the glyph's "ligature carets". However, there is no Cor e Text API to get the
537 // ligature carets.
538 if (glyphStartOffset == glyphEndOffset) {
539 // When there are multiple glyphs per character we need to advan ce 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_characterInCurrent Glyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
547
548 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentChar acter)
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
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->syntheticBoldOffse t();
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_complexTex tRuns[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].h eight) : advances[i];
616
617 if (ch == '\t' && m_run.allowTabs())
618 advance.width = m_font.tabWidth(*fontData, m_run.tabSize(), m_ru n.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 spa ce glyph.
635 if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpace Width && (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 match es 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 expa nsion 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 ideogr aph.
651 m_expansion -= m_expansionPerOpportunity;
652 float expansionAtThisOpportunity = !m_run.applyWordR ounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_exp ansion);
653 m_totalWidth += expansionAtThisOpportunity;
654 if (m_adjustedAdvances.isEmpty())
655 m_leadingExpansion = expansionAtThisOpportunity;
656 else
657 m_adjustedAdvances.last().width += expansionAtTh isOpportunity;
658 previousExpansion = m_expansion;
659 }
660 if (!lastGlyph || m_run.allowsTrailingExpansion()) {
661 m_expansion -= m_expansionPerOpportunity;
662 advance.width += !m_run.applyWordRounding() ? m_expa nsionPerOpportunity : 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()) && (c haracterIndex > 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 a n integer width.
677 // Force characters that are used to determine word boundaries for t he rounding hack
678 // to be integer width, so the following words will start on an inte ger 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::isRoundingHack Character(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 += ext raWidth;
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 the y are combine with a space.
703 if (m_forTextEmphasis && (!Font::canReceiveTextEmphasis(ch) || (U_GE T_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.max X());
714 m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y() );
715 m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.max Y());
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698