Imported Upstream version 58.1
[platform/upstream/icu.git] / source / test / letest / cletest.c
1 // Copyright (C) 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
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
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
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
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: exptected %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
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/ */
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
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
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
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
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
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  */
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
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