OLD | NEW |
| (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 | |
OLD | NEW |