• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 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 
40 U_CDECL_BEGIN
ParamTest(void)41 static 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 
183 bail:
184     le_close(engine);
185     le_fontClose(font);
186 }
187 U_CDECL_END
188 
189 U_CDECL_BEGIN
FactoryTest(void)190 static 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 }
210 U_CDECL_END
211 
212 U_CDECL_BEGIN
AccessTest(void)213 static 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 
282 bail:
283     le_close(engine);
284     le_fontClose(font);
285 }
286 U_CDECL_END
287 
compareResults(const char * testID,TestResult * expected,TestResult * actual)288 static 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: expected %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 
checkFontVersion(le_font * font,const char * testVersionString,le_uint32 testChecksum,const char * testID)339 static 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/ */
getSourceTestData()370 static 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 
getPath(char buffer[2048],const char * filename)389 static 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 
openFont(const char * fontName,const char * checksum,const char * version,const char * testID)398 static 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 
getRTL(const LEUnicode * text,le_int32 charCount)425 static 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 
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)442 static 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 
492 free_expected:
493     le_fontClose(font);
494 }
495 
DataDrivenTest(void)496 static 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  */
GlyphToCharTest(void)516 static 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 
728 close_paragraph:
729     pl_close(paragraph);
730 
731 close_font:
732     le_fontClose(font);
733 
734 finish:
735     return;
736 #endif
737 }
738 
addCTests(TestNode ** root)739 U_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