1 /* 2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 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 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 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 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 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 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 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 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 139 void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics) 140 { 141 // ### 142 metrics->x = metrics->y = metrics->width = metrics->height = metrics->xOffset = metrics->yOffset = 0; 143 } 144 145 HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric) 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 //TESTED_CLASS= 157 //TESTED_FILES= gui/text/qscriptengine.cpp 158 159 class tst_QScriptEngine : public QObject 160 { 161 Q_OBJECT 162 163 public: 164 tst_QScriptEngine(); 165 virtual ~tst_QScriptEngine(); 166 167 168 public slots: 169 void initTestCase(); 170 void cleanupTestCase(); 171 private slots: 172 void devanagari(); 173 void bengali(); 174 void gurmukhi(); 175 // gujarati missing 176 void oriya(); 177 void tamil(); 178 void telugu(); 179 void kannada(); 180 void malayalam(); 181 // sinhala missing 182 183 void khmer(); 184 void linearB(); 185 }; 186 187 tst_QScriptEngine::tst_QScriptEngine() 188 { 189 } 190 191 tst_QScriptEngine::~tst_QScriptEngine() 192 { 193 } 194 195 void tst_QScriptEngine::initTestCase() 196 { 197 FT_Init_FreeType(&freetype); 198 } 199 200 void tst_QScriptEngine::cleanupTestCase() 201 { 202 FT_Done_FreeType(freetype); 203 } 204 205 struct ShapeTable { 206 unsigned short unicode[16]; 207 unsigned short glyphs[16]; 208 }; 209 210 static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) 211 { 212 QString str = QString::fromUtf16( s->unicode ); 213 214 HB_Face hbFace = HB_NewFace(face, hb_getSFntTable); 215 216 HB_FontRec hbFont; 217 hbFont.klass = &hb_fontClass; 218 hbFont.userData = face; 219 hbFont.x_ppem = face->size->metrics.x_ppem; 220 hbFont.y_ppem = face->size->metrics.y_ppem; 221 hbFont.x_scale = face->size->metrics.x_scale; 222 hbFont.y_scale = face->size->metrics.y_scale; 223 224 HB_ShaperItem shaper_item; 225 shaper_item.kerning_applied = false; 226 shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData()); 227 shaper_item.stringLength = str.length(); 228 shaper_item.item.script = script; 229 shaper_item.item.pos = 0; 230 shaper_item.item.length = shaper_item.stringLength; 231 shaper_item.item.bidiLevel = 0; // ### 232 shaper_item.shaperFlags = 0; 233 shaper_item.font = &hbFont; 234 shaper_item.face = hbFace; 235 shaper_item.num_glyphs = shaper_item.item.length; 236 shaper_item.glyphIndicesPresent = false; 237 shaper_item.initialGlyphCount = 0; 238 239 QVarLengthArray<HB_Glyph> hb_glyphs(shaper_item.num_glyphs); 240 QVarLengthArray<HB_GlyphAttributes> hb_attributes(shaper_item.num_glyphs); 241 QVarLengthArray<HB_Fixed> hb_advances(shaper_item.num_glyphs); 242 QVarLengthArray<HB_FixedPoint> hb_offsets(shaper_item.num_glyphs); 243 QVarLengthArray<unsigned short> hb_logClusters(shaper_item.num_glyphs); 244 245 while (1) { 246 hb_glyphs.resize(shaper_item.num_glyphs); 247 hb_attributes.resize(shaper_item.num_glyphs); 248 hb_advances.resize(shaper_item.num_glyphs); 249 hb_offsets.resize(shaper_item.num_glyphs); 250 hb_logClusters.resize(shaper_item.num_glyphs); 251 252 memset(hb_glyphs.data(), 0, hb_glyphs.size() * sizeof(HB_Glyph)); 253 memset(hb_attributes.data(), 0, hb_attributes.size() * sizeof(HB_GlyphAttributes)); 254 memset(hb_advances.data(), 0, hb_advances.size() * sizeof(HB_Fixed)); 255 memset(hb_offsets.data(), 0, hb_offsets.size() * sizeof(HB_FixedPoint)); 256 257 shaper_item.glyphs = hb_glyphs.data(); 258 shaper_item.attributes = hb_attributes.data(); 259 shaper_item.advances = hb_advances.data(); 260 shaper_item.offsets = hb_offsets.data(); 261 shaper_item.log_clusters = hb_logClusters.data(); 262 263 if (HB_ShapeItem(&shaper_item)) 264 break; 265 266 } 267 268 HB_FreeFace(hbFace); 269 270 hb_uint32 nglyphs = 0; 271 const unsigned short *g = s->glyphs; 272 while ( *g ) { 273 nglyphs++; 274 g++; 275 } 276 277 if( nglyphs != shaper_item.num_glyphs ) 278 goto error; 279 280 for (hb_uint32 i = 0; i < nglyphs; ++i) { 281 if ((shaper_item.glyphs[i]&0xffffff) != s->glyphs[i]) 282 goto error; 283 } 284 return true; 285 error: 286 str = ""; 287 const unsigned short *uc = s->unicode; 288 while (*uc) { 289 str += QString("%1 ").arg(*uc, 4, 16); 290 ++uc; 291 } 292 qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d", 293 face->family_name, 294 str.toLatin1().constData(), 295 shaper_item.num_glyphs, nglyphs); 296 297 str = ""; 298 hb_uint32 i = 0; 299 while (i < shaper_item.num_glyphs) { 300 str += QString("%1 ").arg(shaper_item.glyphs[i], 4, 16); 301 ++i; 302 } 303 qDebug(" glyph result = %s", str.toLatin1().constData()); 304 return false; 305 } 306 307 void tst_QScriptEngine::devanagari() 308 { 309 { 310 FT_Face face = loadFace("raghu.ttf"); 311 if (face) { 312 const ShapeTable shape_table [] = { 313 // Ka 314 { { 0x0915, 0x0 }, 315 { 0x0080, 0x0 } }, 316 // Ka Halant 317 { { 0x0915, 0x094d, 0x0 }, 318 { 0x0080, 0x0051, 0x0 } }, 319 // Ka Halant Ka 320 { { 0x0915, 0x094d, 0x0915, 0x0 }, 321 { 0x00c8, 0x0080, 0x0 } }, 322 // Ka MatraI 323 { { 0x0915, 0x093f, 0x0 }, 324 { 0x01d1, 0x0080, 0x0 } }, 325 // Ra Halant Ka 326 { { 0x0930, 0x094d, 0x0915, 0x0 }, 327 { 0x0080, 0x005b, 0x0 } }, 328 // Ra Halant Ka MatraI 329 { { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 }, 330 { 0x01d1, 0x0080, 0x005b, 0x0 } }, 331 // MatraI 332 { { 0x093f, 0x0 }, 333 { 0x01d4, 0x029c, 0x0 } }, 334 // Ka Nukta 335 { { 0x0915, 0x093c, 0x0 }, 336 { 0x00a4, 0x0 } }, 337 // Ka Halant Ra 338 { { 0x0915, 0x094d, 0x0930, 0x0 }, 339 { 0x0110, 0x0 } }, 340 // Ka Halant Ra Halant Ka 341 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 }, 342 { 0x0158, 0x0080, 0x0 } }, 343 { { 0x0930, 0x094d, 0x200d, 0x0 }, 344 { 0x00e2, 0x0 } }, 345 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x200d, 0x0 }, 346 { 0x0158, 0x0 } }, 347 348 { {0}, {0} } 349 }; 350 351 352 const ShapeTable *s = shape_table; 353 while (s->unicode[0]) { 354 QVERIFY( shaping(face, s, HB_Script_Devanagari) ); 355 ++s; 356 } 357 358 FT_Done_Face(face); 359 } else { 360 QSKIP("couln't find raghu.ttf", SkipAll); 361 } 362 } 363 364 { 365 FT_Face face = loadFace("mangal.ttf"); 366 if (face) { 367 const ShapeTable shape_table [] = { 368 // Ka 369 { { 0x0915, 0x0 }, 370 { 0x0080, 0x0 } }, 371 // Ka Halant 372 { { 0x0915, 0x094d, 0x0 }, 373 { 0x0080, 0x0051, 0x0 } }, 374 // Ka Halant Ka 375 { { 0x0915, 0x094d, 0x0915, 0x0 }, 376 { 0x00c8, 0x0080, 0x0 } }, 377 // Ka MatraI 378 { { 0x0915, 0x093f, 0x0 }, 379 { 0x01d1, 0x0080, 0x0 } }, 380 // Ra Halant Ka 381 { { 0x0930, 0x094d, 0x0915, 0x0 }, 382 { 0x0080, 0x005b, 0x0 } }, 383 // Ra Halant Ka MatraI 384 { { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 }, 385 { 0x01d1, 0x0080, 0x005b, 0x0 } }, 386 // MatraI 387 { { 0x093f, 0x0 }, 388 { 0x01d4, 0x029c, 0x0 } }, 389 // Ka Nukta 390 { { 0x0915, 0x093c, 0x0 }, 391 { 0x00a4, 0x0 } }, 392 // Ka Halant Ra 393 { { 0x0915, 0x094d, 0x0930, 0x0 }, 394 { 0x0110, 0x0 } }, 395 // Ka Halant Ra Halant Ka 396 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 }, 397 { 0x0158, 0x0080, 0x0 } }, 398 399 { { 0x92b, 0x94d, 0x930, 0x0 }, 400 { 0x125, 0x0 } }, 401 { { 0x92b, 0x93c, 0x94d, 0x930, 0x0 }, 402 { 0x149, 0x0 } }, 403 { {0}, {0} } 404 }; 405 406 const ShapeTable *s = shape_table; 407 while (s->unicode[0]) { 408 QVERIFY( shaping(face, s, HB_Script_Devanagari) ); 409 ++s; 410 } 411 412 FT_Done_Face(face); 413 } else { 414 QSKIP("couldn't find mangal.ttf", SkipAll); 415 } 416 } 417 } 418 419 void tst_QScriptEngine::bengali() 420 { 421 { 422 FT_Face face = loadFace("AkaashNormal.ttf"); 423 if (face) { 424 const ShapeTable shape_table [] = { 425 // Ka 426 { { 0x0995, 0x0 }, 427 { 0x0151, 0x0 } }, 428 // Ka Halant 429 { { 0x0995, 0x09cd, 0x0 }, 430 { 0x0151, 0x017d, 0x0 } }, 431 // Ka Halant Ka 432 { { 0x0995, 0x09cd, 0x0995, 0x0 }, 433 { 0x019b, 0x0 } }, 434 // Ka MatraI 435 { { 0x0995, 0x09bf, 0x0 }, 436 { 0x0173, 0x0151, 0x0 } }, 437 // Ra Halant Ka 438 { { 0x09b0, 0x09cd, 0x0995, 0x0 }, 439 { 0x0151, 0x0276, 0x0 } }, 440 // Ra Halant Ka MatraI 441 { { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 }, 442 { 0x0173, 0x0151, 0x0276, 0x0 } }, 443 // Ka Nukta 444 { { 0x0995, 0x09bc, 0x0 }, 445 { 0x0151, 0x0171, 0x0 } }, 446 // Ka Halant Ra 447 { { 0x0995, 0x09cd, 0x09b0, 0x0 }, 448 { 0x01f4, 0x0 } }, 449 // Ka Halant Ra Halant Ka 450 { { 0x0995, 0x09cd, 0x09b0, 0x09cd, 0x0995, 0x0 }, 451 { 0x025c, 0x0276, 0x0151, 0x0 } }, 452 // Ya + Halant 453 { { 0x09af, 0x09cd, 0x0 }, 454 { 0x016a, 0x017d, 0x0 } }, 455 // Da Halant Ya -> Da Ya-Phala 456 { { 0x09a6, 0x09cd, 0x09af, 0x0 }, 457 { 0x01e5, 0x0 } }, 458 // A Halant Ya -> A Ya-phala 459 { { 0x0985, 0x09cd, 0x09af, 0x0 }, 460 { 0x0145, 0x01cf, 0x0 } }, 461 // Na Halant Ka 462 { { 0x09a8, 0x09cd, 0x0995, 0x0 }, 463 { 0x026f, 0x0151, 0x0 } }, 464 // Na Halant ZWNJ Ka 465 { { 0x09a8, 0x09cd, 0x200c, 0x0995, 0x0 }, 466 { 0x0164, 0x017d, 0x0151, 0x0 } }, 467 // Na Halant ZWJ Ka 468 { { 0x09a8, 0x09cd, 0x200d, 0x0995, 0x0 }, 469 { 0x026f, 0x0151, 0x0 } }, 470 // Ka Halant ZWNJ Ka 471 { { 0x0995, 0x09cd, 0x200c, 0x0995, 0x0 }, 472 { 0x0151, 0x017d, 0x0151, 0x0 } }, 473 // Ka Halant ZWJ Ka 474 { { 0x0995, 0x09cd, 0x200d, 0x0995, 0x0 }, 475 { 0x025c, 0x0151, 0x0 } }, 476 // Na Halant Ra 477 { { 0x09a8, 0x09cd, 0x09b0, 0x0 }, 478 { 0x0207, 0x0 } }, 479 // Na Halant ZWNJ Ra 480 { { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 }, 481 { 0x0164, 0x017d, 0x016b, 0x0 } }, 482 // Na Halant ZWJ Ra 483 { { 0x09a8, 0x09cd, 0x200d, 0x09b0, 0x0 }, 484 { 0x026f, 0x016b, 0x0 } }, 485 // Na Halant Ba 486 { { 0x09a8, 0x09cd, 0x09ac, 0x0 }, 487 { 0x022f, 0x0 } }, 488 // Na Halant ZWNJ Ba 489 { { 0x09a8, 0x09cd, 0x200c, 0x09ac, 0x0 }, 490 { 0x0164, 0x017d, 0x0167, 0x0 } }, 491 // Na Halant ZWJ Ba 492 { { 0x09a8, 0x09cd, 0x200d, 0x09ac, 0x0 }, 493 { 0x026f, 0x0167, 0x0 } }, 494 // Na Halant Dha 495 { { 0x09a8, 0x09cd, 0x09a7, 0x0 }, 496 { 0x01d3, 0x0 } }, 497 // Na Halant ZWNJ Dha 498 { { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 }, 499 { 0x0164, 0x017d, 0x0163, 0x0 } }, 500 // Na Halant ZWJ Dha 501 { { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 }, 502 { 0x026f, 0x0163, 0x0 } }, 503 // Ra Halant Ka MatraAU 504 { { 0x09b0, 0x09cd, 0x0995, 0x09cc, 0x0 }, 505 { 0x0179, 0x0151, 0x0276, 0x017e, 0x0 } }, 506 // Ra Halant Ba Halant Ba 507 { { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 }, 508 { 0x0232, 0x0276, 0x0 } }, 509 { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x982, 0x0 }, 510 { 0x151, 0x276, 0x172, 0x143, 0x0 } }, 511 { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x983, 0x0 }, 512 { 0x151, 0x276, 0x172, 0x144, 0x0 } }, 513 514 { {0}, {0} } 515 }; 516 517 518 const ShapeTable *s = shape_table; 519 while (s->unicode[0]) { 520 QVERIFY( shaping(face, s, HB_Script_Bengali) ); 521 ++s; 522 } 523 524 FT_Done_Face(face); 525 } else { 526 QSKIP("couln't find AkaashNormal.ttf", SkipAll); 527 } 528 } 529 { 530 FT_Face face = loadFace("MuktiNarrow.ttf"); 531 if (face) { 532 const ShapeTable shape_table [] = { 533 // Ka 534 { { 0x0995, 0x0 }, 535 { 0x0073, 0x0 } }, 536 // Ka Halant 537 { { 0x0995, 0x09cd, 0x0 }, 538 { 0x00b9, 0x0 } }, 539 // Ka Halant Ka 540 { { 0x0995, 0x09cd, 0x0995, 0x0 }, 541 { 0x0109, 0x0 } }, 542 // Ka MatraI 543 { { 0x0995, 0x09bf, 0x0 }, 544 { 0x0095, 0x0073, 0x0 } }, 545 // Ra Halant Ka 546 { { 0x09b0, 0x09cd, 0x0995, 0x0 }, 547 { 0x0073, 0x00e1, 0x0 } }, 548 // Ra Halant Ka MatraI 549 { { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 }, 550 { 0x0095, 0x0073, 0x00e1, 0x0 } }, 551 // MatraI 552 { { 0x09bf, 0x0 }, 553 { 0x0095, 0x01c8, 0x0 } }, 554 // Ka Nukta 555 { { 0x0995, 0x09bc, 0x0 }, 556 { 0x0073, 0x0093, 0x0 } }, 557 // Ka Halant Ra 558 { { 0x0995, 0x09cd, 0x09b0, 0x0 }, 559 { 0x00e5, 0x0 } }, 560 // Ka Halant Ra Halant Ka 561 { { 0x995, 0x9cd, 0x9b0, 0x9cd, 0x995, 0x0 }, 562 { 0x234, 0x24e, 0x73, 0x0 } }, 563 // Ya + Halant 564 { { 0x09af, 0x09cd, 0x0 }, 565 { 0x00d2, 0x0 } }, 566 // Da Halant Ya -> Da Ya-Phala 567 { { 0x09a6, 0x09cd, 0x09af, 0x0 }, 568 { 0x0084, 0x00e2, 0x0 } }, 569 // A Halant Ya -> A Ya-phala 570 { { 0x0985, 0x09cd, 0x09af, 0x0 }, 571 { 0x0067, 0x00e2, 0x0 } }, 572 // Na Halant Ka 573 { { 0x09a8, 0x09cd, 0x0995, 0x0 }, 574 { 0x0188, 0x0 } }, 575 // Na Halant ZWNJ Ka 576 { { 0x9a8, 0x9cd, 0x200c, 0x995, 0x0 }, 577 { 0xcc, 0x73, 0x0 } }, 578 // Na Halant ZWJ Ka 579 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 }, 580 { 0x247, 0x73, 0x0 } }, 581 // Ka Halant ZWNJ Ka 582 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 }, 583 { 0x247, 0x73, 0x0 } }, 584 // Ka Halant ZWJ Ka 585 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 }, 586 { 0x247, 0x73, 0x0 } }, 587 // Na Halant Ra 588 { { 0x09a8, 0x09cd, 0x09b0, 0x0 }, 589 { 0x00f8, 0x0 } }, 590 // Na Halant ZWNJ Ra 591 { { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 }, 592 { 0xcc, 0x8d, 0x0 } }, 593 // Na Halant ZWJ Ra 594 { { 0x9a8, 0x9cd, 0x200d, 0x9b0, 0x0 }, 595 { 0x247, 0x8d, 0x0 } }, 596 // Na Halant Ba 597 { { 0x09a8, 0x09cd, 0x09ac, 0x0 }, 598 { 0x0139, 0x0 } }, 599 // Na Halant ZWNJ Ba 600 { { 0x9a8, 0x9cd, 0x200c, 0x9ac, 0x0 }, 601 { 0xcc, 0x89, 0x0 } }, 602 // Na Halant ZWJ Ba 603 { { 0x9a8, 0x9cd, 0x200d, 0x9ac, 0x0 }, 604 { 0x247, 0x89, 0x0 } }, 605 // Na Halant Dha 606 { { 0x09a8, 0x09cd, 0x09a7, 0x0 }, 607 { 0x0145, 0x0 } }, 608 // Na Halant ZWNJ Dha 609 { { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 }, 610 { 0xcc, 0x85, 0x0 } }, 611 // Na Halant ZWJ Dha 612 { { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 }, 613 { 0x247, 0x85, 0x0 } }, 614 // Ra Halant Ka MatraAU 615 { { 0x9b0, 0x9cd, 0x995, 0x9cc, 0x0 }, 616 { 0x232, 0x73, 0xe1, 0xa0, 0x0 } }, 617 // Ra Halant Ba Halant Ba 618 { { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 }, 619 { 0x013b, 0x00e1, 0x0 } }, 620 621 { {0}, {0} } 622 }; 623 624 625 const ShapeTable *s = shape_table; 626 while (s->unicode[0]) { 627 QVERIFY( shaping(face, s, HB_Script_Bengali) ); 628 ++s; 629 } 630 631 FT_Done_Face(face); 632 } else { 633 QSKIP("couln't find MuktiNarrow.ttf", SkipAll); 634 } 635 } 636 { 637 FT_Face face = loadFace("LikhanNormal.ttf"); 638 if (face) { 639 const ShapeTable shape_table [] = { 640 { { 0x09a8, 0x09cd, 0x09af, 0x0 }, 641 { 0x0192, 0x0 } }, 642 { { 0x09b8, 0x09cd, 0x09af, 0x0 }, 643 { 0x01d6, 0x0 } }, 644 { { 0x09b6, 0x09cd, 0x09af, 0x0 }, 645 { 0x01bc, 0x0 } }, 646 { { 0x09b7, 0x09cd, 0x09af, 0x0 }, 647 { 0x01c6, 0x0 } }, 648 { { 0x09b0, 0x09cd, 0x09a8, 0x09cd, 0x200d, 0x0 }, 649 { 0xd3, 0x12f, 0x0 } }, 650 651 { {0}, {0} } 652 }; 653 654 655 const ShapeTable *s = shape_table; 656 while (s->unicode[0]) { 657 QVERIFY( shaping(face, s, HB_Script_Bengali) ); 658 ++s; 659 } 660 661 FT_Done_Face(face); 662 } else { 663 QSKIP("couln't find LikhanNormal.ttf", SkipAll); 664 } 665 } 666 } 667 668 void tst_QScriptEngine::gurmukhi() 669 { 670 { 671 FT_Face face = loadFace("lohit.punjabi.1.1.ttf"); 672 if (face) { 673 const ShapeTable shape_table [] = { 674 { { 0xA15, 0xA4D, 0xa39, 0x0 }, 675 { 0x3b, 0x8b, 0x0 } }, 676 { {0}, {0} } 677 }; 678 679 680 const ShapeTable *s = shape_table; 681 while (s->unicode[0]) { 682 QVERIFY( shaping(face, s, HB_Script_Gurmukhi) ); 683 ++s; 684 } 685 686 FT_Done_Face(face); 687 } else { 688 QSKIP("couln't find lohit.punjabi.1.1.ttf", SkipAll); 689 } 690 } 691 } 692 693 void tst_QScriptEngine::oriya() 694 { 695 { 696 FT_Face face = loadFace("utkalm.ttf"); 697 if (face) { 698 const ShapeTable shape_table [] = { 699 { { 0xb15, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 }, 700 { 0x150, 0x125, 0x0 } }, 701 { { 0xb24, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 }, 702 { 0x151, 0x120, 0x0 } }, 703 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 }, 704 { 0x152, 0x120, 0x0 } }, 705 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 }, 706 { 0x152, 0x120, 0x0 } }, 707 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 }, 708 { 0x176, 0x0 } }, 709 { { 0xb38, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 }, 710 { 0x177, 0x0 } }, 711 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0xb4d, 0xb2f, 0x0 }, 712 { 0x176, 0x124, 0x0 } }, 713 { {0}, {0} } 714 715 }; 716 717 const ShapeTable *s = shape_table; 718 while (s->unicode[0]) { 719 QVERIFY( shaping(face, s, HB_Script_Oriya) ); 720 ++s; 721 } 722 723 FT_Done_Face(face); 724 } else { 725 QSKIP("couln't find utkalm.ttf", SkipAll); 726 } 727 } 728 } 729 730 731 void tst_QScriptEngine::tamil() 732 { 733 { 734 FT_Face face = loadFace("akruti1.ttf"); 735 if (face) { 736 const ShapeTable shape_table [] = { 737 { { 0x0b95, 0x0bc2, 0x0 }, 738 { 0x004e, 0x0 } }, 739 { { 0x0bae, 0x0bc2, 0x0 }, 740 { 0x009e, 0x0 } }, 741 { { 0x0b9a, 0x0bc2, 0x0 }, 742 { 0x0058, 0x0 } }, 743 { { 0x0b99, 0x0bc2, 0x0 }, 744 { 0x0053, 0x0 } }, 745 { { 0x0bb0, 0x0bc2, 0x0 }, 746 { 0x00a8, 0x0 } }, 747 { { 0x0ba4, 0x0bc2, 0x0 }, 748 { 0x008e, 0x0 } }, 749 { { 0x0b9f, 0x0bc2, 0x0 }, 750 { 0x0062, 0x0 } }, 751 { { 0x0b95, 0x0bc6, 0x0 }, 752 { 0x000a, 0x0031, 0x0 } }, 753 { { 0x0b95, 0x0bca, 0x0 }, 754 { 0x000a, 0x0031, 0x0007, 0x0 } }, 755 { { 0x0b95, 0x0bc6, 0x0bbe, 0x0 }, 756 { 0x000a, 0x0031, 0x007, 0x0 } }, 757 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0 }, 758 { 0x0049, 0x0 } }, 759 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0bca, 0x0 }, 760 { 0x000a, 0x0049, 0x007, 0x0 } }, 761 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0bc6, 0x0bbe, 0x0 }, 762 { 0x000a, 0x0049, 0x007, 0x0 } }, 763 { { 0x0b9f, 0x0bbf, 0x0 }, 764 { 0x005f, 0x0 } }, 765 { { 0x0b9f, 0x0bc0, 0x0 }, 766 { 0x0060, 0x0 } }, 767 { { 0x0bb2, 0x0bc0, 0x0 }, 768 { 0x00ab, 0x0 } }, 769 { { 0x0bb2, 0x0bbf, 0x0 }, 770 { 0x00aa, 0x0 } }, 771 { { 0x0bb0, 0x0bcd, 0x0 }, 772 { 0x00a4, 0x0 } }, 773 { { 0x0bb0, 0x0bbf, 0x0 }, 774 { 0x00a5, 0x0 } }, 775 { { 0x0bb0, 0x0bc0, 0x0 }, 776 { 0x00a6, 0x0 } }, 777 { { 0x0b83, 0x0 }, 778 { 0x0025, 0x0 } }, 779 { { 0x0b83, 0x0b95, 0x0 }, 780 { 0x0025, 0x0031, 0x0 } }, 781 782 { {0}, {0} } 783 }; 784 785 786 const ShapeTable *s = shape_table; 787 while (s->unicode[0]) { 788 QVERIFY( shaping(face, s, HB_Script_Tamil) ); 789 ++s; 790 } 791 792 FT_Done_Face(face); 793 } else { 794 QSKIP("couln't find akruti1.ttf", SkipAll); 795 } 796 } 797 } 798 799 800 void tst_QScriptEngine::telugu() 801 { 802 { 803 FT_Face face = loadFace("Pothana2000.ttf"); 804 if (face) { 805 const ShapeTable shape_table [] = { 806 { { 0xc15, 0xc4d, 0x0 }, 807 { 0xbb, 0x0 } }, 808 { { 0xc15, 0xc4d, 0xc37, 0x0 }, 809 { 0x4b, 0x0 } }, 810 { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0x0 }, 811 { 0xe0, 0x0 } }, 812 { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0xc23, 0x0 }, 813 { 0x4b, 0x91, 0x0 } }, 814 { { 0xc15, 0xc4d, 0xc30, 0x0 }, 815 { 0x5a, 0xb2, 0x0 } }, 816 { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0x0 }, 817 { 0xbb, 0xb2, 0x0 } }, 818 { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0xc15, 0x0 }, 819 { 0x5a, 0xb2, 0x83, 0x0 } }, 820 { { 0xc15, 0xc4d, 0xc30, 0xc3f, 0x0 }, 821 { 0xe2, 0xb2, 0x0 } }, 822 { { 0xc15, 0xc4d, 0xc15, 0xc48, 0x0 }, 823 { 0xe6, 0xb3, 0x83, 0x0 } }, 824 { { 0xc15, 0xc4d, 0xc30, 0xc48, 0x0 }, 825 { 0xe6, 0xb3, 0x9f, 0x0 } }, 826 { {0}, {0} } 827 828 }; 829 830 const ShapeTable *s = shape_table; 831 while (s->unicode[0]) { 832 QVERIFY( shaping(face, s, HB_Script_Telugu) ); 833 ++s; 834 } 835 836 FT_Done_Face(face); 837 } else { 838 QSKIP("couln't find Pothana2000.ttf", SkipAll); 839 } 840 } 841 } 842 843 844 void tst_QScriptEngine::kannada() 845 { 846 { 847 FT_Face face = loadFace("Sampige.ttf"); 848 if (face) { 849 const ShapeTable shape_table [] = { 850 { { 0x0ca8, 0x0ccd, 0x0ca8, 0x0 }, 851 { 0x0049, 0x00ba, 0x0 } }, 852 { { 0x0ca8, 0x0ccd, 0x0ca1, 0x0 }, 853 { 0x0049, 0x00b3, 0x0 } }, 854 { { 0x0caf, 0x0cc2, 0x0 }, 855 { 0x004f, 0x005d, 0x0 } }, 856 { { 0x0ce0, 0x0 }, 857 { 0x006a, 0x0 } }, 858 { { 0x0ce6, 0x0ce7, 0x0ce8, 0x0 }, 859 { 0x006b, 0x006c, 0x006d, 0x0 } }, 860 { { 0x0cb5, 0x0ccb, 0x0 }, 861 { 0x015f, 0x0067, 0x0 } }, 862 { { 0x0cb0, 0x0ccd, 0x0cae, 0x0 }, 863 { 0x004e, 0x0082, 0x0 } }, 864 { { 0x0cb0, 0x0ccd, 0x0c95, 0x0 }, 865 { 0x0036, 0x0082, 0x0 } }, 866 { { 0x0c95, 0x0ccd, 0x0cb0, 0x0 }, 867 { 0x0036, 0x00c1, 0x0 } }, 868 { { 0x0cb0, 0x0ccd, 0x200d, 0x0c95, 0x0 }, 869 { 0x0050, 0x00a7, 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_Kannada) ); 878 ++s; 879 } 880 881 FT_Done_Face(face); 882 } else { 883 QSKIP("couln't find Sampige.ttf", SkipAll); 884 } 885 } 886 { 887 FT_Face face = loadFace("tunga.ttf"); 888 if (face) { 889 const ShapeTable shape_table [] = { 890 { { 0x0cb7, 0x0cc6, 0x0 }, 891 { 0x00b0, 0x006c, 0x0 } }, 892 { { 0x0cb7, 0x0ccd, 0x0 }, 893 { 0x0163, 0x0 } }, 894 895 { {0}, {0} } 896 }; 897 898 899 const ShapeTable *s = shape_table; 900 while (s->unicode[0]) { 901 QVERIFY( shaping(face, s, HB_Script_Kannada) ); 902 ++s; 903 } 904 905 FT_Done_Face(face); 906 } else { 907 QSKIP("couln't find tunga.ttf", SkipAll); 908 } 909 } 910 } 911 912 913 914 void tst_QScriptEngine::malayalam() 915 { 916 { 917 FT_Face face = loadFace("AkrutiMal2Normal.ttf"); 918 if (face) { 919 const ShapeTable shape_table [] = { 920 { { 0x0d15, 0x0d46, 0x0 }, 921 { 0x005e, 0x0034, 0x0 } }, 922 { { 0x0d15, 0x0d47, 0x0 }, 923 { 0x005f, 0x0034, 0x0 } }, 924 { { 0x0d15, 0x0d4b, 0x0 }, 925 { 0x005f, 0x0034, 0x0058, 0x0 } }, 926 { { 0x0d15, 0x0d48, 0x0 }, 927 { 0x0060, 0x0034, 0x0 } }, 928 { { 0x0d15, 0x0d4a, 0x0 }, 929 { 0x005e, 0x0034, 0x0058, 0x0 } }, 930 { { 0x0d30, 0x0d4d, 0x0d15, 0x0 }, 931 { 0x009e, 0x0034, 0x0 } }, 932 { { 0x0d15, 0x0d4d, 0x0d35, 0x0 }, 933 { 0x0034, 0x007a, 0x0 } }, 934 { { 0x0d15, 0x0d4d, 0x0d2f, 0x0 }, 935 { 0x0034, 0x00a2, 0x0 } }, 936 { { 0x0d1f, 0x0d4d, 0x0d1f, 0x0 }, 937 { 0x0069, 0x0 } }, 938 { { 0x0d26, 0x0d4d, 0x0d26, 0x0 }, 939 { 0x0074, 0x0 } }, 940 { { 0x0d30, 0x0d4d, 0x0 }, 941 { 0x009e, 0x0 } }, 942 { { 0x0d30, 0x0d4d, 0x200c, 0x0 }, 943 { 0x009e, 0x0 } }, 944 { { 0x0d30, 0x0d4d, 0x200d, 0x0 }, 945 { 0x009e, 0x0 } }, 946 947 948 { {0}, {0} } 949 }; 950 951 952 const ShapeTable *s = shape_table; 953 while (s->unicode[0]) { 954 QVERIFY( shaping(face, s, HB_Script_Malayalam) ); 955 ++s; 956 } 957 958 FT_Done_Face(face); 959 } else { 960 QSKIP("couln't find AkrutiMal2Normal.ttf", SkipAll); 961 } 962 } 963 } 964 965 966 967 void tst_QScriptEngine::khmer() 968 { 969 { 970 FT_Face face = loadFace("KhmerOS.ttf"); 971 if (face) { 972 const ShapeTable shape_table [] = { 973 { { 0x179a, 0x17cd, 0x0 }, 974 { 0x24c, 0x27f, 0x0 } }, 975 { { 0x179f, 0x17c5, 0x0 }, 976 { 0x273, 0x203, 0x0 } }, 977 { { 0x1790, 0x17d2, 0x1784, 0x17c3, 0x0 }, 978 { 0x275, 0x242, 0x182, 0x0 } }, 979 { { 0x179a, 0x0 }, 980 { 0x24c, 0x0 } }, 981 { { 0x1781, 0x17d2, 0x1798, 0x17c2, 0x0 }, 982 { 0x274, 0x233, 0x197, 0x0 } }, 983 { { 0x1798, 0x17b6, 0x0 }, 984 { 0x1cb, 0x0 } }, 985 { { 0x179a, 0x17b8, 0x0 }, 986 { 0x24c, 0x26a, 0x0 } }, 987 { { 0x1787, 0x17b6, 0x0 }, 988 { 0x1ba, 0x0 } }, 989 { { 0x1798, 0x17d2, 0x1796, 0x17bb, 0x0 }, 990 { 0x24a, 0x195, 0x26d, 0x0 } }, 991 { {0}, {0} } 992 }; 993 994 995 const ShapeTable *s = shape_table; 996 while (s->unicode[0]) { 997 QVERIFY( shaping(face, s, HB_Script_Khmer) ); 998 ++s; 999 } 1000 1001 FT_Done_Face(face); 1002 } else { 1003 QSKIP("couln't find KhmerOS.ttf", SkipAll); 1004 } 1005 } 1006 } 1007 1008 void tst_QScriptEngine::linearB() 1009 { 1010 { 1011 FT_Face face = loadFace("PENUTURE.TTF"); 1012 if (face) { 1013 const ShapeTable shape_table [] = { 1014 { { 0xd800, 0xdc01, 0xd800, 0xdc02, 0xd800, 0xdc03, 0 }, 1015 { 0x5, 0x6, 0x7, 0 } }, 1016 { {0}, {0} } 1017 }; 1018 1019 1020 const ShapeTable *s = shape_table; 1021 while (s->unicode[0]) { 1022 QVERIFY( shaping(face, s, HB_Script_Common) ); 1023 ++s; 1024 } 1025 1026 FT_Done_Face(face); 1027 } else { 1028 QSKIP("couln't find PENUTURE.TTF", SkipAll); 1029 } 1030 } 1031 } 1032 1033 1034 QTEST_MAIN(tst_QScriptEngine) 1035 #include "main.moc" 1036