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