1cb93a386Sopenharmony_ci// Copyright 2021 Google LLC.
2cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h"
3cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
4cb93a386Sopenharmony_ci#include "include/core/SkColor.h"
5cb93a386Sopenharmony_ci#include "include/core/SkEncodedImageFormat.h"
6cb93a386Sopenharmony_ci#include "include/core/SkFontMgr.h"
7cb93a386Sopenharmony_ci#include "include/core/SkFontStyle.h"
8cb93a386Sopenharmony_ci#include "include/core/SkImageEncoder.h"
9cb93a386Sopenharmony_ci#include "include/core/SkPaint.h"
10cb93a386Sopenharmony_ci#include "include/core/SkPoint.h"
11cb93a386Sopenharmony_ci#include "include/core/SkRect.h"
12cb93a386Sopenharmony_ci#include "include/core/SkRefCnt.h"
13cb93a386Sopenharmony_ci#include "include/core/SkScalar.h"
14cb93a386Sopenharmony_ci#include "include/core/SkSpan.h"
15cb93a386Sopenharmony_ci#include "include/core/SkStream.h"
16cb93a386Sopenharmony_ci#include "include/core/SkString.h"
17cb93a386Sopenharmony_ci#include "include/core/SkTypeface.h"
18cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
19cb93a386Sopenharmony_ci#include "tests/Test.h"
20cb93a386Sopenharmony_ci#include "tools/Resources.h"
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_ci#include "experimental/sktext/include/Text.h"
23cb93a386Sopenharmony_ci#include "experimental/sktext/src/Paint.h"
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci#include <string.h>
26cb93a386Sopenharmony_ci#include <algorithm>
27cb93a386Sopenharmony_ci#include <limits>
28cb93a386Sopenharmony_ci#include <memory>
29cb93a386Sopenharmony_ci#include <string>
30cb93a386Sopenharmony_ci#include <utility>
31cb93a386Sopenharmony_ci#include <vector>
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_cistruct GrContextOptions;
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ci#define VeryLongCanvasWidth 1000000
36cb93a386Sopenharmony_ci#define TestCanvasWidth 1000
37cb93a386Sopenharmony_ci#define TestCanvasHeight 600
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ciusing namespace skia::text;
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_cistruct TestLine {
42cb93a386Sopenharmony_ci    size_t index;
43cb93a386Sopenharmony_ci    TextRange lineText;
44cb93a386Sopenharmony_ci    bool hardBreak;
45cb93a386Sopenharmony_ci    SkRect bounds;
46cb93a386Sopenharmony_ci    GlyphRange trailingSpaces;
47cb93a386Sopenharmony_ci    Range<RunIndex> runRange;
48cb93a386Sopenharmony_ci    size_t glyphCount;
49cb93a386Sopenharmony_ci};
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_cistruct TestRun {
52cb93a386Sopenharmony_ci    const SkFont& font;
53cb93a386Sopenharmony_ci    DirTextRange dirTextRange;  // Currently we make sure that the run edges are the grapheme cluster edges
54cb93a386Sopenharmony_ci    SkRect bounds;              // bounds contains the physical boundaries of the run
55cb93a386Sopenharmony_ci    size_t trailingSpaces;      // Depending of TextDirection it goes right to the end (LTR) or left to the start (RTL)
56cb93a386Sopenharmony_ci    SkSpan<const uint16_t> glyphs;
57cb93a386Sopenharmony_ci    SkSpan<const SkPoint> positions;
58cb93a386Sopenharmony_ci    SkSpan<const TextIndex> clusters;
59cb93a386Sopenharmony_ci};
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ciclass TestVisitor : public Visitor {
62cb93a386Sopenharmony_cipublic:
63cb93a386Sopenharmony_ci    void onBeginLine(size_t index, TextRange lineText, bool hardBreak, SkRect bounds) override {
64cb93a386Sopenharmony_ci        SkASSERT(fTestLines.size() == index);
65cb93a386Sopenharmony_ci        fTestLines.push_back({ index, lineText, hardBreak, bounds, EMPTY_RANGE, Range<RunIndex>(fTestRuns.size(), fTestRuns.size()), 0 });
66cb93a386Sopenharmony_ci    }
67cb93a386Sopenharmony_ci    void onEndLine(size_t index, TextRange lineText, GlyphRange trailingSpaces, size_t glyphCount) override {
68cb93a386Sopenharmony_ci        SkASSERT(fTestLines.size() == index + 1);
69cb93a386Sopenharmony_ci        fTestLines.back().trailingSpaces = trailingSpaces;
70cb93a386Sopenharmony_ci        fTestLines.back().runRange.fEnd = fTestRuns.size();
71cb93a386Sopenharmony_ci        fTestLines.back().glyphCount = glyphCount;
72cb93a386Sopenharmony_ci    }
73cb93a386Sopenharmony_ci    void onGlyphRun(const SkFont& font,
74cb93a386Sopenharmony_ci                    DirTextRange dirTextRange,
75cb93a386Sopenharmony_ci                    SkRect bounds,
76cb93a386Sopenharmony_ci                    TextIndex trailingSpaces,
77cb93a386Sopenharmony_ci                    size_t glyphCount,          // Just the number of glyphs
78cb93a386Sopenharmony_ci                    const uint16_t glyphs[],
79cb93a386Sopenharmony_ci                    const SkPoint positions[],        // Positions relative to the line
80cb93a386Sopenharmony_ci                    const TextIndex clusters[]) override
81cb93a386Sopenharmony_ci    {
82cb93a386Sopenharmony_ci        fTestRuns.push_back({font, dirTextRange, bounds, trailingSpaces,
83cb93a386Sopenharmony_ci                            SkSpan<const uint16_t>(&glyphs[0], glyphCount),
84cb93a386Sopenharmony_ci                            SkSpan<const SkPoint>(&positions[0], glyphCount + 1),
85cb93a386Sopenharmony_ci                            SkSpan<const TextIndex>(&clusters[0], glyphCount + 1),
86cb93a386Sopenharmony_ci                            });
87cb93a386Sopenharmony_ci    }
88cb93a386Sopenharmony_ci    void onPlaceholder(TextRange, const SkRect& bounds) override { }
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci    std::vector<TestLine> fTestLines;
91cb93a386Sopenharmony_ci    std::vector<TestRun> fTestRuns;
92cb93a386Sopenharmony_ci};
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ciUNIX_ONLY_TEST(SkText_SelectableText_Bounds, reporter) {
95cb93a386Sopenharmony_ci    sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Roboto", 40.0f, SkFontStyle::Normal());
96cb93a386Sopenharmony_ci    if (fontChain->empty()) return;
97cb93a386Sopenharmony_ci
98cb93a386Sopenharmony_ci    std::u16string utf16(u"    Leading spaces\nTrailing spaces    \nLong text with collapsed      spaces inside wrapped into few lines");
99cb93a386Sopenharmony_ci    UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size()));
100cb93a386Sopenharmony_ci    if (!unicodeText.getUnicode()) return;
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ci    FontBlock fontBlock(utf16.size(), fontChain);
103cb93a386Sopenharmony_ci    auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1));
104cb93a386Sopenharmony_ci    auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr);
105cb93a386Sopenharmony_ci    auto wrappedText = shapedText->wrap(&unicodeText, 440.0f, 500.0f);
106cb93a386Sopenharmony_ci    auto selectableText = wrappedText->prepareToEdit(&unicodeText);
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ci    TestVisitor testVisitor;
109cb93a386Sopenharmony_ci    wrappedText->visit(&testVisitor);
110cb93a386Sopenharmony_ci
111cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, selectableText->countLines() == 5);
112cb93a386Sopenharmony_ci    for (LineIndex lineIndex = 0; lineIndex < selectableText->countLines(); ++lineIndex) {
113cb93a386Sopenharmony_ci        auto& testLine = testVisitor.fTestLines[lineIndex];
114cb93a386Sopenharmony_ci        auto boxLine = selectableText->getLine(lineIndex);
115cb93a386Sopenharmony_ci        SkScalar left = boxLine.fBounds.fLeft;
116cb93a386Sopenharmony_ci        for (auto& box : boxLine.fBoxGlyphs) {
117cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, boxLine.fBounds.contains(box) || box.isEmpty());
118cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, left <= box.fLeft);
119cb93a386Sopenharmony_ci            left = box.fRight;
120cb93a386Sopenharmony_ci        }
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci        GlyphIndex trailingSpaces = boxLine.fBoxGlyphs.size() - 1;
123cb93a386Sopenharmony_ci        for (RunIndex runIndex = testLine.runRange.fEnd; runIndex > testLine.runRange.fStart; --runIndex) {
124cb93a386Sopenharmony_ci            auto& testRun = testVisitor.fTestRuns[runIndex - 1];
125cb93a386Sopenharmony_ci            if (testRun.trailingSpaces == 0) {
126cb93a386Sopenharmony_ci                trailingSpaces -= testRun.glyphs.size();
127cb93a386Sopenharmony_ci            } else {
128cb93a386Sopenharmony_ci                trailingSpaces -= (testRun.glyphs.size() - testRun.trailingSpaces);
129cb93a386Sopenharmony_ci                break;
130cb93a386Sopenharmony_ci            }
131cb93a386Sopenharmony_ci        }
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, boxLine.fTrailingSpacesEnd == testLine.trailingSpaces.fEnd);
134cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, boxLine.fTextEnd == trailingSpaces);
135cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, boxLine.fTextRange == testLine.lineText);
136cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, boxLine.fIndex == lineIndex);
137cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, boxLine.fIsHardBreak == testLine.hardBreak);
138cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, boxLine.fBounds == testLine.bounds);
139cb93a386Sopenharmony_ci    }
140cb93a386Sopenharmony_ci}
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ciUNIX_ONLY_TEST(SkText_SelectableText_Navigation_FirstLast, reporter) {
143cb93a386Sopenharmony_ci    sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Roboto", 40.0f, SkFontStyle::Normal());
144cb93a386Sopenharmony_ci    if (fontChain->empty()) return;
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ci    std::u16string utf16(u"    Leading spaces\nTrailing spaces    \nLong text with collapsed      spaces inside wrapped into few lines");
147cb93a386Sopenharmony_ci    UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size()));
148cb93a386Sopenharmony_ci    if (!unicodeText.getUnicode()) return;
149cb93a386Sopenharmony_ci
150cb93a386Sopenharmony_ci    FontBlock fontBlock(utf16.size(), fontChain);
151cb93a386Sopenharmony_ci    auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1));
152cb93a386Sopenharmony_ci    auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr);
153cb93a386Sopenharmony_ci    auto wrappedText = shapedText->wrap(&unicodeText, 440.0f, 500.0f);
154cb93a386Sopenharmony_ci    auto selectableText = wrappedText->prepareToEdit(&unicodeText);
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci    TestVisitor testVisitor;
157cb93a386Sopenharmony_ci    wrappedText->visit(&testVisitor);
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_ci    // First position
160cb93a386Sopenharmony_ci    auto firstLine = testVisitor.fTestLines.front();
161cb93a386Sopenharmony_ci    auto firstRun = testVisitor.fTestRuns.front();
162cb93a386Sopenharmony_ci    auto firstPosition = selectableText->firstPosition(PositionType::kGraphemeCluster);
163cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, firstPosition.fLineIndex == 0);
164cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, firstPosition.fTextRange == TextRange(0, 0));
165cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, firstPosition.fGlyphRange == GlyphRange(0, 0));
166cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(firstPosition.fBoundaries.fLeft, 0.0f));
167cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(firstPosition.fBoundaries.fTop, 0.0f));
168cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(firstPosition.fBoundaries.width(), 0.0f));
169cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(firstPosition.fBoundaries.height(), firstLine.bounds.height()));
170cb93a386Sopenharmony_ci
171cb93a386Sopenharmony_ci    // Last position
172cb93a386Sopenharmony_ci    auto lastLine = testVisitor.fTestLines.back();
173cb93a386Sopenharmony_ci    auto lastRun = testVisitor.fTestRuns.back();
174cb93a386Sopenharmony_ci    auto lastPosition = selectableText->lastPosition(PositionType::kGraphemeCluster);
175cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, lastPosition.fLineIndex == testVisitor.fTestLines.size() - 1);
176cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, lastPosition.fTextRange == TextRange(utf16.size(), utf16.size()));
177cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, lastPosition.fGlyphRange == GlyphRange(lastRun.glyphs.size(), lastRun.glyphs.size()));
178cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lastPosition.fBoundaries.fLeft, lastRun.positions[lastRun.glyphs.size()].fX));
179cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lastPosition.fBoundaries.fTop, lastLine.bounds.fTop));
180cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lastPosition.fBoundaries.width(), 0.0f));
181cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(lastPosition.fBoundaries.height(), lastLine.bounds.height()));
182cb93a386Sopenharmony_ci}
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ciUNIX_ONLY_TEST(SkText_SelectableText_ScanRightByGraphemeClusters, reporter) {
185cb93a386Sopenharmony_ci    sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Roboto", 40.0f, SkFontStyle::Normal());
186cb93a386Sopenharmony_ci    if (fontChain->empty()) return;
187cb93a386Sopenharmony_ci
188cb93a386Sopenharmony_ci    std::u16string utf16(u"    Small Text   \n");
189cb93a386Sopenharmony_ci    UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size()));
190cb93a386Sopenharmony_ci    if (!unicodeText.getUnicode()) return;
191cb93a386Sopenharmony_ci
192cb93a386Sopenharmony_ci    FontBlock fontBlock(utf16.size(), fontChain);
193cb93a386Sopenharmony_ci    auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1));
194cb93a386Sopenharmony_ci    auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr);
195cb93a386Sopenharmony_ci    auto wrappedText = shapedText->wrap(&unicodeText, 440.0f, 500.0f);
196cb93a386Sopenharmony_ci    auto selectableText = wrappedText->prepareToEdit(&unicodeText);
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ci    TestVisitor testVisitor;
199cb93a386Sopenharmony_ci    wrappedText->visit(&testVisitor);
200cb93a386Sopenharmony_ci
201cb93a386Sopenharmony_ci    auto firstPosition = selectableText->firstPosition(PositionType::kGraphemeCluster);
202cb93a386Sopenharmony_ci    auto lastPosition = selectableText->lastPosition(PositionType::kGraphemeCluster);
203cb93a386Sopenharmony_ci
204cb93a386Sopenharmony_ci    auto position = firstPosition;
205cb93a386Sopenharmony_ci    while (!(position.fGlyphRange == lastPosition.fGlyphRange)) {
206cb93a386Sopenharmony_ci        auto next = selectableText->nextPosition(position);
207cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, position.fTextRange.fEnd == next.fTextRange.fStart);
208cb93a386Sopenharmony_ci        if (position.fLineIndex == next.fLineIndex - 1) {
209cb93a386Sopenharmony_ci            auto line = selectableText->getLine(next.fLineIndex);
210cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, next.fGlyphRange.fStart == 0);
211cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(next.fBoundaries.fLeft, 0.0f));
212cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(next.fBoundaries.fTop, line.fBounds.fTop));
213cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(next.fBoundaries.height(), line.fBounds.height()));
214cb93a386Sopenharmony_ci        } else {
215cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, position.fLineIndex == next.fLineIndex);
216cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, position.fGlyphRange.fEnd == next.fGlyphRange.fStart);
217cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fBoundaries.fRight, next.fBoundaries.fLeft));
218cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fBoundaries.fTop, next.fBoundaries.fTop));
219cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fBoundaries.height(), next.fBoundaries.height()));
220cb93a386Sopenharmony_ci        }
221cb93a386Sopenharmony_ci        position = next;
222cb93a386Sopenharmony_ci    }
223cb93a386Sopenharmony_ci}
224cb93a386Sopenharmony_ci
225cb93a386Sopenharmony_ciUNIX_ONLY_TEST(SkText_SelectableText_ScanLeftByGraphemeClusters, reporter) {
226cb93a386Sopenharmony_ci    sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Roboto", 40.0f, SkFontStyle::Normal());
227cb93a386Sopenharmony_ci    if (fontChain->empty()) return;
228cb93a386Sopenharmony_ci
229cb93a386Sopenharmony_ci    std::u16string utf16(u"    Small Text   \n");
230cb93a386Sopenharmony_ci    UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size()));
231cb93a386Sopenharmony_ci    if (!unicodeText.getUnicode()) return;
232cb93a386Sopenharmony_ci
233cb93a386Sopenharmony_ci    FontBlock fontBlock(utf16.size(), fontChain);
234cb93a386Sopenharmony_ci    auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1));
235cb93a386Sopenharmony_ci    auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr);
236cb93a386Sopenharmony_ci    auto wrappedText = shapedText->wrap(&unicodeText, 440.0f, 500.0f);
237cb93a386Sopenharmony_ci    auto selectableText = wrappedText->prepareToEdit(&unicodeText);
238cb93a386Sopenharmony_ci
239cb93a386Sopenharmony_ci    TestVisitor testVisitor;
240cb93a386Sopenharmony_ci    wrappedText->visit(&testVisitor);
241cb93a386Sopenharmony_ci
242cb93a386Sopenharmony_ci    auto firstPosition = selectableText->firstPosition(PositionType::kGraphemeCluster);
243cb93a386Sopenharmony_ci    auto lastPosition = selectableText->lastPosition(PositionType::kGraphemeCluster);
244cb93a386Sopenharmony_ci
245cb93a386Sopenharmony_ci    auto position = lastPosition;
246cb93a386Sopenharmony_ci    while (!(position.fGlyphRange == firstPosition.fGlyphRange)) {
247cb93a386Sopenharmony_ci        auto prev = selectableText->previousPosition(position);
248cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, position.fTextRange.fEnd == prev.fTextRange.fStart);
249cb93a386Sopenharmony_ci        if (position.fLineIndex == prev.fLineIndex + 1) {
250cb93a386Sopenharmony_ci            auto line = selectableText->getLine(prev.fLineIndex);
251cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, prev.fGlyphRange.fEnd == line.fBoxGlyphs.size());
252cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prev.fBoundaries.fRight, line.fBounds.fRight));
253cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prev.fBoundaries.fTop, line.fBounds.fTop));
254cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prev.fBoundaries.height(), line.fBounds.height()));
255cb93a386Sopenharmony_ci        } else {
256cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, position.fLineIndex == prev.fLineIndex);
257cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, position.fGlyphRange.fStart == prev.fGlyphRange.fEnd);
258cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fBoundaries.fLeft, prev.fBoundaries.fRight));
259cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fBoundaries.fTop, prev.fBoundaries.fTop));
260cb93a386Sopenharmony_ci            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(position.fBoundaries.height(), prev.fBoundaries.height()));
261cb93a386Sopenharmony_ci        }
262cb93a386Sopenharmony_ci        position = prev;
263cb93a386Sopenharmony_ci    }
264cb93a386Sopenharmony_ci}
265cb93a386Sopenharmony_ci
266cb93a386Sopenharmony_ciUNIX_ONLY_TEST(SkText_SelectableText_Navigation_UpDown, reporter) {
267cb93a386Sopenharmony_ci    sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Roboto", 40.0f, SkFontStyle::Normal());
268cb93a386Sopenharmony_ci    if (fontChain->empty()) return;
269cb93a386Sopenharmony_ci
270cb93a386Sopenharmony_ci    std::u16string utf16(u"    Leading spaces\nTrailing spaces    \nLong text with collapsed      spaces inside wrapped into few lines");
271cb93a386Sopenharmony_ci    UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size()));
272cb93a386Sopenharmony_ci    if (!unicodeText.getUnicode()) return;
273cb93a386Sopenharmony_ci
274cb93a386Sopenharmony_ci    FontBlock fontBlock(utf16.size(), fontChain);
275cb93a386Sopenharmony_ci    auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1));
276cb93a386Sopenharmony_ci    auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr);
277cb93a386Sopenharmony_ci    auto wrappedText = shapedText->wrap(&unicodeText, 440.0f, 500.0f);
278cb93a386Sopenharmony_ci    auto selectableText = wrappedText->prepareToEdit(&unicodeText);
279cb93a386Sopenharmony_ci
280cb93a386Sopenharmony_ci    TestVisitor testVisitor;
281cb93a386Sopenharmony_ci    wrappedText->visit(&testVisitor);
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ci    // Upper position
284cb93a386Sopenharmony_ci    auto position = selectableText->lastInLinePosition(PositionType::kGraphemeCluster, 0);
285cb93a386Sopenharmony_ci    while (position.fLineIndex > 0) {
286cb93a386Sopenharmony_ci        auto down = selectableText->downPosition(position);
287cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, position.fLineIndex + 1 == down.fLineIndex);
288cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, position.fBoundaries.centerX() >= down.fBoundaries.centerX());
289cb93a386Sopenharmony_ci        position = down;
290cb93a386Sopenharmony_ci    }
291cb93a386Sopenharmony_ci
292cb93a386Sopenharmony_ci    // Down position
293cb93a386Sopenharmony_ci    position = selectableText->lastInLinePosition(PositionType::kGraphemeCluster, selectableText->countLines() - 1);
294cb93a386Sopenharmony_ci    while (position.fLineIndex < selectableText->countLines() - 1) {
295cb93a386Sopenharmony_ci        auto down = selectableText->downPosition(position);
296cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, position.fLineIndex - 1 == down.fLineIndex);
297cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, position.fBoundaries.centerX() >= down.fBoundaries.centerX());
298cb93a386Sopenharmony_ci        position = down;
299cb93a386Sopenharmony_ci    }
300cb93a386Sopenharmony_ci}
301