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