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