1 /*
2  * Copyright (C) 2015 The Qt Company Ltd
3  *
4  * This is part of HarfBuzz, an OpenType Layout engine library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  */
24 
25 #include <QtTest/QtTest>
26 
27 #include <ft2build.h>
28 #include FT_FREETYPE_H
29 #include FT_TRUETYPE_TABLES_H
30 
31 #include <harfbuzz-shaper.h>
32 #include <harfbuzz-global.h>
33 #include <harfbuzz-gpos.h>
34 
35 static FT_Library freetype;
36 
loadFace(const char * name)37 static FT_Face loadFace(const char *name)
38 {
39     FT_Face face;
40     char path[256];
41 
42     strcpy(path, SRCDIR);
43     strcat(path, "/fonts/");
44     strcat(path, name);
45 
46     if (FT_New_Face(freetype, path, /*index*/0, &face))
47         return 0;
48     return face;
49 }
50 
getChar(const HB_UChar16 * string,hb_uint32 length,hb_uint32 & i)51 static HB_UChar32 getChar(const HB_UChar16 *string, hb_uint32 length, hb_uint32 &i)
52 {
53     HB_UChar32 ch;
54     if (HB_IsHighSurrogate(string[i])
55         && i < length - 1
56         && HB_IsLowSurrogate(string[i + 1])) {
57         ch = HB_SurrogateToUcs4(string[i], string[i + 1]);
58         ++i;
59     } else {
60         ch = string[i];
61     }
62     return ch;
63 }
64 
hb_stringToGlyphs(HB_Font font,const HB_UChar16 * string,hb_uint32 length,HB_Glyph * glyphs,hb_uint32 * numGlyphs,HB_Bool)65 static HB_Bool hb_stringToGlyphs(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool /*rightToLeft*/)
66 {
67     FT_Face face = (FT_Face)font->userData;
68     if (length > *numGlyphs)
69         return false;
70 
71     int glyph_pos = 0;
72     for (hb_uint32 i = 0; i < length; ++i) {
73         glyphs[glyph_pos] = FT_Get_Char_Index(face, getChar(string, length, i));
74         ++glyph_pos;
75     }
76 
77     *numGlyphs = glyph_pos;
78 
79     return true;
80 }
81 
hb_getAdvances(HB_Font,const HB_Glyph *,hb_uint32 numGlyphs,HB_Fixed * advances,int)82 static void hb_getAdvances(HB_Font /*font*/, const HB_Glyph * /*glyphs*/, hb_uint32 numGlyphs, HB_Fixed *advances, int /*flags*/)
83 {
84     for (hb_uint32 i = 0; i < numGlyphs; ++i)
85         advances[i] = 0; // ### not tested right now
86 }
87 
hb_canRender(HB_Font font,const HB_UChar16 * string,hb_uint32 length)88 static HB_Bool hb_canRender(HB_Font font, const HB_UChar16 *string, hb_uint32 length)
89 {
90     FT_Face face = (FT_Face)font->userData;
91 
92     for (hb_uint32 i = 0; i < length; ++i)
93         if (!FT_Get_Char_Index(face, getChar(string, length, i)))
94             return false;
95 
96     return true;
97 }
98 
hb_getSFntTable(void * font,HB_Tag tableTag,HB_Byte * buffer,HB_UInt * length)99 static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
100 {
101     FT_Face face = (FT_Face)font;
102     FT_ULong ftlen = *length;
103     FT_Error error = 0;
104 
105     if (!FT_IS_SFNT(face))
106         return HB_Err_Invalid_Argument;
107 
108     error = FT_Load_Sfnt_Table(face, tableTag, 0, buffer, &ftlen);
109     *length = ftlen;
110     return (HB_Error)error;
111 }
112 
hb_getPointInOutline(HB_Font font,HB_Glyph glyph,int flags,hb_uint32 point,HB_Fixed * xpos,HB_Fixed * ypos,hb_uint32 * nPoints)113 HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
114 {
115     HB_Error error = HB_Err_Ok;
116     FT_Face face = (FT_Face)font->userData;
117 
118     int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
119 
120     if ((error = (HB_Error)FT_Load_Glyph(face, glyph, load_flags)))
121         return error;
122 
123     if (face->glyph->format != ft_glyph_format_outline)
124         return (HB_Error)HB_Err_Invalid_SubTable;
125 
126     *nPoints = face->glyph->outline.n_points;
127     if (!(*nPoints))
128         return HB_Err_Ok;
129 
130     if (point > *nPoints)
131         return (HB_Error)HB_Err_Invalid_SubTable;
132 
133     *xpos = face->glyph->outline.points[point].x;
134     *ypos = face->glyph->outline.points[point].y;
135 
136     return HB_Err_Ok;
137 }
138 
hb_getGlyphMetrics(HB_Font,HB_Glyph,HB_GlyphMetrics * metrics)139 void hb_getGlyphMetrics(HB_Font, HB_Glyph, HB_GlyphMetrics *metrics)
140 {
141     // ###
142     metrics->x = metrics->y = metrics->width = metrics->height = metrics->xOffset = metrics->yOffset = 0;
143 }
144 
hb_getFontMetric(HB_Font,HB_FontMetric)145 HB_Fixed hb_getFontMetric(HB_Font, HB_FontMetric )
146 {
147     return 0; // ####
148 }
149 
150 const HB_FontClass hb_fontClass = {
151     hb_stringToGlyphs, hb_getAdvances, hb_canRender,
152     hb_getPointInOutline, hb_getGlyphMetrics, hb_getFontMetric
153 };
154 
155 
156 class tst_QScriptEngine : public QObject
157 {
158 Q_OBJECT
159 
160 public:
161     tst_QScriptEngine();
162     virtual ~tst_QScriptEngine();
163 
164 
165 public slots:
166     void initTestCase();
167     void cleanupTestCase();
168 private slots:
169     void greek();
170 
171     void devanagari();
172     void bengali();
173     void gurmukhi();
174     // gujarati missing
175     void oriya();
176     void tamil();
177     void telugu();
178     void kannada();
179     void malayalam();
180     void sinhala();
181 
182     void khmer();
183     void nko();
184     void linearB();
185 };
186 
tst_QScriptEngine()187 tst_QScriptEngine::tst_QScriptEngine()
188 {
189 }
190 
~tst_QScriptEngine()191 tst_QScriptEngine::~tst_QScriptEngine()
192 {
193 }
194 
initTestCase()195 void tst_QScriptEngine::initTestCase()
196 {
197     FT_Init_FreeType(&freetype);
198 }
199 
cleanupTestCase()200 void tst_QScriptEngine::cleanupTestCase()
201 {
202     FT_Done_FreeType(freetype);
203 }
204 
205 class Shaper
206 {
207 public:
208     Shaper(FT_Face face, HB_Script script, const QString &str);
209 
210     HB_FontRec hbFont;
211     HB_ShaperItem shaper_item;
212     QVarLengthArray<HB_Glyph> hb_glyphs;
213     QVarLengthArray<HB_GlyphAttributes> hb_attributes;
214     QVarLengthArray<HB_Fixed> hb_advances;
215     QVarLengthArray<HB_FixedPoint> hb_offsets;
216     QVarLengthArray<unsigned short> hb_logClusters;
217 
218 };
219 
Shaper(FT_Face face,HB_Script script,const QString & str)220 Shaper::Shaper(FT_Face face, HB_Script script, const QString &str)
221 {
222     HB_Face hbFace = HB_NewFace(face, hb_getSFntTable);
223 
224     hbFont.klass = &hb_fontClass;
225     hbFont.userData = face;
226     hbFont.x_ppem  = face->size->metrics.x_ppem;
227     hbFont.y_ppem  = face->size->metrics.y_ppem;
228     hbFont.x_scale = face->size->metrics.x_scale;
229     hbFont.y_scale = face->size->metrics.y_scale;
230 
231     shaper_item.kerning_applied = false;
232     shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData());
233     shaper_item.stringLength = str.length();
234     shaper_item.item.script = script;
235     shaper_item.item.pos = 0;
236     shaper_item.item.length = shaper_item.stringLength;
237     shaper_item.item.bidiLevel = 0; // ###
238     shaper_item.shaperFlags = 0;
239     shaper_item.font = &hbFont;
240     shaper_item.face = hbFace;
241     shaper_item.num_glyphs = shaper_item.item.length;
242     shaper_item.glyphIndicesPresent = false;
243     shaper_item.initialGlyphCount = 0;
244 
245 
246     while (1) {
247         hb_glyphs.resize(shaper_item.num_glyphs);
248         hb_attributes.resize(shaper_item.num_glyphs);
249         hb_advances.resize(shaper_item.num_glyphs);
250         hb_offsets.resize(shaper_item.num_glyphs);
251         hb_logClusters.resize(shaper_item.num_glyphs);
252 
253         memset(hb_glyphs.data(), 0, hb_glyphs.size() * sizeof(HB_Glyph));
254         memset(hb_attributes.data(), 0, hb_attributes.size() * sizeof(HB_GlyphAttributes));
255         memset(hb_advances.data(), 0, hb_advances.size() * sizeof(HB_Fixed));
256         memset(hb_offsets.data(), 0, hb_offsets.size() * sizeof(HB_FixedPoint));
257 
258         shaper_item.glyphs = hb_glyphs.data();
259         shaper_item.attributes = hb_attributes.data();
260         shaper_item.advances = hb_advances.data();
261         shaper_item.offsets = hb_offsets.data();
262         shaper_item.log_clusters = hb_logClusters.data();
263 
264         if (HB_ShapeItem(&shaper_item))
265             break;
266     }
267 
268     HB_FreeFace(hbFace);
269 }
270 
271 
decomposedShaping(FT_Face face,HB_Script script,const QChar & ch)272 static bool decomposedShaping(FT_Face face, HB_Script script, const QChar &ch)
273 {
274     QString uc = QString().append(ch);
275     Shaper shaper(face, script, uc);
276 
277     uc = uc.normalized(QString::NormalizationForm_D);
278     Shaper decomposed(face, script, uc);
279 
280     if( shaper.shaper_item.num_glyphs != decomposed.shaper_item.num_glyphs )
281         goto error;
282 
283     for (unsigned int i = 0; i < shaper.shaper_item.num_glyphs; ++i) {
284         if ((shaper.shaper_item.glyphs[i]&0xffffff) != (decomposed.shaper_item.glyphs[i]&0xffffff))
285             goto error;
286     }
287     return true;
288  error:
289     QString str = "";
290     int i = 0;
291     while (i < uc.length()) {
292         str += QString("%1 ").arg(uc[i].unicode(), 4, 16);
293         ++i;
294     }
295     qDebug("%s: decomposedShaping of char %4x failed\n    decomposedString: %s\n   nglyphs=%d, decomposed nglyphs %d",
296            face->family_name,
297            ch.unicode(), str.toLatin1().data(),
298            shaper.shaper_item.num_glyphs,
299            decomposed.shaper_item.num_glyphs);
300 
301     str = "";
302     i = 0;
303     while (i < shaper.shaper_item.num_glyphs) {
304         str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16);
305         ++i;
306     }
307     qDebug("    composed glyph result   = %s", str.toLatin1().constData());
308     str = "";
309     i = 0;
310     while (i < decomposed.shaper_item.num_glyphs) {
311         str += QString("%1 ").arg(decomposed.shaper_item.glyphs[i], 4, 16);
312         ++i;
313     }
314     qDebug("    decomposed glyph result = %s", str.toLatin1().constData());
315     return false;
316 }
317 
318 struct ShapeTable {
319     unsigned short unicode[16];
320     unsigned short glyphs[16];
321 };
322 
shaping(FT_Face face,const ShapeTable * s,HB_Script script)323 static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script)
324 {
325     Shaper shaper(face, script, QString::fromUtf16( s->unicode ));
326 
327     hb_uint32 nglyphs = 0;
328     const unsigned short *g = s->glyphs;
329     while ( *g ) {
330 	nglyphs++;
331 	g++;
332     }
333 
334     if( nglyphs != shaper.shaper_item.num_glyphs )
335 	goto error;
336 
337     for (hb_uint32 i = 0; i < nglyphs; ++i) {
338         if ((shaper.shaper_item.glyphs[i]&0xffffff) != s->glyphs[i])
339 	    goto error;
340     }
341     return true;
342  error:
343     QString str = "";
344     const unsigned short *uc = s->unicode;
345     while (*uc) {
346 	str += QString("%1 ").arg(*uc, 4, 16);
347 	++uc;
348     }
349     qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d",
350            face->family_name,
351            str.toLatin1().constData(),
352            shaper.shaper_item.num_glyphs, nglyphs);
353 
354     str = "";
355     hb_uint32 i = 0;
356     while (i < shaper.shaper_item.num_glyphs) {
357         str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16);
358 	++i;
359     }
360     qDebug("    glyph result = %s", str.toLatin1().constData());
361     return false;
362 }
363 
364 
greek()365 void tst_QScriptEngine::greek()
366 {
367     FT_Face face = loadFace("DejaVuSans.ttf");
368     if (face) {
369         for (int uc = 0x1f00; uc <= 0x1fff; ++uc) {
370             QString str;
371             str.append(uc);
372             if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) {
373                 //qDebug() << "skipping" << Qt::hex << uc;
374                 continue;
375             }
376             if (uc == 0x1fc1 || uc == 0x1fed)
377                 continue;
378             QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) );
379         }
380         FT_Done_Face(face);
381     } else {
382         QSKIP("couln't find DejaVu Sans");
383     }
384 
385 
386     face = loadFace("SBL_grk.ttf");
387     if (face) {
388         for (int uc = 0x1f00; uc <= 0x1fff; ++uc) {
389             QString str;
390             str.append(uc);
391             if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) {
392                 //qDebug() << "skipping" << Qt::hex << uc;
393                 continue;
394             }
395             if (uc == 0x1fc1 || uc == 0x1fed)
396                 continue;
397             QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) );
398 
399         }
400 
401         const ShapeTable shape_table [] = {
402             { { 0x3b1, 0x300, 0x313, 0x0 },
403               { 0xb8, 0x3d3, 0x3c7, 0x0 } },
404             { { 0x3b1, 0x313, 0x300, 0x0 },
405               { 0xd4, 0x0 } },
406 
407             { {0}, {0} }
408         };
409 
410 
411         const ShapeTable *s = shape_table;
412         while (s->unicode[0]) {
413             QVERIFY( shaping(face, s, HB_Script_Greek) );
414             ++s;
415         }
416 
417         FT_Done_Face(face);
418     } else {
419         QSKIP("couln't find DejaVu Sans");
420     }
421 }
422 
423 
devanagari()424 void tst_QScriptEngine::devanagari()
425 {
426     {
427         FT_Face face = loadFace("raghu.ttf");
428         if (face) {
429 	    const ShapeTable shape_table [] = {
430 		// Ka
431 		{ { 0x0915, 0x0 },
432 		  { 0x0080, 0x0 } },
433 		// Ka Halant
434 		{ { 0x0915, 0x094d, 0x0 },
435 		  { 0x0080, 0x0051, 0x0 } },
436 		// Ka Halant Ka
437 		{ { 0x0915, 0x094d, 0x0915, 0x0 },
438 		  { 0x00c8, 0x0080, 0x0 } },
439 		// Ka MatraI
440 		{ { 0x0915, 0x093f, 0x0 },
441 		  { 0x01d1, 0x0080, 0x0 } },
442 		// Ra Halant Ka
443 		{ { 0x0930, 0x094d, 0x0915, 0x0 },
444 		  { 0x0080, 0x005b, 0x0 } },
445 		// Ra Halant Ka MatraI
446 		{ { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
447 		  { 0x01d1, 0x0080, 0x005b, 0x0 } },
448 		// MatraI
449 		{ { 0x093f, 0x0 },
450 		  { 0x01d4, 0x029c, 0x0 } },
451 		// Ka Nukta
452 		{ { 0x0915, 0x093c, 0x0 },
453 		  { 0x00a4, 0x0 } },
454 		// Ka Halant Ra
455 		{ { 0x0915, 0x094d, 0x0930, 0x0 },
456 		  { 0x0110, 0x0 } },
457 		// Ka Halant Ra Halant Ka
458 		{ { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
459 		  { 0x0158, 0x0080, 0x0 } },
460 		{ { 0x0930, 0x094d, 0x200d, 0x0 },
461 		  { 0x00e2, 0x0 } },
462 		{ { 0x0915, 0x094d, 0x0930, 0x094d, 0x200d, 0x0 },
463 		  { 0x0158, 0x0 } },
464 
465 		{ {0}, {0} }
466 	    };
467 
468 
469 	    const ShapeTable *s = shape_table;
470 	    while (s->unicode[0]) {
471 		QVERIFY( shaping(face, s, HB_Script_Devanagari) );
472 		++s;
473 	    }
474 
475             FT_Done_Face(face);
476 	} else {
477 	    QSKIP("couln't find raghu.ttf");
478 	}
479     }
480 
481     {
482         FT_Face face = loadFace("mangal.ttf");
483         if (face) {
484 	    const ShapeTable shape_table [] = {
485 		// Ka
486 		{ { 0x0915, 0x0 },
487 		  { 0x0080, 0x0 } },
488 		// Ka Halant
489 		{ { 0x0915, 0x094d, 0x0 },
490 		  { 0x0080, 0x0051, 0x0 } },
491 		// Ka Halant Ka
492 		{ { 0x0915, 0x094d, 0x0915, 0x0 },
493 		  { 0x00c8, 0x0080, 0x0 } },
494 		// Ka MatraI
495 		{ { 0x0915, 0x093f, 0x0 },
496 		  { 0x01d1, 0x0080, 0x0 } },
497 		// Ra Halant Ka
498 		{ { 0x0930, 0x094d, 0x0915, 0x0 },
499 		  { 0x0080, 0x005b, 0x0 } },
500 		// Ra Halant Ka MatraI
501 		{ { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
502 		  { 0x01d1, 0x0080, 0x005b, 0x0 } },
503 		// MatraI
504 		{ { 0x093f, 0x0 },
505 		  { 0x01d4, 0x029c, 0x0 } },
506 		// Ka Nukta
507 		{ { 0x0915, 0x093c, 0x0 },
508 		  { 0x00a4, 0x0 } },
509 		// Ka Halant Ra
510 		{ { 0x0915, 0x094d, 0x0930, 0x0 },
511 		  { 0x0110, 0x0 } },
512 		// Ka Halant Ra Halant Ka
513 		{ { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
514 		  { 0x0158, 0x0080, 0x0 } },
515 
516                 { { 0x92b, 0x94d, 0x930, 0x0 },
517                   { 0x125, 0x0 } },
518                 { { 0x92b, 0x93c, 0x94d, 0x930, 0x0 },
519                   { 0x149, 0x0 } },
520 		{ {0}, {0} }
521 	    };
522 
523 	    const ShapeTable *s = shape_table;
524 	    while (s->unicode[0]) {
525 		QVERIFY( shaping(face, s, HB_Script_Devanagari) );
526 		++s;
527 	    }
528 
529             FT_Done_Face(face);
530 	} else {
531 	    QSKIP("couldn't find mangal.ttf");
532 	}
533     }
534 }
535 
bengali()536 void tst_QScriptEngine::bengali()
537 {
538     {
539         FT_Face face = loadFace("AkaashNormal.ttf");
540         if (face) {
541 	    const ShapeTable shape_table [] = {
542 		// Ka
543 		{ { 0x0995, 0x0 },
544 		  { 0x0151, 0x0 } },
545 		// Ka Halant
546 		{ { 0x0995, 0x09cd, 0x0 },
547 		  { 0x0151, 0x017d, 0x0 } },
548 		// Ka Halant Ka
549 		{ { 0x0995, 0x09cd, 0x0995, 0x0 },
550 		  { 0x019b, 0x0 } },
551 		// Ka MatraI
552 		{ { 0x0995, 0x09bf, 0x0 },
553 		  { 0x0173, 0x0151, 0x0 } },
554 		// Ra Halant Ka
555 		{ { 0x09b0, 0x09cd, 0x0995, 0x0 },
556 		  { 0x0151, 0x0276, 0x0 } },
557 		// Ra Halant Ka MatraI
558 		{ { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
559 		  { 0x0173, 0x0151, 0x0276, 0x0 } },
560 		// Ka Nukta
561 		{ { 0x0995, 0x09bc, 0x0 },
562 		  { 0x0151, 0x0171, 0x0 } },
563 		// Ka Halant Ra
564 		{ { 0x0995, 0x09cd, 0x09b0, 0x0 },
565 		  { 0x01f4, 0x0 } },
566 		// Ka Halant Ra Halant Ka
567 		{ { 0x0995, 0x09cd, 0x09b0, 0x09cd, 0x0995, 0x0 },
568 		  { 0x025c, 0x0276, 0x0151, 0x0 } },
569 		// Ya + Halant
570 		{ { 0x09af, 0x09cd, 0x0 },
571 		  { 0x016a, 0x017d, 0x0 } },
572 		// Da Halant Ya -> Da Ya-Phala
573 		{ { 0x09a6, 0x09cd, 0x09af, 0x0 },
574 		  { 0x01e5, 0x0 } },
575 		// A Halant Ya -> A Ya-phala
576 		{ { 0x0985, 0x09cd, 0x09af, 0x0 },
577 		  { 0x0145, 0x01cf, 0x0 } },
578 		// Na Halant Ka
579 		{ { 0x09a8, 0x09cd, 0x0995, 0x0 },
580 		  { 0x026f, 0x0151, 0x0 } },
581 		// Na Halant ZWNJ Ka
582 		{ { 0x09a8, 0x09cd, 0x200c, 0x0995, 0x0 },
583 		  { 0x0164, 0x017d, 0x0151, 0x0 } },
584 		// Na Halant ZWJ Ka
585 		{ { 0x09a8, 0x09cd, 0x200d, 0x0995, 0x0 },
586 		  { 0x026f, 0x0151, 0x0 } },
587 		// Ka Halant ZWNJ Ka
588 		{ { 0x0995, 0x09cd, 0x200c, 0x0995, 0x0 },
589 		  { 0x0151, 0x017d, 0x0151, 0x0 } },
590 		// Ka Halant ZWJ Ka
591 		{ { 0x0995, 0x09cd, 0x200d, 0x0995, 0x0 },
592 		  { 0x025c, 0x0151, 0x0 } },
593 		// Na Halant Ra
594 		{ { 0x09a8, 0x09cd, 0x09b0, 0x0 },
595 		  { 0x0207, 0x0 } },
596 		// Na Halant ZWNJ Ra
597 		{ { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
598 		  { 0x0164, 0x017d, 0x016b, 0x0 } },
599 		// Na Halant ZWJ Ra
600 		{ { 0x09a8, 0x09cd, 0x200d, 0x09b0, 0x0 },
601 		  { 0x026f, 0x016b, 0x0 } },
602 		// Na Halant Ba
603 		{ { 0x09a8, 0x09cd, 0x09ac, 0x0 },
604 		  { 0x022f, 0x0 } },
605 		// Na Halant ZWNJ Ba
606 		{ { 0x09a8, 0x09cd, 0x200c, 0x09ac, 0x0 },
607 		  { 0x0164, 0x017d, 0x0167, 0x0 } },
608 		// Na Halant ZWJ Ba
609 		{ { 0x09a8, 0x09cd, 0x200d, 0x09ac, 0x0 },
610 		  { 0x026f, 0x0167, 0x0 } },
611 		// Na Halant Dha
612 		{ { 0x09a8, 0x09cd, 0x09a7, 0x0 },
613 		  { 0x01d3, 0x0 } },
614 		// Na Halant ZWNJ Dha
615 		{ { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
616 		  { 0x0164, 0x017d, 0x0163, 0x0 } },
617 		// Na Halant ZWJ Dha
618 		{ { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
619 		  { 0x026f, 0x0163, 0x0 } },
620 		// Ra Halant Ka MatraAU
621 		{ { 0x09b0, 0x09cd, 0x0995, 0x09cc, 0x0 },
622 		  { 0x0179, 0x0151, 0x0276, 0x017e, 0x0 } },
623 		// Ra Halant Ba Halant Ba
624 		{ { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
625 		  { 0x0232, 0x0276, 0x0 } },
626                 { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x982, 0x0 },
627                   { 0x151, 0x276, 0x172, 0x143, 0x0 } },
628                 { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x983, 0x0 },
629                   { 0x151, 0x276, 0x172, 0x144, 0x0 } },
630                 // test decomposed two parts matras
631                 { { 0x995, 0x9c7, 0x9be, 0x0 },
632                   { 0x179, 0x151, 0x172, 0x0 } },
633                 { { 0x995, 0x9c7, 0x9d7, 0x0 },
634                   { 0x179, 0x151, 0x17e, 0x0 } },
635                 { { 0x9b0, 0x9cd, 0x9ad, 0x0 },
636                   { 0x168, 0x276, 0x0 } },
637                 { { 0x9f0, 0x9cd, 0x9ad, 0x0 },
638                   { 0x168, 0x276, 0x0 } },
639                 { { 0x9f1, 0x9cd, 0x9ad, 0x0 },
640                   { 0x191, 0x17d, 0x168, 0x0 } },
641 
642                 // Ra ZWJ Halant Ya
643                 { { 0x09b0, 0x200d, 0x09cd, 0x09af, 0x0 },
644                   { 0x016b, 0x01cf, 0x0 } },
645 
646 		{ {0}, {0} }
647 	    };
648 
649 
650 	    const ShapeTable *s = shape_table;
651 	    while (s->unicode[0]) {
652 		QVERIFY( shaping(face, s, HB_Script_Bengali) );
653 		++s;
654 	    }
655 
656             FT_Done_Face(face);
657 	} else {
658 	    QSKIP("couln't find AkaashNormal.ttf");
659 	}
660     }
661     {
662         FT_Face face = loadFace("MuktiNarrow.ttf");
663         if (face) {
664 	    const ShapeTable shape_table [] = {
665 		// Ka
666 		{ { 0x0995, 0x0 },
667 		  { 0x0073, 0x0 } },
668 		// Ka Halant
669 		{ { 0x0995, 0x09cd, 0x0 },
670 		  { 0x00b9, 0x0 } },
671 		// Ka Halant Ka
672 		{ { 0x0995, 0x09cd, 0x0995, 0x0 },
673 		  { 0x0109, 0x0 } },
674 		// Ka MatraI
675 		{ { 0x0995, 0x09bf, 0x0 },
676 		  { 0x0095, 0x0073, 0x0 } },
677 		// Ra Halant Ka
678 		{ { 0x09b0, 0x09cd, 0x0995, 0x0 },
679 		  { 0x0073, 0x00e1, 0x0 } },
680 		// Ra Halant Ka MatraI
681 		{ { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
682 		  { 0x0095, 0x0073, 0x00e1, 0x0 } },
683 		// MatraI
684  		{ { 0x09bf, 0x0 },
685 		  { 0x0095, 0x01c8, 0x0 } },
686 		// Ka Nukta
687 		{ { 0x0995, 0x09bc, 0x0 },
688 		  { 0x0073, 0x0093, 0x0 } },
689 		// Ka Halant Ra
690 		{ { 0x0995, 0x09cd, 0x09b0, 0x0 },
691 		  { 0x00e5, 0x0 } },
692 		// Ka Halant Ra Halant Ka
693                 { { 0x995, 0x9cd, 0x9b0, 0x9cd, 0x995, 0x0 },
694                   { 0x234, 0x24e, 0x73, 0x0 } },
695 		// Ya + Halant
696 		{ { 0x09af, 0x09cd, 0x0 },
697 		  { 0x00d2, 0x0 } },
698 		// Da Halant Ya -> Da Ya-Phala
699 		{ { 0x09a6, 0x09cd, 0x09af, 0x0 },
700 		  { 0x0084, 0x00e2, 0x0 } },
701 		// A Halant Ya -> A Ya-phala
702 		{ { 0x0985, 0x09cd, 0x09af, 0x0 },
703 		  { 0x0067, 0x00e2, 0x0 } },
704 		// Na Halant Ka
705 		{ { 0x09a8, 0x09cd, 0x0995, 0x0 },
706 		  { 0x0188, 0x0 } },
707 		// Na Halant ZWNJ Ka
708                 { { 0x9a8, 0x9cd, 0x200c, 0x995, 0x0 },
709                   { 0xcc, 0x73, 0x0 } },
710 		// Na Halant ZWJ Ka
711                 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
712                   { 0x247, 0x73, 0x0 } },
713 		// Ka Halant ZWNJ Ka
714                 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
715                   { 0x247, 0x73, 0x0 } },
716 		// Ka Halant ZWJ Ka
717                 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
718                   { 0x247, 0x73, 0x0 } },
719 		// Na Halant Ra
720 		{ { 0x09a8, 0x09cd, 0x09b0, 0x0 },
721 		  { 0x00f8, 0x0 } },
722 		// Na Halant ZWNJ Ra
723 		{ { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
724 		  { 0xcc, 0x8d, 0x0 } },
725 		// Na Halant ZWJ Ra
726                 { { 0x9a8, 0x9cd, 0x200d, 0x9b0, 0x0 },
727                   { 0x247, 0x8d, 0x0 } },
728 		// Na Halant Ba
729 		{ { 0x09a8, 0x09cd, 0x09ac, 0x0 },
730 		  { 0x0139, 0x0 } },
731 		// Na Halant ZWNJ Ba
732                 { { 0x9a8, 0x9cd, 0x200c, 0x9ac, 0x0 },
733                   { 0xcc, 0x89, 0x0 } },
734 		// Na Halant ZWJ Ba
735                 { { 0x9a8, 0x9cd, 0x200d, 0x9ac, 0x0 },
736                   { 0x247, 0x89, 0x0 } },
737 		// Na Halant Dha
738 		{ { 0x09a8, 0x09cd, 0x09a7, 0x0 },
739 		  { 0x0145, 0x0 } },
740 		// Na Halant ZWNJ Dha
741 		{ { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
742 		  { 0xcc, 0x85, 0x0 } },
743 		// Na Halant ZWJ Dha
744 		{ { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
745 		  { 0x247, 0x85, 0x0 } },
746 		// Ra Halant Ka MatraAU
747                 { { 0x9b0, 0x9cd, 0x995, 0x9cc, 0x0 },
748                   { 0x232, 0x73, 0xe1, 0xa0, 0x0 } },
749 		// Ra Halant Ba Halant Ba
750 		{ { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
751 		  { 0x013b, 0x00e1, 0x0 } },
752 
753                 // Init feature for vowel sign E should only be
754                 // applied when it's initial character (QTBUG-13620)
755                 { { 0x09a8, 0x09c7, 0x0 },
756                   { 0x0232, 0x0086, 0x0 } },
757                 { { 0x09a8, 0x09a8, 0x09c7, 0x0 },
758                   { 0x0086, 0x009b, 0x0086, 0x0 } },
759 
760 		{ {0}, {0} }
761 	    };
762 
763 
764 	    const ShapeTable *s = shape_table;
765 	    while (s->unicode[0]) {
766 		QVERIFY( shaping(face, s, HB_Script_Bengali) );
767 		++s;
768 	    }
769 
770             FT_Done_Face(face);
771 	} else {
772 	    QSKIP("couln't find MuktiNarrow.ttf");
773 	}
774     }
775     {
776         FT_Face face = loadFace("LikhanNormal.ttf");
777         if (face) {
778 	    const ShapeTable shape_table [] = {
779 		{ { 0x09a8, 0x09cd, 0x09af, 0x0 },
780                   { 0x01ca, 0x0 } },
781 		{ { 0x09b8, 0x09cd, 0x09af, 0x0 },
782                   { 0x020e, 0x0 } },
783 		{ { 0x09b6, 0x09cd, 0x09af, 0x0 },
784                   { 0x01f4, 0x0 } },
785 		{ { 0x09b7, 0x09cd, 0x09af, 0x0 },
786                   { 0x01fe, 0x0 } },
787 		{ { 0x09b0, 0x09cd, 0x09a8, 0x09cd, 0x200d, 0x0 },
788                   { 0x10b, 0x167, 0x0 } },
789                 { { 0x9b0, 0x9cd, 0x9ad, 0x0 },
790                   { 0xa1, 0x167, 0x0 } },
791                 { { 0x9f0, 0x9cd, 0x9ad, 0x0 },
792                   { 0xa1, 0x167, 0x0 } },
793                 { { 0x9f1, 0x9cd, 0x9ad, 0x0 },
794                   { 0x11c, 0xa1, 0x0 } },
795 
796 		{ {0}, {0} }
797 	    };
798 
799 
800 	    const ShapeTable *s = shape_table;
801 	    while (s->unicode[0]) {
802 		QVERIFY( shaping(face, s, HB_Script_Bengali) );
803 		++s;
804 	    }
805 
806             FT_Done_Face(face);
807 	} else {
808 	    QSKIP("couln't find LikhanNormal.ttf");
809 	}
810     }
811 }
812 
gurmukhi()813 void tst_QScriptEngine::gurmukhi()
814 {
815     {
816         FT_Face face = loadFace("lohit_pa.ttf");
817         if (face) {
818 	    const ShapeTable shape_table [] = {
819 		{ { 0xA15, 0xA4D, 0xa39, 0x0 },
820 		  { 0x3b, 0x8b, 0x0 } },
821 		{ {0}, {0} }
822 	    };
823 
824 
825 	    const ShapeTable *s = shape_table;
826 	    while (s->unicode[0]) {
827 		QVERIFY( shaping(face, s, HB_Script_Gurmukhi) );
828 		++s;
829 	    }
830 
831             FT_Done_Face(face);
832 	} else {
833 	    QSKIP("couln't find lohit.punjabi.1.1.ttf");
834 	}
835     }
836 }
837 
oriya()838 void tst_QScriptEngine::oriya()
839 {
840     {
841         FT_Face face = loadFace("utkalm.ttf");
842         if (face) {
843 	    const ShapeTable shape_table [] = {
844                 { { 0xb15, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
845                   { 0x150, 0x125, 0x0 } },
846                 { { 0xb24, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
847                   { 0x151, 0x120, 0x0 } },
848                 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
849                   { 0x152, 0x120, 0x0 } },
850                 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
851                   { 0x152, 0x120, 0x0 } },
852                 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
853                   { 0x176, 0x0 } },
854                 { { 0xb38, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
855                   { 0x177, 0x0 } },
856                 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0xb4d, 0xb2f, 0x0 },
857                   { 0x176, 0x124, 0x0 } },
858 
859                 // QTBUG-13542
860                 { { 0x0b2c, 0x0b4d, 0x0b21, 0x0 },
861                   { 0x0089, 0x00fc, 0x0 } },
862                 { { 0x0b36, 0x0b4d, 0x0b2b, 0x0 },
863                   { 0x0092, 0x0105, 0x0 } },
864                 { { 0x0b36, 0x0b4d, 0x0b1f, 0x0 },
865                   { 0x0092, 0x00fa, 0x0 } },
866                 { { 0x0b39, 0x0b4d, 0x0b1f, 0x0 },
867                   { 0x0095, 0x00fa, 0x0 } },
868                 { { 0x0b15, 0x0b4d, 0x0b16, 0x0 },
869                   { 0x0073, 0x00f1, 0x0 } },
870 
871                 { {0}, {0} }
872 
873             };
874 
875 	    const ShapeTable *s = shape_table;
876 	    while (s->unicode[0]) {
877 		QVERIFY( shaping(face, s, HB_Script_Oriya) );
878 		++s;
879 	    }
880 
881             FT_Done_Face(face);
882 	} else {
883 	    QSKIP("couln't find utkalm.ttf");
884 	}
885     }
886 }
887 
888 
tamil()889 void tst_QScriptEngine::tamil()
890 {
891     {
892         FT_Face face = loadFace("akruti1.ttf");
893         if (face) {
894 	    const ShapeTable shape_table [] = {
895 		{ { 0x0b95, 0x0bc2, 0x0 },
896 		  { 0x004e, 0x0 } },
897 		{ { 0x0bae, 0x0bc2, 0x0 },
898 		  { 0x009e, 0x0 } },
899 		{ { 0x0b9a, 0x0bc2, 0x0 },
900 		  { 0x0058, 0x0 } },
901 		{ { 0x0b99, 0x0bc2, 0x0 },
902 		  { 0x0053, 0x0 } },
903 		{ { 0x0bb0, 0x0bc2, 0x0 },
904 		  { 0x00a8, 0x0 } },
905 		{ { 0x0ba4, 0x0bc2, 0x0 },
906 		  { 0x008e, 0x0 } },
907 		{ { 0x0b9f, 0x0bc2, 0x0 },
908 		  { 0x0062, 0x0 } },
909 		{ { 0x0b95, 0x0bc6, 0x0 },
910 		  { 0x000a, 0x0031, 0x0 } },
911 		{ { 0x0b95, 0x0bca, 0x0 },
912 		  { 0x000a, 0x0031, 0x0007, 0x0 } },
913 		{ { 0x0b95, 0x0bc6, 0x0bbe, 0x0 },
914 		  { 0x000a, 0x0031, 0x007, 0x0 } },
915 		{ { 0x0b95, 0x0bcd, 0x0bb7, 0x0 },
916 		  { 0x0049, 0x0 } },
917 		{ { 0x0b95, 0x0bcd, 0x0bb7, 0x0bca, 0x0 },
918 		  { 0x000a, 0x0049, 0x007, 0x0 } },
919 		{ { 0x0b95, 0x0bcd, 0x0bb7, 0x0bc6, 0x0bbe, 0x0 },
920 		  { 0x000a, 0x0049, 0x007, 0x0 } },
921 		{ { 0x0b9f, 0x0bbf, 0x0 },
922 		  { 0x005f, 0x0 } },
923 		{ { 0x0b9f, 0x0bc0, 0x0 },
924 		  { 0x0060, 0x0 } },
925 		{ { 0x0bb2, 0x0bc0, 0x0 },
926 		  { 0x00ab, 0x0 } },
927 		{ { 0x0bb2, 0x0bbf, 0x0 },
928 		  { 0x00aa, 0x0 } },
929 		{ { 0x0bb0, 0x0bcd, 0x0 },
930 		  { 0x00a4, 0x0 } },
931 		{ { 0x0bb0, 0x0bbf, 0x0 },
932 		  { 0x00a5, 0x0 } },
933 		{ { 0x0bb0, 0x0bc0, 0x0 },
934 		  { 0x00a6, 0x0 } },
935 		{ { 0x0b83, 0x0 },
936 		  { 0x0025, 0x0 } },
937 		{ { 0x0b83, 0x0b95, 0x0 },
938 		  { 0x0025, 0x0031, 0x0 } },
939 
940 		{ {0}, {0} }
941 	    };
942 
943 
944 	    const ShapeTable *s = shape_table;
945 	    while (s->unicode[0]) {
946 		QVERIFY( shaping(face, s, HB_Script_Tamil) );
947 		++s;
948 	    }
949 
950             FT_Done_Face(face);
951 	} else {
952 	    QSKIP("couln't find akruti1.ttf");
953 	}
954     }
955 }
956 
957 
telugu()958 void tst_QScriptEngine::telugu()
959 {
960     {
961         FT_Face face = loadFace("Pothana2000.ttf");
962         if (face) {
963 	    const ShapeTable shape_table [] = {
964                 { { 0xc15, 0xc4d, 0x0 },
965                   { 0xbb, 0x0 } },
966                 { { 0xc15, 0xc4d, 0xc37, 0x0 },
967                   { 0x4b, 0x0 } },
968                 { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0x0 },
969                   { 0xe0, 0x0 } },
970                 { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0xc23, 0x0 },
971                   { 0x4b, 0x91, 0x0 } },
972                 { { 0xc15, 0xc4d, 0xc30, 0x0 },
973                   { 0x5a, 0xb2, 0x0 } },
974                 { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0x0 },
975                   { 0xbb, 0xb2, 0x0 } },
976                 { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0xc15, 0x0 },
977                   { 0x5a, 0xb2, 0x83, 0x0 } },
978                 { { 0xc15, 0xc4d, 0xc30, 0xc3f, 0x0 },
979                   { 0xe2, 0xb2, 0x0 } },
980                 { { 0xc15, 0xc4d, 0xc15, 0xc48, 0x0 },
981                   { 0xe6, 0xb3, 0x83, 0x0 } },
982                 { { 0xc15, 0xc4d, 0xc30, 0xc48, 0x0 },
983                   { 0xe6, 0xb3, 0x9f, 0x0 } },
984                 { { 0xc15, 0xc46, 0xc56, 0x0 },
985                   { 0xe6, 0xb3, 0x0 } },
986                 { {0}, {0} }
987             };
988 
989 	    const ShapeTable *s = shape_table;
990 	    while (s->unicode[0]) {
991 		QVERIFY( shaping(face, s, HB_Script_Telugu) );
992 		++s;
993 	    }
994 
995             FT_Done_Face(face);
996 	} else {
997 	    QSKIP("couln't find Pothana2000.ttf");
998 	}
999     }
1000 }
1001 
1002 
kannada()1003 void tst_QScriptEngine::kannada()
1004 {
1005     {
1006         FT_Face face = loadFace("Sampige.ttf");
1007         if (face) {
1008 	    const ShapeTable shape_table [] = {
1009 		{ { 0x0ca8, 0x0ccd, 0x0ca8, 0x0 },
1010 		  { 0x0049, 0x00ba, 0x0 } },
1011 		{ { 0x0ca8, 0x0ccd, 0x0ca1, 0x0 },
1012 		  { 0x0049, 0x00b3, 0x0 } },
1013 		{ { 0x0caf, 0x0cc2, 0x0 },
1014 		  { 0x004f, 0x005d, 0x0 } },
1015 		{ { 0x0ce0, 0x0 },
1016 		  { 0x006a, 0x0 } },
1017 		{ { 0x0ce6, 0x0ce7, 0x0ce8, 0x0 },
1018 		  { 0x006b, 0x006c, 0x006d, 0x0 } },
1019 		{ { 0x0cb5, 0x0ccb, 0x0 },
1020 		  { 0x015f, 0x0067, 0x0 } },
1021 		{ { 0x0cb0, 0x0ccd, 0x0cae, 0x0 },
1022 		  { 0x004e, 0x0082, 0x0 } },
1023 		{ { 0x0cb0, 0x0ccd, 0x0c95, 0x0 },
1024 		  { 0x0036, 0x0082, 0x0 } },
1025 		{ { 0x0c95, 0x0ccd, 0x0cb0, 0x0 },
1026 		  { 0x0036, 0x00c1, 0x0 } },
1027 		{ { 0x0cb0, 0x0ccd, 0x200d, 0x0c95, 0x0 },
1028 		  { 0x0050, 0x00a7, 0x0 } },
1029 
1030                 // Kaphala
1031                 { { 0x0cb0, 0x200d, 0x0ccd, 0x0c95, 0x0 },
1032                   { 0x0050, 0x00a7, 0x0 } },
1033 
1034 		{ {0}, {0} }
1035 	    };
1036 
1037 
1038 	    const ShapeTable *s = shape_table;
1039 	    while (s->unicode[0]) {
1040 		QVERIFY( shaping(face, s, HB_Script_Kannada) );
1041 		++s;
1042 	    }
1043 
1044             FT_Done_Face(face);
1045 	} else {
1046 	    QSKIP("couln't find Sampige.ttf");
1047 	}
1048     }
1049     {
1050         FT_Face face = loadFace("tunga.ttf");
1051         if (face) {
1052 	    const ShapeTable shape_table [] = {
1053 		{ { 0x0cb7, 0x0cc6, 0x0 },
1054 		  { 0x00b0, 0x006c, 0x0 } },
1055 		{ { 0x0cb7, 0x0ccd, 0x0 },
1056 		  { 0x0163, 0x0 } },
1057                 { { 0xc95, 0xcbf, 0xcd5, 0x0 },
1058                   { 0x114, 0x73, 0x0 } },
1059                 { { 0xc95, 0xcc6, 0xcd5, 0x0 },
1060                   { 0x90, 0x6c, 0x73, 0x0 } },
1061                 { { 0xc95, 0xcc6, 0xcd6, 0x0 },
1062                   { 0x90, 0x6c, 0x74, 0x0 } },
1063                 { { 0xc95, 0xcc6, 0xcc2, 0x0 },
1064                   { 0x90, 0x6c, 0x69, 0x0 } },
1065                 { { 0xc95, 0xcca, 0xcd5, 0x0 },
1066                   { 0x90, 0x6c, 0x69, 0x73, 0x0 } },
1067 
1068 
1069 		{ {0}, {0} }
1070 	    };
1071 
1072 
1073 	    const ShapeTable *s = shape_table;
1074 	    while (s->unicode[0]) {
1075 		QVERIFY( shaping(face, s, HB_Script_Kannada) );
1076 		++s;
1077 	    }
1078 
1079             FT_Done_Face(face);
1080 	} else {
1081 	    QSKIP("couln't find tunga.ttf");
1082 	}
1083     }
1084 }
1085 
1086 
1087 
malayalam()1088 void tst_QScriptEngine::malayalam()
1089 {
1090     {
1091         FT_Face face = loadFace("AkrutiMal2Normal.ttf");
1092         if (face) {
1093 	    const ShapeTable shape_table [] = {
1094 		{ { 0x0d15, 0x0d46, 0x0 },
1095 		  { 0x005e, 0x0034, 0x0 } },
1096 		{ { 0x0d15, 0x0d47, 0x0 },
1097 		  { 0x005f, 0x0034, 0x0 } },
1098 		{ { 0x0d15, 0x0d4b, 0x0 },
1099 		  { 0x005f, 0x0034, 0x0058, 0x0 } },
1100 		{ { 0x0d15, 0x0d48, 0x0 },
1101 		  { 0x0060, 0x0034, 0x0 } },
1102 		{ { 0x0d15, 0x0d4a, 0x0 },
1103 		  { 0x005e, 0x0034, 0x0058, 0x0 } },
1104 		{ { 0x0d30, 0x0d4d, 0x0d15, 0x0 },
1105 		  { 0x009e, 0x0034, 0x0 } },
1106 		{ { 0x0d15, 0x0d4d, 0x0d35, 0x0 },
1107 		  { 0x0034, 0x007a, 0x0 } },
1108 		{ { 0x0d15, 0x0d4d, 0x0d2f, 0x0 },
1109 		  { 0x0034, 0x00a2, 0x0 } },
1110 		{ { 0x0d1f, 0x0d4d, 0x0d1f, 0x0 },
1111 		  { 0x0069, 0x0 } },
1112 		{ { 0x0d26, 0x0d4d, 0x0d26, 0x0 },
1113 		  { 0x0074, 0x0 } },
1114 		{ { 0x0d30, 0x0d4d, 0x0 },
1115 		  { 0x009e, 0x0 } },
1116 		{ { 0x0d30, 0x0d4d, 0x200c, 0x0 },
1117 		  { 0x009e, 0x0 } },
1118 		{ { 0x0d30, 0x0d4d, 0x200d, 0x0 },
1119 		  { 0x009e, 0x0 } },
1120                 { { 0xd15, 0xd46, 0xd3e, 0x0 },
1121                   { 0x5e, 0x34, 0x58, 0x0 } },
1122                 { { 0xd15, 0xd47, 0xd3e, 0x0 },
1123                   { 0x5f, 0x34, 0x58, 0x0 } },
1124                 { { 0xd15, 0xd46, 0xd57, 0x0 },
1125                   { 0x5e, 0x34, 0x65, 0x0 } },
1126                 { { 0xd15, 0xd57, 0x0 },
1127                   { 0x34, 0x65, 0x0 } },
1128                 { { 0xd1f, 0xd4d, 0xd1f, 0xd41, 0xd4d, 0x0 },
1129                   { 0x69, 0x5b, 0x64, 0x0 } },
1130 
1131 		{ {0}, {0} }
1132 	    };
1133 
1134 
1135 	    const ShapeTable *s = shape_table;
1136 	    while (s->unicode[0]) {
1137 		QVERIFY( shaping(face, s, HB_Script_Malayalam) );
1138 		++s;
1139 	    }
1140 
1141             FT_Done_Face(face);
1142 	} else {
1143 	    QSKIP("couln't find AkrutiMal2Normal.ttf");
1144 	}
1145     }
1146 
1147     {
1148         FT_Face face = loadFace("Rachana.ttf");
1149         if (face) {
1150             const ShapeTable shape_table [] = {
1151                 { { 0xd37, 0xd4d, 0xd1f, 0xd4d, 0xd30, 0xd40, 0x0 },
1152                   { 0x385, 0xa3, 0x0 } },
1153                 { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
1154                   { 0x2ff, 0x0 } },
1155                 { { 0xd33, 0xd4d, 0xd33, 0x0 },
1156                   { 0x3f8, 0x0 } },
1157                 { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
1158                   { 0x2ff, 0x0 } },
1159                 { { 0xd30, 0xd4d, 0x200d, 0xd35, 0xd4d, 0xd35, 0x0 },
1160                   { 0xf3, 0x350, 0x0 } },
1161 
1162                 { {0}, {0} }
1163             };
1164 
1165 
1166             const ShapeTable *s = shape_table;
1167             while (s->unicode[0]) {
1168                 QVERIFY( shaping(face, s, HB_Script_Malayalam) );
1169                 ++s;
1170             }
1171 
1172             FT_Done_Face(face);
1173         } else {
1174             QSKIP("couln't find Rachana.ttf");
1175         }
1176     }
1177 
1178 }
1179 
sinhala()1180 void tst_QScriptEngine::sinhala()
1181 {
1182     {
1183         FT_Face face = loadFace("FM-MalithiUW46.ttf");
1184         if (face) {
1185             const ShapeTable shape_table [] = {
1186                 { { 0xd9a, 0xdd9, 0xdcf, 0x0 },
1187                   { 0x4a, 0x61, 0x42, 0x0 } },
1188                 { { 0xd9a, 0xdd9, 0xddf, 0x0 },
1189                   { 0x4a, 0x61, 0x50, 0x0 } },
1190                 { { 0xd9a, 0xdd9, 0xdca, 0x0 },
1191                   { 0x4a, 0x62, 0x0 } },
1192                 { { 0xd9a, 0xddc, 0xdca, 0x0 },
1193                   { 0x4a, 0x61, 0x42, 0x41, 0x0 } },
1194                 { { 0xd9a, 0xdda, 0x0 },
1195                   { 0x4a, 0x62, 0x0 } },
1196                 { { 0xd9a, 0xddd, 0x0 },
1197                   { 0x4a, 0x61, 0x42, 0x41, 0x0 } },
1198                 { {0}, {0} }
1199             };
1200 
1201             const ShapeTable *s = shape_table;
1202             while (s->unicode[0]) {
1203                 QVERIFY( shaping(face, s, HB_Script_Sinhala) );
1204                 ++s;
1205             }
1206 
1207             FT_Done_Face(face);
1208         } else {
1209             QSKIP("couln't find FM-MalithiUW46.ttf");
1210         }
1211     }
1212 }
1213 
1214 
khmer()1215 void tst_QScriptEngine::khmer()
1216 {
1217     {
1218         FT_Face face = loadFace("KhmerOS.ttf");
1219         if (face) {
1220 	    const ShapeTable shape_table [] = {
1221 		{ { 0x179a, 0x17cd, 0x0 },
1222 		  { 0x24c, 0x27f, 0x0 } },
1223 		{ { 0x179f, 0x17c5, 0x0 },
1224 		  { 0x273, 0x203, 0x0 } },
1225 		{ { 0x1790, 0x17d2, 0x1784, 0x17c3, 0x0 },
1226 		  { 0x275, 0x242, 0x182, 0x0 } },
1227 		{ { 0x179a, 0x0 },
1228 		  { 0x24c, 0x0 } },
1229 		{ { 0x1781, 0x17d2, 0x1798, 0x17c2, 0x0 },
1230 		  { 0x274, 0x233, 0x197, 0x0 } },
1231 		{ { 0x1798, 0x17b6, 0x0 },
1232 		  { 0x1cb, 0x0 } },
1233 		{ { 0x179a, 0x17b8, 0x0 },
1234 		  { 0x24c, 0x26a, 0x0 } },
1235 		{ { 0x1787, 0x17b6, 0x0 },
1236 		  { 0x1ba, 0x0 } },
1237 		{ { 0x1798, 0x17d2, 0x1796, 0x17bb, 0x0 },
1238 		  { 0x24a, 0x195, 0x26d, 0x0 } },
1239 		{ {0}, {0} }
1240 	    };
1241 
1242 
1243 	    const ShapeTable *s = shape_table;
1244 	    while (s->unicode[0]) {
1245 		QVERIFY( shaping(face, s, HB_Script_Khmer) );
1246 		++s;
1247 	    }
1248 
1249             FT_Done_Face(face);
1250 	} else {
1251 	    QSKIP("couln't find KhmerOS.ttf");
1252 	}
1253     }
1254 }
1255 
nko()1256 void tst_QScriptEngine::nko()
1257 {
1258     {
1259         FT_Face face = loadFace("DejaVuSans.ttf");
1260         if (face) {
1261 	    const ShapeTable shape_table [] = {
1262                 { { 0x7ca, 0x0 },
1263                   { 0x5c1, 0x0 } },
1264                 { { 0x7ca, 0x7ca, 0x0 },
1265                   { 0x14db, 0x14d9, 0x0 } },
1266                 { { 0x7ca, 0x7fa, 0x7ca, 0x0 },
1267                   { 0x14db, 0x5ec, 0x14d9, 0x0 } },
1268                 { { 0x7ca, 0x7f3, 0x7ca, 0x0 },
1269                   { 0x14db, 0x5e7, 0x14d9, 0x0 } },
1270                 { { 0x7ca, 0x7f3, 0x7fa, 0x7ca, 0x0 },
1271                   { 0x14db, 0x5e7, 0x5ec, 0x14d9, 0x0 } },
1272                 { {0}, {0} }
1273 	    };
1274 
1275 
1276 	    const ShapeTable *s = shape_table;
1277 	    while (s->unicode[0]) {
1278                 QVERIFY( shaping(face, s, HB_Script_Nko) );
1279 		++s;
1280 	    }
1281 
1282             FT_Done_Face(face);
1283 	} else {
1284 	    QSKIP("couln't find DejaVuSans.ttf");
1285 	}
1286     }
1287 }
1288 
1289 
linearB()1290 void tst_QScriptEngine::linearB()
1291 {
1292     {
1293         FT_Face face = loadFace("penuture.ttf");
1294         if (face) {
1295 	    const ShapeTable shape_table [] = {
1296 		{ { 0xd800, 0xdc01, 0xd800, 0xdc02, 0xd800, 0xdc03,  0 },
1297                   { 0x5, 0x6, 0x7, 0 } },
1298 		{ {0}, {0} }
1299 	    };
1300 
1301 
1302 	    const ShapeTable *s = shape_table;
1303 	    while (s->unicode[0]) {
1304 		QVERIFY( shaping(face, s, HB_Script_Common) );
1305 		++s;
1306 	    }
1307 
1308             FT_Done_Face(face);
1309 	} else {
1310 	    QSKIP("couln't find PENUTURE.TTF");
1311 	}
1312     }
1313 }
1314 
1315 
1316 QTEST_MAIN(tst_QScriptEngine)
1317 #include "main.moc"
1318