• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *******************************************************************************
3  *
4  *   Copyright (C) 1999-2007, 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 
344         log_info("Test %s: this may not be the same font used to generate the test data.\n", testID);
345         log_info("Your font's version string is \"%s\"\n", fontVersionString);
346         log_info("The expected version string is \"%s\"\n", testVersionString);
347         log_info("If you see errors, they may be due to the version of the font you're using.\n");
348 
349         le_deleteNameString(font, fontVersionString);
350     }
351 }
352 
353 /* Returns the path to icu/source/test/testdata/ */
getSourceTestData()354 static const char *getSourceTestData() {
355 #ifdef U_TOPSRCDIR
356     const char *srcDataDir = U_TOPSRCDIR U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
357 #else
358     const char *srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
359     FILE *f = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"rbbitst.txt", "r");
360 
361     if (f != NULL) {
362         /* We're in icu/source/test/letest/ */
363         fclose(f);
364     } else {
365         /* We're in icu/source/test/letest/(Debug|Release) */
366         srcDataDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
367     }
368 #endif
369 
370     return srcDataDir;
371 }
372 
getPath(char buffer[2048],const char * filename)373 static const char *getPath(char buffer[2048], const char *filename) {
374     const char *testDataDirectory = getSourceTestData();
375 
376     strcpy(buffer, testDataDirectory);
377     strcat(buffer, filename);
378 
379     return buffer;
380 }
381 
openFont(const char * fontName,const char * checksum,const char * version,const char * testID)382 static le_font *openFont(const char *fontName, const char *checksum, const char *version, const char *testID)
383 {
384     char path[2048];
385     le_font *font;
386     LEErrorCode fontStatus = LE_NO_ERROR;
387 
388 	if (fontName != NULL) {
389 		font = le_portableFontOpen(getPath(path, fontName), 12, &fontStatus);
390 
391 		if (LE_FAILURE(fontStatus)) {
392 			log_info("Test %s: can't open font %s - test skipped.\n", testID, fontName);
393 			le_fontClose(font);
394 			return NULL;
395 		} else {
396 			le_uint32 cksum = 0;
397 
398 			sscanf(checksum, "%x", &cksum);
399 
400 			checkFontVersion(font, version, cksum, testID);
401 		}
402 	} else {
403 		font = le_simpleFontOpen(12, &fontStatus);
404 	}
405 
406     return font;
407 }
408 
getRTL(const LEUnicode * text,le_int32 charCount)409 static le_bool getRTL(const LEUnicode *text, le_int32 charCount)
410 {
411     UBiDiLevel paraLevel;
412     UErrorCode status = U_ZERO_ERROR;
413     UBiDi *ubidi = ubidi_openSized(charCount, 0, &status);
414 
415     ubidi_setPara(ubidi, text, charCount, UBIDI_DEFAULT_LTR, NULL, &status);
416     paraLevel = ubidi_getParaLevel(ubidi);
417     ubidi_close(ubidi);
418 
419     return paraLevel & 1;
420 }
421 
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)422 static void doTestCase (const char *testID,
423 				 const char *fontName,
424 				 const char *fontVersion,
425 				 const char *fontChecksum,
426 				 le_int32 scriptCode,
427 				 le_int32 languageCode,
428 				 const LEUnicode *text,
429 				 le_int32 charCount,
430 				 TestResult *expected)
431 {
432 	LEErrorCode status = LE_NO_ERROR;
433 	le_engine *engine;
434 	le_font *font = openFont(fontName, fontChecksum, fontVersion, testID);
435 	le_int32 typoFlags = 3; /* kerning + ligatures */
436 	TestResult actual;
437 
438 	if (font == NULL) {
439 		/* error message already printed. */
440 		return;
441 	}
442 
443 	if (fontName == NULL) {
444 		typoFlags |= 0x80000000L;  /* use CharSubstitutionFilter... */
445 	}
446 
447     engine = le_create(font, scriptCode, languageCode, typoFlags, &status);
448 
449     if (LE_FAILURE(status)) {
450         log_err("Test %s: could not create a LayoutEngine.\n", testID);
451         goto free_expected;
452     }
453 
454     actual.glyphCount = le_layoutChars(engine, text, 0, charCount, charCount, getRTL(text, charCount), 0, 0, &status);
455 
456     actual.glyphs    = NEW_ARRAY(LEGlyphID, actual.glyphCount);
457     actual.indices   = NEW_ARRAY(le_int32, actual.glyphCount);
458     actual.positions = NEW_ARRAY(float, actual.glyphCount * 2 + 2);
459 
460     le_getGlyphs(engine, actual.glyphs, &status);
461     le_getCharIndices(engine, actual.indices, &status);
462     le_getGlyphPositions(engine, actual.positions, &status);
463 
464     compareResults(testID, expected, &actual);
465 
466     DELETE_ARRAY(actual.positions);
467     DELETE_ARRAY(actual.indices);
468     DELETE_ARRAY(actual.glyphs);
469 
470     le_close(engine);
471 
472 free_expected:
473     le_fontClose(font);
474 }
475 
DataDrivenTest(void)476 static void U_CALLCONV DataDrivenTest(void)
477 {
478     char path[2048];
479     const char *testFilePath = getPath(path, "letest.xml");
480 
481 	readTestFile(testFilePath, doTestCase);
482 }
483 
484 /*
485  * From ticket:5923:
486  *
487  * Build a paragraph that contains a mixture of left to right and right to left text.
488  * Break it into multiple lines and make sure that the glyphToCharMap for run in each
489  * line is correct.
490  *
491  * Note: it might be a good idea to also check the glyphs and positions for each run,
492  * that we get the expected number of runs per line and that the line breaks are where
493  * we expect them to be. Really, it would be a good idea to make a whole test suite
494  * for pl_paragraph.
495  */
GlyphToCharTest(void)496 static void U_CALLCONV GlyphToCharTest(void)
497 {
498     LEErrorCode status = LE_NO_ERROR;
499     le_font *font;
500     pl_fontRuns *fontRuns;
501     pl_paragraph *paragraph;
502     const pl_line *line;
503     /*
504      * This is the same text that's in <icu>/source/samples/layout/Sample.txt
505      */
506     LEUnicode chars[] = {
507         /*BOM*/ 0x0054, 0x0068, 0x0065, 0x0020, 0x004c, 0x0061, 0x0079,
508         0x006f, 0x0075, 0x0074, 0x0045, 0x006e, 0x0067, 0x0069, 0x006e,
509         0x0065, 0x0020, 0x0064, 0x006f, 0x0065, 0x0073, 0x0020, 0x0061,
510         0x006c, 0x006c, 0x0020, 0x0074, 0x0068, 0x0065, 0x0020, 0x0077,
511         0x006f, 0x0072, 0x006b, 0x0020, 0x006e, 0x0065, 0x0063, 0x0065,
512         0x0073, 0x0073, 0x0061, 0x0072, 0x0079, 0x0020, 0x0074, 0x006f,
513         0x0020, 0x0064, 0x0069, 0x0073, 0x0070, 0x006c, 0x0061, 0x0079,
514         0x0020, 0x0055, 0x006e, 0x0069, 0x0063, 0x006f, 0x0064, 0x0065,
515         0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072,
516         0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e,
517         0x0020, 0x006c, 0x0061, 0x006e, 0x0067, 0x0075, 0x0061, 0x0067,
518         0x0065, 0x0073, 0x0020, 0x0077, 0x0069, 0x0074, 0x0068, 0x0020,
519         0x0063, 0x006f, 0x006d, 0x0070, 0x006c, 0x0065, 0x0078, 0x0020,
520         0x0077, 0x0072, 0x0069, 0x0074, 0x0069, 0x006e, 0x0067, 0x0020,
521         0x0073, 0x0079, 0x0073, 0x0074, 0x0065, 0x006d, 0x0073, 0x0020,
522         0x0073, 0x0075, 0x0063, 0x0068, 0x0020, 0x0061, 0x0073, 0x0020,
523         0x0048, 0x0069, 0x006e, 0x0064, 0x0069, 0x0020, 0x0028, 0x0939,
524         0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0029, 0x0020, 0x0054,
525         0x0068, 0x0061, 0x0069, 0x0020, 0x0028, 0x0e44, 0x0e17, 0x0e22,
526         0x0029, 0x0020, 0x0061, 0x006e, 0x0064, 0x0020, 0x0041, 0x0072,
527         0x0061, 0x0062, 0x0069, 0x0063, 0x0020, 0x0028, 0x0627, 0x0644,
528         0x0639, 0x0631, 0x0628, 0x064a, 0x0629, 0x0029, 0x002e, 0x0020,
529         0x0048, 0x0065, 0x0072, 0x0065, 0x0027, 0x0073, 0x0020, 0x0061,
530         0x0020, 0x0073, 0x0061, 0x006d, 0x0070, 0x006c, 0x0065, 0x0020,
531         0x006f, 0x0066, 0x0020, 0x0073, 0x006f, 0x006d, 0x0065, 0x0020,
532         0x0074, 0x0065, 0x0078, 0x0074, 0x0020, 0x0077, 0x0072, 0x0069,
533         0x0074, 0x0074, 0x0065, 0x006e, 0x0020, 0x0069, 0x006e, 0x0020,
534         0x0053, 0x0061, 0x006e, 0x0073, 0x006b, 0x0072, 0x0069, 0x0074,
535         0x003a, 0x0020, 0x0936, 0x094d, 0x0930, 0x0940, 0x092e, 0x0926,
536         0x094d, 0x0020, 0x092d, 0x0917, 0x0935, 0x0926, 0x094d, 0x0917,
537         0x0940, 0x0924, 0x093e, 0x0020, 0x0905, 0x0927, 0x094d, 0x092f,
538         0x093e, 0x092f, 0x0020, 0x0905, 0x0930, 0x094d, 0x091c, 0x0941,
539         0x0928, 0x0020, 0x0935, 0x093f, 0x0937, 0x093e, 0x0926, 0x0020,
540         0x092f, 0x094b, 0x0917, 0x0020, 0x0927, 0x0943, 0x0924, 0x0930,
541         0x093e, 0x0937, 0x094d, 0x091f, 0x094d, 0x0930, 0x0020, 0x0909,
542         0x0935, 0x093e, 0x091a, 0x0964, 0x0020, 0x0927, 0x0930, 0x094d,
543         0x092e, 0x0915, 0x094d, 0x0937, 0x0947, 0x0924, 0x094d, 0x0930,
544         0x0947, 0x0020, 0x0915, 0x0941, 0x0930, 0x0941, 0x0915, 0x094d,
545         0x0937, 0x0947, 0x0924, 0x094d, 0x0930, 0x0947, 0x0020, 0x0938,
546         0x092e, 0x0935, 0x0947, 0x0924, 0x093e, 0x0020, 0x092f, 0x0941,
547         0x092f, 0x0941, 0x0924, 0x094d, 0x0938, 0x0935, 0x0903, 0x0020,
548         0x092e, 0x093e, 0x092e, 0x0915, 0x093e, 0x0903, 0x0020, 0x092a,
549         0x093e, 0x0923, 0x094d, 0x0921, 0x0935, 0x093e, 0x0936, 0x094d,
550         0x091a, 0x0948, 0x0935, 0x0020, 0x0915, 0x093f, 0x092e, 0x0915,
551         0x0941, 0x0930, 0x094d, 0x0935, 0x0924, 0x0020, 0x0938, 0x0902,
552         0x091c, 0x092f, 0x0020, 0x0048, 0x0065, 0x0072, 0x0065, 0x0027,
553         0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d, 0x0070,
554         0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073, 0x006f,
555         0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074, 0x0020,
556         0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e, 0x0020,
557         0x0069, 0x006e, 0x0020, 0x0041, 0x0072, 0x0061, 0x0062, 0x0069,
558         0x0063, 0x003a, 0x0020, 0x0623, 0x0633, 0x0627, 0x0633, 0x064b,
559         0x0627, 0x060c, 0x0020, 0x062a, 0x062a, 0x0639, 0x0627, 0x0645,
560         0x0644, 0x0020, 0x0627, 0x0644, 0x062d, 0x0648, 0x0627, 0x0633,
561         0x064a, 0x0628, 0x0020, 0x0641, 0x0642, 0x0637, 0x0020, 0x0645,
562         0x0639, 0x0020, 0x0627, 0x0644, 0x0623, 0x0631, 0x0642, 0x0627,
563         0x0645, 0x060c, 0x0020, 0x0648, 0x062a, 0x0642, 0x0648, 0x0645,
564         0x0020, 0x0628, 0x062a, 0x062e, 0x0632, 0x064a, 0x0646, 0x0020,
565         0x0627, 0x0644, 0x0623, 0x062d, 0x0631, 0x0641, 0x0020, 0x0648,
566         0x0627, 0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020,
567         0x0627, 0x0644, 0x0623, 0x062e, 0x0631, 0x0649, 0x0020, 0x0628,
568         0x0639, 0x062f, 0x0020, 0x0623, 0x0646, 0x0020, 0x062a, 0x064f,
569         0x0639, 0x0637, 0x064a, 0x0020, 0x0631, 0x0642, 0x0645, 0x0627,
570         0x0020, 0x0645, 0x0639, 0x064a, 0x0646, 0x0627, 0x0020, 0x0644,
571         0x0643, 0x0644, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
572         0x0645, 0x0646, 0x0647, 0x0627, 0x002e, 0x0020, 0x0648, 0x0642,
573         0x0628, 0x0644, 0x0020, 0x0627, 0x062e, 0x062a, 0x0631, 0x0627,
574         0x0639, 0x0020, 0x0022, 0x064a, 0x0648, 0x0646, 0x0650, 0x0643,
575         0x0648, 0x062f, 0x0022, 0x060c, 0x0020, 0x0643, 0x0627, 0x0646,
576         0x0020, 0x0647, 0x0646, 0x0627, 0x0643, 0x0020, 0x0645, 0x0626,
577         0x0627, 0x062a, 0x0020, 0x0627, 0x0644, 0x0623, 0x0646, 0x0638,
578         0x0645, 0x0629, 0x0020, 0x0644, 0x0644, 0x062a, 0x0634, 0x0641,
579         0x064a, 0x0631, 0x0020, 0x0648, 0x062a, 0x062e, 0x0635, 0x064a,
580         0x0635, 0x0020, 0x0647, 0x0630, 0x0647, 0x0020, 0x0627, 0x0644,
581         0x0623, 0x0631, 0x0642, 0x0627, 0x0645, 0x0020, 0x0644, 0x0644,
582         0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x060c, 0x0020, 0x0648,
583         0x0644, 0x0645, 0x0020, 0x064a, 0x0648, 0x062c, 0x062f, 0x0020,
584         0x0646, 0x0638, 0x0627, 0x0645, 0x0020, 0x062a, 0x0634, 0x0641,
585         0x064a, 0x0631, 0x0020, 0x0648, 0x0627, 0x062d, 0x062f, 0x0020,
586         0x064a, 0x062d, 0x062a, 0x0648, 0x064a, 0x0020, 0x0639, 0x0644,
587         0x0649, 0x0020, 0x062c, 0x0645, 0x064a, 0x0639, 0x0020, 0x0627,
588         0x0644, 0x0645, 0x062d, 0x0627, 0x0631, 0x0641, 0x0020, 0x0627,
589         0x0644, 0x0636, 0x0631, 0x0648, 0x0631, 0x064a, 0x0629, 0x0020,
590         0x0061, 0x006e, 0x0064, 0x0020, 0x0068, 0x0065, 0x0072, 0x0065,
591         0x0027, 0x0073, 0x0020, 0x0061, 0x0020, 0x0073, 0x0061, 0x006d,
592         0x0070, 0x006c, 0x0065, 0x0020, 0x006f, 0x0066, 0x0020, 0x0073,
593         0x006f, 0x006d, 0x0065, 0x0020, 0x0074, 0x0065, 0x0078, 0x0074,
594         0x0020, 0x0077, 0x0072, 0x0069, 0x0074, 0x0074, 0x0065, 0x006e,
595         0x0020, 0x0069, 0x006e, 0x0020, 0x0054, 0x0068, 0x0061, 0x0069,
596         0x003a, 0x0020, 0x0e1a, 0x0e17, 0x0e17, 0x0e35, 0x0e48, 0x0e51,
597         0x0e1e, 0x0e32, 0x0e22, 0x0e38, 0x0e44, 0x0e0b, 0x0e42, 0x0e04,
598         0x0e25, 0x0e19, 0x0e42, 0x0e14, 0x0e42, 0x0e23, 0x0e18, 0x0e35,
599         0x0e2d, 0x0e32, 0x0e28, 0x0e31, 0x0e22, 0x0e2d, 0x0e22, 0x0e39,
600         0x0e48, 0x0e17, 0x0e48, 0x0e32, 0x0e21, 0x0e01, 0x0e25, 0x0e32,
601         0x0e07, 0x0e17, 0x0e38, 0x0e48, 0x0e07, 0x0e43, 0x0e2b, 0x0e0d,
602         0x0e48, 0x0e43, 0x0e19, 0x0e41, 0x0e04, 0x0e19, 0x0e0b, 0x0e31,
603         0x0e2a, 0x0e01, 0x0e31, 0x0e1a, 0x0e25, 0x0e38, 0x0e07, 0x0e40,
604         0x0e2e, 0x0e19, 0x0e23, 0x0e35, 0x0e0a, 0x0e32, 0x0e27, 0x0e44,
605         0x0e23, 0x0e48, 0x0e41, 0x0e25, 0x0e30, 0x0e1b, 0x0e49, 0x0e32,
606         0x0e40, 0x0e2d, 0x0e47, 0x0e21, 0x0e20, 0x0e23, 0x0e23, 0x0e22,
607         0x0e32, 0x0e0a, 0x0e32, 0x0e27, 0x0e44, 0x0e23, 0x0e48, 0x0e1a,
608         0x0e49, 0x0e32, 0x0e19, 0x0e02, 0x0e2d, 0x0e07, 0x0e1e, 0x0e27,
609         0x0e01, 0x0e40, 0x0e02, 0x0e32, 0x0e2b, 0x0e25, 0x0e31, 0x0e07,
610         0x0e40, 0x0e25, 0x0e47, 0x0e01, 0x0e40, 0x0e1e, 0x0e23, 0x0e32,
611         0x0e30, 0x0e44, 0x0e21, 0x0e49, 0x0e2a, 0x0e23, 0x0e49, 0x0e32,
612         0x0e07, 0x0e1a, 0x0e49, 0x0e32, 0x0e19, 0x0e15, 0x0e49, 0x0e2d,
613         0x0e07, 0x0e02, 0x0e19, 0x0e21, 0x0e32, 0x0e14, 0x0e49, 0x0e27,
614         0x0e22, 0x0e40, 0x0e01, 0x0e27, 0x0e35, 0x0e22, 0x0e19, 0x0e40,
615         0x0e1b, 0x0e47, 0x0e19, 0x0e23, 0x0e30, 0x0e22, 0x0e30, 0x0e17,
616         0x0e32, 0x0e07, 0x0e2b, 0x0e25, 0x0e32, 0x0e22, 0x0e44, 0x0e21,
617         0x0e25, 0x0e4c
618     };
619     le_int32 charCount = LE_ARRAY_SIZE(chars);
620     le_int32 charIndex = 0, lineNumber = 1;
621     le_int32 run, i;
622     const float lineWidth = 600;
623 
624     font = le_simpleFontOpen(12, &status);
625 
626     if (LE_FAILURE(status)) {
627         log_err("le_simpleFontOpen(12, &status) failed");
628         goto finish;
629     }
630 
631     fontRuns = pl_openEmptyFontRuns(0);
632     pl_addFontRun(fontRuns, font, charCount);
633 
634     paragraph = pl_create(chars, charCount, fontRuns, NULL, NULL, NULL, 0, FALSE, &status);
635 
636     pl_closeFontRuns(fontRuns);
637 
638     if (LE_FAILURE(status)) {
639         log_err("pl_create failed.");
640         goto close_font;
641     }
642 
643     pl_reflow(paragraph);
644     while ((line = pl_nextLine(paragraph, lineWidth)) != NULL) {
645         le_int32 runCount = pl_countLineRuns(line);
646 
647         for(run = 0; run < runCount; run += 1) {
648             const pl_visualRun *visualRun = pl_getLineVisualRun(line, run);
649             const le_int32 glyphCount = pl_getVisualRunGlyphCount(visualRun);
650             const le_int32 *glyphToCharMap = pl_getVisualRunGlyphToCharMap(visualRun);
651 
652             if (pl_getVisualRunDirection(visualRun) == UBIDI_RTL) {
653                 /*
654                  * For a right to left run, make sure that the character indices
655                  * increase from the right most glyph to the left most glyph. If
656                  * there are any one to many glyph substitutions, we might get several
657                  * glyphs in a row with the same character index.
658                  */
659                 for(i = glyphCount - 1; i >= 0; i -= 1) {
660                     le_int32 ix = glyphToCharMap[i];
661 
662                     if (ix != charIndex) {
663                         if (ix != charIndex - 1) {
664                             log_err("Bad glyph to char index for glyph %d on line %d: expected %d, got %d\n",
665                                 i, lineNumber, charIndex, ix);
666                             goto close_paragraph; /* once there's one error, we can't count on anything else... */
667                         }
668                     } else {
669                         charIndex += 1;
670                     }
671                 }
672             } else {
673                 /*
674                  * We can't just check the order of the character indices
675                  * for left to right runs because Indic text might have been
676                  * reordered. What we can do is find the minimum and maximum
677                  * character indices in the run and make sure that the minimum
678                  * is equal to charIndex and then advance charIndex to the maximum.
679                  */
680                 le_int32 minIndex = 0x7FFFFFFF, maxIndex = -1;
681 
682                 for(i = 0; i < glyphCount; i += 1) {
683                     le_int32 ix = glyphToCharMap[i];
684 
685                     if (ix > maxIndex) {
686                         maxIndex = ix;
687                     }
688 
689                     if (ix < minIndex) {
690                         minIndex = ix;
691                     }
692                 }
693 
694                 if (minIndex != charIndex) {
695                     log_err("Bad minIndex for run %d on line %d: expected %d, got %d\n",
696                         run, lineNumber, charIndex, minIndex);
697                     goto close_paragraph; /* once there's one error, we can't count on anything else... */
698                 }
699 
700                 charIndex = maxIndex + 1;
701             }
702         }
703 
704         lineNumber += 1;
705     }
706 
707 close_paragraph:
708     pl_close(paragraph);
709 
710 close_font:
711     le_fontClose(font);
712 
713 finish:
714     return;
715 }
716 
addCTests(TestNode ** root)717 U_CFUNC void addCTests(TestNode **root)
718 {
719     addTest(root, &ParamTest,       "c_api/ParameterTest");
720     addTest(root, &FactoryTest,     "c_api/FactoryTest");
721     addTest(root, &AccessTest,      "c_layout/AccessTest");
722     addTest(root, &DataDrivenTest,  "c_layout/DataDrivenTest");
723     addTest(root, &GlyphToCharTest, "c_paragraph/GlyphToCharTest");
724 }
725 
726 
727