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