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