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