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