1cb93a386Sopenharmony_ci// Copyright 2021 Google LLC.
2cb93a386Sopenharmony_ci#include "experimental/sktext/include/Text.h"
3cb93a386Sopenharmony_ci#include "experimental/sktext/src/LogicalRun.h"
4cb93a386Sopenharmony_ci#include "experimental/sktext/src/VisualRun.h"
5cb93a386Sopenharmony_ci#include <memory>
6cb93a386Sopenharmony_ci#include <stack>
7cb93a386Sopenharmony_cinamespace skia {
8cb93a386Sopenharmony_cinamespace text {
9cb93a386Sopenharmony_ciUnicodeText::UnicodeText(std::unique_ptr<SkUnicode> unicode, SkSpan<uint16_t> utf16)
10cb93a386Sopenharmony_ci    : fText16(std::u16string((char16_t*)utf16.data(), utf16.size()))
11cb93a386Sopenharmony_ci    , fUnicode(std::move(unicode)) {
12cb93a386Sopenharmony_ci    initialize(utf16);
13cb93a386Sopenharmony_ci}
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ciUnicodeText::UnicodeText(std::unique_ptr<SkUnicode> unicode, const SkString& utf8)
16cb93a386Sopenharmony_ci    : fUnicode(std::move(unicode)) {
17cb93a386Sopenharmony_ci    fText16 = fUnicode->convertUtf8ToUtf16(utf8);
18cb93a386Sopenharmony_ci    initialize(SkSpan<uint16_t>((uint16_t*)fText16.data(), fText16.size()));
19cb93a386Sopenharmony_ci}
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_cibool UnicodeText::isWhitespaces(TextRange range) const {
22cb93a386Sopenharmony_ci    for (auto i = range.fStart; i < range.fEnd; ++i) {
23cb93a386Sopenharmony_ci        if (!this->hasProperty(i, CodeUnitFlags::kPartOfWhiteSpace)) {
24cb93a386Sopenharmony_ci            return false;
25cb93a386Sopenharmony_ci        }
26cb93a386Sopenharmony_ci    }
27cb93a386Sopenharmony_ci    return true;
28cb93a386Sopenharmony_ci}
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_civoid UnicodeText::initialize(SkSpan<uint16_t> utf16) {
31cb93a386Sopenharmony_ci    if (!fUnicode) {
32cb93a386Sopenharmony_ci        SkASSERT(fUnicode);
33cb93a386Sopenharmony_ci        return;
34cb93a386Sopenharmony_ci    }
35cb93a386Sopenharmony_ci    // Get white spaces
36cb93a386Sopenharmony_ci    fCodeUnitProperties.push_back_n(utf16.size() + 1, CodeUnitFlags::kNoCodeUnitFlag);
37cb93a386Sopenharmony_ci    this->fUnicode->forEachCodepoint((char16_t*)utf16.data(), utf16.size(),
38cb93a386Sopenharmony_ci       [this](SkUnichar unichar, int32_t start, int32_t end) {
39cb93a386Sopenharmony_ci            if (this->fUnicode->isWhitespace(unichar)) {
40cb93a386Sopenharmony_ci                for (auto i = start; i < end; ++i) {
41cb93a386Sopenharmony_ci                    fCodeUnitProperties[i] |=  CodeUnitFlags::kPartOfWhiteSpace;
42cb93a386Sopenharmony_ci                }
43cb93a386Sopenharmony_ci            }
44cb93a386Sopenharmony_ci       });
45cb93a386Sopenharmony_ci    // Get graphemes
46cb93a386Sopenharmony_ci    this->fUnicode->forEachBreak((char16_t*)utf16.data(), utf16.size(), SkUnicode::BreakType::kGraphemes,
47cb93a386Sopenharmony_ci                           [this](SkBreakIterator::Position pos, SkBreakIterator::Status) {
48cb93a386Sopenharmony_ci                                fCodeUnitProperties[pos]|= CodeUnitFlags::kGraphemeStart;
49cb93a386Sopenharmony_ci                            });
50cb93a386Sopenharmony_ci    // Get line breaks
51cb93a386Sopenharmony_ci    this->fUnicode->forEachBreak((char16_t*)utf16.data(), utf16.size(), SkUnicode::BreakType::kLines,
52cb93a386Sopenharmony_ci                           [this](SkBreakIterator::Position pos, SkBreakIterator::Status status) {
53cb93a386Sopenharmony_ci                                if (status == (SkBreakIterator::Status)SkUnicode::LineBreakType::kHardLineBreak) {
54cb93a386Sopenharmony_ci                                    // Hard line breaks clears off all the other flags
55cb93a386Sopenharmony_ci                                    // TODO: Treat \n as a formatting mark and do not pass it to SkShaper
56cb93a386Sopenharmony_ci                                    fCodeUnitProperties[pos - 1] = CodeUnitFlags::kHardLineBreakBefore;
57cb93a386Sopenharmony_ci                                } else {
58cb93a386Sopenharmony_ci                                    fCodeUnitProperties[pos] |= CodeUnitFlags::kSoftLineBreakBefore;
59cb93a386Sopenharmony_ci                                }
60cb93a386Sopenharmony_ci                            });
61cb93a386Sopenharmony_ci}
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_cistd::unique_ptr<FontResolvedText> UnicodeText::resolveFonts(SkSpan<FontBlock> blocks) {
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci    auto fontResolvedText = std::make_unique<FontResolvedText>();
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci    TextRange adjustedBlock(0, 0);
68cb93a386Sopenharmony_ci    TextIndex index = 0;
69cb93a386Sopenharmony_ci    for (auto& block : blocks) {
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci        index += block.charCount;
72cb93a386Sopenharmony_ci        adjustedBlock.fStart = adjustedBlock.fEnd;
73cb93a386Sopenharmony_ci        adjustedBlock.fEnd = index;
74cb93a386Sopenharmony_ci        if (adjustedBlock.fStart >= adjustedBlock.fEnd) {
75cb93a386Sopenharmony_ci            // The last block adjustment went over the entire block
76cb93a386Sopenharmony_ci            continue;
77cb93a386Sopenharmony_ci        }
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci        // Move the end of the block to the right until it's on the grapheme edge
80cb93a386Sopenharmony_ci        while (adjustedBlock.fEnd < this->fText16.size() &&  !this->hasProperty(adjustedBlock.fEnd, CodeUnitFlags::kGraphemeStart)) {
81cb93a386Sopenharmony_ci            ++adjustedBlock.fEnd;
82cb93a386Sopenharmony_ci        }
83cb93a386Sopenharmony_ci        SkASSERT(block.type == BlockType::kFontChain);
84cb93a386Sopenharmony_ci        fontResolvedText->resolveChain(this, adjustedBlock, *block.chain);
85cb93a386Sopenharmony_ci    }
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci    std::sort(fontResolvedText->fResolvedFonts.begin(), fontResolvedText->fResolvedFonts.end(),
88cb93a386Sopenharmony_ci              [](const ResolvedFontBlock& a, const ResolvedFontBlock& b) {
89cb93a386Sopenharmony_ci                return a.textRange.fStart < b.textRange.fStart;
90cb93a386Sopenharmony_ci              });
91cb93a386Sopenharmony_ci/*
92cb93a386Sopenharmony_ci    SkDebugf("Resolved:\n");
93cb93a386Sopenharmony_ci    for (auto& f : fontResolvedText->fResolvedFonts) {
94cb93a386Sopenharmony_ci        SkDebugf("[%d:%d)\n", f.textRange.fStart, f.textRange.fEnd);
95cb93a386Sopenharmony_ci    }
96cb93a386Sopenharmony_ci*/
97cb93a386Sopenharmony_ci    return std::move(fontResolvedText);
98cb93a386Sopenharmony_ci}
99cb93a386Sopenharmony_ci
100cb93a386Sopenharmony_cibool FontResolvedText::resolveChain(UnicodeText* unicodeText, TextRange textRange, const FontChain& fontChain) {
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ci    std::deque<TextRange> unresolvedTexts;
103cb93a386Sopenharmony_ci    unresolvedTexts.push_back(textRange);
104cb93a386Sopenharmony_ci    for (auto fontIndex = 0; fontIndex < fontChain.count(); ++fontIndex) {
105cb93a386Sopenharmony_ci        auto typeface = fontChain[fontIndex];
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci        std::deque<TextRange> newUnresolvedTexts;
108cb93a386Sopenharmony_ci        // Check all text range that have not been resolved yet
109cb93a386Sopenharmony_ci        while (!unresolvedTexts.empty()) {
110cb93a386Sopenharmony_ci            // Take the first unresolved
111cb93a386Sopenharmony_ci            auto unresolvedText = unresolvedTexts.front();
112cb93a386Sopenharmony_ci            unresolvedTexts.pop_front();
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci            // Resolve font for the entire grapheme
115cb93a386Sopenharmony_ci            auto start = newUnresolvedTexts.size();
116cb93a386Sopenharmony_ci            unicodeText->forEachGrapheme(unresolvedText, [&](TextRange grapheme) {
117cb93a386Sopenharmony_ci                auto count = typeface->textToGlyphs(unicodeText->getText16().data() + grapheme.fStart, grapheme.width() * 2, SkTextEncoding::kUTF16, nullptr, 0);
118cb93a386Sopenharmony_ci                SkAutoTArray<SkGlyphID> glyphs(count);
119cb93a386Sopenharmony_ci                typeface->textToGlyphs(unicodeText->getText16().data() + grapheme.fStart, grapheme.width() * 2, SkTextEncoding::kUTF16, glyphs.data(), count);
120cb93a386Sopenharmony_ci                for (auto i = 0; i < count; ++i) {
121cb93a386Sopenharmony_ci                    if (glyphs[i] == 0) {
122cb93a386Sopenharmony_ci                        if (newUnresolvedTexts.empty() || newUnresolvedTexts.back().fEnd < grapheme.fStart) {
123cb93a386Sopenharmony_ci                            // It's a new unresolved block
124cb93a386Sopenharmony_ci                            newUnresolvedTexts.push_back(grapheme);
125cb93a386Sopenharmony_ci                        } else {
126cb93a386Sopenharmony_ci                            // Let's extend the last unresolved block
127cb93a386Sopenharmony_ci                            newUnresolvedTexts.back().fEnd = grapheme.fEnd;
128cb93a386Sopenharmony_ci                        }
129cb93a386Sopenharmony_ci                        break;
130cb93a386Sopenharmony_ci                    }
131cb93a386Sopenharmony_ci                }
132cb93a386Sopenharmony_ci            });
133cb93a386Sopenharmony_ci            // Let's fill the resolved blocks with the current font
134cb93a386Sopenharmony_ci            TextRange resolvedText(unresolvedText.fStart, unresolvedText.fStart);
135cb93a386Sopenharmony_ci            for (auto newUnresolvedText : newUnresolvedTexts) {
136cb93a386Sopenharmony_ci                if (start > 0) {
137cb93a386Sopenharmony_ci                    --start;
138cb93a386Sopenharmony_ci                    continue;
139cb93a386Sopenharmony_ci                }
140cb93a386Sopenharmony_ci                resolvedText.fEnd = newUnresolvedText.fStart;
141cb93a386Sopenharmony_ci                if (resolvedText.width() > 0) {
142cb93a386Sopenharmony_ci                    // Add another resolved block
143cb93a386Sopenharmony_ci                    fResolvedFonts.emplace_back(resolvedText, typeface, fontChain.fontSize(), SkFontStyle::Normal());
144cb93a386Sopenharmony_ci                }
145cb93a386Sopenharmony_ci                resolvedText.fStart = newUnresolvedText.fEnd;
146cb93a386Sopenharmony_ci            }
147cb93a386Sopenharmony_ci            resolvedText.fEnd = unresolvedText.fEnd;
148cb93a386Sopenharmony_ci            if (resolvedText.width() > 0) {
149cb93a386Sopenharmony_ci                // Add the last resolved block
150cb93a386Sopenharmony_ci                fResolvedFonts.emplace_back(resolvedText, typeface, fontChain.fontSize(), SkFontStyle::Normal());
151cb93a386Sopenharmony_ci            }
152cb93a386Sopenharmony_ci        }
153cb93a386Sopenharmony_ci
154cb93a386Sopenharmony_ci        // Try the next font in chain
155cb93a386Sopenharmony_ci        SkASSERT(unresolvedTexts.empty());
156cb93a386Sopenharmony_ci        unresolvedTexts = std::move(newUnresolvedTexts);
157cb93a386Sopenharmony_ci    }
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_ci    return unresolvedTexts.empty();
160cb93a386Sopenharmony_ci}
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci// Font iterator that finds all formatting marks
163cb93a386Sopenharmony_ci// and breaks runs on them (so we can select and interpret them later)
164cb93a386Sopenharmony_ciclass FormattingFontIterator final : public SkShaper::FontRunIterator {
165cb93a386Sopenharmony_cipublic:
166cb93a386Sopenharmony_ci    FormattingFontIterator(TextIndex textCount,
167cb93a386Sopenharmony_ci                           SkSpan<ResolvedFontBlock> fontBlocks,
168cb93a386Sopenharmony_ci                           SkSpan<TextIndex> marks)
169cb93a386Sopenharmony_ci            : fTextCount(textCount)
170cb93a386Sopenharmony_ci            , fFontBlocks(fontBlocks)
171cb93a386Sopenharmony_ci            , fFormattingMarks(marks)
172cb93a386Sopenharmony_ci            , fCurrentBlock(fontBlocks.begin())
173cb93a386Sopenharmony_ci            , fCurrentMark(marks.begin())
174cb93a386Sopenharmony_ci            , fCurrentFontIndex(fCurrentBlock->textRange.fEnd) {
175cb93a386Sopenharmony_ci        fCurrentFont = this->createFont(*fCurrentBlock);
176cb93a386Sopenharmony_ci    }
177cb93a386Sopenharmony_ci    void consume() override {
178cb93a386Sopenharmony_ci        SkASSERT(fCurrentBlock < fFontBlocks.end());
179cb93a386Sopenharmony_ci        SkASSERT(fCurrentMark < fFormattingMarks.end());
180cb93a386Sopenharmony_ci        if (fCurrentFontIndex > *fCurrentMark) {
181cb93a386Sopenharmony_ci            ++fCurrentMark;
182cb93a386Sopenharmony_ci            return;
183cb93a386Sopenharmony_ci        }
184cb93a386Sopenharmony_ci        if (fCurrentFontIndex == *fCurrentMark) {
185cb93a386Sopenharmony_ci            ++fCurrentMark;
186cb93a386Sopenharmony_ci        }
187cb93a386Sopenharmony_ci        ++fCurrentBlock;
188cb93a386Sopenharmony_ci        if (fCurrentBlock < fFontBlocks.end()) {
189cb93a386Sopenharmony_ci            fCurrentFontIndex = fCurrentBlock->textRange.fEnd;
190cb93a386Sopenharmony_ci            fCurrentFont = this->createFont(*fCurrentBlock);
191cb93a386Sopenharmony_ci        }
192cb93a386Sopenharmony_ci    }
193cb93a386Sopenharmony_ci    size_t endOfCurrentRun() const override {
194cb93a386Sopenharmony_ci        SkASSERT(fCurrentMark != fFormattingMarks.end() || fCurrentBlock != fFontBlocks.end());
195cb93a386Sopenharmony_ci        if (fCurrentMark == fFormattingMarks.end()) {
196cb93a386Sopenharmony_ci            return fCurrentFontIndex;
197cb93a386Sopenharmony_ci        } else if (fCurrentBlock == fFontBlocks.end()) {
198cb93a386Sopenharmony_ci            return *fCurrentMark;
199cb93a386Sopenharmony_ci        } else {
200cb93a386Sopenharmony_ci            return std::min(fCurrentFontIndex, *fCurrentMark);
201cb93a386Sopenharmony_ci        }
202cb93a386Sopenharmony_ci    }
203cb93a386Sopenharmony_ci    bool atEnd() const override {
204cb93a386Sopenharmony_ci        return (fCurrentBlock == fFontBlocks.end() || fCurrentFontIndex == fTextCount) &&
205cb93a386Sopenharmony_ci               (fCurrentMark == fFormattingMarks.end() || *fCurrentMark == fTextCount);
206cb93a386Sopenharmony_ci    }
207cb93a386Sopenharmony_ci    const SkFont& currentFont() const override { return fCurrentFont; }
208cb93a386Sopenharmony_ci    SkFont createFont(const ResolvedFontBlock& resolvedFont) const {
209cb93a386Sopenharmony_ci        SkFont font(resolvedFont.typeface, resolvedFont.size);
210cb93a386Sopenharmony_ci        font.setEdging(SkFont::Edging::kAntiAlias);
211cb93a386Sopenharmony_ci        font.setHinting(SkFontHinting::kSlight);
212cb93a386Sopenharmony_ci        font.setSubpixel(true);
213cb93a386Sopenharmony_ci        return font;
214cb93a386Sopenharmony_ci    }
215cb93a386Sopenharmony_ciprivate:
216cb93a386Sopenharmony_ci    TextIndex const fTextCount;
217cb93a386Sopenharmony_ci    SkSpan<ResolvedFontBlock> fFontBlocks;
218cb93a386Sopenharmony_ci    SkSpan<TextIndex> fFormattingMarks;
219cb93a386Sopenharmony_ci    ResolvedFontBlock* fCurrentBlock;
220cb93a386Sopenharmony_ci    TextIndex* fCurrentMark;
221cb93a386Sopenharmony_ci    TextIndex fCurrentFontIndex;
222cb93a386Sopenharmony_ci    SkFont fCurrentFont;
223cb93a386Sopenharmony_ci};
224cb93a386Sopenharmony_ci
225cb93a386Sopenharmony_cistd::unique_ptr<ShapedText> FontResolvedText::shape(UnicodeText* unicodeText,
226cb93a386Sopenharmony_ci                                                    TextDirection textDirection) {
227cb93a386Sopenharmony_ci    // Get utf8 <-> utf16 conversion tables.
228cb93a386Sopenharmony_ci    // We need to pass to SkShaper indices in utf8 and then convert them back to utf16 for SkText
229cb93a386Sopenharmony_ci    auto text16 = unicodeText->getText16();
230cb93a386Sopenharmony_ci    auto text8 = SkUnicode::convertUtf16ToUtf8(std::u16string(text16.data(), text16.size()));
231cb93a386Sopenharmony_ci    size_t utf16Index = 0;
232cb93a386Sopenharmony_ci    SkTArray<size_t, true> UTF16FromUTF8;
233cb93a386Sopenharmony_ci    SkTArray<size_t, true> UTF8FromUTF16;
234cb93a386Sopenharmony_ci    UTF16FromUTF8.push_back_n(text8.size() + 1, utf16Index);
235cb93a386Sopenharmony_ci    UTF8FromUTF16.push_back_n(text16.size() + 1, utf16Index);
236cb93a386Sopenharmony_ci    unicodeText->getUnicode()->forEachCodepoint(text8.c_str(), text8.size(),
237cb93a386Sopenharmony_ci    [&](SkUnichar unichar, int32_t start, int32_t end, int32_t count) {
238cb93a386Sopenharmony_ci        // utf8 index group of 1, 2 or 3 can be represented with one utf16 index group
239cb93a386Sopenharmony_ci        for (auto i = start; i < end; ++i) {
240cb93a386Sopenharmony_ci            UTF16FromUTF8[i] = utf16Index;
241cb93a386Sopenharmony_ci        }
242cb93a386Sopenharmony_ci        // utf16 index group of 1 or 2  can refer to the same group of utf8 indices
243cb93a386Sopenharmony_ci        for (; count != 0; --count) {
244cb93a386Sopenharmony_ci            UTF8FromUTF16[utf16Index++] = start;
245cb93a386Sopenharmony_ci        }
246cb93a386Sopenharmony_ci    });
247cb93a386Sopenharmony_ci    UTF16FromUTF8[text8.size()] = text16.size();
248cb93a386Sopenharmony_ci    UTF8FromUTF16[text16.size()] = text8.size();
249cb93a386Sopenharmony_ci    // Break text into pieces by font blocks and by formatting marks
250cb93a386Sopenharmony_ci    // Formatting marks: \n (and possibly some other later)
251cb93a386Sopenharmony_ci    std::vector<size_t> formattingMarks;
252cb93a386Sopenharmony_ci    for (size_t i = 0; i < text16.size(); ++i) {
253cb93a386Sopenharmony_ci        if (unicodeText->isHardLineBreak(i)) {
254cb93a386Sopenharmony_ci            formattingMarks.emplace_back(UTF8FromUTF16[i]);
255cb93a386Sopenharmony_ci            formattingMarks.emplace_back(UTF8FromUTF16[i + 1]);
256cb93a386Sopenharmony_ci            ++i;
257cb93a386Sopenharmony_ci        }
258cb93a386Sopenharmony_ci    }
259cb93a386Sopenharmony_ci    formattingMarks.emplace_back(text8.size()/* UTF8FromUTF16[text16.size() */);
260cb93a386Sopenharmony_ci    // Convert fontBlocks from utf16 to utf8
261cb93a386Sopenharmony_ci    SkTArray<ResolvedFontBlock, true> fontBlocks8;
262cb93a386Sopenharmony_ci    TextIndex index8 = 0;
263cb93a386Sopenharmony_ci    for (auto& fb : fResolvedFonts) {
264cb93a386Sopenharmony_ci        TextRange text8(UTF8FromUTF16[fb.textRange.fStart], UTF8FromUTF16[fb.textRange.fEnd]);
265cb93a386Sopenharmony_ci        fontBlocks8.emplace_back(text8, fb.typeface, fb.size, fb.style);
266cb93a386Sopenharmony_ci    }
267cb93a386Sopenharmony_ci    auto shapedText = std::make_unique<ShapedText>();
268cb93a386Sopenharmony_ci    // Shape the text
269cb93a386Sopenharmony_ci    FormattingFontIterator fontIter(text8.size(),
270cb93a386Sopenharmony_ci                                    SkSpan<ResolvedFontBlock>(fontBlocks8.data(), fontBlocks8.size()),
271cb93a386Sopenharmony_ci                                    SkSpan<TextIndex>(&formattingMarks[0], formattingMarks.size()));
272cb93a386Sopenharmony_ci    SkShaper::TrivialLanguageRunIterator langIter(text8.c_str(), text8.size());
273cb93a386Sopenharmony_ci    std::unique_ptr<SkShaper::BiDiRunIterator> bidiIter(
274cb93a386Sopenharmony_ci        SkShaper::MakeSkUnicodeBidiRunIterator(
275cb93a386Sopenharmony_ci            unicodeText->getUnicode(), text8.c_str(), text8.size(), textDirection == TextDirection::kLtr ? 0 : 1));
276cb93a386Sopenharmony_ci    std::unique_ptr<SkShaper::ScriptRunIterator> scriptIter(
277cb93a386Sopenharmony_ci        SkShaper::MakeSkUnicodeHbScriptRunIterator(unicodeText->getUnicode(), text8.c_str(), text8.size()));
278cb93a386Sopenharmony_ci    auto shaper = SkShaper::MakeShapeDontWrapOrReorder();
279cb93a386Sopenharmony_ci    if (shaper == nullptr) {
280cb93a386Sopenharmony_ci        // For instance, loadICU does not work. We have to stop the process
281cb93a386Sopenharmony_ci        return nullptr;
282cb93a386Sopenharmony_ci    }
283cb93a386Sopenharmony_ci    shaper->shape(
284cb93a386Sopenharmony_ci            text8.c_str(), text8.size(),
285cb93a386Sopenharmony_ci            fontIter, *bidiIter, *scriptIter, langIter,
286cb93a386Sopenharmony_ci            std::numeric_limits<SkScalar>::max(), shapedText.get());
287cb93a386Sopenharmony_ci    if (shapedText->fLogicalRuns.empty()) {
288cb93a386Sopenharmony_ci        // Create a fake run for an empty text (to avoid all the checks)
289cb93a386Sopenharmony_ci        SkShaper::RunHandler::RunInfo emptyInfo {
290cb93a386Sopenharmony_ci            fontIter.createFont(fResolvedFonts.front()),
291cb93a386Sopenharmony_ci            0,
292cb93a386Sopenharmony_ci            SkVector::Make(0.0f, 0.0f),
293cb93a386Sopenharmony_ci            0,
294cb93a386Sopenharmony_ci            SkShaper::RunHandler::Range(0, 0)
295cb93a386Sopenharmony_ci        };
296cb93a386Sopenharmony_ci        shapedText->fLogicalRuns.emplace_back(emptyInfo, 0, 0.0f);
297cb93a386Sopenharmony_ci        shapedText->fLogicalRuns.front().commit();
298cb93a386Sopenharmony_ci    }
299cb93a386Sopenharmony_ci    // Fill out all code unit properties
300cb93a386Sopenharmony_ci    for (auto& logicalRun : shapedText->fLogicalRuns) {
301cb93a386Sopenharmony_ci        // Convert utf8 range into utf16 range
302cb93a386Sopenharmony_ci        logicalRun.convertUtf16Range([&](unsigned long index8) {
303cb93a386Sopenharmony_ci            return UTF16FromUTF8[index8];
304cb93a386Sopenharmony_ci        });
305cb93a386Sopenharmony_ci        // Convert all utf8 indexes into utf16 indexes (and also shift them to be on the entire text scale, too)
306cb93a386Sopenharmony_ci        logicalRun.convertClusterIndexes([&](TextIndex clusterIndex8) {
307cb93a386Sopenharmony_ci            return UTF16FromUTF8[clusterIndex8];
308cb93a386Sopenharmony_ci        });
309cb93a386Sopenharmony_ci        // Detect and mark line break runs
310cb93a386Sopenharmony_ci        if (logicalRun.getTextRange().width() == 1 &&
311cb93a386Sopenharmony_ci            logicalRun.size() == 1 &&
312cb93a386Sopenharmony_ci            unicodeText->isHardLineBreak(logicalRun.getTextRange().fStart)) {
313cb93a386Sopenharmony_ci            logicalRun.setRunType(LogicalRunType::kLineBreak);
314cb93a386Sopenharmony_ci        }
315cb93a386Sopenharmony_ci    }
316cb93a386Sopenharmony_ci    return std::move(shapedText);
317cb93a386Sopenharmony_ci}
318cb93a386Sopenharmony_ci
319cb93a386Sopenharmony_ci// TODO: Implement the vertical restriction (height) and add ellipsis
320cb93a386Sopenharmony_cistd::unique_ptr<WrappedText> ShapedText::wrap(UnicodeText* unicodeText, float width, float height) {
321cb93a386Sopenharmony_ci    auto wrappedText = std::unique_ptr<WrappedText>(new WrappedText());
322cb93a386Sopenharmony_ci    // line + spaces + clusters
323cb93a386Sopenharmony_ci    Stretch line;
324cb93a386Sopenharmony_ci    Stretch spaces;
325cb93a386Sopenharmony_ci    Stretch clusters;
326cb93a386Sopenharmony_ci    Stretch cluster;
327cb93a386Sopenharmony_ci    for (size_t runIndex = 0; runIndex < this->fLogicalRuns.size(); ++runIndex ) {
328cb93a386Sopenharmony_ci        auto& run = this->fLogicalRuns[runIndex];
329cb93a386Sopenharmony_ci        if (run.getRunType() == LogicalRunType::kLineBreak) {
330cb93a386Sopenharmony_ci            // This is the end of the word, the end of the line
331cb93a386Sopenharmony_ci            if (!clusters.isEmpty()) {
332cb93a386Sopenharmony_ci                line.moveTo(spaces);
333cb93a386Sopenharmony_ci                line.moveTo(clusters);
334cb93a386Sopenharmony_ci                spaces = clusters;
335cb93a386Sopenharmony_ci            }
336cb93a386Sopenharmony_ci            this->addLine(wrappedText.get(), unicodeText->getUnicode(), line, spaces, true);
337cb93a386Sopenharmony_ci            line = spaces;
338cb93a386Sopenharmony_ci            clusters = spaces;
339cb93a386Sopenharmony_ci            continue;
340cb93a386Sopenharmony_ci        }
341cb93a386Sopenharmony_ci        TextMetrics runMetrics(run.fFont);
342cb93a386Sopenharmony_ci
343cb93a386Sopenharmony_ci        // Let's wrap the text
344cb93a386Sopenharmony_ci        GlyphRange clusterGlyphs;
345cb93a386Sopenharmony_ci        DirTextRange clusterText(EMPTY_RANGE, run.leftToRight());
346cb93a386Sopenharmony_ci        for (size_t glyphIndex = 0; glyphIndex < run.fPositions.size(); ++glyphIndex) {
347cb93a386Sopenharmony_ci            auto textIndex = run.fClusters[glyphIndex];
348cb93a386Sopenharmony_ci
349cb93a386Sopenharmony_ci            if (clusterText == EMPTY_RANGE) {
350cb93a386Sopenharmony_ci                // The beginning of a new line (or the first one)
351cb93a386Sopenharmony_ci                clusterText = DirTextRange(textIndex, textIndex, run.leftToRight());
352cb93a386Sopenharmony_ci                clusterGlyphs = GlyphRange(glyphIndex, glyphIndex);
353cb93a386Sopenharmony_ci
354cb93a386Sopenharmony_ci                Stretch empty(GlyphPos(runIndex, glyphIndex), textIndex, runMetrics);
355cb93a386Sopenharmony_ci                line = empty;
356cb93a386Sopenharmony_ci                spaces = empty;
357cb93a386Sopenharmony_ci                clusters = empty;
358cb93a386Sopenharmony_ci                continue;
359cb93a386Sopenharmony_ci            }
360cb93a386Sopenharmony_ci
361cb93a386Sopenharmony_ci            if (textIndex == clusterText.fStart) {
362cb93a386Sopenharmony_ci                // Skip until the next cluster
363cb93a386Sopenharmony_ci                continue;
364cb93a386Sopenharmony_ci            }
365cb93a386Sopenharmony_ci
366cb93a386Sopenharmony_ci            // Finish the cluster (notice that it belongs to a single run)
367cb93a386Sopenharmony_ci            clusterText.fStart = clusterText.fEnd;
368cb93a386Sopenharmony_ci            clusterText.fEnd = textIndex;
369cb93a386Sopenharmony_ci            clusterGlyphs.fStart = clusterGlyphs.fEnd;
370cb93a386Sopenharmony_ci            clusterGlyphs.fEnd = glyphIndex;
371cb93a386Sopenharmony_ci            cluster = Stretch(runIndex, clusterGlyphs, clusterText.normalized(), run.calculateWidth(clusterGlyphs), runMetrics);
372cb93a386Sopenharmony_ci
373cb93a386Sopenharmony_ci            auto isSoftLineBreak = unicodeText->isSoftLineBreak(cluster.textStart());
374cb93a386Sopenharmony_ci            auto isWhitespaces = unicodeText->isWhitespaces(cluster.textRange());
375cb93a386Sopenharmony_ci            auto isEndOfText = run.leftToRight() ? textIndex == run.fUtf16Range.fEnd : textIndex == run.fUtf16Range.fStart;
376cb93a386Sopenharmony_ci            // line + spaces + clusters + cluster
377cb93a386Sopenharmony_ci            if (isWhitespaces) {
378cb93a386Sopenharmony_ci                // This is the end of the word
379cb93a386Sopenharmony_ci                if (!clusters.isEmpty()) {
380cb93a386Sopenharmony_ci                    line.moveTo(spaces);
381cb93a386Sopenharmony_ci                    line.moveTo(clusters);
382cb93a386Sopenharmony_ci                    spaces = clusters;
383cb93a386Sopenharmony_ci                }
384cb93a386Sopenharmony_ci                spaces.moveTo(cluster);
385cb93a386Sopenharmony_ci                clusters = cluster;
386cb93a386Sopenharmony_ci                // Whitespaces do not extend the line width so no wrapping
387cb93a386Sopenharmony_ci                continue;
388cb93a386Sopenharmony_ci            } else if (!SkScalarIsFinite(width)) {
389cb93a386Sopenharmony_ci                // No wrapping - the endless line
390cb93a386Sopenharmony_ci                clusters.moveTo(cluster);
391cb93a386Sopenharmony_ci                continue;
392cb93a386Sopenharmony_ci            }
393cb93a386Sopenharmony_ci            // Now let's find out if we can add the cluster to the line
394cb93a386Sopenharmony_ci            auto currentWidth = line.width() + spaces.width() + clusters.width() + cluster.width();
395cb93a386Sopenharmony_ci            if (currentWidth > width) {
396cb93a386Sopenharmony_ci                // Finally, the wrapping case
397cb93a386Sopenharmony_ci                if (line.isEmpty()) {
398cb93a386Sopenharmony_ci                    if (spaces.isEmpty() && clusters.isEmpty()) {
399cb93a386Sopenharmony_ci                        // There is only this cluster and it's too long; we are drawing it anyway
400cb93a386Sopenharmony_ci                        line.moveTo(cluster);
401cb93a386Sopenharmony_ci                    } else {
402cb93a386Sopenharmony_ci                        // We break the only one word on the line by this cluster
403cb93a386Sopenharmony_ci                        line.moveTo(clusters);
404cb93a386Sopenharmony_ci                    }
405cb93a386Sopenharmony_ci                } else {
406cb93a386Sopenharmony_ci                  // We move clusters + cluster on the next line
407cb93a386Sopenharmony_ci                  // TODO: Parametrise possible ways of breaking too long word
408cb93a386Sopenharmony_ci                  //  (start it from a new line or squeeze the part of it on this line)
409cb93a386Sopenharmony_ci                }
410cb93a386Sopenharmony_ci                this->addLine(wrappedText.get(), unicodeText->getUnicode(), line, spaces, false);
411cb93a386Sopenharmony_ci                line = spaces;
412cb93a386Sopenharmony_ci            }
413cb93a386Sopenharmony_ci            clusters.moveTo(cluster);
414cb93a386Sopenharmony_ci
415cb93a386Sopenharmony_ci            clusterGlyphs.fStart = clusterGlyphs.fEnd;
416cb93a386Sopenharmony_ci            clusterText.fStart = clusterText.fEnd;
417cb93a386Sopenharmony_ci        }
418cb93a386Sopenharmony_ci    }
419cb93a386Sopenharmony_ci
420cb93a386Sopenharmony_ci    // Deal with the last line
421cb93a386Sopenharmony_ci    if (!clusters.isEmpty()) {
422cb93a386Sopenharmony_ci        line.moveTo(spaces);
423cb93a386Sopenharmony_ci        line.moveTo(clusters);
424cb93a386Sopenharmony_ci        spaces = clusters;
425cb93a386Sopenharmony_ci    } else if (wrappedText->fVisualLines.empty()) {
426cb93a386Sopenharmony_ci        // Empty text; we still need a line to avoid checking for empty lines every time
427cb93a386Sopenharmony_ci        line.moveTo(cluster);
428cb93a386Sopenharmony_ci        spaces.moveTo(cluster);
429cb93a386Sopenharmony_ci    }
430cb93a386Sopenharmony_ci    this->addLine(wrappedText.get(), unicodeText->getUnicode(), line, spaces, false);
431cb93a386Sopenharmony_ci    wrappedText->fActualSize.fWidth = width;
432cb93a386Sopenharmony_ci    return std::move(wrappedText);
433cb93a386Sopenharmony_ci}
434cb93a386Sopenharmony_ci
435cb93a386Sopenharmony_ciSkTArray<int32_t> ShapedText::getVisualOrder(SkUnicode* unicode, RunIndex startRun, RunIndex endRun) {
436cb93a386Sopenharmony_ci    auto numRuns = endRun - startRun + 1;
437cb93a386Sopenharmony_ci    SkTArray<int32_t> results;
438cb93a386Sopenharmony_ci    results.push_back_n(numRuns);
439cb93a386Sopenharmony_ci    if (numRuns == 0) {
440cb93a386Sopenharmony_ci        return results;
441cb93a386Sopenharmony_ci    }
442cb93a386Sopenharmony_ci    SkTArray<SkUnicode::BidiLevel> runLevels;
443cb93a386Sopenharmony_ci    runLevels.push_back_n(numRuns);
444cb93a386Sopenharmony_ci    size_t runLevelsIndex = 0;
445cb93a386Sopenharmony_ci    for (RunIndex runIndex = startRun; runIndex <= endRun; ++runIndex) {
446cb93a386Sopenharmony_ci        runLevels[runLevelsIndex++] = fLogicalRuns[runIndex].bidiLevel();
447cb93a386Sopenharmony_ci    }
448cb93a386Sopenharmony_ci    SkASSERT(runLevelsIndex == numRuns);
449cb93a386Sopenharmony_ci    unicode->reorderVisual(runLevels.data(), numRuns, results.data());
450cb93a386Sopenharmony_ci    return results;
451cb93a386Sopenharmony_ci}
452cb93a386Sopenharmony_ci
453cb93a386Sopenharmony_ci// TODO: Fill line fOffset.fY
454cb93a386Sopenharmony_civoid ShapedText::addLine(WrappedText* wrappedText, SkUnicode* unicode, Stretch& stretch, Stretch& spaces, bool hardLineBreak) {
455cb93a386Sopenharmony_ci    auto spacesStart = spaces.glyphStart();
456cb93a386Sopenharmony_ci    Stretch lineStretch = stretch;
457cb93a386Sopenharmony_ci    lineStretch.moveTo(spaces);
458cb93a386Sopenharmony_ci    auto startRun = lineStretch.glyphStart().runIndex();
459cb93a386Sopenharmony_ci    auto endRun = lineStretch.glyphEnd().runIndex();
460cb93a386Sopenharmony_ci    // Reorder and cut (if needed) runs so they fit the line
461cb93a386Sopenharmony_ci    auto visualOrder = std::move(this->getVisualOrder(unicode, startRun, endRun));
462cb93a386Sopenharmony_ci    // Walk through the line's runs in visual order
463cb93a386Sopenharmony_ci    auto firstRunIndex = startRun;
464cb93a386Sopenharmony_ci    auto runStart = wrappedText->fVisualRuns.size();
465cb93a386Sopenharmony_ci    SkScalar runOffsetInLine = 0.0f;
466cb93a386Sopenharmony_ci    for (auto visualIndex : visualOrder) {
467cb93a386Sopenharmony_ci        auto& logicalRun = fLogicalRuns[firstRunIndex + visualIndex];
468cb93a386Sopenharmony_ci        if (logicalRun.getRunType() == LogicalRunType::kLineBreak) {
469cb93a386Sopenharmony_ci            SkASSERT(false);
470cb93a386Sopenharmony_ci        }
471cb93a386Sopenharmony_ci        bool isFirstRun = startRun == (firstRunIndex + visualIndex);
472cb93a386Sopenharmony_ci        bool isLastRun = endRun == (firstRunIndex + visualIndex);
473cb93a386Sopenharmony_ci        bool isSpaceRun = spacesStart.runIndex() == (firstRunIndex + visualIndex);
474cb93a386Sopenharmony_ci        auto glyphStart = isFirstRun ? lineStretch.glyphStart().glyphIndex() : 0;
475cb93a386Sopenharmony_ci        auto glyphEnd = isLastRun ? lineStretch.glyphEnd().glyphIndex() : logicalRun.size();
476cb93a386Sopenharmony_ci        auto glyphSize = glyphEnd - glyphStart;
477cb93a386Sopenharmony_ci        auto glyphSpaces = isSpaceRun ? spacesStart.glyphIndex() : glyphEnd;
478cb93a386Sopenharmony_ci        if (glyphSpaces > glyphStart) {
479cb93a386Sopenharmony_ci            auto textStart = isFirstRun ? lineStretch.textRange().fStart : logicalRun.fUtf16Range.fStart;
480cb93a386Sopenharmony_ci            auto textEnd = isLastRun ? lineStretch.textRange().fEnd : logicalRun.fUtf16Range.fEnd;
481cb93a386Sopenharmony_ci            wrappedText->fVisualRuns.emplace_back(TextRange(textStart, textEnd),
482cb93a386Sopenharmony_ci                                                  glyphSpaces - glyphStart,
483cb93a386Sopenharmony_ci                                                  logicalRun.fFont,
484cb93a386Sopenharmony_ci                                                  lineStretch.textMetrics().baseline(),
485cb93a386Sopenharmony_ci                                                  SkPoint::Make(runOffsetInLine, wrappedText->fActualSize.fHeight),
486cb93a386Sopenharmony_ci                                                  logicalRun.leftToRight(),
487cb93a386Sopenharmony_ci                                                  SkSpan<SkPoint>(&logicalRun.fPositions[glyphStart], glyphSize + 1),
488cb93a386Sopenharmony_ci                                                  SkSpan<SkGlyphID>(&logicalRun.fGlyphs[glyphStart], glyphSize),
489cb93a386Sopenharmony_ci                                                  SkSpan<uint32_t>((uint32_t*)&logicalRun.fClusters[glyphStart], glyphSize + 1));
490cb93a386Sopenharmony_ci        }
491cb93a386Sopenharmony_ci        runOffsetInLine += logicalRun.calculateWidth(glyphStart, glyphEnd);
492cb93a386Sopenharmony_ci    }
493cb93a386Sopenharmony_ci    auto runRange = wrappedText->fVisualRuns.size() == runStart
494cb93a386Sopenharmony_ci                    ? SkSpan<VisualRun>(nullptr, 0)
495cb93a386Sopenharmony_ci                    : SkSpan<VisualRun>(&wrappedText->fVisualRuns[runStart], wrappedText->fVisualRuns.size() - runStart);
496cb93a386Sopenharmony_ci    wrappedText->fVisualLines.emplace_back(lineStretch.textRange(), hardLineBreak, wrappedText->fActualSize.fHeight, runRange);
497cb93a386Sopenharmony_ci    wrappedText->fActualSize.fHeight += lineStretch.textMetrics().height();
498cb93a386Sopenharmony_ci    wrappedText->fActualSize.fWidth = std::max(wrappedText->fActualSize.fWidth, lineStretch.width());
499cb93a386Sopenharmony_ci    stretch.clean();
500cb93a386Sopenharmony_ci    spaces.clean();
501cb93a386Sopenharmony_ci}
502cb93a386Sopenharmony_ci
503cb93a386Sopenharmony_civoid WrappedText::format(TextAlign textAlign, TextDirection textDirection) {
504cb93a386Sopenharmony_ci    if (fAligned == textAlign) {
505cb93a386Sopenharmony_ci        return;
506cb93a386Sopenharmony_ci    }
507cb93a386Sopenharmony_ci    SkScalar verticalOffset = 0.0f;
508cb93a386Sopenharmony_ci    for (auto& line : this->fVisualLines) {
509cb93a386Sopenharmony_ci        if (textAlign == TextAlign::kLeft) {
510cb93a386Sopenharmony_ci            // Good by default
511cb93a386Sopenharmony_ci        } else if (textAlign == TextAlign::kCenter) {
512cb93a386Sopenharmony_ci            line.fOffset.fX = (this->fActualSize.width() - line.fActualWidth) / 2.0f;
513cb93a386Sopenharmony_ci        } else {
514cb93a386Sopenharmony_ci            // TODO: Implement all formatting features
515cb93a386Sopenharmony_ci        }
516cb93a386Sopenharmony_ci        line.fOffset.fY = verticalOffset;
517cb93a386Sopenharmony_ci        verticalOffset += line.fTextMetrics.height();
518cb93a386Sopenharmony_ci    }
519cb93a386Sopenharmony_ci}
520cb93a386Sopenharmony_ci
521cb93a386Sopenharmony_civoid WrappedText::visit(Visitor* visitor) const {
522cb93a386Sopenharmony_ci    size_t lineIndex = 0;
523cb93a386Sopenharmony_ci    SkScalar verticalOffset = 0.0f;
524cb93a386Sopenharmony_ci    for (auto& line : fVisualLines) {
525cb93a386Sopenharmony_ci        visitor->onBeginLine(lineIndex, line.text(), line.isHardBreak(), SkRect::MakeXYWH(0, verticalOffset, line.fActualWidth, line.fTextMetrics.height()));
526cb93a386Sopenharmony_ci        // Select the runs that are on the line
527cb93a386Sopenharmony_ci        size_t glyphCount = 0ul;
528cb93a386Sopenharmony_ci        for (auto& run : line.fRuns) {
529cb93a386Sopenharmony_ci            auto diff = line.fTextMetrics.above() - run.fTextMetrics.above();
530cb93a386Sopenharmony_ci            SkRect boundingRect = SkRect::MakeXYWH(line.fOffset.fX + run.fPositions[0].fX, line.fOffset.fY + diff, run.width(), run.fTextMetrics.height());
531cb93a386Sopenharmony_ci            visitor->onGlyphRun(run.fFont, run.dirTextRange(), boundingRect, run.trailingSpacesStart(),
532cb93a386Sopenharmony_ci                                run.size(), run.fGlyphs.data(), run.fPositions.data(), run.fClusters.data());
533cb93a386Sopenharmony_ci            glyphCount += run.size();
534cb93a386Sopenharmony_ci        }
535cb93a386Sopenharmony_ci        visitor->onEndLine(lineIndex, line.text(), line.trailingSpaces(), glyphCount);
536cb93a386Sopenharmony_ci        verticalOffset += line.fTextMetrics.height();
537cb93a386Sopenharmony_ci        ++lineIndex;
538cb93a386Sopenharmony_ci    }
539cb93a386Sopenharmony_ci}
540cb93a386Sopenharmony_ci
541cb93a386Sopenharmony_cistd::vector<TextIndex> WrappedText::chunksToBlocks(SkSpan<size_t> chunks) {
542cb93a386Sopenharmony_ci    std::vector<TextIndex> blocks;
543cb93a386Sopenharmony_ci    blocks.reserve(chunks.size() + 1);
544cb93a386Sopenharmony_ci    TextIndex index = 0;
545cb93a386Sopenharmony_ci    for (auto chunk : chunks) {
546cb93a386Sopenharmony_ci        blocks.emplace_back(index);
547cb93a386Sopenharmony_ci        index += chunk;
548cb93a386Sopenharmony_ci    }
549cb93a386Sopenharmony_ci    blocks.emplace_back(index);
550cb93a386Sopenharmony_ci    return std::move(blocks);
551cb93a386Sopenharmony_ci}
552cb93a386Sopenharmony_ci
553cb93a386Sopenharmony_ciSkSpan<TextIndex> WrappedText::limitBlocks(TextRange textRange, SkSpan<TextIndex> blocks) {
554cb93a386Sopenharmony_ci    TextRange limited = EMPTY_RANGE;
555cb93a386Sopenharmony_ci    for (auto i = 0ul; i < blocks.size(); ++i) {
556cb93a386Sopenharmony_ci        auto block = blocks[i];
557cb93a386Sopenharmony_ci        if (textRange.fEnd < block) {
558cb93a386Sopenharmony_ci            continue;
559cb93a386Sopenharmony_ci        } else if (textRange.fStart >= block) {
560cb93a386Sopenharmony_ci            break;
561cb93a386Sopenharmony_ci        } else if (limited.fStart == EMPTY_INDEX) {
562cb93a386Sopenharmony_ci            limited.fStart = i;
563cb93a386Sopenharmony_ci        }
564cb93a386Sopenharmony_ci        limited.fEnd = i;
565cb93a386Sopenharmony_ci    }
566cb93a386Sopenharmony_ci
567cb93a386Sopenharmony_ci    return SkSpan<TextIndex>(&blocks[textRange.fStart], textRange.width());
568cb93a386Sopenharmony_ci}
569cb93a386Sopenharmony_ci
570cb93a386Sopenharmony_civoid WrappedText::visit(UnicodeText* unicodeText, Visitor* visitor, PositionType positionType, SkSpan<size_t> chunks) const {
571cb93a386Sopenharmony_ci    // Decor blocks have to be sorted by text cannot intersect but can skip some parts of the text
572cb93a386Sopenharmony_ci    // (in which case we use default text style from paragraph style)
573cb93a386Sopenharmony_ci    // The edges of the decor blocks don't have to match glyph, grapheme or even unicode code point edges
574cb93a386Sopenharmony_ci    // It's out responsibility to adjust them to some reasonable values
575cb93a386Sopenharmony_ci    // [a:b) -> [c:d) where
576cb93a386Sopenharmony_ci    // c is closest GG cluster edge to a from the left and d is closest GG cluster edge to b from the left
577cb93a386Sopenharmony_ci    auto textBlocks = WrappedText::chunksToBlocks(chunks);
578cb93a386Sopenharmony_ci    SkScalar verticalOffset = 0.0f;
579cb93a386Sopenharmony_ci    LineIndex lineIndex = 0ul;
580cb93a386Sopenharmony_ci    size_t glyphCount = 0ul;
581cb93a386Sopenharmony_ci    for (auto& line : fVisualLines) {
582cb93a386Sopenharmony_ci        visitor->onBeginLine(lineIndex, line.text(), line.isHardBreak(), SkRect::MakeXYWH(0, verticalOffset, line.fActualWidth, line.fTextMetrics.height()));
583cb93a386Sopenharmony_ci        RunIndex runIndex = 0ul;
584cb93a386Sopenharmony_ci        auto lineBlocks = WrappedText::limitBlocks(line.fText, SkSpan<TextIndex>(textBlocks.data(), textBlocks.size()));
585cb93a386Sopenharmony_ci        for (auto& run : fVisualRuns) {
586cb93a386Sopenharmony_ci            run.forEachTextBlockInGlyphRange(lineBlocks, [&](DirTextRange dirTextRange) {
587cb93a386Sopenharmony_ci                GlyphRange glyphRange = this->textToGlyphs(unicodeText, positionType, runIndex, dirTextRange);
588cb93a386Sopenharmony_ci                auto diff = line.fTextMetrics.above() - run.fTextMetrics.above();
589cb93a386Sopenharmony_ci                SkRect boundingRect =
590cb93a386Sopenharmony_ci                        SkRect::MakeXYWH(line.fOffset.fX + run.fPositions[glyphRange.fStart].fX,
591cb93a386Sopenharmony_ci                                         line.fOffset.fY + diff,
592cb93a386Sopenharmony_ci                                         run.calculateWidth(glyphRange),
593cb93a386Sopenharmony_ci                                         run.fTextMetrics.height());
594cb93a386Sopenharmony_ci                visitor->onGlyphRun(run.fFont,
595cb93a386Sopenharmony_ci                                    dirTextRange,
596cb93a386Sopenharmony_ci                                    boundingRect,
597cb93a386Sopenharmony_ci                                    run.trailingSpacesStart(),
598cb93a386Sopenharmony_ci                                    glyphRange.width(),
599cb93a386Sopenharmony_ci                                    &run.fGlyphs[glyphRange.fStart],
600cb93a386Sopenharmony_ci                                    &run.fPositions[glyphRange.fStart],
601cb93a386Sopenharmony_ci                                    &run.fClusters[glyphRange.fStart]);
602cb93a386Sopenharmony_ci            });
603cb93a386Sopenharmony_ci            ++runIndex;
604cb93a386Sopenharmony_ci            glyphCount += run.size();
605cb93a386Sopenharmony_ci        }
606cb93a386Sopenharmony_ci        visitor->onEndLine(lineIndex, line.text(), line.trailingSpaces(), glyphCount);
607cb93a386Sopenharmony_ci        verticalOffset += line.fTextMetrics.height();
608cb93a386Sopenharmony_ci        ++lineIndex;
609cb93a386Sopenharmony_ci    }
610cb93a386Sopenharmony_ci}
611cb93a386Sopenharmony_ci
612cb93a386Sopenharmony_ci// TODO: Implement more effective search
613cb93a386Sopenharmony_ciGlyphRange WrappedText::textToGlyphs(UnicodeText* unicodeText, PositionType positionType, RunIndex runIndex, DirTextRange dirTextRange) const {
614cb93a386Sopenharmony_ci    SkASSERT(runIndex < fVisualRuns.size());
615cb93a386Sopenharmony_ci    auto& run = fVisualRuns[runIndex];
616cb93a386Sopenharmony_ci    SkASSERT(run.fDirTextRange.contains(dirTextRange));
617cb93a386Sopenharmony_ci    GlyphRange glyphRange(0, run.size());
618cb93a386Sopenharmony_ci    for (GlyphIndex glyph = 0; glyph < run.size(); ++glyph) {
619cb93a386Sopenharmony_ci        auto textIndex = run.fClusters[glyph];
620cb93a386Sopenharmony_ci        if (positionType == PositionType::kGraphemeCluster && unicodeText->hasProperty(textIndex, CodeUnitFlags::kGraphemeStart)) {
621cb93a386Sopenharmony_ci            if (dirTextRange.after(textIndex)) {
622cb93a386Sopenharmony_ci                glyphRange.fStart = glyph;
623cb93a386Sopenharmony_ci            } else if (dirTextRange.before(textIndex)) {
624cb93a386Sopenharmony_ci                glyphRange.fEnd = glyph;
625cb93a386Sopenharmony_ci            } else {
626cb93a386Sopenharmony_ci                return glyphRange;
627cb93a386Sopenharmony_ci            }
628cb93a386Sopenharmony_ci        }
629cb93a386Sopenharmony_ci    }
630cb93a386Sopenharmony_ci    SkASSERT(false);
631cb93a386Sopenharmony_ci    return glyphRange;
632cb93a386Sopenharmony_ci}
633cb93a386Sopenharmony_ci
634cb93a386Sopenharmony_cistd::unique_ptr<SelectableText> WrappedText::prepareToEdit(UnicodeText* unicodeText) const {
635cb93a386Sopenharmony_ci    auto selectableText = std::make_unique<SelectableText>();
636cb93a386Sopenharmony_ci    this->visit(selectableText.get());
637cb93a386Sopenharmony_ci    selectableText->fGlyphUnitProperties.push_back_n(unicodeText->getText16().size() + 1, GlyphUnitFlags::kNoGlyphUnitFlag);
638cb93a386Sopenharmony_ci    for (auto index = 0; index < unicodeText->getText16().size(); ++index) {
639cb93a386Sopenharmony_ci        if (unicodeText->hasProperty(index, CodeUnitFlags::kHardLineBreakBefore)) {
640cb93a386Sopenharmony_ci            selectableText->fGlyphUnitProperties[index] = GlyphUnitFlags::kGraphemeClusterStart;
641cb93a386Sopenharmony_ci        }
642cb93a386Sopenharmony_ci    }
643cb93a386Sopenharmony_ci    for (const auto& run : fVisualRuns) {
644cb93a386Sopenharmony_ci        for (auto& cluster : run.fClusters) {
645cb93a386Sopenharmony_ci            if (unicodeText->hasProperty(cluster, CodeUnitFlags::kGraphemeStart)) {
646cb93a386Sopenharmony_ci                selectableText->fGlyphUnitProperties[cluster] = GlyphUnitFlags::kGraphemeClusterStart;
647cb93a386Sopenharmony_ci            }
648cb93a386Sopenharmony_ci        }
649cb93a386Sopenharmony_ci    }
650cb93a386Sopenharmony_ci    return selectableText;
651cb93a386Sopenharmony_ci}
652cb93a386Sopenharmony_ci
653cb93a386Sopenharmony_civoid SelectableText::onBeginLine(size_t index, TextRange lineText, bool hardBreak, SkRect bounds) {
654cb93a386Sopenharmony_ci    SkASSERT(fBoxLines.size() == index);
655cb93a386Sopenharmony_ci    fBoxLines.emplace_back(index, lineText, hardBreak, bounds);
656cb93a386Sopenharmony_ci}
657cb93a386Sopenharmony_ci
658cb93a386Sopenharmony_civoid SelectableText::onEndLine(size_t index, TextRange lineText, GlyphRange trailingSpaces, size_t glyphCount) {
659cb93a386Sopenharmony_ci    auto& line = fBoxLines.back();
660cb93a386Sopenharmony_ci    line.fTextEnd = trailingSpaces.fStart;
661cb93a386Sopenharmony_ci    line.fTrailingSpacesEnd = trailingSpaces.fEnd;
662cb93a386Sopenharmony_ci    SkASSERT(line.fTextByGlyph.size() == glyphCount);
663cb93a386Sopenharmony_ci    line.fBoxGlyphs.emplace_back(SkRect::MakeXYWH(line.fBounds.fRight, line.fBounds.fTop, 0.0f, line.fBounds.height()));
664cb93a386Sopenharmony_ci    if (line.fTextByGlyph.empty()) {
665cb93a386Sopenharmony_ci        // Let's create an empty fake box to avoid all the checks
666cb93a386Sopenharmony_ci        line.fTextByGlyph.emplace_back(lineText.fEnd);
667cb93a386Sopenharmony_ci    }
668cb93a386Sopenharmony_ci    line.fTextByGlyph.emplace_back(lineText.fEnd);
669cb93a386Sopenharmony_ci}
670cb93a386Sopenharmony_ci
671cb93a386Sopenharmony_civoid SelectableText::onGlyphRun(const SkFont& font,
672cb93a386Sopenharmony_ci                                DirTextRange dirTextRange,
673cb93a386Sopenharmony_ci                                SkRect bounds,
674cb93a386Sopenharmony_ci                                TextIndex trailingSpaces,
675cb93a386Sopenharmony_ci                                size_t glyphCount,
676cb93a386Sopenharmony_ci                                const uint16_t glyphs[],
677cb93a386Sopenharmony_ci                                const SkPoint positions[],
678cb93a386Sopenharmony_ci                                const TextIndex clusters[]) {
679cb93a386Sopenharmony_ci    auto& line = fBoxLines.back();
680cb93a386Sopenharmony_ci    auto start = line.fTextByGlyph.size();
681cb93a386Sopenharmony_ci    line.fBoxGlyphs.push_back_n(glyphCount);
682cb93a386Sopenharmony_ci    line.fTextByGlyph.push_back_n(glyphCount);
683cb93a386Sopenharmony_ci    for (auto i = 0; i < glyphCount; ++i) {
684cb93a386Sopenharmony_ci        auto pos = positions[i];
685cb93a386Sopenharmony_ci        auto pos1 = positions[i + 1];
686cb93a386Sopenharmony_ci        line.fBoxGlyphs[start + i] = SkRect::MakeXYWH(pos.fX, bounds.fTop, pos1.fX - pos.fX, bounds.height());
687cb93a386Sopenharmony_ci        line.fTextByGlyph[start + i] = clusters[i];
688cb93a386Sopenharmony_ci    }
689cb93a386Sopenharmony_ci}
690cb93a386Sopenharmony_ci
691cb93a386Sopenharmony_ci// TODO: Do something (logN) that is not a linear search
692cb93a386Sopenharmony_ciPosition SelectableText::findPosition(PositionType positionType, const BoxLine& line, SkScalar x) const {
693cb93a386Sopenharmony_ci    Position position(positionType);
694cb93a386Sopenharmony_ci    position.fGlyphRange = GlyphRange(0, line.fBoxGlyphs.size() - 1);
695cb93a386Sopenharmony_ci    position.fTextRange = line.fTextRange;
696cb93a386Sopenharmony_ci    position.fBoundaries.fTop = line.fBounds.fTop;
697cb93a386Sopenharmony_ci    position.fBoundaries.fBottom = line.fBounds.fBottom;
698cb93a386Sopenharmony_ci    // We look for the narrowest glyph range adjusted to positionType that contains the point.
699cb93a386Sopenharmony_ci    // So far we made sure that one unit of any positionType does not cross the run edges
700cb93a386Sopenharmony_ci    // Therefore it's going to be represented by a single text range only
701cb93a386Sopenharmony_ci    for (; position.fGlyphRange.fStart < position.fGlyphRange.fEnd; ++position.fGlyphRange.fStart) {
702cb93a386Sopenharmony_ci        auto glyphBox = line.fBoxGlyphs[position.fGlyphRange.fStart];
703cb93a386Sopenharmony_ci        if (glyphBox.fLeft > x) {
704cb93a386Sopenharmony_ci            break;
705cb93a386Sopenharmony_ci        }
706cb93a386Sopenharmony_ci        if (position.fPositionType == PositionType::kGraphemeCluster) {
707cb93a386Sopenharmony_ci            auto textIndex = line.fTextByGlyph[position.fGlyphRange.fStart];
708cb93a386Sopenharmony_ci            if (this->hasProperty(textIndex, GlyphUnitFlags::kGraphemeClusterStart)) {
709cb93a386Sopenharmony_ci                position.fTextRange.fStart = textIndex;
710cb93a386Sopenharmony_ci            }
711cb93a386Sopenharmony_ci        } else {
712cb93a386Sopenharmony_ci            // TODO: Implement
713cb93a386Sopenharmony_ci            SkASSERT(false);
714cb93a386Sopenharmony_ci        }
715cb93a386Sopenharmony_ci    }
716cb93a386Sopenharmony_ci    for (; position.fGlyphRange.fEnd > position.fGlyphRange.fStart ; --position.fGlyphRange.fEnd) {
717cb93a386Sopenharmony_ci        auto glyphBox = line.fBoxGlyphs[position.fGlyphRange.fStart];
718cb93a386Sopenharmony_ci        if (glyphBox.fRight <= x) {
719cb93a386Sopenharmony_ci            break;
720cb93a386Sopenharmony_ci        }
721cb93a386Sopenharmony_ci        if (position.fPositionType == PositionType::kGraphemeCluster) {
722cb93a386Sopenharmony_ci            auto textIndex = line.fTextByGlyph[position.fGlyphRange.fEnd];
723cb93a386Sopenharmony_ci            if (this->hasProperty(textIndex, GlyphUnitFlags::kGraphemeClusterStart)) {
724cb93a386Sopenharmony_ci                position.fTextRange.fEnd = textIndex;
725cb93a386Sopenharmony_ci                break;
726cb93a386Sopenharmony_ci            }
727cb93a386Sopenharmony_ci        } else {
728cb93a386Sopenharmony_ci            // TODO: Implement
729cb93a386Sopenharmony_ci            SkASSERT(false);
730cb93a386Sopenharmony_ci        }
731cb93a386Sopenharmony_ci    }
732cb93a386Sopenharmony_ci    position.fLineIndex = line.fIndex;
733cb93a386Sopenharmony_ci    position.fBoundaries.fLeft = line.fBoxGlyphs[position.fGlyphRange.fStart].fLeft;
734cb93a386Sopenharmony_ci    position.fBoundaries.fRight = line.fBoxGlyphs[position.fGlyphRange.fEnd].fRight;
735cb93a386Sopenharmony_ci    return position;
736cb93a386Sopenharmony_ci}
737cb93a386Sopenharmony_ci
738cb93a386Sopenharmony_ciPosition SelectableText::adjustedPosition(PositionType positionType, SkPoint xy) const {
739cb93a386Sopenharmony_ci    xy.fX = std::min(xy.fX, this->fActualSize.fWidth);
740cb93a386Sopenharmony_ci    xy.fY = std::min(xy.fY, this->fActualSize.fHeight);
741cb93a386Sopenharmony_ci    Position position(positionType);
742cb93a386Sopenharmony_ci    for (auto& line : fBoxLines) {
743cb93a386Sopenharmony_ci        if (line.fBounds.fTop > xy.fY) {
744cb93a386Sopenharmony_ci            // We are past the point vertically
745cb93a386Sopenharmony_ci            break;
746cb93a386Sopenharmony_ci        } else if (line.fBounds.fBottom <= xy.fY) {
747cb93a386Sopenharmony_ci            // We haven't reached the point vertically yet
748cb93a386Sopenharmony_ci            continue;
749cb93a386Sopenharmony_ci        }
750cb93a386Sopenharmony_ci        return this->findPosition(positionType, line, xy.fX);
751cb93a386Sopenharmony_ci    }
752cb93a386Sopenharmony_ci    return this->lastPosition(positionType);
753cb93a386Sopenharmony_ci}
754cb93a386Sopenharmony_ci
755cb93a386Sopenharmony_ciPosition SelectableText::previousPosition(Position current) const {
756cb93a386Sopenharmony_ci    const BoxLine* currentLine = &fBoxLines[current.fLineIndex];
757cb93a386Sopenharmony_ci    if (this->isFirstOnTheLine(current)) {
758cb93a386Sopenharmony_ci        // Go to the previous line
759cb93a386Sopenharmony_ci        if (current.fLineIndex == 0) {
760cb93a386Sopenharmony_ci            // We reached the end; there is nowhere to move
761cb93a386Sopenharmony_ci            current.fGlyphRange = GlyphRange(0, 0);
762cb93a386Sopenharmony_ci            return current;
763cb93a386Sopenharmony_ci        } else {
764cb93a386Sopenharmony_ci            current.fLineIndex -= 1;
765cb93a386Sopenharmony_ci            currentLine = &fBoxLines[current.fLineIndex];
766cb93a386Sopenharmony_ci            current.fGlyphRange.fStart = currentLine->fBoxGlyphs.size();
767cb93a386Sopenharmony_ci        }
768cb93a386Sopenharmony_ci    }
769cb93a386Sopenharmony_ci    auto position = this->findPosition(current.fPositionType, *currentLine, currentLine->fBoxGlyphs[current.fGlyphRange.fStart].centerX());
770cb93a386Sopenharmony_ci    if (current.fPositionType == PositionType::kGraphemeCluster) {
771cb93a386Sopenharmony_ci        // Either way we found us a grapheme cluster (just make sure of it)
772cb93a386Sopenharmony_ci        SkASSERT(this->hasProperty(current.fTextRange.fStart, GlyphUnitFlags::kGraphemeClusterStart));
773cb93a386Sopenharmony_ci    }
774cb93a386Sopenharmony_ci    return position;
775cb93a386Sopenharmony_ci}
776cb93a386Sopenharmony_ci
777cb93a386Sopenharmony_ciPosition SelectableText::nextPosition(Position current) const {
778cb93a386Sopenharmony_ci    const BoxLine* currentLine = &fBoxLines[current.fLineIndex];
779cb93a386Sopenharmony_ci    if (this->isLastOnTheLine(current)) {
780cb93a386Sopenharmony_ci        // Go to the next line
781cb93a386Sopenharmony_ci        if (current.fLineIndex == this->fBoxLines.size() - 1) {
782cb93a386Sopenharmony_ci            // We reached the end; there is nowhere to move
783cb93a386Sopenharmony_ci            current.fGlyphRange = GlyphRange(currentLine->fBoxGlyphs.size(), currentLine->fBoxGlyphs.size());
784cb93a386Sopenharmony_ci            return current;
785cb93a386Sopenharmony_ci        } else {
786cb93a386Sopenharmony_ci            current.fLineIndex += 1;
787cb93a386Sopenharmony_ci            currentLine = &fBoxLines[current.fLineIndex];
788cb93a386Sopenharmony_ci            current.fGlyphRange.fEnd = 0;
789cb93a386Sopenharmony_ci        }
790cb93a386Sopenharmony_ci    }
791cb93a386Sopenharmony_ci    auto position = this->findPosition(current.fPositionType, *currentLine, currentLine->fBoxGlyphs[current.fGlyphRange.fStart].centerX());
792cb93a386Sopenharmony_ci    if (current.fPositionType == PositionType::kGraphemeCluster) {
793cb93a386Sopenharmony_ci        // Either way we found us a grapheme cluster (just make sure of it)
794cb93a386Sopenharmony_ci        SkASSERT(this->hasProperty(current.fTextRange.fEnd, GlyphUnitFlags::kGraphemeClusterStart));
795cb93a386Sopenharmony_ci    }
796cb93a386Sopenharmony_ci    return position;
797cb93a386Sopenharmony_ci}
798cb93a386Sopenharmony_ci
799cb93a386Sopenharmony_ciPosition SelectableText::upPosition(Position current) const {
800cb93a386Sopenharmony_ci
801cb93a386Sopenharmony_ci    if (current.fLineIndex == 0) {
802cb93a386Sopenharmony_ci        // We are on the first line; just move to the first position
803cb93a386Sopenharmony_ci        return this->firstPosition(current.fPositionType);
804cb93a386Sopenharmony_ci    }
805cb93a386Sopenharmony_ci
806cb93a386Sopenharmony_ci    // Go to the previous line
807cb93a386Sopenharmony_ci    const BoxLine* currentLine = &fBoxLines[current.fLineIndex];
808cb93a386Sopenharmony_ci    auto position = this->findPosition(current.fPositionType, fBoxLines[current.fLineIndex - 1], currentLine->fBoxGlyphs[current.fGlyphRange.fStart].centerX());
809cb93a386Sopenharmony_ci    if (current.fPositionType == PositionType::kGraphemeCluster) {
810cb93a386Sopenharmony_ci        // Either way we found us a grapheme cluster (just make sure of it)
811cb93a386Sopenharmony_ci        SkASSERT(this->hasProperty(current.fTextRange.fEnd, GlyphUnitFlags::kGraphemeClusterStart));
812cb93a386Sopenharmony_ci    }
813cb93a386Sopenharmony_ci    return position;
814cb93a386Sopenharmony_ci}
815cb93a386Sopenharmony_ci
816cb93a386Sopenharmony_ciPosition SelectableText::downPosition(Position current) const {
817cb93a386Sopenharmony_ci
818cb93a386Sopenharmony_ci    if (current.fLineIndex == this->countLines() - 1) {
819cb93a386Sopenharmony_ci        // We are on the last line; just move to the last position
820cb93a386Sopenharmony_ci        return this->lastPosition(current.fPositionType);
821cb93a386Sopenharmony_ci    }
822cb93a386Sopenharmony_ci
823cb93a386Sopenharmony_ci    // Go to the next line
824cb93a386Sopenharmony_ci    const BoxLine* currentLine = &fBoxLines[current.fLineIndex];
825cb93a386Sopenharmony_ci    auto position = this->findPosition(current.fPositionType, fBoxLines[current.fLineIndex + 1], currentLine->fBoxGlyphs[current.fGlyphRange.fStart].centerX());
826cb93a386Sopenharmony_ci    if (current.fPositionType == PositionType::kGraphemeCluster) {
827cb93a386Sopenharmony_ci        // Either way we found us a grapheme cluster (just make sure of it)
828cb93a386Sopenharmony_ci        SkASSERT(this->hasProperty(current.fTextRange.fEnd, GlyphUnitFlags::kGraphemeClusterStart));
829cb93a386Sopenharmony_ci    }
830cb93a386Sopenharmony_ci    return position;
831cb93a386Sopenharmony_ci}
832cb93a386Sopenharmony_ci
833cb93a386Sopenharmony_ciPosition SelectableText::firstPosition(PositionType positionType) const {
834cb93a386Sopenharmony_ci    auto firstLine = fBoxLines.front();
835cb93a386Sopenharmony_ci    auto firstGlyph = firstLine.fBoxGlyphs.front();
836cb93a386Sopenharmony_ci    Position beginningOfText(positionType);
837cb93a386Sopenharmony_ci    // Set the glyph range after the last glyph
838cb93a386Sopenharmony_ci    beginningOfText.fGlyphRange = GlyphRange { 0, 0};
839cb93a386Sopenharmony_ci    beginningOfText.fLineIndex = 0;
840cb93a386Sopenharmony_ci    beginningOfText.fBoundaries = SkRect::MakeXYWH(firstGlyph.fLeft, firstGlyph.fTop, 0, firstGlyph.height());
841cb93a386Sopenharmony_ci    beginningOfText.fTextRange = this->glyphsToText(beginningOfText);
842cb93a386Sopenharmony_ci    beginningOfText.fLineIndex = 0;
843cb93a386Sopenharmony_ci    return beginningOfText;
844cb93a386Sopenharmony_ci}
845cb93a386Sopenharmony_ci
846cb93a386Sopenharmony_ciPosition SelectableText::lastPosition(PositionType positionType) const {
847cb93a386Sopenharmony_ci    auto lastLine = fBoxLines.back();
848cb93a386Sopenharmony_ci    auto lastGlyph = lastLine.fBoxGlyphs.back();
849cb93a386Sopenharmony_ci    Position endOfText(positionType);
850cb93a386Sopenharmony_ci    endOfText.fLineIndex = lastLine.fIndex;
851cb93a386Sopenharmony_ci    endOfText.fGlyphRange = GlyphRange(lastLine.fBoxGlyphs.size() - 1, lastLine.fBoxGlyphs.size() - 1);
852cb93a386Sopenharmony_ci    endOfText.fBoundaries = SkRect::MakeXYWH(lastGlyph.fRight, lastGlyph.fTop, 0, lastGlyph.height());
853cb93a386Sopenharmony_ci    endOfText.fTextRange = this->glyphsToText(endOfText);
854cb93a386Sopenharmony_ci    endOfText.fLineIndex = lastLine.fIndex;
855cb93a386Sopenharmony_ci    return endOfText;
856cb93a386Sopenharmony_ci}
857cb93a386Sopenharmony_ci
858cb93a386Sopenharmony_ciPosition SelectableText::firstInLinePosition(PositionType positionType, LineIndex lineIndex) const {
859cb93a386Sopenharmony_ci    SkASSERT(lineIndex >= 0 && lineIndex < fBoxLines.size());
860cb93a386Sopenharmony_ci    auto& line = fBoxLines[lineIndex];
861cb93a386Sopenharmony_ci    return this->findPosition(positionType, line, line.fBounds.left());
862cb93a386Sopenharmony_ci}
863cb93a386Sopenharmony_ci
864cb93a386Sopenharmony_ciPosition SelectableText::lastInLinePosition(PositionType positionType, LineIndex lineIndex) const {
865cb93a386Sopenharmony_ci    auto& line = fBoxLines[lineIndex];
866cb93a386Sopenharmony_ci    return this->findPosition(positionType, line, line.fBounds.right());
867cb93a386Sopenharmony_ci}
868cb93a386Sopenharmony_ci
869cb93a386Sopenharmony_ci
870cb93a386Sopenharmony_ciTextRange SelectableText::glyphsToText(Position position) const {
871cb93a386Sopenharmony_ci    SkASSERT(position.fPositionType != PositionType::kRandomText);
872cb93a386Sopenharmony_ci    auto line = this->getLine(position.fLineIndex);
873cb93a386Sopenharmony_ci    TextRange textRange = EMPTY_RANGE;
874cb93a386Sopenharmony_ci    for (auto glyph = position.fGlyphRange.fStart; glyph <= position.fGlyphRange.fEnd; ++glyph) {
875cb93a386Sopenharmony_ci        if (textRange.fStart == EMPTY_INDEX) {
876cb93a386Sopenharmony_ci            textRange.fStart = line.fTextByGlyph[glyph];
877cb93a386Sopenharmony_ci        }
878cb93a386Sopenharmony_ci        textRange.fEnd = line.fTextByGlyph[glyph];
879cb93a386Sopenharmony_ci    }
880cb93a386Sopenharmony_ci    return textRange;
881cb93a386Sopenharmony_ci}
882cb93a386Sopenharmony_ci} // namespace text
883cb93a386Sopenharmony_ci} // namespace skia
884