Home | History | Annotate | Download | only in letest
      1 /*
      2  *******************************************************************************
      3  *
      4  *   Copyright (C) 1999-2008, 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     LEErrorCode status = LE_NO_ERROR;
    515     le_font *font;
    516     pl_fontRuns *fontRuns;
    517     pl_paragraph *paragraph;
    518     const pl_line *line;
    519     /*
    520      * This is the same text that's in <icu>/source/samples/layout/Sample.txt
    521      */
    522     LEUnicode chars[] = {
    523         /*BOM*/ 0x0054, 0x0068, 0x0065, 0x0020, 0x004c, 0x0061, 0x0079,
    524         0x006f, 0x0075, 0x0074, 0x0045, 0x006e, 0x0067, 0x0069, 0x006e,
    525         0x0065, 0x0020, 0x0064, 0x006f, 0x0065, 0x0073, 0x0020, 0x0061,
    526         0x006c, 0x006c, 0x0020, 0x0074, 0x0068, 0x0065, 0x0020, 0x0077,
    527         0x006f, 0x0072, 0x006b, 0x0020, 0x006e, 0x0065, 0x0063, 0x0065,
    528         0x0073, 0x0073, 0x0061, 0x0072, 0x0079, 0x0020, 0x0074, 0x006f,
    529         0x0020, 0x0064, 0x0069, 0x0073, 0x0070, 0x006c, 0x0061, 0x0079,
    530         0x0020, 0x0055, 0x006e, 0x0069, 0x0063, 0x006f, 0x0064, 0x0065,
    531         0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072,
    532         0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e,
    533         0x0020, 0x006c, 0x0061, 0x006e, 0x0067, 0x0075, 0x0061, 0x0067,
    534         0x0065, 0x0073, 0x0020, 0x0077, 0x0069, 0x0074, 0x0068, 0x0020,
    535         0x0063, 0x006f, 0x006d, 0x0070, 0x006c, 0x0065, 0x0078, 0x0020,
    536         0x0077, 0x0072, 0x0069, 0x0074, 0x0069, 0x006e, 0x0067, 0x0020,
    537         0x0073, 0x0079, 0x0073, 0x0074, 0x0065, 0x006d, 0x0073, 0x0020,
    538         0x0073, 0x0075, 0x0063, 0x0068, 0x0020, 0x0061, 0x0073, 0x0020,
    539         0x0048, 0x0069, 0x006e, 0x0064, 0x0069, 0x0020, 0x0028, 0x0939,
    540         0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0029, 0x0020, 0x0054,
    541         0x0068, 0x0061, 0x0069, 0x0020, 0x0028, 0x0e44, 0x0e17, 0x0e22,
    542         0x0029, 0x0020, 0x0061, 0x006e, 0x0064, 0x0020, 0x0041, 0x0072,
    543         0x0061, 0x0062, 0x0069, 0x0063, 0x0020, 0x0028, 0x0627, 0x0644,
    544         0x0639, 0x0631, 0x0628, 0x064a, 0x0629, 0x0029, 0x002e, 0x0020,
    545         0x0048, 0x0065, 0x0072, 0x0065, 0x0027, 0x0073, 0x0020, 0x0061,
    546         0x0020, 0x0073, 0x0061, 0x006d, 0x0070, 0x006c, 0x0065, 0x0020,
    547         0x006f, 0x0066, 0x0020, 0x0073, 0x006f, 0x006d, 0x0065, 0x0020,
    548         0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072, 0x0069,
    549         0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e, 0x0020,
    550         0x0053, 0x0061, 0x006e, 0x0073, 0x006b, 0x0072, 0x0069, 0x0074,
    551         0x003a, 0x0020, 0x0936, 0x094d, 0x0930, 0x0940, 0x092e, 0x0926,
    552         0x094d, 0x0020, 0x092d, 0x0917, 0x0935, 0x0926, 0x094d, 0x0917,
    553         0x0940, 0x0924, 0x093e, 0x0020, 0x0905, 0x0927, 0x094d, 0x092f,
    554         0x093e, 0x092f, 0x0020, 0x0905, 0x0930, 0x094d, 0x091c, 0x0941,
    555         0x0928, 0x0020, 0x0935, 0x093f, 0x0937, 0x093e, 0x0926, 0x0020,
    556         0x092f, 0x094b, 0x0917, 0x0020, 0x0927, 0x0943, 0x0924, 0x0930,
    557         0x093e, 0x0937, 0x094d, 0x091f, 0x094d, 0x0930, 0x0020, 0x0909,
    558         0x0935, 0x093e, 0x091a, 0x0964, 0x0020, 0x0927, 0x0930, 0x094d,
    559         0x092e, 0x0915, 0x094d, 0x0937, 0x0947, 0x0924, 0x094d, 0x0930,
    560         0x0947, 0x0020, 0x0915, 0x0941, 0x0930, 0x0941, 0x0915, 0x094d,
    561         0x0937, 0x0947, 0x0924, 0x094d, 0x0930, 0x0947, 0x0020, 0x0938,
    562         0x092e, 0x0935, 0x0947, 0x0924, 0x093e, 0x0020, 0x092f, 0x0941,
    563         0x092f, 0x0941, 0x0924, 0x094d, 0x0938, 0x0935, 0x0903, 0x0020,
    564         0x092e, 0x093e, 0x092e, 0x0915, 0x093e, 0x0903, 0x0020, 0x092a,
    565         0x093e, 0x0923, 0x094d, 0x0921, 0x0935, 0x093e, 0x0936, 0x094d,
    566         0x091a, 0x0948, 0x0935, 0x0020, 0x0915, 0x093f, 0x092e, 0x0915,
    567         0x0941, 0x0930, 0x094d, 0x0935, 0x0924, 0x0020, 0x0938, 0x0902,
    568         0x091c, 0x092f, 0x0020, 0x0048, 0x0065, 0x0072, 0x0065, 0x0027,
    569         0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d, 0x0070,
    570         0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073, 0x006f,
    571         0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020,
    572         0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020,
    573         0x0069, 0x006e, 0x0020, 0x0041, 0x0072, 0x0061, 0x0062, 0x0069,
    574         0x0063, 0x003a, 0x0020, 0x0623, 0x0633, 0x0627, 0x0633, 0x064b,
    575         0x0627, 0x060c, 0x0020, 0x062a, 0x062a, 0x0639, 0x0627, 0x0645,
    576         0x0644, 0x0020, 0x0627, 0x0644, 0x062d, 0x0648, 0x0627, 0x0633,
    577         0x064a, 0x0628, 0x0020, 0x0641, 0x0642, 0x0637, 0x0020, 0x0645,
    578         0x0639, 0x0020, 0x0627, 0x0644, 0x0623, 0x0631, 0x0642, 0x0627,
    579         0x0645, 0x060c, 0x0020, 0x0648, 0x062a, 0x0642, 0x0648, 0x0645,
    580         0x0020, 0x0628, 0x062a, 0x062e, 0x0632, 0x064a, 0x0646, 0x0020,
    581         0x0627, 0x0644, 0x0623, 0x062d, 0x0631, 0x0641, 0x0020, 0x0648,
    582         0x0627, 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020,
    583         0x0627, 0x0644, 0x0623, 0x062e, 0x0631, 0x0649, 0x0020, 0x0628,
    584         0x0639, 0x062f, 0x0020, 0x0623, 0x0646, 0x0020, 0x062a, 0x064f,
    585         0x0639, 0x0637, 0x064a, 0x0020, 0x0631, 0x0642, 0x0645, 0x0627,
    586         0x0020, 0x0645, 0x0639, 0x064a, 0x0646, 0x0627, 0x0020, 0x0644,
    587         0x0643, 0x0644, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
    588         0x0645, 0x0646, 0x0647, 0x0627, 0x002e, 0x0020, 0x0648, 0x0642,
    589         0x0628, 0x0644, 0x0020, 0x0627, 0x062e, 0x062a, 0x0631, 0x0627,
    590         0x0639, 0x0020, 0x0022, 0x064a, 0x0648, 0x0646, 0x0650, 0x0643,
    591         0x0648, 0x062f, 0x0022, 0x060c, 0x0020, 0x0643, 0x0627, 0x0646,
    592         0x0020, 0x0647, 0x0646, 0x0627, 0x0643, 0x0020, 0x0645, 0x0626,
    593         0x0627, 0x062a, 0x0020, 0x0627, 0x0644, 0x0623, 0x0646, 0x0638,
    594         0x0645, 0x0629, 0x0020, 0x0644, 0x0644, 0x062a, 0x0634, 0x0641,
    595         0x064a, 0x0631, 0x0020, 0x0648, 0x062a, 0x062e, 0x0635, 0x064a,
    596         0x0635, 0x0020, 0x0647, 0x0630, 0x0647, 0x0020, 0x0627, 0x0644,
    597         0x0623, 0x0631, 0x0642, 0x0627, 0x0645, 0x0020, 0x0644, 0x0644,
    598         0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x060c, 0x0020, 0x0648,
    599         0x0644, 0x0645, 0x0020, 0x064a, 0x0648, 0x062c, 0x062f, 0x0020,
    600         0x0646, 0x0638, 0x0627, 0x0645, 0x0020, 0x062a, 0x0634, 0x0641,
    601         0x064a, 0x0631, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
    602         0x064a, 0x062d, 0x062a, 0x0648, 0x064a, 0x0020, 0x0639, 0x0644,
    603         0x0649, 0x0020, 0x062c, 0x0645, 0x064a, 0x0639, 0x0020, 0x0627,
    604         0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020, 0x0627,
    605         0x0644, 0x0636, 0x0631, 0x0648, 0x0631, 0x064a, 0x0629, 0x0020,
    606         0x0061, 0x006e, 0x0064, 0x0020, 0x0068, 0x0065, 0x0072, 0x0065,
    607         0x0027, 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d,
    608         0x0070, 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073,
    609         0x006f, 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074,
    610         0x0020, 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e,
    611         0x0020, 0x0069, 0x006e, 0x0020, 0x0054, 0x0068, 0x0061, 0x0069,
    612         0x003a, 0x0020, 0x0e1a, 0x0e17, 0x0e17, 0x0e35, 0x0e48, 0x0e51,
    613         0x0e1e, 0x0e32, 0x0e22, 0x0e38, 0x0e44, 0x0e0b, 0x0e42, 0x0e04,
    614         0x0e25, 0x0e19, 0x0e42, 0x0e14, 0x0e42, 0x0e23, 0x0e18, 0x0e35,
    615         0x0e2d, 0x0e32, 0x0e28, 0x0e31, 0x0e22, 0x0e2d, 0x0e22, 0x0e39,
    616         0x0e48, 0x0e17, 0x0e48, 0x0e32, 0x0e21, 0x0e01, 0x0e25, 0x0e32,
    617         0x0e07, 0x0e17, 0x0e38, 0x0e48, 0x0e07, 0x0e43, 0x0e2b, 0x0e0d,
    618         0x0e48, 0x0e43, 0x0e19, 0x0e41, 0x0e04, 0x0e19, 0x0e0b, 0x0e31,
    619         0x0e2a, 0x0e01, 0x0e31, 0x0e1a, 0x0e25, 0x0e38, 0x0e07, 0x0e40,
    620         0x0e2e, 0x0e19, 0x0e23, 0x0e35, 0x0e0a, 0x0e32, 0x0e27, 0x0e44,
    621         0x0e23, 0x0e48, 0x0e41, 0x0e25, 0x0e30, 0x0e1b, 0x0e49, 0x0e32,
    622         0x0e40, 0x0e2d, 0x0e47, 0x0e21, 0x0e20, 0x0e23, 0x0e23, 0x0e22,
    623         0x0e32, 0x0e0a, 0x0e32, 0x0e27, 0x0e44, 0x0e23, 0x0e48, 0x0e1a,
    624         0x0e49, 0x0e32, 0x0e19, 0x0e02, 0x0e2d, 0x0e07, 0x0e1e, 0x0e27,
    625         0x0e01, 0x0e40, 0x0e02, 0x0e32, 0x0e2b, 0x0e25, 0x0e31, 0x0e07,
    626         0x0e40, 0x0e25, 0x0e47, 0x0e01, 0x0e40, 0x0e1e, 0x0e23, 0x0e32,
    627         0x0e30, 0x0e44, 0x0e21, 0x0e49, 0x0e2a, 0x0e23, 0x0e49, 0x0e32,
    628         0x0e07, 0x0e1a, 0x0e49, 0x0e32, 0x0e19, 0x0e15, 0x0e49, 0x0e2d,
    629         0x0e07, 0x0e02, 0x0e19, 0x0e21, 0x0e32, 0x0e14, 0x0e49, 0x0e27,
    630         0x0e22, 0x0e40, 0x0e01, 0x0e27, 0x0e35, 0x0e22, 0x0e19, 0x0e40,
    631         0x0e1b, 0x0e47, 0x0e19, 0x0e23, 0x0e30, 0x0e22, 0x0e30, 0x0e17,
    632         0x0e32, 0x0e07, 0x0e2b, 0x0e25, 0x0e32, 0x0e22, 0x0e44, 0x0e21,
    633         0x0e25, 0x0e4c
    634     };
    635     le_int32 charCount = LE_ARRAY_SIZE(chars);
    636     le_int32 charIndex = 0, lineNumber = 1;
    637     le_int32 run, i;
    638     const float lineWidth = 600;
    639 
    640     font = le_simpleFontOpen(12, &status);
    641 
    642     if (LE_FAILURE(status)) {
    643         log_err("le_simpleFontOpen(12, &status) failed");
    644         goto finish;
    645     }
    646 
    647     fontRuns = pl_openEmptyFontRuns(0);
    648     pl_addFontRun(fontRuns, font, charCount);
    649 
    650     paragraph = pl_create(chars, charCount, fontRuns, NULL, NULL, NULL, 0, FALSE, &status);
    651 
    652     pl_closeFontRuns(fontRuns);
    653 
    654     if (LE_FAILURE(status)) {
    655         log_err("pl_create failed.");
    656         goto close_font;
    657     }
    658 
    659     pl_reflow(paragraph);
    660     while ((line = pl_nextLine(paragraph, lineWidth)) != NULL) {
    661         le_int32 runCount = pl_countLineRuns(line);
    662 
    663         for(run = 0; run < runCount; run += 1) {
    664             const pl_visualRun *visualRun = pl_getLineVisualRun(line, run);
    665             const le_int32 glyphCount = pl_getVisualRunGlyphCount(visualRun);
    666             const le_int32 *glyphToCharMap = pl_getVisualRunGlyphToCharMap(visualRun);
    667 
    668             if (pl_getVisualRunDirection(visualRun) == UBIDI_RTL) {
    669                 /*
    670                  * For a right to left run, make sure that the character indices
    671                  * increase from the right most glyph to the left most glyph. If
    672                  * there are any one to many glyph substitutions, we might get several
    673                  * glyphs in a row with the same character index.
    674                  */
    675                 for(i = glyphCount - 1; i >= 0; i -= 1) {
    676                     le_int32 ix = glyphToCharMap[i];
    677 
    678                     if (ix != charIndex) {
    679                         if (ix != charIndex - 1) {
    680                             log_err("Bad glyph to char index for glyph %d on line %d: expected %d, got %d\n",
    681                                 i, lineNumber, charIndex, ix);
    682                             goto close_paragraph; /* once there's one error, we can't count on anything else... */
    683                         }
    684                     } else {
    685                         charIndex += 1;
    686                     }
    687                 }
    688             } else {
    689                 /*
    690                  * We can't just check the order of the character indices
    691                  * for left to right runs because Indic text might have been
    692                  * reordered. What we can do is find the minimum and maximum
    693                  * character indices in the run and make sure that the minimum
    694                  * is equal to charIndex and then advance charIndex to the maximum.
    695                  */
    696                 le_int32 minIndex = 0x7FFFFFFF, maxIndex = -1;
    697 
    698                 for(i = 0; i < glyphCount; i += 1) {
    699                     le_int32 ix = glyphToCharMap[i];
    700 
    701                     if (ix > maxIndex) {
    702                         maxIndex = ix;
    703                     }
    704 
    705                     if (ix < minIndex) {
    706                         minIndex = ix;
    707                     }
    708                 }
    709 
    710                 if (minIndex != charIndex) {
    711                     log_err("Bad minIndex for run %d on line %d: expected %d, got %d\n",
    712                         run, lineNumber, charIndex, minIndex);
    713                     goto close_paragraph; /* once there's one error, we can't count on anything else... */
    714                 }
    715 
    716                 charIndex = maxIndex + 1;
    717             }
    718         }
    719 
    720         lineNumber += 1;
    721     }
    722 
    723 close_paragraph:
    724     pl_close(paragraph);
    725 
    726 close_font:
    727     le_fontClose(font);
    728 
    729 finish:
    730     return;
    731 }
    732 
    733 U_CFUNC void addCTests(TestNode **root)
    734 {
    735     addTest(root, &ParamTest,       "c_api/ParameterTest");
    736     addTest(root, &FactoryTest,     "c_api/FactoryTest");
    737     addTest(root, &AccessTest,      "c_layout/AccessTest");
    738     addTest(root, &DataDrivenTest,  "c_layout/DataDrivenTest");
    739     addTest(root, &GlyphToCharTest, "c_paragraph/GlyphToCharTest");
    740 }
    741 
    742 
    743