• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
36 U_CDECL_BEGIN
ParamTest(void)37 static 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 
179 bail:
180     le_close(engine);
181     le_fontClose(font);
182 }
183 U_CDECL_END
184 
185 U_CDECL_BEGIN
FactoryTest(void)186 static 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 }
206 U_CDECL_END
207 
208 U_CDECL_BEGIN
AccessTest(void)209 static 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 
278 bail:
279     le_close(engine);
280     le_fontClose(font);
281 }
282 U_CDECL_END
283 
compareResults(const char * testID,TestResult * expected,TestResult * actual)284 static 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 
checkFontVersion(le_font * font,const char * testVersionString,le_uint32 testChecksum,const char * testID)335 static 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/ */
getSourceTestData()366 static 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 
getPath(char buffer[2048],const char * filename)385 static 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 
openFont(const char * fontName,const char * checksum,const char * version,const char * testID)394 static 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 
getRTL(const LEUnicode * text,le_int32 charCount)421 static 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 
doTestCase(const char * testID,const char * fontName,const char * fontVersion,const char * fontChecksum,le_int32 scriptCode,le_int32 languageCode,const LEUnicode * text,le_int32 charCount,TestResult * expected)438 static 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 
488 free_expected:
489     le_fontClose(font);
490 }
491 
DataDrivenTest(void)492 static 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  */
GlyphToCharTest(void)512 static 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 
723 close_paragraph:
724     pl_close(paragraph);
725 
726 close_font:
727     le_fontClose(font);
728 
729 finish:
730     return;
731 }
732 
addCTests(TestNode ** root)733 U_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