• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 Google LLC.
2 #include "include/core/SkBitmap.h"
3 #include "include/core/SkCanvas.h"
4 #include "include/core/SkColor.h"
5 #include "include/core/SkEncodedImageFormat.h"
6 #include "include/core/SkFontMgr.h"
7 #include "include/core/SkFontStyle.h"
8 #include "include/core/SkImageEncoder.h"
9 #include "include/core/SkPaint.h"
10 #include "include/core/SkPoint.h"
11 #include "include/core/SkRect.h"
12 #include "include/core/SkRefCnt.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkSpan.h"
15 #include "include/core/SkStream.h"
16 #include "include/core/SkString.h"
17 #include "include/core/SkTypeface.h"
18 #include "include/core/SkTypes.h"
19 #include "tests/Test.h"
20 #include "tools/Resources.h"
21 
22 #include "experimental/sktext/include/Text.h"
23 #include "experimental/sktext/src/Paint.h"
24 
25 #include <string.h>
26 #include <algorithm>
27 #include <limits>
28 #include <memory>
29 #include <string>
30 #include <utility>
31 #include <vector>
32 
33 struct GrContextOptions;
34 
35 #define VeryLongCanvasWidth 1000000
36 #define TestCanvasWidth 1000
37 #define TestCanvasHeight 600
38 
39 using namespace skia::text;
40 
41 struct TestLine {
42     size_t index;
43     TextRange lineText;
44     bool hardBreak;
45     SkRect bounds;
46     GlyphRange trailingSpaces;
47     Range<RunIndex> runRange;
48 };
49 
50 struct TestRun {
51     const SkFont& font;
52     DirTextRange dirTextRange;  // Currently we make sure that the run edges are the grapheme cluster edges
53     SkRect bounds;              // bounds contains the physical boundaries of the run
54     size_t trailingSpaces;      // Depending of TextDirection it goes right to the end (LTR) or left to the start (RTL)
55     SkSpan<const uint16_t> glyphs;
56     SkSpan<const SkPoint> positions;
57     SkSpan<const TextIndex> clusters;
58 };
59 
60 class TestVisitor : public Visitor {
61 public:
onBeginLine(size_t index,TextRange lineText,bool hardBreak,SkRect bounds)62     void onBeginLine(size_t index, TextRange lineText, bool hardBreak, SkRect bounds) override {
63         SkASSERT(fTestLines.size() == index);
64         fTestLines.push_back({ index, lineText, hardBreak, bounds, EMPTY_RANGE, Range<RunIndex>(fTestRuns.size(), fTestRuns.size()) });
65     }
onEndLine(size_t index,TextRange lineText,GlyphRange trailingSpaces,size_t glyphCount)66     void onEndLine(size_t index, TextRange lineText, GlyphRange trailingSpaces, size_t glyphCount) override {
67         SkASSERT(fTestLines.size() == index + 1);
68         fTestLines.back().trailingSpaces = trailingSpaces;
69         fTestLines.back().runRange.fEnd = fTestRuns.size();
70     }
onGlyphRun(const SkFont & font,DirTextRange dirTextRange,SkRect bounds,TextIndex trailingSpaces,size_t glyphCount,const uint16_t glyphs[],const SkPoint positions[],const TextIndex clusters[])71     void onGlyphRun(const SkFont& font,
72                     DirTextRange dirTextRange,
73                     SkRect bounds,
74                     TextIndex trailingSpaces,
75                     size_t glyphCount,            // Just the number of glyphs
76                     const uint16_t glyphs[],
77                     const SkPoint positions[],        // Positions relative to the line
78                     const TextIndex clusters[]) override
79     {
80         fTestRuns.push_back({font, dirTextRange, bounds, trailingSpaces,
81                             SkSpan<const uint16_t>(&glyphs[0], glyphCount),
82                             SkSpan<const SkPoint>(&positions[0], glyphCount + 1),
83                             SkSpan<const TextIndex>(&clusters[0], glyphCount + 1),
84                             });
85     }
onPlaceholder(TextRange,const SkRect & bounds)86     void onPlaceholder(TextRange, const SkRect& bounds) override { }
87 
88     std::vector<TestLine> fTestLines;
89     std::vector<TestRun> fTestRuns;
90 };
91 
UNIX_ONLY_TEST(SkText_WrappedText_Spaces,reporter)92 UNIX_ONLY_TEST(SkText_WrappedText_Spaces, reporter) {
93     sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Roboto", 40.0f, SkFontStyle::Normal());
94     if (fontChain->empty()) return;
95 
96     std::u16string utf16(u"    Leading spaces\nTrailing spaces    \nLong text with collapsed      spaces inside wrapped into few lines");
97     UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size()));
98     if (!unicodeText.getUnicode()) return;
99 
100     FontBlock fontBlock(utf16.size(), fontChain);
101     auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1));
102     auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr);
103     auto wrappedText = shapedText->wrap(&unicodeText, 440.0f, 500.0f);
104 
105     TestVisitor testVisitor;
106     wrappedText->visit(&testVisitor);
107 
108     REPORTER_ASSERT(reporter, testVisitor.fTestLines.size() == 5);
109     REPORTER_ASSERT(reporter, testVisitor.fTestRuns.size() == 5);
110 
111     REPORTER_ASSERT(reporter, testVisitor.fTestLines[0].trailingSpaces.width() == 0);
112     REPORTER_ASSERT(reporter, testVisitor.fTestLines[1].trailingSpaces.width() == 4);
113     REPORTER_ASSERT(reporter, testVisitor.fTestLines[2].trailingSpaces.width() == 6);
114     REPORTER_ASSERT(reporter, testVisitor.fTestLines[3].trailingSpaces.width() == 1);
115     REPORTER_ASSERT(reporter, testVisitor.fTestLines[4].trailingSpaces.width() == 0);
116 
117     auto break1 = utf16.find_first_of(u"\n");
118     auto break2 = utf16.find_last_of(u"\n");
119 
120     REPORTER_ASSERT(reporter, testVisitor.fTestLines[0].lineText.width() == break1);
121     REPORTER_ASSERT(reporter, testVisitor.fTestLines[1].lineText.width() == break2 - break1 - 1);
122 
123     RunIndex runIndex = 0;
124     SkScalar verticalOffset = 0.0f;
125     for (int lineIndex = 0; lineIndex < testVisitor.fTestLines.size(); ++lineIndex) {
126         auto& line = testVisitor.fTestLines[lineIndex];
127         REPORTER_ASSERT(reporter, line.runRange == Range<RunIndex>(runIndex, runIndex + 1));
128         REPORTER_ASSERT(reporter, line.runRange.width() == 1);
129         auto& run = testVisitor.fTestRuns[runIndex];
130         REPORTER_ASSERT(reporter, line.lineText == run.dirTextRange);
131         REPORTER_ASSERT(reporter, runIndex <= 1 ? line.hardBreak : !line.hardBreak);
132         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(verticalOffset, line.bounds.fTop));
133 
134         // There is only one line that is wrapped and it has enough trailing spaces to exceed the line width
135         REPORTER_ASSERT(reporter, (line.index == 2 ? line.bounds.width() > 440.0f: line.bounds.width() < 440.0f));
136         verticalOffset = line.bounds.fBottom;
137         ++runIndex;
138     }
139 }
140 
UNIX_ONLY_TEST(SkText_WrappedText_LongRTL,reporter)141 UNIX_ONLY_TEST(SkText_WrappedText_LongRTL, reporter) {
142     sk_sp<TrivialFontChain> fontChain = sk_make_sp<TrivialFontChain>("Noto Naskh Arabic", 40.0f, SkFontStyle::Normal());
143     if (fontChain->empty()) return;
144 
145     std::u16string utf16(u"يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُيَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُيَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ يَهْدِيْكُمُ اللَّهُ وَيُصْلِحُ بَالَكُمُ");
146     UnicodeText unicodeText(SkUnicode::Make(), SkSpan<uint16_t>((uint16_t*)utf16.data(), utf16.size()));
147     if (!unicodeText.getUnicode()) return;
148 
149     FontBlock fontBlock(utf16.size(), fontChain);
150     auto fontResolvedText = unicodeText.resolveFonts(SkSpan<FontBlock>(&fontBlock, 1));
151     auto shapedText = fontResolvedText->shape(&unicodeText, TextDirection::kLtr);
152     auto wrappedText = shapedText->wrap(&unicodeText, 800.0f, 800.0f);
153 
154     TestVisitor testVisitor;
155     wrappedText->visit(&testVisitor);
156 
157     REPORTER_ASSERT(reporter, testVisitor.fTestLines.size() == 4);
158     REPORTER_ASSERT(reporter, testVisitor.fTestRuns.size() == 4);
159 
160     REPORTER_ASSERT(reporter, testVisitor.fTestLines[0].trailingSpaces.width() == 1);
161     REPORTER_ASSERT(reporter, testVisitor.fTestLines[1].trailingSpaces.width() == 1);
162     REPORTER_ASSERT(reporter, testVisitor.fTestLines[2].trailingSpaces.width() == 1);
163     REPORTER_ASSERT(reporter, testVisitor.fTestLines[3].trailingSpaces.width() == 0);
164 }
165 
166