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
38U_CDECL_BEGIN
39static 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
181bail:
182    le_close(engine);
183    le_fontClose(font);
184}
185U_CDECL_END
186
187U_CDECL_BEGIN
188static 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}
208U_CDECL_END
209
210U_CDECL_BEGIN
211static 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
280bail:
281    le_close(engine);
282    le_fontClose(font);
283}
284U_CDECL_END
285
286static 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
337static 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/ */
368static 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
387static 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
396static 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
423static 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
440static 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
490free_expected:
491    le_fontClose(font);
492}
493
494static 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 */
514static 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
726close_paragraph:
727    pl_close(paragraph);
728
729close_font:
730    le_fontClose(font);
731
732finish:
733    return;
734#endif
735}
736
737U_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