Home | History | Annotate | Download | only in letest
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4  *******************************************************************************
      5  *
      6  *   Copyright (C) 1999-2014, International Business Machines
      7  *   Corporation and others.  All Rights Reserved.
      8  *
      9  *******************************************************************************
     10  */
     11 
     12 #ifndef USING_ICULEHB /* C API not available under HB */
     13 
     14 #include "unicode/utypes.h"
     15 #include "unicode/ubidi.h"
     16 #include "unicode/uscript.h"
     17 #include "unicode/ctest.h"
     18 
     19 #include "layout/LETypes.h"
     20 #include "layout/LEScripts.h"
     21 #include "layout/loengine.h"
     22 
     23 #include "layout/playout.h"
     24 #include "layout/plruns.h"
     25 
     26 #include "cfonts.h"
     27 
     28 #include "letest.h"
     29 
     30 #include "sfnt.h"
     31 #include "xmlreader.h"
     32 #include "putilimp.h" /* for U_FILE_SEP_STRING */
     33 
     34 #include <stdlib.h>
     35 #include <stdio.h>
     36 #include <string.h>
     37 
     38 #define CH_COMMA 0x002C
     39 
     40 U_CDECL_BEGIN
     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
    750