1/*
2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26#include "ComplexTextController.h"
27
28#include "CharacterProperties.h"
29#include "FloatSize.h"
30#include "FontCascade.h"
31#include "RenderBlock.h"
32#include "RenderText.h"
33#include "TextRun.h"
34#include <unicode/ubrk.h>
35#include <wtf/Optional.h>
36#include <wtf/StdLibExtras.h>
37#include <wtf/text/TextBreakIterator.h>
38#include <wtf/unicode/CharacterNames.h>
39
40#if PLATFORM(IOS_FAMILY)
41#include <CoreText/CoreText.h>
42#endif
43
44namespace WebCore {
45
46#if PLATFORM(WIN)
47
48class TextLayout {
49};
50
51void TextLayoutDeleter::operator()(TextLayout*) const
52{
53}
54
55std::unique_ptr<TextLayout, TextLayoutDeleter> FontCascade::createLayout(RenderText&, float, bool) const
56{
57 return nullptr;
58}
59
60float FontCascade::width(TextLayout&, unsigned, unsigned, HashSet<const Font*>*)
61{
62 ASSERT_NOT_REACHED();
63 return 0;
64}
65
66#else
67
68class TextLayout {
69 WTF_MAKE_FAST_ALLOCATED;
70public:
71 static bool isNeeded(RenderText& text, const FontCascade& font)
72 {
73 TextRun run = RenderBlock::constructTextRun(text, text.style());
74 return font.codePath(run) == FontCascade::Complex;
75 }
76
77 TextLayout(RenderText& text, const FontCascade& font, float xPos)
78 : m_font(font)
79 , m_run(constructTextRun(text, xPos))
80 , m_controller(std::make_unique<ComplexTextController>(m_font, m_run, true))
81 {
82 }
83
84 float width(unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts)
85 {
86 m_controller->advance(from, 0, ByWholeGlyphs, fallbackFonts);
87 float beforeWidth = m_controller->runWidthSoFar();
88 if (m_font.wordSpacing() && from && FontCascade::treatAsSpace(m_run[from]))
89 beforeWidth += m_font.wordSpacing();
90 m_controller->advance(from + len, 0, ByWholeGlyphs, fallbackFonts);
91 float afterWidth = m_controller->runWidthSoFar();
92 return afterWidth - beforeWidth;
93 }
94
95private:
96 static TextRun constructTextRun(RenderText& text, float xPos)
97 {
98 TextRun run = RenderBlock::constructTextRun(text, text.style());
99 run.setXPos(xPos);
100 return run;
101 }
102
103 // ComplexTextController has only references to its FontCascade and TextRun so they must be kept alive here.
104 FontCascade m_font;
105 TextRun m_run;
106 std::unique_ptr<ComplexTextController> m_controller;
107};
108
109void TextLayoutDeleter::operator()(TextLayout* layout) const
110{
111 delete layout;
112}
113
114std::unique_ptr<TextLayout, TextLayoutDeleter> FontCascade::createLayout(RenderText& text, float xPos, bool collapseWhiteSpace) const
115{
116 if (!collapseWhiteSpace || !TextLayout::isNeeded(text, *this))
117 return nullptr;
118 return std::unique_ptr<TextLayout, TextLayoutDeleter>(new TextLayout(text, *this, xPos));
119}
120
121float FontCascade::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts)
122{
123 return layout.width(from, len, fallbackFonts);
124}
125
126void ComplexTextController::computeExpansionOpportunity()
127{
128 if (!m_expansion)
129 m_expansionPerOpportunity = 0;
130 else {
131 unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? TextDirection::LTR : TextDirection::RTL, m_run.expansionBehavior()).first;
132
133 if (!expansionOpportunityCount)
134 m_expansionPerOpportunity = 0;
135 else
136 m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
137 }
138}
139
140ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const Font*>* fallbackFonts, bool forTextEmphasis)
141 : m_fallbackFonts(fallbackFonts)
142 , m_font(font)
143 , m_run(run)
144 , m_end(run.length())
145 , m_expansion(run.expansion())
146 , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
147 , m_forTextEmphasis(forTextEmphasis)
148{
149#if PLATFORM(WIN)
150 ASSERT_NOT_REACHED();
151#endif
152
153 computeExpansionOpportunity();
154
155 collectComplexTextRuns();
156
157 finishConstruction();
158}
159
160ComplexTextController::ComplexTextController(const FontCascade& font, const TextRun& run, Vector<Ref<ComplexTextRun>>& runs)
161 : m_font(font)
162 , m_run(run)
163 , m_end(run.length())
164 , m_expansion(run.expansion())
165{
166 computeExpansionOpportunity();
167
168 for (auto& run : runs)
169 m_complexTextRuns.append(run.ptr());
170
171 finishConstruction();
172}
173
174void ComplexTextController::finishConstruction()
175{
176 adjustGlyphsAndAdvances();
177
178 if (!m_isLTROnly) {
179 m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
180
181 m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
182 unsigned glyphCountSoFar = 0;
183 for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
184 m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
185 glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
186 }
187 }
188}
189
190unsigned ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
191{
192 if (h >= m_totalWidth)
193 return m_run.ltr() ? m_end : 0;
194
195 if (h < 0)
196 return m_run.ltr() ? 0 : m_end;
197
198 float x = h;
199
200 size_t runCount = m_complexTextRuns.size();
201 unsigned offsetIntoAdjustedGlyphs = 0;
202
203 for (size_t r = 0; r < runCount; ++r) {
204 const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
205 for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
206 unsigned index = offsetIntoAdjustedGlyphs + j;
207 float adjustedAdvance = m_adjustedBaseAdvances[index].width();
208 if (x < adjustedAdvance) {
209 unsigned hitGlyphStart = complexTextRun.indexAt(j);
210 unsigned hitGlyphEnd;
211 if (m_run.ltr())
212 hitGlyphEnd = std::max(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : complexTextRun.indexEnd());
213 else
214 hitGlyphEnd = std::max(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : complexTextRun.indexEnd());
215
216 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
217 // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions().
218 unsigned hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
219 unsigned stringLength = complexTextRun.stringLength();
220 CachedTextBreakIterator cursorPositionIterator(StringView(complexTextRun.characters(), stringLength), TextBreakIterator::Mode::Caret, nullAtom());
221 unsigned clusterStart;
222 if (cursorPositionIterator.isBoundary(hitIndex))
223 clusterStart = hitIndex;
224 else
225 clusterStart = cursorPositionIterator.preceding(hitIndex).valueOr(0);
226
227 if (!includePartialGlyphs)
228 return complexTextRun.stringLocation() + clusterStart;
229
230 unsigned clusterEnd = cursorPositionIterator.following(hitIndex).valueOr(stringLength);
231
232 float clusterWidth;
233 // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
234 // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
235 // reordering and no font fallback should occur within a CTLine.
236 if (clusterEnd - clusterStart > 1) {
237 clusterWidth = adjustedAdvance;
238 if (j) {
239 unsigned firstGlyphBeforeCluster = j - 1;
240 while (complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
241 float width = m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width();
242 clusterWidth += width;
243 x += width;
244 if (!firstGlyphBeforeCluster)
245 break;
246 firstGlyphBeforeCluster--;
247 }
248 }
249 unsigned firstGlyphAfterCluster = j + 1;
250 while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
251 clusterWidth += m_adjustedBaseAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width();
252 firstGlyphAfterCluster++;
253 }
254 } else {
255 clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
256 x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
257 }
258 if (x <= clusterWidth / 2)
259 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
260 return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
261 }
262 x -= adjustedAdvance;
263 }
264 offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
265 }
266
267 ASSERT_NOT_REACHED();
268 return 0;
269}
270
271// FIXME: We should consider reimplementing this function using ICU to advance by grapheme.
272// The current implementation only considers explicitly emoji sequences and emoji variations.
273static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
274{
275 ASSERT(iterator < end);
276
277 markCount = 0;
278
279 unsigned i = 0;
280 unsigned remainingCharacters = end - iterator;
281 U16_NEXT(iterator, i, remainingCharacters, baseCharacter);
282 iterator = iterator + i;
283 if (U_IS_SURROGATE(baseCharacter))
284 return false;
285
286 // Consume marks.
287 bool sawEmojiGroupCandidate = isEmojiGroupCandidate(baseCharacter);
288 bool sawJoiner = false;
289 bool sawRegionalIndicator = isEmojiRegionalIndicator(baseCharacter);
290 while (iterator < end) {
291 UChar32 nextCharacter;
292 unsigned markLength = 0;
293 bool shouldContinue = false;
294 ASSERT(end >= iterator);
295 U16_NEXT(iterator, markLength, static_cast<unsigned>(end - iterator), nextCharacter);
296
297 if (isVariationSelector(nextCharacter) || isEmojiFitzpatrickModifier(nextCharacter))
298 shouldContinue = true;
299
300 if (sawRegionalIndicator && isEmojiRegionalIndicator(nextCharacter)) {
301 shouldContinue = true;
302 sawRegionalIndicator = false;
303 }
304
305 if (sawJoiner && isEmojiGroupCandidate(nextCharacter))
306 shouldContinue = true;
307
308 sawJoiner = false;
309 if (sawEmojiGroupCandidate && nextCharacter == zeroWidthJoiner) {
310 sawJoiner = true;
311 shouldContinue = true;
312 }
313
314 if (!shouldContinue && !(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
315 break;
316
317 markCount += markLength;
318 iterator += markLength;
319 }
320
321 return true;
322}
323
324// FIXME: Capitalization is language-dependent and context-dependent and should operate on grapheme clusters instead of codepoints.
325static inline Optional<UChar32> capitalized(UChar32 baseCharacter)
326{
327 if (U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK)
328 return WTF::nullopt;
329
330 UChar32 uppercaseCharacter = u_toupper(baseCharacter);
331 ASSERT(uppercaseCharacter == baseCharacter || (U_IS_BMP(baseCharacter) == U_IS_BMP(uppercaseCharacter)));
332 if (uppercaseCharacter != baseCharacter)
333 return uppercaseCharacter;
334 return WTF::nullopt;
335}
336
337static bool shouldSynthesize(bool dontSynthesizeSmallCaps, const Font* nextFont, UChar32 baseCharacter, Optional<UChar32> capitalizedBase, FontVariantCaps fontVariantCaps, bool engageAllSmallCapsProcessing)
338{
339 if (dontSynthesizeSmallCaps)
340 return false;
341 if (!nextFont || nextFont == Font::systemFallback())
342 return false;
343 if (engageAllSmallCapsProcessing && isASCIISpace(baseCharacter))
344 return false;
345 if (!engageAllSmallCapsProcessing && !capitalizedBase)
346 return false;
347 return !nextFont->variantCapsSupportsCharacterForSynthesis(fontVariantCaps, baseCharacter);
348}
349
350void ComplexTextController::collectComplexTextRuns()
351{
352 if (!m_end)
353 return;
354
355 // We break up glyph run generation for the string by Font.
356 const UChar* cp;
357
358 if (m_run.is8Bit()) {
359 String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
360 m_stringsFor8BitRuns.append(WTFMove(stringFor8BitRun));
361 cp = m_stringsFor8BitRuns.last().characters16();
362 } else
363 cp = m_run.characters16();
364
365 auto fontVariantCaps = m_font.fontDescription().variantCaps();
366 bool dontSynthesizeSmallCaps = !static_cast<bool>(m_font.fontDescription().fontSynthesis() & FontSynthesisSmallCaps);
367 bool engageAllSmallCapsProcessing = fontVariantCaps == FontVariantCaps::AllSmall || fontVariantCaps == FontVariantCaps::AllPetite;
368 bool engageSmallCapsProcessing = engageAllSmallCapsProcessing || fontVariantCaps == FontVariantCaps::Small || fontVariantCaps == FontVariantCaps::Petite;
369
370 if (engageAllSmallCapsProcessing || engageSmallCapsProcessing)
371 m_smallCapsBuffer.resize(m_end);
372
373 unsigned indexOfFontTransition = 0;
374 const UChar* curr = cp;
375 const UChar* end = cp + m_end;
376
377 const Font* font;
378 const Font* nextFont;
379 const Font* synthesizedFont = nullptr;
380 const Font* smallSynthesizedFont = nullptr;
381
382 unsigned markCount;
383 UChar32 baseCharacter;
384 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
385 return;
386
387 nextFont = m_font.fontForCombiningCharacterSequence(cp, curr - cp);
388
389 bool isSmallCaps = false;
390 bool nextIsSmallCaps = false;
391
392 auto capitalizedBase = capitalized(baseCharacter);
393 if (shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) {
394 synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
395 smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
396 UChar32 characterToWrite = capitalizedBase ? capitalizedBase.value() : cp[0];
397 unsigned characterIndex = 0;
398 U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, characterToWrite);
399 for (unsigned i = characterIndex; cp + i < curr; ++i)
400 m_smallCapsBuffer[i] = cp[i];
401 nextIsSmallCaps = true;
402 }
403
404 while (curr < end) {
405 font = nextFont;
406 isSmallCaps = nextIsSmallCaps;
407 unsigned index = curr - cp;
408
409 if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
410 return;
411
412 if (synthesizedFont) {
413 if (auto capitalizedBase = capitalized(baseCharacter)) {
414 unsigned characterIndex = index;
415 U16_APPEND_UNSAFE(m_smallCapsBuffer, characterIndex, capitalizedBase.value());
416 for (unsigned i = 0; i < markCount; ++i)
417 m_smallCapsBuffer[i + characterIndex] = cp[i + characterIndex];
418 nextIsSmallCaps = true;
419 } else {
420 if (engageAllSmallCapsProcessing) {
421 for (unsigned i = 0; i < curr - cp - index; ++i)
422 m_smallCapsBuffer[index + i] = cp[index + i];
423 }
424 nextIsSmallCaps = engageAllSmallCapsProcessing;
425 }
426 }
427
428 if (baseCharacter == zeroWidthJoiner)
429 nextFont = font;
430 else
431 nextFont = m_font.fontForCombiningCharacterSequence(cp + index, curr - cp - index);
432
433 capitalizedBase = capitalized(baseCharacter);
434 if (!synthesizedFont && shouldSynthesize(dontSynthesizeSmallCaps, nextFont, baseCharacter, capitalizedBase, fontVariantCaps, engageAllSmallCapsProcessing)) {
435 // Rather than synthesize each character individually, we should synthesize the entire "run" if any character requires synthesis.
436 synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
437 smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
438 nextIsSmallCaps = true;
439 curr = cp + indexOfFontTransition;
440 continue;
441 }
442
443 if (nextFont != font || nextIsSmallCaps != isSmallCaps) {
444 unsigned itemLength = index - indexOfFontTransition;
445 if (itemLength) {
446 unsigned itemStart = indexOfFontTransition;
447 if (synthesizedFont) {
448 if (isSmallCaps)
449 collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
450 else
451 collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
452 } else
453 collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, font);
454 if (nextFont != font) {
455 synthesizedFont = nullptr;
456 smallSynthesizedFont = nullptr;
457 nextIsSmallCaps = false;
458 }
459 }
460 indexOfFontTransition = index;
461 }
462 }
463
464 ASSERT(m_end >= indexOfFontTransition);
465 unsigned itemLength = m_end - indexOfFontTransition;
466 if (itemLength) {
467 unsigned itemStart = indexOfFontTransition;
468 if (synthesizedFont) {
469 if (nextIsSmallCaps)
470 collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
471 else
472 collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
473 } else
474 collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, nextFont);
475 }
476
477 if (!m_run.ltr())
478 m_complexTextRuns.reverse();
479}
480
481unsigned ComplexTextController::ComplexTextRun::indexAt(unsigned i) const
482{
483 ASSERT(i < m_glyphCount);
484
485 return m_coreTextIndices[i];
486}
487
488void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
489{
490 ASSERT(m_isMonotonic);
491 m_isMonotonic = false;
492
493 Vector<bool, 64> mappedIndices(m_stringLength, false);
494 for (unsigned i = 0; i < m_glyphCount; ++i) {
495 ASSERT(indexAt(i) < m_stringLength);
496 mappedIndices[indexAt(i)] = true;
497 }
498
499 m_glyphEndOffsets.grow(m_glyphCount);
500 for (unsigned i = 0; i < m_glyphCount; ++i) {
501 unsigned nextMappedIndex = m_indexEnd;
502 for (unsigned j = indexAt(i) + 1; j < m_stringLength; ++j) {
503 if (mappedIndices[j]) {
504 nextMappedIndex = j;
505 break;
506 }
507 }
508 m_glyphEndOffsets[i] = nextMappedIndex;
509 }
510}
511
512unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
513{
514 leftmostGlyph = 0;
515
516 size_t runCount = m_complexTextRuns.size();
517 if (m_currentRun >= runCount)
518 return runCount;
519
520 if (m_isLTROnly) {
521 for (unsigned i = 0; i < m_currentRun; ++i)
522 leftmostGlyph += m_complexTextRuns[i]->glyphCount();
523 return m_currentRun;
524 }
525
526 if (m_runIndices.isEmpty()) {
527 unsigned firstRun = 0;
528 unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
529 for (unsigned i = 1; i < runCount; ++i) {
530 unsigned offset = stringBegin(*m_complexTextRuns[i]);
531 if (offset < firstRunOffset) {
532 firstRun = i;
533 firstRunOffset = offset;
534 }
535 }
536 m_runIndices.uncheckedAppend(firstRun);
537 }
538
539 while (m_runIndices.size() <= m_currentRun) {
540 unsigned offset = stringEnd(*m_complexTextRuns[m_runIndices.last()]);
541
542 for (unsigned i = 0; i < runCount; ++i) {
543 if (offset == stringBegin(*m_complexTextRuns[i])) {
544 m_runIndices.uncheckedAppend(i);
545 break;
546 }
547 }
548 }
549
550 unsigned currentRunIndex = m_runIndices[m_currentRun];
551 leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
552 return currentRunIndex;
553}
554
555unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
556{
557 if (m_isLTROnly) {
558 leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
559 return m_currentRun;
560 }
561
562 m_currentRun++;
563 leftmostGlyph = 0;
564 return indexOfCurrentRun(leftmostGlyph);
565}
566
567float ComplexTextController::runWidthSoFarFraction(unsigned glyphStartOffset, unsigned glyphEndOffset, unsigned oldCharacterInCurrentGlyph, GlyphIterationStyle iterationStyle) const
568{
569 // FIXME: Instead of dividing the glyph's advance equally between the characters, this
570 // could use the glyph's "ligature carets". This is available in CoreText via CTFontGetLigatureCaretPositions().
571 if (glyphStartOffset == glyphEndOffset) {
572 // When there are multiple glyphs per character we need to advance by the full width of the glyph.
573 ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
574 return 1;
575 }
576
577 if (iterationStyle == ByWholeGlyphs) {
578 if (!oldCharacterInCurrentGlyph)
579 return 1;
580 return 0;
581 }
582
583 return static_cast<float>(m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
584}
585
586void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const Font*>* fallbackFonts)
587{
588 if (offset > m_end)
589 offset = m_end;
590
591 if (offset <= m_currentCharacter) {
592 m_runWidthSoFar = 0;
593 m_numGlyphsSoFar = 0;
594 m_currentRun = 0;
595 m_glyphInCurrentRun = 0;
596 m_characterInCurrentGlyph = 0;
597 }
598
599 m_currentCharacter = offset;
600
601 size_t runCount = m_complexTextRuns.size();
602
603 unsigned indexOfLeftmostGlyphInCurrentRun = 0; // Relative to the beginning of ComplexTextController.
604 unsigned currentRunIndex = indexOfCurrentRun(indexOfLeftmostGlyphInCurrentRun);
605 while (m_currentRun < runCount) {
606 const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
607 bool ltr = complexTextRun.isLTR();
608 unsigned glyphCount = complexTextRun.glyphCount();
609 unsigned glyphIndexIntoCurrentRun = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
610 unsigned glyphIndexIntoComplexTextController = indexOfLeftmostGlyphInCurrentRun + glyphIndexIntoCurrentRun;
611 if (fallbackFonts && &complexTextRun.font() != &m_font.primaryFont())
612 fallbackFonts->add(&complexTextRun.font());
613
614 // We must store the initial advance for the first glyph we are going to draw.
615 // When leftmostGlyph is 0, it represents the first glyph to draw, taking into
616 // account the text direction.
617 if (!indexOfLeftmostGlyphInCurrentRun && glyphBuffer)
618 glyphBuffer->setInitialAdvance(GlyphBufferAdvance(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height()));
619
620 while (m_glyphInCurrentRun < glyphCount) {
621 unsigned glyphStartOffset = complexTextRun.indexAt(glyphIndexIntoCurrentRun);
622 unsigned glyphEndOffset;
623 if (complexTextRun.isMonotonic()) {
624 if (ltr)
625 glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun + 1 < glyphCount ? complexTextRun.indexAt(glyphIndexIntoCurrentRun + 1) : complexTextRun.indexEnd());
626 else
627 glyphEndOffset = std::max(glyphStartOffset, glyphIndexIntoCurrentRun > 0 ? complexTextRun.indexAt(glyphIndexIntoCurrentRun - 1) : complexTextRun.indexEnd());
628 } else
629 glyphEndOffset = complexTextRun.endOffsetAt(glyphIndexIntoCurrentRun);
630
631 FloatSize adjustedBaseAdvance = m_adjustedBaseAdvances[glyphIndexIntoComplexTextController];
632
633 if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
634 return;
635
636 if (glyphBuffer && !m_characterInCurrentGlyph) {
637 auto currentGlyphOrigin = glyphOrigin(glyphIndexIntoComplexTextController);
638 GlyphBufferAdvance paintAdvance(adjustedBaseAdvance);
639 if (!glyphIndexIntoCurrentRun) {
640 // The first layout advance of every run includes the "initial layout advance." However, here, we need
641 // paint advances, so subtract it out before transforming the layout advance into a paint advance.
642 paintAdvance.setWidth(paintAdvance.width() - (complexTextRun.initialAdvance().width() - currentGlyphOrigin.x()));
643 paintAdvance.setHeight(paintAdvance.height() - (complexTextRun.initialAdvance().height() - currentGlyphOrigin.y()));
644 }
645 paintAdvance.setWidth(paintAdvance.width() + glyphOrigin(glyphIndexIntoComplexTextController + 1).x() - currentGlyphOrigin.x());
646 paintAdvance.setHeight(paintAdvance.height() + glyphOrigin(glyphIndexIntoComplexTextController + 1).y() - currentGlyphOrigin.y());
647 if (glyphIndexIntoCurrentRun == glyphCount - 1 && currentRunIndex + 1 < runCount) {
648 // Our paint advance points to the end of the run. However, the next run may have an
649 // initial advance, and our paint advance needs to point to the location of the next
650 // glyph. So, we need to add in the next run's initial advance.
651 paintAdvance.setWidth(paintAdvance.width() - glyphOrigin(glyphIndexIntoComplexTextController + 1).x() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().width());
652 paintAdvance.setHeight(paintAdvance.height() - glyphOrigin(glyphIndexIntoComplexTextController + 1).y() + m_complexTextRuns[currentRunIndex + 1]->initialAdvance().height());
653 }
654 paintAdvance.setHeight(-paintAdvance.height()); // Increasing y points down
655 glyphBuffer->add(m_adjustedGlyphs[glyphIndexIntoComplexTextController], &complexTextRun.font(), paintAdvance, complexTextRun.indexAt(m_glyphInCurrentRun));
656 }
657
658 unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
659 m_characterInCurrentGlyph = std::min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
660 m_runWidthSoFar += adjustedBaseAdvance.width() * runWidthSoFarFraction(glyphStartOffset, glyphEndOffset, oldCharacterInCurrentGlyph, iterationStyle);
661
662 if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
663 return;
664
665 m_numGlyphsSoFar++;
666 m_glyphInCurrentRun++;
667 m_characterInCurrentGlyph = 0;
668 if (ltr) {
669 glyphIndexIntoCurrentRun++;
670 glyphIndexIntoComplexTextController++;
671 } else {
672 glyphIndexIntoCurrentRun--;
673 glyphIndexIntoComplexTextController--;
674 }
675 }
676 currentRunIndex = incrementCurrentRun(indexOfLeftmostGlyphInCurrentRun);
677 m_glyphInCurrentRun = 0;
678 }
679}
680
681static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
682{
683 bool expandLeft = ideograph;
684 bool expandRight = ideograph;
685 if (treatAsSpace) {
686 if (ltr)
687 expandRight = true;
688 else
689 expandLeft = true;
690 }
691 if (isAfterExpansion)
692 expandLeft = false;
693 ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
694 ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
695 if (forbidLeadingExpansion)
696 expandLeft = false;
697 if (forbidTrailingExpansion)
698 expandRight = false;
699 if (forceLeadingExpansion)
700 expandLeft = true;
701 if (forceTrailingExpansion)
702 expandRight = true;
703 return std::make_pair(expandLeft, expandRight);
704}
705
706void ComplexTextController::adjustGlyphsAndAdvances()
707{
708 bool afterExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
709 size_t runCount = m_complexTextRuns.size();
710 bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
711 bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
712 bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
713 bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
714 bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
715
716 // We are iterating in glyph order, not string order. Compare this to WidthIterator::advanceInternal()
717 for (size_t runIndex = 0; runIndex < runCount; ++runIndex) {
718 ComplexTextRun& complexTextRun = *m_complexTextRuns[runIndex];
719 unsigned glyphCount = complexTextRun.glyphCount();
720 const Font& font = complexTextRun.font();
721
722 if (!complexTextRun.isLTR())
723 m_isLTROnly = false;
724
725 const CGGlyph* glyphs = complexTextRun.glyphs();
726 const FloatSize* advances = complexTextRun.baseAdvances();
727
728 float spaceWidth = font.spaceWidth() - font.syntheticBoldOffset();
729 const UChar* cp = complexTextRun.characters();
730 FloatPoint glyphOrigin;
731 unsigned lastCharacterIndex = m_run.ltr() ? std::numeric_limits<unsigned>::min() : std::numeric_limits<unsigned>::max();
732 bool isMonotonic = true;
733
734 for (unsigned i = 0; i < glyphCount; i++) {
735 unsigned characterIndex = complexTextRun.indexAt(i);
736 if (m_run.ltr()) {
737 if (characterIndex < lastCharacterIndex)
738 isMonotonic = false;
739 } else {
740 if (characterIndex > lastCharacterIndex)
741 isMonotonic = false;
742 }
743 UChar ch = *(cp + characterIndex);
744
745 bool treatAsSpace = FontCascade::treatAsSpace(ch);
746 CGGlyph glyph = treatAsSpace ? font.spaceGlyph() : glyphs[i];
747 FloatSize advance = treatAsSpace ? FloatSize(spaceWidth, advances[i].height()) : advances[i];
748
749 if (ch == '\t' && m_run.allowTabs())
750 advance.setWidth(m_font.tabWidth(font, m_run.tabSize(), m_run.xPos() + m_totalWidth));
751 else if (FontCascade::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
752 advance.setWidth(0);
753 glyph = font.spaceGlyph();
754 }
755
756 if (!i) {
757 advance.expand(complexTextRun.initialAdvance().width(), complexTextRun.initialAdvance().height());
758 if (auto* origins = complexTextRun.glyphOrigins())
759 advance.expand(-origins[0].x(), -origins[0].y());
760 }
761
762 advance.expand(font.syntheticBoldOffset(), 0);
763
764 if (hasExtraSpacing) {
765 // If we're a glyph with an advance, add in letter-spacing.
766 // That way we weed out zero width lurkers. This behavior matches the fast text code path.
767 if (advance.width())
768 advance.expand(m_font.letterSpacing(), 0);
769
770 unsigned characterIndexInRun = characterIndex + complexTextRun.stringLocation();
771 bool isFirstCharacter = !(characterIndex + complexTextRun.stringLocation());
772 bool isLastCharacter = characterIndexInRun + 1 == m_run.length() || (U16_IS_LEAD(ch) && characterIndexInRun + 2 == m_run.length() && U16_IS_TRAIL(*(cp + characterIndex + 1)));
773
774 bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
775 bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
776 bool forbidLeadingExpansion = false;
777 bool forbidTrailingExpansion = false;
778 if (runForcesLeadingExpansion)
779 forceLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter;
780 if (runForcesTrailingExpansion)
781 forceTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter;
782 if (runForbidsLeadingExpansion)
783 forbidLeadingExpansion = m_run.ltr() ? isFirstCharacter : isLastCharacter;
784 if (runForbidsTrailingExpansion)
785 forbidTrailingExpansion = m_run.ltr() ? isLastCharacter : isFirstCharacter;
786 // Handle justification and word-spacing.
787 bool ideograph = FontCascade::isCJKIdeographOrSymbol(ch);
788 if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
789 // Distribute the run's total expansion evenly over all expansion opportunities in the run.
790 if (m_expansion) {
791 bool expandLeft, expandRight;
792 std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), afterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
793 if (expandLeft) {
794 m_expansion -= m_expansionPerOpportunity;
795 // Increase previous width
796 if (m_adjustedBaseAdvances.isEmpty()) {
797 advance.expand(m_expansionPerOpportunity, 0);
798 complexTextRun.growInitialAdvanceHorizontally(m_expansionPerOpportunity);
799 } else {
800 m_adjustedBaseAdvances.last().expand(m_expansionPerOpportunity, 0);
801 m_totalWidth += m_expansionPerOpportunity;
802 }
803 }
804 if (expandRight) {
805 m_expansion -= m_expansionPerOpportunity;
806 advance.expand(m_expansionPerOpportunity, 0);
807 afterExpansion = true;
808 }
809 } else
810 afterExpansion = false;
811
812 // Account for word-spacing.
813 if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || runIndex > 0 || ch == noBreakSpace) && m_font.wordSpacing())
814 advance.expand(m_font.wordSpacing(), 0);
815 } else
816 afterExpansion = false;
817 }
818
819 m_totalWidth += advance.width();
820
821 // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
822 if (m_forTextEmphasis && (!FontCascade::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
823 glyph = 0;
824
825 m_adjustedBaseAdvances.append(advance);
826 if (auto* origins = complexTextRun.glyphOrigins()) {
827 ASSERT(m_glyphOrigins.size() < m_adjustedBaseAdvances.size());
828 m_glyphOrigins.grow(m_adjustedBaseAdvances.size());
829 m_glyphOrigins[m_glyphOrigins.size() - 1] = origins[i];
830 ASSERT(m_glyphOrigins.size() == m_adjustedBaseAdvances.size());
831 }
832 m_adjustedGlyphs.append(glyph);
833
834 FloatRect glyphBounds = font.boundsForGlyph(glyph);
835 glyphBounds.move(glyphOrigin.x(), glyphOrigin.y());
836 m_minGlyphBoundingBoxX = std::min(m_minGlyphBoundingBoxX, glyphBounds.x());
837 m_maxGlyphBoundingBoxX = std::max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
838 m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, glyphBounds.y());
839 m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
840 glyphOrigin.move(advance);
841
842 lastCharacterIndex = characterIndex;
843 }
844 if (!isMonotonic)
845 complexTextRun.setIsNonMonotonic();
846 }
847}
848
849// Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on
850// glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path.
851ComplexTextController::ComplexTextRun::ComplexTextRun(const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
852 : m_font(font)
853 , m_characters(characters)
854 , m_stringLength(stringLength)
855 , m_indexBegin(indexBegin)
856 , m_indexEnd(indexEnd)
857 , m_stringLocation(stringLocation)
858 , m_isLTR(ltr)
859{
860 auto runLengthInCodeUnits = m_indexEnd - m_indexBegin;
861 m_coreTextIndices.reserveInitialCapacity(runLengthInCodeUnits);
862 unsigned r = m_indexBegin;
863 while (r < m_indexEnd) {
864 m_coreTextIndices.uncheckedAppend(r);
865 UChar32 character;
866 U16_NEXT(m_characters, r, m_stringLength, character);
867 }
868 m_glyphCount = m_coreTextIndices.size();
869 if (!ltr) {
870 for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end)
871 std::swap(m_coreTextIndices[r], m_coreTextIndices[end]);
872 }
873
874 // Synthesize a run of missing glyphs.
875 m_glyphs.fill(0, m_glyphCount);
876 m_baseAdvances.fill(FloatSize(m_font.widthForGlyph(0), 0), m_glyphCount);
877}
878
879ComplexTextController::ComplexTextRun::ComplexTextRun(const Vector<FloatSize>& advances, const Vector<FloatPoint>& origins, const Vector<Glyph>& glyphs, const Vector<unsigned>& stringIndices, FloatSize initialAdvance, const Font& font, const UChar* characters, unsigned stringLocation, unsigned stringLength, unsigned indexBegin, unsigned indexEnd, bool ltr)
880 : m_baseAdvances(advances)
881 , m_glyphOrigins(origins)
882 , m_glyphs(glyphs)
883 , m_coreTextIndices(stringIndices)
884 , m_initialAdvance(initialAdvance)
885 , m_font(font)
886 , m_characters(characters)
887 , m_stringLength(stringLength)
888 , m_indexBegin(indexBegin)
889 , m_indexEnd(indexEnd)
890 , m_glyphCount(glyphs.size())
891 , m_stringLocation(stringLocation)
892 , m_isLTR(ltr)
893{
894}
895
896#endif
897
898} // namespace WebCore
899