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}; 49cb93a386Sopenharmony_ci 50cb93a386Sopenharmony_cistruct TestRun { 51cb93a386Sopenharmony_ci const SkFont& font; 52cb93a386Sopenharmony_ci DirTextRange dirTextRange; // Currently we make sure that the run edges are the grapheme cluster edges 53cb93a386Sopenharmony_ci SkRect bounds; // bounds contains the physical boundaries of the run 54cb93a386Sopenharmony_ci size_t trailingSpaces; // Depending of TextDirection it goes right to the end (LTR) or left to the start (RTL) 55cb93a386Sopenharmony_ci SkSpan<const uint16_t> glyphs; 56cb93a386Sopenharmony_ci SkSpan<const SkPoint> positions; 57cb93a386Sopenharmony_ci SkSpan<const TextIndex> clusters; 58cb93a386Sopenharmony_ci}; 59cb93a386Sopenharmony_ci 60cb93a386Sopenharmony_ciclass TestVisitor : public Visitor { 61cb93a386Sopenharmony_cipublic: 62cb93a386Sopenharmony_ci void onBeginLine(size_t index, TextRange lineText, bool hardBreak, SkRect bounds) override { 63cb93a386Sopenharmony_ci SkASSERT(fTestLines.size() == index); 64cb93a386Sopenharmony_ci fTestLines.push_back({ index, lineText, hardBreak, bounds, EMPTY_RANGE, Range<RunIndex>(fTestRuns.size(), fTestRuns.size()) }); 65cb93a386Sopenharmony_ci } 66cb93a386Sopenharmony_ci void onEndLine(size_t index, TextRange lineText, GlyphRange trailingSpaces, size_t glyphCount) override { 67cb93a386Sopenharmony_ci SkASSERT(fTestLines.size() == index + 1); 68cb93a386Sopenharmony_ci fTestLines.back().trailingSpaces = trailingSpaces; 69cb93a386Sopenharmony_ci fTestLines.back().runRange.fEnd = fTestRuns.size(); 70cb93a386Sopenharmony_ci } 71cb93a386Sopenharmony_ci void onGlyphRun(const SkFont& font, 72cb93a386Sopenharmony_ci DirTextRange dirTextRange, 73cb93a386Sopenharmony_ci SkRect bounds, 74cb93a386Sopenharmony_ci TextIndex trailingSpaces, 75cb93a386Sopenharmony_ci size_t glyphCount, // Just the number of glyphs 76cb93a386Sopenharmony_ci const uint16_t glyphs[], 77cb93a386Sopenharmony_ci const SkPoint positions[], // Positions relative to the line 78cb93a386Sopenharmony_ci const TextIndex clusters[]) override 79cb93a386Sopenharmony_ci { 80cb93a386Sopenharmony_ci fTestRuns.push_back({font, dirTextRange, bounds, trailingSpaces, 81cb93a386Sopenharmony_ci SkSpan<const uint16_t>(&glyphs[0], glyphCount), 82cb93a386Sopenharmony_ci SkSpan<const SkPoint>(&positions[0], glyphCount + 1), 83cb93a386Sopenharmony_ci SkSpan<const TextIndex>(&clusters[0], glyphCount + 1), 84cb93a386Sopenharmony_ci }); 85cb93a386Sopenharmony_ci } 86cb93a386Sopenharmony_ci void onPlaceholder(TextRange, const SkRect& bounds) override { } 87cb93a386Sopenharmony_ci 88cb93a386Sopenharmony_ci std::vector<TestLine> fTestLines; 89cb93a386Sopenharmony_ci std::vector<TestRun> fTestRuns; 90cb93a386Sopenharmony_ci}; 91cb93a386Sopenharmony_ci 92cb93a386Sopenharmony_ciUNIX_ONLY_TEST(SkText_WrappedText_Spaces, reporter) { 93cb93a386Sopenharmony_ci sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Roboto", 40.0f, SkFontStyle::Normal()); 94cb93a386Sopenharmony_ci if (fontChain->empty()) return; 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ci std::u16string utf16(u" Leading spaces\nTrailing spaces \nLong text with collapsed spaces inside wrapped into few lines"); 97cb93a386Sopenharmony_ci UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size())); 98cb93a386Sopenharmony_ci if (!unicodeText.getUnicode()) return; 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_ci FontBlock fontBlock(utf16.size(), fontChain); 101cb93a386Sopenharmony_ci auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1)); 102cb93a386Sopenharmony_ci auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr); 103cb93a386Sopenharmony_ci auto wrappedText = shapedText->wrap(&unicodeText, 440.0f, 500.0f); 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_ci TestVisitor testVisitor; 106cb93a386Sopenharmony_ci wrappedText->visit(&testVisitor); 107cb93a386Sopenharmony_ci 108cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines.size() == 5); 109cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestRuns.size() == 5); 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[0].trailingSpaces.width() == 0); 112cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[1].trailingSpaces.width() == 4); 113cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[2].trailingSpaces.width() == 6); 114cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[3].trailingSpaces.width() == 1); 115cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[4].trailingSpaces.width() == 0); 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci auto break1 = utf16.find_first_of(u"\n"); 118cb93a386Sopenharmony_ci auto break2 = utf16.find_last_of(u"\n"); 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[0].lineText.width() == break1); 121cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[1].lineText.width() == break2 - break1 - 1); 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci RunIndex runIndex = 0; 124cb93a386Sopenharmony_ci SkScalar verticalOffset = 0.0f; 125cb93a386Sopenharmony_ci for (int lineIndex = 0; lineIndex < testVisitor.fTestLines.size(); ++lineIndex) { 126cb93a386Sopenharmony_ci auto& line = testVisitor.fTestLines[lineIndex]; 127cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, line.runRange == Range<RunIndex>(runIndex, runIndex + 1)); 128cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, line.runRange.width() == 1); 129cb93a386Sopenharmony_ci auto& run = testVisitor.fTestRuns[runIndex]; 130cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, line.lineText == run.dirTextRange); 131cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, runIndex <= 1 ? line.hardBreak : !line.hardBreak); 132cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, SkScalarNearlyEqual(verticalOffset, line.bounds.fTop)); 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_ci // There is only one line that is wrapped and it has enough trailing spaces to exceed the line width 135cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, (line.index == 2 ? line.bounds.width() > 440.0f: line.bounds.width() < 440.0f)); 136cb93a386Sopenharmony_ci verticalOffset = line.bounds.fBottom; 137cb93a386Sopenharmony_ci ++runIndex; 138cb93a386Sopenharmony_ci } 139cb93a386Sopenharmony_ci} 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_ciUNIX_ONLY_TEST(SkText_WrappedText_LongRTL, reporter) { 142cb93a386Sopenharmony_ci sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Noto Naskh Arabic", 40.0f, SkFontStyle::Normal()); 143cb93a386Sopenharmony_ci if (fontChain->empty()) return; 144cb93a386Sopenharmony_ci 145cb93a386Sopenharmony_ci std::u16string utf16(u"يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُيَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُيَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ"); 146cb93a386Sopenharmony_ci UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size())); 147cb93a386Sopenharmony_ci if (!unicodeText.getUnicode()) return; 148cb93a386Sopenharmony_ci 149cb93a386Sopenharmony_ci FontBlock fontBlock(utf16.size(), fontChain); 150cb93a386Sopenharmony_ci auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1)); 151cb93a386Sopenharmony_ci auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr); 152cb93a386Sopenharmony_ci auto wrappedText = shapedText->wrap(&unicodeText, 800.0f, 800.0f); 153cb93a386Sopenharmony_ci 154cb93a386Sopenharmony_ci TestVisitor testVisitor; 155cb93a386Sopenharmony_ci wrappedText->visit(&testVisitor); 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines.size() == 4); 158cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestRuns.size() == 4); 159cb93a386Sopenharmony_ci 160cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[0].trailingSpaces.width() == 1); 161cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[1].trailingSpaces.width() == 1); 162cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[2].trailingSpaces.width() == 1); 163cb93a386Sopenharmony_ci REPORTER_ASSERT(reporter, testVisitor.fTestLines[3].trailingSpaces.width() == 0); 164cb93a386Sopenharmony_ci} 165cb93a386Sopenharmony_ci 166