1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 * Copyright (C) 1999-2014, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 *******************************************************************************
10 */
11
12 #ifndef USING_ICULEHB /* C API not available under HB */
13
14 #include "unicode/utypes.h"
15 #include "unicode/ubidi.h"
16 #include "unicode/uscript.h"
17 #include "unicode/ctest.h"
18
19 #include "layout/LETypes.h"
20 #include "layout/LEScripts.h"
21 #include "layout/loengine.h"
22
23 #include "layout/playout.h"
24 #include "layout/plruns.h"
25
26 #include "cfonts.h"
27
28 #include "letest.h"
29
30 #include "sfnt.h"
31 #include "xmlreader.h"
32 #include "putilimp.h" /* for U_FILE_SEP_STRING */
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #define CH_COMMA 0x002C
39
40 U_CDECL_BEGIN
ParamTest(void)41 static void U_CALLCONV ParamTest(void)
42 {
43 LEErrorCode status = LE_NO_ERROR;
44 le_font *font = le_simpleFontOpen(12, &status);
45 le_engine *engine = le_create(font, arabScriptCode, -1, 0, &status);
46 LEGlyphID *glyphs = NULL;
47 le_int32 *indices = NULL;
48 float *positions = NULL;
49 le_int32 glyphCount = 0;
50
51 float x = 0.0, y = 0.0;
52 LEUnicode chars[] = {
53 0x0045, 0x006E, 0x0067, 0x006C, 0x0069, 0x0073, 0x0068, 0x0020, /* "English " */
54 0x0645, 0x0627, 0x0646, 0x062A, 0x0648, 0x0634, /* MEM ALIF KAF NOON TEH WAW SHEEN */
55 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x02E /* " text." */
56 };
57
58
59 glyphCount = le_getGlyphCount(engine, &status);
60 if (glyphCount != 0) {
61 log_err("Calling getGlyphCount() on an empty layout returned %d.\n", glyphCount);
62 }
63
64 glyphs = NEW_ARRAY(LEGlyphID, glyphCount + 10);
65 indices = NEW_ARRAY(le_int32, glyphCount + 10);
66 positions = NEW_ARRAY(float, glyphCount + 10);
67
68 le_getGlyphs(engine, NULL, &status);
69
70 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
71 log_err("Calling getGlyphs(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
72 }
73
74 status = LE_NO_ERROR;
75 le_getGlyphs(engine, glyphs, &status);
76
77 if (status != LE_NO_LAYOUT_ERROR) {
78 log_err("Calling getGlyphs(glyphs, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
79 }
80
81 status = LE_NO_ERROR;
82 le_getCharIndices(engine, NULL, &status);
83
84 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
85 log_err("Calling getCharIndices(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
86 }
87
88 status = LE_NO_ERROR;
89 le_getCharIndices(engine, indices, &status);
90
91 if (status != LE_NO_LAYOUT_ERROR) {
92 log_err("Calling getCharIndices(indices, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
93 }
94
95 status = LE_NO_ERROR;
96 le_getCharIndicesWithBase(engine, NULL, 1024, &status);
97
98 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
99 log_err("Calling getCharIndices(NULL, 1024, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
100 }
101
102 status = LE_NO_ERROR;
103 le_getCharIndicesWithBase(engine, indices, 1024, &status);
104
105 if (status != LE_NO_LAYOUT_ERROR) {
106 log_err("Calling getCharIndices(indices, 1024, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
107 }
108
109 status = LE_NO_ERROR;
110 le_getGlyphPositions(engine, NULL, &status);
111
112 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
113 log_err("Calling getGlyphPositions(NULL, status) did not return LE_ILLEGAL_ARGUMENT_ERROR.\n");
114 }
115
116 status = LE_NO_ERROR;
117 le_getGlyphPositions(engine, positions, &status);
118
119 if (status != LE_NO_LAYOUT_ERROR) {
120 log_err("Calling getGlyphPositions(positions, status) on an empty layout did not return LE_NO_LAYOUT_ERROR.\n");
121 }
122
123 DELETE_ARRAY(positions);
124 DELETE_ARRAY(indices);
125 DELETE_ARRAY(glyphs);
126
127 status = LE_NO_ERROR;
128 glyphCount = le_layoutChars(engine, NULL, 0, 0, 0, false, 0.0, 0.0, &status);
129
130 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
131 log_err("Calling layoutChars(NULL, 0, 0, 0, false, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
132 }
133
134 status = LE_NO_ERROR;
135 glyphCount = le_layoutChars(engine, chars, -1, 6, 20, true, 0.0, 0.0, &status);
136
137 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
138 log_err("Calling layoutChars(chars, -1, 6, 20, true, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
139 }
140
141 status = LE_NO_ERROR;
142 glyphCount = le_layoutChars(engine, chars, 8, -1, 20, true, 0.0, 0.0, &status);
143
144 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
145 log_err("Calling layoutChars(chars, 8, -1, 20, true, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
146 }
147
148 status = LE_NO_ERROR;
149 glyphCount = le_layoutChars(engine, chars, 8, 6, -1, true, 0.0, 0.0, &status);
150
151 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
152 log_err("Calling layoutChars((chars, 8, 6, -1, true, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
153 }
154
155 status = LE_NO_ERROR;
156 glyphCount = le_layoutChars(engine, chars, 8, 6, 10, true, 0.0, 0.0, &status);
157
158 if (status != LE_ILLEGAL_ARGUMENT_ERROR) {
159 log_err("Calling layoutChars(chars, 8, 6, 10, true, 0.0, 0.0, status) did not fail w/ LE_ILLEGAL_ARGUMENT_ERROR.\n");
160 }
161
162 status = LE_NO_ERROR;
163 glyphCount = le_layoutChars(engine, chars, 8, 6, 20, true, 0.0, 0.0, &status);
164
165 if (LE_FAILURE(status)) {
166 log_err("Calling layoutChars(chars, 8, 6, 20, true, 0.0, 0.0, status) failed.\n");
167 goto bail;
168 }
169
170 le_getGlyphPosition(engine, -1, &x, &y, &status);
171
172 if (status != LE_INDEX_OUT_OF_BOUNDS_ERROR) {
173 log_err("Calling getGlyphPosition(-1, x, y, status) did not fail w/ LE_INDEX_OUT_OF_BOUNDS_ERROR.\n");
174 }
175
176 status = LE_NO_ERROR;
177 le_getGlyphPosition(engine, glyphCount + 1, &x, &y, &status);
178
179 if (status != LE_INDEX_OUT_OF_BOUNDS_ERROR) {
180 log_err("Calling getGlyphPosition(glyphCount + 1, x, y, status) did not fail w/ LE_INDEX_OUT_OF_BOUNDS_ERROR.\n");
181 }
182
183 bail:
184 le_close(engine);
185 le_fontClose(font);
186 }
187 U_CDECL_END
188
189 U_CDECL_BEGIN
FactoryTest(void)190 static void U_CALLCONV FactoryTest(void)
191 {
192 LEErrorCode status = LE_NO_ERROR;
193 le_font *font = le_simpleFontOpen(12, &status);
194 le_engine *engine = NULL;
195 le_int32 scriptCode;
196
197 for(scriptCode = 0; scriptCode < scriptCodeCount; scriptCode += 1) {
198 status = LE_NO_ERROR;
199 engine = le_create(font, scriptCode, -1, 0, &status);
200
201 if (LE_FAILURE(status)) {
202 log_err("Could not create a LayoutEngine for script \'%s\'.\n", uscript_getShortName((UScriptCode)scriptCode));
203 }
204
205 le_close(engine);
206 }
207
208 le_fontClose(font);
209 }
210 U_CDECL_END
211
212 U_CDECL_BEGIN
AccessTest(void)213 static void U_CALLCONV AccessTest(void)
214 {
215 LEErrorCode status = LE_NO_ERROR;
216 le_font *font = le_simpleFontOpen(12, &status);
217 le_engine *engine =le_create(font, arabScriptCode, -1, 0, &status);
218 le_int32 glyphCount;
219 LEGlyphID glyphs[6];
220 le_int32 biasedIndices[6], indices[6], glyph;
221 float positions[6 * 2 + 2];
222 LEUnicode chars[] = {
223 0x0045, 0x006E, 0x0067, 0x006C, 0x0069, 0x0073, 0x0068, 0x0020, /* "English " */
224 0x0645, 0x0627, 0x0646, 0x062A, 0x0648, 0x0634, /* MEM ALIF KAF NOON TEH WAW SHEEN */
225 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x02E /* " text." */
226 };
227
228 if (LE_FAILURE(status)) {
229 log_err("Could not create LayoutEngine.\n");
230 goto bail;
231 }
232
233 glyphCount = le_layoutChars(engine, chars, 8, 6, 20, true, 0.0, 0.0, &status);
234
235 if (LE_FAILURE(status) || glyphCount != 6) {
236 log_err("layoutChars(chars, 8, 6, 20, true, 0.0, 0.0, status) failed.\n");
237 goto bail;
238 }
239
240 le_getGlyphs(engine, glyphs, &status);
241 le_getCharIndices(engine, indices, &status);
242 le_getGlyphPositions(engine, positions, &status);
243
244 if (LE_FAILURE(status)) {
245 log_err("Could not get glyph, indices and position arrays.\n");
246 goto bail;
247 }
248
249 status = LE_NO_ERROR;
250 le_getCharIndicesWithBase(engine, biasedIndices, 1024, &status);
251
252 if (LE_FAILURE(status)) {
253 log_err("getCharIndices(biasedIndices, 1024, status) failed.\n");
254 } else {
255 for (glyph = 0; glyph < glyphCount; glyph += 1) {
256 if (biasedIndices[glyph] != (indices[glyph] + 1024)) {
257 log_err("biasedIndices[%d] != indices[%d] + 1024: %8X, %8X\n",
258 glyph, glyph, biasedIndices[glyph], indices[glyph]);
259 break;
260 }
261 }
262 }
263
264 status = LE_NO_ERROR;
265 for (glyph = 0; glyph <= glyphCount; glyph += 1) {
266 float x = 0.0, y = 0.0;
267
268 le_getGlyphPosition(engine, glyph, &x, &y, &status);
269
270 if (LE_FAILURE(status)) {
271 log_err("getGlyphPosition(%d, x, y, status) failed.\n", glyph);
272 break;
273 }
274
275 if (x != positions[glyph*2] || y != positions[glyph*2 + 1]) {
276 log_err("getGlyphPosition(%d, x, y, status) returned bad position: (%f, %f) != (%f, %f)\n",
277 glyph, x, y, positions[glyph*2], positions[glyph*2 + 1]);
278 break;
279 }
280 }
281
282 bail:
283 le_close(engine);
284 le_fontClose(font);
285 }
286 U_CDECL_END
287
compareResults(const char * testID,TestResult * expected,TestResult * actual)288 static le_bool compareResults(const char *testID, TestResult *expected, TestResult *actual)
289 {
290 le_int32 i;
291
292 /* NOTE: we'll stop on the first failure 'cause once there's one error, it may cascade... */
293 if (actual->glyphCount != expected->glyphCount) {
294 log_err("Test %s: incorrect glyph count: expected %d, got %d\n",
295 testID, expected->glyphCount, actual->glyphCount);
296 return false;
297 }
298
299 for (i = 0; i < actual->glyphCount; i += 1) {
300 if (actual->glyphs[i] != expected->glyphs[i]) {
301 log_err("Test %s: incorrect id for glyph %d: expected %4X, got %4X\n",
302 testID, i, expected->glyphs[i], actual->glyphs[i]);
303 return false;
304 }
305 }
306
307 for (i = 0; i < actual->glyphCount; i += 1) {
308 if (actual->indices[i] != expected->indices[i]) {
309 log_err("Test %s: incorrect index for glyph %d: expected %8X, got %8X\n",
310 testID, i, expected->indices[i], actual->indices[i]);
311 return false;
312 }
313 }
314
315 for (i = 0; i <= actual->glyphCount; i += 1) {
316 double xError = uprv_fabs(actual->positions[i * 2] - expected->positions[i * 2]);
317 double yError = uprv_fabs(actual->positions[i * 2 + 1] - expected->positions[i * 2 + 1]);
318
319 if (xError > 0.0001) {
320 log_err("Test %s: incorrect x position for glyph %d: expected %f, got %f\n",
321 testID, i, expected->positions[i * 2], actual->positions[i * 2]);
322 return false;
323 }
324
325 if (yError < 0) {
326 yError = -yError;
327 }
328
329 if (yError > 0.0001) {
330 log_err("Test %s: incorrect y position for glyph %d: expected %f, got %f\n",
331 testID, i, expected->positions[i * 2 + 1], actual->positions[i * 2 + 1]);
332 return false;
333 }
334 }
335
336 return true;
337 }
338
checkFontVersion(le_font * font,const char * testVersionString,le_uint32 testChecksum,const char * testID)339 static void checkFontVersion(le_font *font, const char *testVersionString,
340 le_uint32 testChecksum, const char *testID)
341 {
342 le_uint32 fontChecksum = le_getFontChecksum(font);
343
344 if (fontChecksum != testChecksum) {
345 const char *fontVersionString = le_getNameString(font, NAME_VERSION_STRING,
346 PLATFORM_MACINTOSH, MACINTOSH_ROMAN, MACINTOSH_ENGLISH);
347 const LEUnicode16 *uFontVersionString = NULL;
348
349 if (fontVersionString == NULL) {
350 uFontVersionString = le_getUnicodeNameString(font, NAME_VERSION_STRING,
351 PLATFORM_MICROSOFT, MICROSOFT_UNICODE_BMP, MICROSOFT_ENGLISH);
352 }
353
354 log_info("Test %s: this may not be the same font used to generate the test data.\n", testID);
355
356 if (uFontVersionString != NULL) {
357 log_info("Your font's version string is \"%S\"\n", uFontVersionString);
358 le_deleteUnicodeNameString(font, uFontVersionString);
359 } else {
360 log_info("Your font's version string is \"%s\"\n", fontVersionString);
361 le_deleteNameString(font, fontVersionString);
362 }
363
364 log_info("The expected version string is \"%s\"\n", testVersionString);
365 log_info("If you see errors, they may be due to the version of the font you're using.\n");
366 }
367 }
368
369 /* Returns the path to icu/source/test/testdata/ */
getSourceTestData()370 static const char *getSourceTestData() {
371 #ifdef U_TOPSRCDIR
372 const char *srcDataDir = U_TOPSRCDIR U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
373 #else
374 const char *srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
375 FILE *f = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"rbbitst.txt", "r");
376
377 if (f != NULL) {
378 /* We're in icu/source/test/letest/ */
379 fclose(f);
380 } else {
381 /* We're in icu/source/test/letest/(Debug|Release) */
382 srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
383 }
384 #endif
385
386 return srcDataDir;
387 }
388
getPath(char buffer[2048],const char * filename)389 static const char *getPath(char buffer[2048], const char *filename) {
390 const char *testDataDirectory = getSourceTestData();
391
392 strcpy(buffer, testDataDirectory);
393 strcat(buffer, filename);
394
395 return buffer;
396 }
397
openFont(const char * fontName,const char * checksum,const char * version,const char * testID)398 static le_font *openFont(const char *fontName, const char *checksum, const char *version, const char *testID)
399 {
400 char path[2048];
401 le_font *font;
402 LEErrorCode fontStatus = LE_NO_ERROR;
403
404 if (fontName != NULL) {
405 font = le_portableFontOpen(getPath(path, fontName), 12, &fontStatus);
406
407 if (LE_FAILURE(fontStatus)) {
408 log_info("Test %s: can't open font %s - test skipped.\n", testID, fontName);
409 le_fontClose(font);
410 return NULL;
411 } else {
412 le_uint32 cksum = 0;
413
414 sscanf(checksum, "%x", &cksum);
415
416 checkFontVersion(font, version, cksum, testID);
417 }
418 } else {
419 font = le_simpleFontOpen(12, &fontStatus);
420 }
421
422 return font;
423 }
424
getRTL(const LEUnicode * text,le_int32 charCount)425 static le_bool getRTL(const LEUnicode *text, le_int32 charCount)
426 {
427 UBiDiLevel level;
428 le_int32 limit = -1;
429 UErrorCode status = U_ZERO_ERROR;
430 UBiDi *ubidi = ubidi_openSized(charCount, 0, &status);
431
432 ubidi_setPara(ubidi, text, charCount, UBIDI_DEFAULT_LTR, NULL, &status);
433
434 /* TODO: Should check that there's only a single logical run... */
435 ubidi_getLogicalRun(ubidi, 0, &limit, &level);
436
437 ubidi_close(ubidi);
438
439 return level & 1;
440 }
441
doTestCase(const char * testID,const char * fontName,const char * fontVersion,const char * fontChecksum,le_int32 scriptCode,le_int32 languageCode,const LEUnicode * text,le_int32 charCount,TestResult * expected)442 static void doTestCase (const char *testID,
443 const char *fontName,
444 const char *fontVersion,
445 const char *fontChecksum,
446 le_int32 scriptCode,
447 le_int32 languageCode,
448 const LEUnicode *text,
449 le_int32 charCount,
450 TestResult *expected)
451 {
452 LEErrorCode status = LE_NO_ERROR;
453 le_engine *engine;
454 le_font *font = openFont(fontName, fontChecksum, fontVersion, testID);
455 le_int32 typoFlags = 3; /* kerning + ligatures */
456 TestResult actual;
457
458 if (font == NULL) {
459 /* error message already printed. */
460 return;
461 }
462
463 if (fontName == NULL) {
464 typoFlags |= 0x80000000L; /* use CharSubstitutionFilter... */
465 }
466
467 engine = le_create(font, scriptCode, languageCode, typoFlags, &status);
468
469 if (LE_FAILURE(status)) {
470 log_err("Test %s: could not create a LayoutEngine.\n", testID);
471 goto free_expected;
472 }
473
474 actual.glyphCount = le_layoutChars(engine, text, 0, charCount, charCount, getRTL(text, charCount), 0, 0, &status);
475
476 actual.glyphs = NEW_ARRAY(LEGlyphID, actual.glyphCount);
477 actual.indices = NEW_ARRAY(le_int32, actual.glyphCount);
478 actual.positions = NEW_ARRAY(float, actual.glyphCount * 2 + 2);
479
480 le_getGlyphs(engine, actual.glyphs, &status);
481 le_getCharIndices(engine, actual.indices, &status);
482 le_getGlyphPositions(engine, actual.positions, &status);
483
484 compareResults(testID, expected, &actual);
485
486 DELETE_ARRAY(actual.positions);
487 DELETE_ARRAY(actual.indices);
488 DELETE_ARRAY(actual.glyphs);
489
490 le_close(engine);
491
492 free_expected:
493 le_fontClose(font);
494 }
495
DataDrivenTest(void)496 static void U_CALLCONV DataDrivenTest(void)
497 {
498 char path[2048];
499 const char *testFilePath = getPath(path, "letest.xml");
500
501 readTestFile(testFilePath, doTestCase);
502 }
503
504 /*
505 * From ticket:5923:
506 *
507 * Build a paragraph that contains a mixture of left to right and right to left text.
508 * Break it into multiple lines and make sure that the glyphToCharMap for run in each
509 * line is correct.
510 *
511 * Note: it might be a good idea to also check the glyphs and positions for each run,
512 * that we get the expected number of runs per line and that the line breaks are where
513 * we expect them to be. Really, it would be a good idea to make a whole test suite
514 * for pl_paragraph.
515 */
GlyphToCharTest(void)516 static void U_CALLCONV GlyphToCharTest(void)
517 {
518 #if !UCONFIG_NO_BREAK_ITERATION
519 LEErrorCode status = LE_NO_ERROR;
520 le_font *font;
521 pl_fontRuns *fontRuns;
522 pl_paragraph *paragraph;
523 const pl_line *line;
524 /*
525 * This is the same text that's in <icu>/source/samples/layout/Sample.txt
526 */
527 LEUnicode chars[] = {
528 /*BOM*/ 0x0054, 0x0068, 0x0065, 0x0020, 0x004c, 0x0061, 0x0079,
529 0x006f, 0x0075, 0x0074, 0x0045, 0x006e, 0x0067, 0x0069, 0x006e,
530 0x0065, 0x0020, 0x0064, 0x006f, 0x0065, 0x0073, 0x0020, 0x0061,
531 0x006c, 0x006c, 0x0020, 0x0074, 0x0068, 0x0065, 0x0020, 0x0077,
532 0x006f, 0x0072, 0x006b, 0x0020, 0x006e, 0x0065, 0x0063, 0x0065,
533 0x0073, 0x0073, 0x0061, 0x0072, 0x0079, 0x0020, 0x0074, 0x006f,
534 0x0020, 0x0064, 0x0069, 0x0073, 0x0070, 0x006c, 0x0061, 0x0079,
535 0x0020, 0x0055, 0x006e, 0x0069, 0x0063, 0x006f, 0x0064, 0x0065,
536 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072,
537 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e,
538 0x0020, 0x006c, 0x0061, 0x006e, 0x0067, 0x0075, 0x0061, 0x0067,
539 0x0065, 0x0073, 0x0020, 0x0077, 0x0069, 0x0074, 0x0068, 0x0020,
540 0x0063, 0x006f, 0x006d, 0x0070, 0x006c, 0x0065, 0x0078, 0x0020,
541 0x0077, 0x0072, 0x0069, 0x0074, 0x0069, 0x006e, 0x0067, 0x0020,
542 0x0073, 0x0079, 0x0073, 0x0074, 0x0065, 0x006d, 0x0073, 0x0020,
543 0x0073, 0x0075, 0x0063, 0x0068, 0x0020, 0x0061, 0x0073, 0x0020,
544 0x0048, 0x0069, 0x006e, 0x0064, 0x0069, 0x0020, 0x0028, 0x0939,
545 0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0029, 0x0020, 0x0054,
546 0x0068, 0x0061, 0x0069, 0x0020, 0x0028, 0x0e44, 0x0e17, 0x0e22,
547 0x0029, 0x0020, 0x0061, 0x006e, 0x0064, 0x0020, 0x0041, 0x0072,
548 0x0061, 0x0062, 0x0069, 0x0063, 0x0020, 0x0028, 0x0627, 0x0644,
549 0x0639, 0x0631, 0x0628, 0x064a, 0x0629, 0x0029, 0x002e, 0x0020,
550 0x0048, 0x0065, 0x0072, 0x0065, 0x0027, 0x0073, 0x0020, 0x0061,
551 0x0020, 0x0073, 0x0061, 0x006d, 0x0070, 0x006c, 0x0065, 0x0020,
552 0x006f, 0x0066, 0x0020, 0x0073, 0x006f, 0x006d, 0x0065, 0x0020,
553 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072, 0x0069,
554 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e, 0x0020,
555 0x0053, 0x0061, 0x006e, 0x0073, 0x006b, 0x0072, 0x0069, 0x0074,
556 0x003a, 0x0020, 0x0936, 0x094d, 0x0930, 0x0940, 0x092e, 0x0926,
557 0x094d, 0x0020, 0x092d, 0x0917, 0x0935, 0x0926, 0x094d, 0x0917,
558 0x0940, 0x0924, 0x093e, 0x0020, 0x0905, 0x0927, 0x094d, 0x092f,
559 0x093e, 0x092f, 0x0020, 0x0905, 0x0930, 0x094d, 0x091c, 0x0941,
560 0x0928, 0x0020, 0x0935, 0x093f, 0x0937, 0x093e, 0x0926, 0x0020,
561 0x092f, 0x094b, 0x0917, 0x0020, 0x0927, 0x0943, 0x0924, 0x0930,
562 0x093e, 0x0937, 0x094d, 0x091f, 0x094d, 0x0930, 0x0020, 0x0909,
563 0x0935, 0x093e, 0x091a, 0x0964, 0x0020, 0x0927, 0x0930, 0x094d,
564 0x092e, 0x0915, 0x094d, 0x0937, 0x0947, 0x0924, 0x094d, 0x0930,
565 0x0947, 0x0020, 0x0915, 0x0941, 0x0930, 0x0941, 0x0915, 0x094d,
566 0x0937, 0x0947, 0x0924, 0x094d, 0x0930, 0x0947, 0x0020, 0x0938,
567 0x092e, 0x0935, 0x0947, 0x0924, 0x093e, 0x0020, 0x092f, 0x0941,
568 0x092f, 0x0941, 0x0924, 0x094d, 0x0938, 0x0935, 0x0903, 0x0020,
569 0x092e, 0x093e, 0x092e, 0x0915, 0x093e, 0x0903, 0x0020, 0x092a,
570 0x093e, 0x0923, 0x094d, 0x0921, 0x0935, 0x093e, 0x0936, 0x094d,
571 0x091a, 0x0948, 0x0935, 0x0020, 0x0915, 0x093f, 0x092e, 0x0915,
572 0x0941, 0x0930, 0x094d, 0x0935, 0x0924, 0x0020, 0x0938, 0x0902,
573 0x091c, 0x092f, 0x0020, 0x0048, 0x0065, 0x0072, 0x0065, 0x0027,
574 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d, 0x0070,
575 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073, 0x006f,
576 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020,
577 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020,
578 0x0069, 0x006e, 0x0020, 0x0041, 0x0072, 0x0061, 0x0062, 0x0069,
579 0x0063, 0x003a, 0x0020, 0x0623, 0x0633, 0x0627, 0x0633, 0x064b,
580 0x0627, 0x060c, 0x0020, 0x062a, 0x062a, 0x0639, 0x0627, 0x0645,
581 0x0644, 0x0020, 0x0627, 0x0644, 0x062d, 0x0648, 0x0627, 0x0633,
582 0x064a, 0x0628, 0x0020, 0x0641, 0x0642, 0x0637, 0x0020, 0x0645,
583 0x0639, 0x0020, 0x0627, 0x0644, 0x0623, 0x0631, 0x0642, 0x0627,
584 0x0645, 0x060c, 0x0020, 0x0648, 0x062a, 0x0642, 0x0648, 0x0645,
585 0x0020, 0x0628, 0x062a, 0x062e, 0x0632, 0x064a, 0x0646, 0x0020,
586 0x0627, 0x0644, 0x0623, 0x062d, 0x0631, 0x0641, 0x0020, 0x0648,
587 0x0627, 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020,
588 0x0627, 0x0644, 0x0623, 0x062e, 0x0631, 0x0649, 0x0020, 0x0628,
589 0x0639, 0x062f, 0x0020, 0x0623, 0x0646, 0x0020, 0x062a, 0x064f,
590 0x0639, 0x0637, 0x064a, 0x0020, 0x0631, 0x0642, 0x0645, 0x0627,
591 0x0020, 0x0645, 0x0639, 0x064a, 0x0646, 0x0627, 0x0020, 0x0644,
592 0x0643, 0x0644, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
593 0x0645, 0x0646, 0x0647, 0x0627, 0x002e, 0x0020, 0x0648, 0x0642,
594 0x0628, 0x0644, 0x0020, 0x0627, 0x062e, 0x062a, 0x0631, 0x0627,
595 0x0639, 0x0020, 0x0022, 0x064a, 0x0648, 0x0646, 0x0650, 0x0643,
596 0x0648, 0x062f, 0x0022, 0x060c, 0x0020, 0x0643, 0x0627, 0x0646,
597 0x0020, 0x0647, 0x0646, 0x0627, 0x0643, 0x0020, 0x0645, 0x0626,
598 0x0627, 0x062a, 0x0020, 0x0627, 0x0644, 0x0623, 0x0646, 0x0638,
599 0x0645, 0x0629, 0x0020, 0x0644, 0x0644, 0x062a, 0x0634, 0x0641,
600 0x064a, 0x0631, 0x0020, 0x0648, 0x062a, 0x062e, 0x0635, 0x064a,
601 0x0635, 0x0020, 0x0647, 0x0630, 0x0647, 0x0020, 0x0627, 0x0644,
602 0x0623, 0x0631, 0x0642, 0x0627, 0x0645, 0x0020, 0x0644, 0x0644,
603 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x060c, 0x0020, 0x0648,
604 0x0644, 0x0645, 0x0020, 0x064a, 0x0648, 0x062c, 0x062f, 0x0020,
605 0x0646, 0x0638, 0x0627, 0x0645, 0x0020, 0x062a, 0x0634, 0x0641,
606 0x064a, 0x0631, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
607 0x064a, 0x062d, 0x062a, 0x0648, 0x064a, 0x0020, 0x0639, 0x0644,
608 0x0649, 0x0020, 0x062c, 0x0645, 0x064a, 0x0639, 0x0020, 0x0627,
609 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020, 0x0627,
610 0x0644, 0x0636, 0x0631, 0x0648, 0x0631, 0x064a, 0x0629, 0x0020,
611 0x0061, 0x006e, 0x0064, 0x0020, 0x0068, 0x0065, 0x0072, 0x0065,
612 0x0027, 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d,
613 0x0070, 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073,
614 0x006f, 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074,
615 0x0020, 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e,
616 0x0020, 0x0069, 0x006e, 0x0020, 0x0054, 0x0068, 0x0061, 0x0069,
617 0x003a, 0x0020, 0x0e1a, 0x0e17, 0x0e17, 0x0e35, 0x0e48, 0x0e51,
618 0x0e1e, 0x0e32, 0x0e22, 0x0e38, 0x0e44, 0x0e0b, 0x0e42, 0x0e04,
619 0x0e25, 0x0e19, 0x0e42, 0x0e14, 0x0e42, 0x0e23, 0x0e18, 0x0e35,
620 0x0e2d, 0x0e32, 0x0e28, 0x0e31, 0x0e22, 0x0e2d, 0x0e22, 0x0e39,
621 0x0e48, 0x0e17, 0x0e48, 0x0e32, 0x0e21, 0x0e01, 0x0e25, 0x0e32,
622 0x0e07, 0x0e17, 0x0e38, 0x0e48, 0x0e07, 0x0e43, 0x0e2b, 0x0e0d,
623 0x0e48, 0x0e43, 0x0e19, 0x0e41, 0x0e04, 0x0e19, 0x0e0b, 0x0e31,
624 0x0e2a, 0x0e01, 0x0e31, 0x0e1a, 0x0e25, 0x0e38, 0x0e07, 0x0e40,
625 0x0e2e, 0x0e19, 0x0e23, 0x0e35, 0x0e0a, 0x0e32, 0x0e27, 0x0e44,
626 0x0e23, 0x0e48, 0x0e41, 0x0e25, 0x0e30, 0x0e1b, 0x0e49, 0x0e32,
627 0x0e40, 0x0e2d, 0x0e47, 0x0e21, 0x0e20, 0x0e23, 0x0e23, 0x0e22,
628 0x0e32, 0x0e0a, 0x0e32, 0x0e27, 0x0e44, 0x0e23, 0x0e48, 0x0e1a,
629 0x0e49, 0x0e32, 0x0e19, 0x0e02, 0x0e2d, 0x0e07, 0x0e1e, 0x0e27,
630 0x0e01, 0x0e40, 0x0e02, 0x0e32, 0x0e2b, 0x0e25, 0x0e31, 0x0e07,
631 0x0e40, 0x0e25, 0x0e47, 0x0e01, 0x0e40, 0x0e1e, 0x0e23, 0x0e32,
632 0x0e30, 0x0e44, 0x0e21, 0x0e49, 0x0e2a, 0x0e23, 0x0e49, 0x0e32,
633 0x0e07, 0x0e1a, 0x0e49, 0x0e32, 0x0e19, 0x0e15, 0x0e49, 0x0e2d,
634 0x0e07, 0x0e02, 0x0e19, 0x0e21, 0x0e32, 0x0e14, 0x0e49, 0x0e27,
635 0x0e22, 0x0e40, 0x0e01, 0x0e27, 0x0e35, 0x0e22, 0x0e19, 0x0e40,
636 0x0e1b, 0x0e47, 0x0e19, 0x0e23, 0x0e30, 0x0e22, 0x0e30, 0x0e17,
637 0x0e32, 0x0e07, 0x0e2b, 0x0e25, 0x0e32, 0x0e22, 0x0e44, 0x0e21,
638 0x0e25, 0x0e4c
639 };
640 le_int32 charCount = LE_ARRAY_SIZE(chars);
641 le_int32 charIndex = 0, lineNumber = 1;
642 le_int32 run, i;
643 const float lineWidth = 600;
644
645 font = le_simpleFontOpen(12, &status);
646
647 if (LE_FAILURE(status)) {
648 log_err("le_simpleFontOpen(12, &status) failed");
649 goto finish;
650 }
651
652 fontRuns = pl_openEmptyFontRuns(0);
653 pl_addFontRun(fontRuns, font, charCount);
654
655 paragraph = pl_create(chars, charCount, fontRuns, NULL, NULL, NULL, 0, false, &status);
656
657 pl_closeFontRuns(fontRuns);
658
659 if (LE_FAILURE(status)) {
660 log_err("pl_create failed.");
661 goto close_font;
662 }
663
664 pl_reflow(paragraph);
665 while ((line = pl_nextLine(paragraph, lineWidth)) != NULL) {
666 le_int32 runCount = pl_countLineRuns(line);
667
668 for(run = 0; run < runCount; run += 1) {
669 const pl_visualRun *visualRun = pl_getLineVisualRun(line, run);
670 const le_int32 glyphCount = pl_getVisualRunGlyphCount(visualRun);
671 const le_int32 *glyphToCharMap = pl_getVisualRunGlyphToCharMap(visualRun);
672
673 if (pl_getVisualRunDirection(visualRun) == UBIDI_RTL) {
674 /*
675 * For a right to left run, make sure that the character indices
676 * increase from the right most glyph to the left most glyph. If
677 * there are any one to many glyph substitutions, we might get several
678 * glyphs in a row with the same character index.
679 */
680 for(i = glyphCount - 1; i >= 0; i -= 1) {
681 le_int32 ix = glyphToCharMap[i];
682
683 if (ix != charIndex) {
684 if (ix != charIndex - 1) {
685 log_err("Bad glyph to char index for glyph %d on line %d: expected %d, got %d\n",
686 i, lineNumber, charIndex, ix);
687 goto close_paragraph; /* once there's one error, we can't count on anything else... */
688 }
689 } else {
690 charIndex += 1;
691 }
692 }
693 } else {
694 /*
695 * We can't just check the order of the character indices
696 * for left to right runs because Indic text might have been
697 * reordered. What we can do is find the minimum and maximum
698 * character indices in the run and make sure that the minimum
699 * is equal to charIndex and then advance charIndex to the maximum.
700 */
701 le_int32 minIndex = 0x7FFFFFFF, maxIndex = -1;
702
703 for(i = 0; i < glyphCount; i += 1) {
704 le_int32 ix = glyphToCharMap[i];
705
706 if (ix > maxIndex) {
707 maxIndex = ix;
708 }
709
710 if (ix < minIndex) {
711 minIndex = ix;
712 }
713 }
714
715 if (minIndex != charIndex) {
716 log_err("Bad minIndex for run %d on line %d: expected %d, got %d\n",
717 run, lineNumber, charIndex, minIndex);
718 goto close_paragraph; /* once there's one error, we can't count on anything else... */
719 }
720
721 charIndex = maxIndex + 1;
722 }
723 }
724
725 lineNumber += 1;
726 }
727
728 close_paragraph:
729 pl_close(paragraph);
730
731 close_font:
732 le_fontClose(font);
733
734 finish:
735 return;
736 #endif
737 }
738
addCTests(TestNode ** root)739 U_CFUNC void addCTests(TestNode **root)
740 {
741 addTest(root, &ParamTest, "c_api/ParameterTest");
742 addTest(root, &FactoryTest, "c_api/FactoryTest");
743 addTest(root, &AccessTest, "c_layout/AccessTest");
744 addTest(root, &DataDrivenTest, "c_layout/DataDrivenTest");
745 addTest(root, &GlyphToCharTest, "c_paragraph/GlyphToCharTest");
746 }
747
748
749 #endif
750