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