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