1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "minikin/MeasuredText.h"
18 
19 #include <gtest/gtest.h>
20 
21 #include "minikin/LineBreaker.h"
22 #include "minikin/Measurement.h"
23 
24 #include "FontTestUtils.h"
25 #include "UnicodeUtils.h"
26 
27 namespace minikin {
28 
29 constexpr float CHAR_WIDTH = 10.0;  // Mock implementation always returns 10.0 for advance.
30 
TEST(MeasuredTextTest,RunTests)31 TEST(MeasuredTextTest, RunTests) {
32     constexpr uint32_t CHAR_COUNT = 6;
33     constexpr float REPLACEMENT_WIDTH = 20.0f;
34     auto font = buildFontCollection("Ascii.ttf");
35     int lbStyle = (int)LineBreakStyle::None;
36     int lbWordStyle = (int)LineBreakWordStyle::None;
37 
38     MeasuredTextBuilder builder;
39 
40     MinikinPaint paint1(font);
41     paint1.size = 10.0f;  // make 1em = 10px
42     builder.addStyleRun(0, 2, std::move(paint1), lbStyle, lbWordStyle, true /* can hyphenate */,
43                         false /* is RTL */);
44     builder.addReplacementRun(2, 4, REPLACEMENT_WIDTH, 0 /* locale list id */);
45     MinikinPaint paint2(font);
46     paint2.size = 10.0f;  // make 1em = 10px
47     builder.addStyleRun(4, 6, std::move(paint2), lbStyle, lbWordStyle, true /* can hyphenate */,
48                         false /* is RTL */);
49 
50     std::vector<uint16_t> text(CHAR_COUNT, 'a');
51 
52     std::unique_ptr<MeasuredText> measuredText = builder.build(
53             text, true /* compute hyphenation */, false /* compute full layout */,
54             false /* computeBounds */, false /* ignore kerning */, nullptr /* no hint */);
55 
56     ASSERT_TRUE(measuredText);
57 
58     // ReplacementRun assigns all width to the first character and leave zeros others.
59     std::vector<float> expectedWidths = {CHAR_WIDTH, CHAR_WIDTH, REPLACEMENT_WIDTH,
60                                          0,          CHAR_WIDTH, CHAR_WIDTH};
61 
62     EXPECT_EQ(expectedWidths, measuredText->widths);
63 }
64 
TEST(MeasuredTextTest,getBoundsTest)65 TEST(MeasuredTextTest, getBoundsTest) {
66     auto text = utf8ToUtf16("Hello, World!");
67     auto font = buildFontCollection("Ascii.ttf");
68     int lbStyle = (int)LineBreakStyle::None;
69     int lbWordStyle = (int)LineBreakWordStyle::None;
70 
71     MeasuredTextBuilder builder;
72     MinikinPaint paint(font);
73     paint.size = 10.0f;
74     builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
75                         true /* can hyphenate */, false /* is RTL */);
76     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
77                             false /* computeBounds */, false /* ignore kerning */,
78                             nullptr /* no hint */);
79 
80     EXPECT_EQ(MinikinRect(0.0f, 0.0f, 0.0f, 0.0f), mt->getBounds(text, Range(0, 0)));
81     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(0, 1)));
82     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 20.0f, 0.0f), mt->getBounds(text, Range(0, 2)));
83     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(1, 2)));
84     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 130.0f, 0.0f), mt->getBounds(text, Range(0, text.size())));
85 }
86 
TEST(MeasuredTextTest,getBoundsTest_LTR)87 TEST(MeasuredTextTest, getBoundsTest_LTR) {
88     auto text = utf8ToUtf16("\u0028");  // U+0028 has 1em in LTR, 3em in RTL.
89     auto font = buildFontCollection("Bbox.ttf");
90     int lbStyle = (int)LineBreakStyle::None;
91     int lbWordStyle = (int)LineBreakWordStyle::None;
92 
93     MeasuredTextBuilder builder;
94     MinikinPaint paint(font);
95     paint.size = 10.0f;
96     builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
97                         true /* can hyphenate */, false /* is RTL */);
98     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
99                             false /* computeBounds */, false /* ignore kerning */,
100                             nullptr /* no hint */);
101 
102     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(0, 1)));
103 }
104 
TEST(MeasuredTextTest,getBoundsTest_RTL)105 TEST(MeasuredTextTest, getBoundsTest_RTL) {
106     auto text = utf8ToUtf16("\u0028");  // U+0028 has 1em in LTR, 3em in RTL.
107     auto font = buildFontCollection("Bbox.ttf");
108     int lbStyle = (int)LineBreakStyle::None;
109     int lbWordStyle = (int)LineBreakWordStyle::None;
110 
111     MeasuredTextBuilder builder;
112     MinikinPaint paint(font);
113     paint.size = 10.0f;
114     builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
115                         true /* can hyphenate */, true /* is RTL */);
116     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
117                             false /* computeBounds */, false /* ignore kerning */,
118                             nullptr /* no hint */);
119 
120     EXPECT_EQ(MinikinRect(0.0f, -30.0f, 30.0f, 0.0f), mt->getBounds(text, Range(0, 2)));
121 }
122 
TEST(MeasuredTextTest,getBoundsTest_multiStyle)123 TEST(MeasuredTextTest, getBoundsTest_multiStyle) {
124     auto text = utf8ToUtf16("Hello, World!");
125     auto font = buildFontCollection("Ascii.ttf");
126     uint32_t helloLength = 7;  // length of "Hello, "
127     int lbStyle = (int)LineBreakStyle::None;
128     int lbWordStyle = (int)LineBreakWordStyle::None;
129 
130     MeasuredTextBuilder builder;
131     MinikinPaint paint(font);
132     paint.size = 10.0f;
133     builder.addStyleRun(0, helloLength, std::move(paint), lbStyle, lbWordStyle,
134                         true /* can hyphenate */, false /* is RTL */);
135     MinikinPaint paint2(font);
136     paint2.size = 20.0f;
137     builder.addStyleRun(helloLength, text.size(), std::move(paint2), lbStyle, lbWordStyle,
138                         true /* can hyphenate */, false /* is RTL */);
139     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
140                             false /* computeBounds */, false /* ignore kerning */,
141                             nullptr /* no hint */);
142 
143     // In this example, the glyph shape is as wollows.
144     // (y axis, em unit)
145     // -2               ┌───┬───┬───┬───┬───┬───┐
146     //                  │   │   │   │   │   │   │
147     // -1 ┌─┬─┬─┬─┬─┬─┬─┤ W │ o │ r │ l │ d │ ! │
148     //    │H│e│l│l│o│,│ │   │   │   │   │   │   │
149     //  0 └─┴─┴─┴─┴─┴─┴─┴───┴───┴───┴───┴───┴───┘
150     //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9  (x axis, em unit)
151     EXPECT_EQ(MinikinRect(0.0f, 0.0f, 0.0f, 0.0f), mt->getBounds(text, Range(0, 0)));
152     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(0, 1)));
153     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 20.0f, 0.0f), mt->getBounds(text, Range(0, 2)));
154     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), mt->getBounds(text, Range(1, 2)));
155     EXPECT_EQ(MinikinRect(0.0f, 0.0f, 0.0f, 0.0f), mt->getBounds(text, Range(7, 7)));
156     EXPECT_EQ(MinikinRect(0.0f, -20.0f, 20.0f, 0.0f), mt->getBounds(text, Range(7, 8)));
157     EXPECT_EQ(MinikinRect(0.0f, -20.0f, 30.0f, 0.0f), mt->getBounds(text, Range(6, 8)));
158     EXPECT_EQ(MinikinRect(0.0f, -20.0f, 190.0f, 0.0f), mt->getBounds(text, Range(0, text.size())));
159 }
160 
TEST(MeasuredTextTest,getExtentTest)161 TEST(MeasuredTextTest, getExtentTest) {
162     auto text = utf8ToUtf16("Hello, World!");
163     auto font = buildFontCollection("Ascii.ttf");
164     int lbStyle = (int)LineBreakStyle::None;
165     int lbWordStyle = (int)LineBreakWordStyle::None;
166 
167     MeasuredTextBuilder builder;
168     MinikinPaint paint(font);
169     paint.size = 10.0f;
170     builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
171                         true /* can hyphenate */, false /* is RTL */);
172     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
173                             false /* computeBounds */, false /* ignore kernign */,
174                             nullptr /* no hint */);
175 
176     EXPECT_EQ(MinikinExtent(0.0f, 0.0f), mt->getExtent(text, Range(0, 0)));
177     EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(0, 1)));
178     EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(0, 2)));
179     EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(1, 2)));
180     EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(0, text.size())));
181 }
182 
TEST(MeasuredTextTest,getExtentTest_multiStyle)183 TEST(MeasuredTextTest, getExtentTest_multiStyle) {
184     auto text = utf8ToUtf16("Hello, World!");
185     auto font = buildFontCollection("Ascii.ttf");
186     uint32_t helloLength = 7;  // length of "Hello, "
187     int lbStyle = (int)LineBreakStyle::None;
188     int lbWordStyle = (int)LineBreakWordStyle::None;
189 
190     MeasuredTextBuilder builder;
191     MinikinPaint paint(font);
192     paint.size = 10.0f;
193     builder.addStyleRun(0, helloLength, std::move(paint), lbStyle, lbWordStyle,
194                         true /* can hyphenate */, false /* is RTL */);
195     MinikinPaint paint2(font);
196     paint2.size = 20.0f;
197     builder.addStyleRun(helloLength, text.size(), std::move(paint2), 0 /* no line break */,
198                         0 /* no line break word style */, true /* can hyphenate */,
199                         false /* is RTL */);
200     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
201                             false /* computeBounds */, false /* ignore kerning */,
202                             nullptr /* no hint */);
203 
204     EXPECT_EQ(MinikinExtent(0.0f, 0.0f), mt->getExtent(text, Range(0, 0)));
205     EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(0, 1)));
206     EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(0, 2)));
207     EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), mt->getExtent(text, Range(1, 2)));
208     EXPECT_EQ(MinikinExtent(0.0f, 0.0f), mt->getExtent(text, Range(7, 7)));
209     EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), mt->getExtent(text, Range(7, 8)));
210     EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), mt->getExtent(text, Range(6, 8)));
211     EXPECT_EQ(MinikinExtent(-160.0f, 40.0f), mt->getExtent(text, Range(0, text.size())));
212 }
213 
TEST(MeasuredTextTest,buildLayoutTest)214 TEST(MeasuredTextTest, buildLayoutTest) {
215     auto text = utf8ToUtf16("Hello, World!");
216     auto font = buildFontCollection("Ascii.ttf");
217     Range fullContext(0, text.size());
218     int lbStyle = (int)LineBreakStyle::None;
219     int lbWordStyle = (int)LineBreakWordStyle::None;
220 
221     MeasuredTextBuilder builder;
222     MinikinPaint paint(font);
223     paint.size = 10.0f;
224     builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
225                         true /* can hyphenate */, false /* is RTL */);
226     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
227                             false /* computeBounds */, false /* ignore kerning */,
228                             nullptr /* no hint */);
229 
230     MinikinRect rect;
231     MinikinPaint samePaint(font);
232     samePaint.size = 10.0f;
233 
234     Layout layout = mt->buildLayout(text, Range(0, 0), fullContext, samePaint,
235                                     StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
236     EXPECT_EQ(0u, layout.nGlyphs());
237 
238     layout = mt->buildLayout(text, Range(0, 1), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
239                              EndHyphenEdit::NO_EDIT);
240     ASSERT_EQ(1u, layout.nGlyphs());
241     EXPECT_TRUE(layout.getFont(0));
242     EXPECT_EQ(0.0f, layout.getX(0));
243     EXPECT_EQ(0.0f, layout.getY(0));
244     EXPECT_EQ(10.0f, layout.getAdvance());
245     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
246     EXPECT_EQ(1u, layout.getAdvances().size());
247     getBounds(text, Range(0, 1), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
248               EndHyphenEdit::NO_EDIT, &rect);
249     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), rect);
250 
251     layout = mt->buildLayout(text, Range(0, 2), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
252                              EndHyphenEdit::NO_EDIT);
253     ASSERT_EQ(2u, layout.nGlyphs());
254     EXPECT_TRUE(layout.getFont(0) && layout.getFont(0) == layout.getFont(1));
255     EXPECT_EQ(0.0f, layout.getX(0));
256     EXPECT_EQ(0.0f, layout.getY(0));
257     EXPECT_EQ(10.0f, layout.getX(1));
258     EXPECT_EQ(0.0f, layout.getY(1));
259     EXPECT_EQ(20.0f, layout.getAdvance());
260     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
261     EXPECT_EQ(10.0f, layout.getCharAdvance(1));
262     EXPECT_EQ(2u, layout.getAdvances().size());
263     getBounds(text, Range(0, 2), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
264               EndHyphenEdit::NO_EDIT, &rect);
265     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 20.0f, 0.0f), rect);
266 
267     layout = mt->buildLayout(text, Range(1, 2), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
268                              EndHyphenEdit::NO_EDIT);
269     ASSERT_EQ(1u, layout.nGlyphs());
270     EXPECT_TRUE(layout.getFont(0));
271     EXPECT_EQ(0.0f, layout.getX(0));
272     EXPECT_EQ(0.0f, layout.getY(0));
273     EXPECT_EQ(10.0f, layout.getAdvance());
274     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
275     EXPECT_EQ(1u, layout.getAdvances().size());
276     getBounds(text, Range(1, 2), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
277               EndHyphenEdit::NO_EDIT, &rect);
278     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), rect);
279 
280     layout = mt->buildLayout(text, Range(0, text.size()), fullContext, samePaint,
281                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
282     ASSERT_EQ(text.size(), layout.nGlyphs());
283     EXPECT_TRUE(layout.getFont(0));
284     for (uint32_t i = 0; i < text.size(); ++i) {
285         EXPECT_EQ(layout.getFont(0), layout.getFont(i)) << i;
286         EXPECT_EQ(10.0f * i, layout.getX(i)) << i;
287         EXPECT_EQ(0.0f, layout.getY(i)) << i;
288         EXPECT_EQ(10.0f, layout.getCharAdvance(i)) << i;
289     }
290     EXPECT_EQ(130.0f, layout.getAdvance());
291     EXPECT_EQ(text.size(), layout.getAdvances().size());
292     getBounds(text, Range(0, text.size()), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
293               EndHyphenEdit::NO_EDIT, &rect);
294     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 130.0f, 0.0f), rect);
295 }
296 
TEST(MeasuredTextTest,buildLayoutTest_multiStyle)297 TEST(MeasuredTextTest, buildLayoutTest_multiStyle) {
298     auto text = utf8ToUtf16("Hello, World!");
299     auto font = buildFontCollection("Ascii.ttf");
300     uint32_t helloLength = 7;  // length of "Hello, "
301     Range fullContext(0, text.size());
302     int lbStyle = (int)LineBreakStyle::None;
303     int lbWordStyle = (int)LineBreakWordStyle::None;
304 
305     MeasuredTextBuilder builder;
306     MinikinPaint paint(font);
307     paint.size = 10.0f;
308     builder.addStyleRun(0, helloLength, std::move(paint), lbStyle, lbWordStyle,
309                         true /* can hyphenate */, false /* is RTL */);
310     MinikinPaint paint2(font);
311     paint2.size = 20.0f;
312     builder.addStyleRun(helloLength, text.size(), std::move(paint2), lbStyle, lbWordStyle,
313                         true /* can hyphenate */, false /* is RTL */);
314     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
315                             false /* computeBounds */, false /* ignore kerning */,
316                             nullptr /* no hint */);
317 
318     MinikinRect rect;
319     MinikinPaint samePaint(font);
320     samePaint.size = 10.0f;
321 
322     Layout layout = mt->buildLayout(text, Range(0, 0), fullContext, samePaint,
323                                     StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
324     EXPECT_EQ(0u, layout.nGlyphs());
325 
326     layout = mt->buildLayout(text, Range(0, 1), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
327                              EndHyphenEdit::NO_EDIT);
328     ASSERT_EQ(1u, layout.nGlyphs());
329     EXPECT_TRUE(layout.getFont(0));
330     EXPECT_EQ(0.0f, layout.getX(0));
331     EXPECT_EQ(0.0f, layout.getY(0));
332     EXPECT_EQ(10.0f, layout.getAdvance());
333     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
334     EXPECT_EQ(1u, layout.getAdvances().size());
335     getBounds(text, Range(0, 1), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
336               EndHyphenEdit::NO_EDIT, &rect);
337     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), rect);
338 
339     layout = mt->buildLayout(text, Range(0, 2), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
340                              EndHyphenEdit::NO_EDIT);
341     ASSERT_EQ(2u, layout.nGlyphs());
342     EXPECT_TRUE(layout.getFont(0) && layout.getFont(0) == layout.getFont(1));
343     EXPECT_EQ(0.0f, layout.getX(0));
344     EXPECT_EQ(0.0f, layout.getY(0));
345     EXPECT_EQ(10.0f, layout.getX(1));
346     EXPECT_EQ(0.0f, layout.getY(1));
347     EXPECT_EQ(20.0f, layout.getAdvance());
348     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
349     EXPECT_EQ(10.0f, layout.getCharAdvance(1));
350     EXPECT_EQ(2u, layout.getAdvances().size());
351     getBounds(text, Range(0, 2), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
352               EndHyphenEdit::NO_EDIT, &rect);
353     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 20.0f, 0.0f), rect);
354 
355     layout = mt->buildLayout(text, Range(1, 2), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
356                              EndHyphenEdit::NO_EDIT);
357     ASSERT_EQ(1u, layout.nGlyphs());
358     EXPECT_TRUE(layout.getFont(0));
359     EXPECT_EQ(0.0f, layout.getX(0));
360     EXPECT_EQ(0.0f, layout.getY(0));
361     EXPECT_EQ(10.0f, layout.getAdvance());
362     EXPECT_EQ(10.0f, layout.getCharAdvance(0));
363     EXPECT_EQ(1u, layout.getAdvances().size());
364     getBounds(text, Range(1, 2), Bidi::LTR, samePaint, StartHyphenEdit::NO_EDIT,
365               EndHyphenEdit::NO_EDIT, &rect);
366     EXPECT_EQ(MinikinRect(0.0f, -10.0f, 10.0f, 0.0f), rect);
367 
368     layout = mt->buildLayout(text, Range(7, 7), fullContext, samePaint, StartHyphenEdit::NO_EDIT,
369                              EndHyphenEdit::NO_EDIT);
370     EXPECT_EQ(0u, layout.nGlyphs());
371 
372     MinikinPaint samePaint2(font);
373     samePaint2.size = 20.0f;
374     layout = mt->buildLayout(text, Range(7, 8), fullContext, samePaint2, StartHyphenEdit::NO_EDIT,
375                              EndHyphenEdit::NO_EDIT);
376     ASSERT_EQ(1u, layout.nGlyphs());
377     EXPECT_TRUE(layout.getFont(0));
378     EXPECT_EQ(0.0f, layout.getX(0));
379     EXPECT_EQ(0.0f, layout.getY(0));
380     EXPECT_EQ(20.0f, layout.getAdvance());
381     EXPECT_EQ(20.0f, layout.getCharAdvance(0));
382     EXPECT_EQ(1u, layout.getAdvances().size());
383     getBounds(text, Range(7, 8), Bidi::LTR, samePaint2, StartHyphenEdit::NO_EDIT,
384               EndHyphenEdit::NO_EDIT, &rect);
385     EXPECT_EQ(MinikinRect(0.0f, -20.0f, 20.0f, 0.0f), rect);
386 }
387 
TEST(MeasuredTextTest,buildLayoutTest_differentPaint)388 TEST(MeasuredTextTest, buildLayoutTest_differentPaint) {
389     auto text = utf8ToUtf16("Hello, World!");
390     auto font = buildFontCollection("Ascii.ttf");
391     Range fullContext(0, text.size());
392     int lbStyle = (int)LineBreakStyle::None;
393     int lbWordStyle = (int)LineBreakWordStyle::None;
394 
395     MeasuredTextBuilder builder;
396     MinikinPaint paint(font);
397     paint.size = 10.0f;
398     builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
399                         true /* can hyphenate */, false /* is RTL */);
400     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
401                             false /* computeBounds */, false /* ignore kerning */,
402                             nullptr /* no hint */);
403 
404     MinikinRect rect;
405     MinikinPaint differentPaint(font);
406     differentPaint.size = 20.0f;
407 
408     Layout layout = mt->buildLayout(text, Range(0, 0), fullContext, differentPaint,
409                                     StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
410     EXPECT_EQ(0u, layout.nGlyphs());
411 
412     layout = mt->buildLayout(text, Range(0, 1), fullContext, differentPaint,
413                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
414     ASSERT_EQ(1u, layout.nGlyphs());
415     EXPECT_TRUE(layout.getFont(0));
416     EXPECT_EQ(0.0f, layout.getX(0));
417     EXPECT_EQ(0.0f, layout.getY(0));
418     EXPECT_EQ(20.0f, layout.getAdvance());
419     EXPECT_EQ(20.0f, layout.getCharAdvance(0));
420     EXPECT_EQ(1u, layout.getAdvances().size());
421     getBounds(text, Range(0, 1), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
422               EndHyphenEdit::NO_EDIT, &rect);
423     EXPECT_EQ(MinikinRect(0.0f, -20.0f, 20.0f, 0.0f), rect);
424 
425     layout = mt->buildLayout(text, Range(0, 2), fullContext, differentPaint,
426                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
427     ASSERT_EQ(2u, layout.nGlyphs());
428     EXPECT_TRUE(layout.getFont(0) && layout.getFont(0) == layout.getFont(1));
429     EXPECT_EQ(0.0f, layout.getX(0));
430     EXPECT_EQ(0.0f, layout.getY(0));
431     EXPECT_EQ(20.0f, layout.getX(1));
432     EXPECT_EQ(0.0f, layout.getY(1));
433     EXPECT_EQ(40.0f, layout.getAdvance());
434     EXPECT_EQ(20.0f, layout.getCharAdvance(0));
435     EXPECT_EQ(20.0f, layout.getCharAdvance(1));
436     EXPECT_EQ(2u, layout.getAdvances().size());
437     getBounds(text, Range(0, 2), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
438               EndHyphenEdit::NO_EDIT, &rect);
439     EXPECT_EQ(MinikinRect(0.0f, -20.0f, 40.0f, 0.0f), rect);
440 
441     layout = mt->buildLayout(text, Range(1, 2), fullContext, differentPaint,
442                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
443     ASSERT_EQ(1u, layout.nGlyphs());
444     EXPECT_TRUE(layout.getFont(0));
445     EXPECT_EQ(0.0f, layout.getX(0));
446     EXPECT_EQ(0.0f, layout.getY(0));
447     EXPECT_EQ(20.0f, layout.getAdvance());
448     EXPECT_EQ(20.0f, layout.getCharAdvance(0));
449     EXPECT_EQ(1u, layout.getAdvances().size());
450     getBounds(text, Range(1, 2), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
451               EndHyphenEdit::NO_EDIT, &rect);
452     EXPECT_EQ(MinikinRect(0.0f, -20.0f, 20.0f, 0.0f), rect);
453 
454     layout = mt->buildLayout(text, Range(0, text.size()), fullContext, differentPaint,
455                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
456     ASSERT_EQ(text.size(), layout.nGlyphs());
457     EXPECT_TRUE(layout.getFont(0));
458     for (uint32_t i = 0; i < text.size(); ++i) {
459         EXPECT_EQ(layout.getFont(0), layout.getFont(i)) << i;
460         EXPECT_EQ(20.0f * i, layout.getX(i)) << i;
461         EXPECT_EQ(0.0f, layout.getY(i)) << i;
462         EXPECT_EQ(20.0f, layout.getCharAdvance(i)) << i;
463     }
464     EXPECT_EQ(260.0f, layout.getAdvance());
465     EXPECT_EQ(text.size(), layout.getAdvances().size());
466     getBounds(text, Range(0, text.size()), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
467               EndHyphenEdit::NO_EDIT, &rect);
468     EXPECT_EQ(MinikinRect(0.0f, -20.0f, 260.0f, 0.0f), rect);
469 }
470 
TEST(MeasuredTextTest,buildLayoutTest_multiStyle_differentPaint)471 TEST(MeasuredTextTest, buildLayoutTest_multiStyle_differentPaint) {
472     auto text = utf8ToUtf16("Hello, World!");
473     auto font = buildFontCollection("Ascii.ttf");
474     uint32_t helloLength = 7;  // length of "Hello, "
475     Range fullContext(0, text.size());
476     int lbStyle = (int)LineBreakStyle::None;
477     int lbWordStyle = (int)LineBreakWordStyle::None;
478 
479     MeasuredTextBuilder builder;
480     MinikinPaint paint(font);
481     paint.size = 10.0f;
482     builder.addStyleRun(0, helloLength, std::move(paint), lbStyle, lbWordStyle,
483                         true /* can hyphenate */, false /* is RTL */);
484     MinikinPaint paint2(font);
485     paint2.size = 20.0f;
486     builder.addStyleRun(helloLength, text.size(), std::move(paint2), lbStyle, lbWordStyle,
487                         true /* can hyphenate */, false /* is RTL */);
488     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
489                             false /* computeBounds */, false /* ignore kerning */,
490                             nullptr /* no hint */);
491 
492     MinikinRect rect;
493     MinikinPaint differentPaint(font);
494     differentPaint.size = 30.0f;
495 
496     Layout layout = mt->buildLayout(text, Range(0, 0), fullContext, differentPaint,
497                                     StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
498     EXPECT_EQ(0u, layout.nGlyphs());
499 
500     layout = mt->buildLayout(text, Range(0, 1), fullContext, differentPaint,
501                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
502     ASSERT_EQ(1u, layout.nGlyphs());
503     EXPECT_TRUE(layout.getFont(0));
504     EXPECT_EQ(0.0f, layout.getX(0));
505     EXPECT_EQ(0.0f, layout.getY(0));
506     EXPECT_EQ(30.0f, layout.getAdvance());
507     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
508     EXPECT_EQ(1u, layout.getAdvances().size());
509     getBounds(text, Range(0, 1), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
510               EndHyphenEdit::NO_EDIT, &rect);
511     EXPECT_EQ(MinikinRect(0.0f, -30.0f, 30.0f, 0.0f), rect);
512 
513     layout = mt->buildLayout(text, Range(0, 2), fullContext, differentPaint,
514                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
515     ASSERT_EQ(2u, layout.nGlyphs());
516     EXPECT_TRUE(layout.getFont(0) && layout.getFont(0) == layout.getFont(1));
517     EXPECT_EQ(0.0f, layout.getX(0));
518     EXPECT_EQ(0.0f, layout.getY(0));
519     EXPECT_EQ(30.0f, layout.getX(1));
520     EXPECT_EQ(0.0f, layout.getY(1));
521     EXPECT_EQ(60.0f, layout.getAdvance());
522     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
523     EXPECT_EQ(30.0f, layout.getCharAdvance(1));
524     EXPECT_EQ(2u, layout.getAdvances().size());
525     getBounds(text, Range(0, 2), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
526               EndHyphenEdit::NO_EDIT, &rect);
527     EXPECT_EQ(MinikinRect(0.0f, -30.0f, 60.0f, 0.0f), rect);
528 
529     layout = mt->buildLayout(text, Range(1, 2), fullContext, differentPaint,
530                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
531     ASSERT_EQ(1u, layout.nGlyphs());
532     EXPECT_TRUE(layout.getFont(0));
533     EXPECT_EQ(0.0f, layout.getX(0));
534     EXPECT_EQ(0.0f, layout.getY(0));
535     EXPECT_EQ(30.0f, layout.getAdvance());
536     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
537     EXPECT_EQ(1u, layout.getAdvances().size());
538     getBounds(text, Range(1, 2), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
539               EndHyphenEdit::NO_EDIT, &rect);
540     EXPECT_EQ(MinikinRect(0.0f, -30.0f, 30.0f, 0.0f), rect);
541 
542     layout = mt->buildLayout(text, Range(7, 7), fullContext, differentPaint,
543                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
544     EXPECT_EQ(0u, layout.nGlyphs());
545 
546     layout = mt->buildLayout(text, Range(7, 8), fullContext, differentPaint,
547                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
548     ASSERT_EQ(1u, layout.nGlyphs());
549     EXPECT_TRUE(layout.getFont(0));
550     EXPECT_EQ(0.0f, layout.getX(0));
551     EXPECT_EQ(0.0f, layout.getY(0));
552     EXPECT_EQ(30.0f, layout.getAdvance());
553     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
554     EXPECT_EQ(1u, layout.getAdvances().size());
555     getBounds(text, Range(7, 8), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
556               EndHyphenEdit::NO_EDIT, &rect);
557     EXPECT_EQ(MinikinRect(0.0f, -30.0f, 30.0f, 0.0f), rect);
558 
559     layout = mt->buildLayout(text, Range(6, 8), fullContext, differentPaint,
560                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
561     ASSERT_EQ(2u, layout.nGlyphs());
562     EXPECT_TRUE(layout.getFont(0) && layout.getFont(0) == layout.getFont(1));
563     EXPECT_EQ(0.0f, layout.getX(0));
564     EXPECT_EQ(0.0f, layout.getY(0));
565     EXPECT_EQ(30.0f, layout.getX(1));
566     EXPECT_EQ(0.0f, layout.getY(1));
567     EXPECT_EQ(60.0f, layout.getAdvance());
568     EXPECT_EQ(30.0f, layout.getCharAdvance(0));
569     EXPECT_EQ(30.0f, layout.getCharAdvance(1));
570     EXPECT_EQ(2u, layout.getAdvances().size());
571     getBounds(text, Range(6, 8), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
572               EndHyphenEdit::NO_EDIT, &rect);
573     EXPECT_EQ(MinikinRect(0.0f, -30.0f, 60.0f, 0.0f), rect);
574 
575     layout = mt->buildLayout(text, Range(0, text.size()), fullContext, differentPaint,
576                              StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
577     ASSERT_EQ(text.size(), layout.nGlyphs());
578     EXPECT_TRUE(layout.getFont(0));
579     for (uint32_t i = 0; i < text.size(); ++i) {
580         EXPECT_EQ(layout.getFont(0), layout.getFont(i)) << i;
581         EXPECT_EQ(30.0f * i, layout.getX(i)) << i;
582         EXPECT_EQ(0.0f, layout.getY(i)) << i;
583         EXPECT_EQ(30.0f, layout.getCharAdvance(i)) << i;
584     }
585     EXPECT_EQ(390.0f, layout.getAdvance());
586     EXPECT_EQ(text.size(), layout.getAdvances().size());
587     getBounds(text, Range(0, text.size()), Bidi::LTR, differentPaint, StartHyphenEdit::NO_EDIT,
588               EndHyphenEdit::NO_EDIT, &rect);
589     EXPECT_EQ(MinikinRect(0.0f, -30.0f, 390.0f, 0.0f), rect);
590 }
591 
TEST(MeasuredTextTest,testLineBreakStyle_from_builder)592 TEST(MeasuredTextTest, testLineBreakStyle_from_builder) {
593     auto text = utf8ToUtf16("Hello, World!");
594     auto font = buildFontCollection("Ascii.ttf");
595     int lbStyle = (int)LineBreakStyle::Loose;           // loose
596     int lbWordStyle = (int)LineBreakWordStyle::Phrase;  // phrase
597 
598     MeasuredTextBuilder looseStyleBuilder;
599     MinikinPaint paint(font);
600     looseStyleBuilder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
601                                   true /* can hyphenate */, false);
602     auto mt = looseStyleBuilder.build(text, true /* hyphenation */, true /* full layout */,
603                                       false /* computeBounds */, false /* ignore kerning */,
604                                       nullptr /* no hint */);
605 
606     EXPECT_EQ((size_t)1, mt->runs.size());
607     EXPECT_EQ(LineBreakStyle::Loose, mt->runs[0]->lineBreakStyle());
608     EXPECT_EQ(LineBreakWordStyle::Phrase, mt->runs[0]->lineBreakWordStyle());
609 
610     lbStyle = (int)LineBreakStyle::Normal;  // normal
611     MeasuredTextBuilder normalStyleBuilder;
612     MinikinPaint normalStylePaint(font);
613     normalStyleBuilder.addStyleRun(0, text.size(), std::move(normalStylePaint), lbStyle,
614                                    lbWordStyle, true /* can hyphenate */, false);
615     mt = normalStyleBuilder.build(text, true /* hyphenation */, true /* full layout */,
616                                   false /* computeBounds */, false /* ignore kerning */,
617                                   nullptr /* no hint */);
618 
619     EXPECT_EQ((size_t)1, mt->runs.size());
620     EXPECT_EQ(LineBreakStyle::Normal, mt->runs[0]->lineBreakStyle());
621     EXPECT_EQ(LineBreakWordStyle::Phrase, mt->runs[0]->lineBreakWordStyle());
622 
623     lbStyle = (int)LineBreakStyle::Strict;        // strict
624     lbWordStyle = (int)LineBreakWordStyle::None;  // no word style
625     MeasuredTextBuilder strictStyleBuilder;
626     MinikinPaint strictStylePaint(font);
627     strictStyleBuilder.addStyleRun(0, text.size(), std::move(strictStylePaint), lbStyle,
628                                    lbWordStyle, true /* can hyphenate */, false);
629     mt = strictStyleBuilder.build(text, true /* hyphenation */, true /* full layout */,
630                                   false /* computeBounds */, false /* ignore kerning */,
631                                   nullptr /* no hint */);
632 
633     EXPECT_EQ((size_t)1, mt->runs.size());
634     EXPECT_EQ(LineBreakStyle::Strict, mt->runs[0]->lineBreakStyle());
635     EXPECT_EQ(LineBreakWordStyle::None, mt->runs[0]->lineBreakWordStyle());
636 }
637 
TEST(MeasuredTextTest,testLineBreakStyle_from_run)638 TEST(MeasuredTextTest, testLineBreakStyle_from_run) {
639     auto text = utf8ToUtf16("Hello, World!");
640     auto font = buildFontCollection("Ascii.ttf");
641     int lbStyle = (int)LineBreakStyle::Strict;
642     int lbWordStyle = (int)LineBreakWordStyle::Phrase;
643     Range range(0, text.size());
644     MinikinPaint paint(font);
645 
646     StyleRun styleRun(range, std::move(paint), lbStyle, lbWordStyle, true /* can hyphenate */,
647                       false /* isRtl */);
648     EXPECT_EQ(LineBreakStyle::Strict, styleRun.lineBreakStyle());
649     EXPECT_EQ(LineBreakWordStyle::Phrase, styleRun.lineBreakWordStyle());
650 
651     ReplacementRun replacementRun(range, 10.0f /* width */, 0 /* locale list id */);
652     EXPECT_EQ(LineBreakStyle::None, replacementRun.lineBreakStyle());
653     EXPECT_EQ(LineBreakWordStyle::None, replacementRun.lineBreakWordStyle());
654 }
655 
TEST(MeasuredTextTest,hasOverhang_false)656 TEST(MeasuredTextTest, hasOverhang_false) {
657     auto text = utf8ToUtf16("Hello, World!");
658     auto font = buildFontCollection("Ascii.ttf");
659     int lbStyle = (int)LineBreakStyle::None;
660     int lbWordStyle = (int)LineBreakWordStyle::None;
661 
662     MeasuredTextBuilder builder;
663     MinikinPaint paint(font);
664     paint.size = 10.0f;
665     builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
666                         true /* hyphenation */, false /* is RTL */);
667     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
668                             true /* computeBounds */, false /* ignore kerning */,
669                             nullptr /* no hint */);
670     EXPECT_FALSE(mt->hasOverhang(Range(0, text.size())));
671 }
672 
TEST(MeasuredTextTest,hasOverhang_true)673 TEST(MeasuredTextTest, hasOverhang_true) {
674     auto text = utf8ToUtf16("b");
675     auto font = buildFontCollection("OvershootTest.ttf");
676     int lbStyle = (int)LineBreakStyle::None;
677     int lbWordStyle = (int)LineBreakWordStyle::None;
678 
679     MeasuredTextBuilder builder;
680     MinikinPaint paint(font);
681     paint.size = 10.0f;
682     builder.addStyleRun(0, text.size(), std::move(paint), lbStyle, lbWordStyle,
683                         true /* hyphenation */, false /* is RTL */);
684     auto mt = builder.build(text, true /* hyphenation */, true /* full layout */,
685                             true /* computeBounds */, false /* ignore kerning */,
686                             nullptr /* no hint */);
687     EXPECT_TRUE(mt->hasOverhang(Range(0, text.size())));
688 }
689 
690 }  // namespace minikin
691