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