src/3rdparty/harfbuzz/tests/shaping/main.cpp
changeset 22 79de32ba3296
parent 3 41300fa6a67c
equal deleted inserted replaced
19:fcece45ef507 22:79de32ba3296
   134     *ypos = face->glyph->outline.points[point].y;
   134     *ypos = face->glyph->outline.points[point].y;
   135 
   135 
   136     return HB_Err_Ok;
   136     return HB_Err_Ok;
   137 }
   137 }
   138 
   138 
   139 void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics)
   139 void hb_getGlyphMetrics(HB_Font, HB_Glyph, HB_GlyphMetrics *metrics)
   140 {
   140 {
   141     // ###
   141     // ###
   142     metrics->x = metrics->y = metrics->width = metrics->height = metrics->xOffset = metrics->yOffset = 0;
   142     metrics->x = metrics->y = metrics->width = metrics->height = metrics->xOffset = metrics->yOffset = 0;
   143 }
   143 }
   144 
   144 
   145 HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric)
   145 HB_Fixed hb_getFontMetric(HB_Font, HB_FontMetric )
   146 {
   146 {
   147     return 0; // ####
   147     return 0; // ####
   148 }
   148 }
   149 
   149 
   150 const HB_FontClass hb_fontClass = {
   150 const HB_FontClass hb_fontClass = {
   167 
   167 
   168 public slots:
   168 public slots:
   169     void initTestCase();
   169     void initTestCase();
   170     void cleanupTestCase();
   170     void cleanupTestCase();
   171 private slots:
   171 private slots:
       
   172     void greek();
       
   173 
   172     void devanagari();
   174     void devanagari();
   173     void bengali();
   175     void bengali();
   174     void gurmukhi();
   176     void gurmukhi();
   175     // gujarati missing
   177     // gujarati missing
   176     void oriya();
   178     void oriya();
   201 void tst_QScriptEngine::cleanupTestCase()
   203 void tst_QScriptEngine::cleanupTestCase()
   202 {
   204 {
   203     FT_Done_FreeType(freetype);
   205     FT_Done_FreeType(freetype);
   204 }
   206 }
   205 
   207 
   206 struct ShapeTable {
   208 class Shaper
   207     unsigned short unicode[16];
   209 {
   208     unsigned short glyphs[16];
   210 public:
       
   211     Shaper(FT_Face face, HB_Script script, const QString &str);
       
   212 
       
   213     HB_FontRec hbFont;
       
   214     HB_ShaperItem shaper_item;
       
   215     QVarLengthArray<HB_Glyph> hb_glyphs;
       
   216     QVarLengthArray<HB_GlyphAttributes> hb_attributes;
       
   217     QVarLengthArray<HB_Fixed> hb_advances;
       
   218     QVarLengthArray<HB_FixedPoint> hb_offsets;
       
   219     QVarLengthArray<unsigned short> hb_logClusters;
       
   220 
   209 };
   221 };
   210 
   222 
   211 static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script)
   223 Shaper::Shaper(FT_Face face, HB_Script script, const QString &str)
   212 {
   224 {
   213     QString str = QString::fromUtf16( s->unicode );
       
   214 
       
   215     HB_Face hbFace = HB_NewFace(face, hb_getSFntTable);
   225     HB_Face hbFace = HB_NewFace(face, hb_getSFntTable);
   216 
   226 
   217     HB_FontRec hbFont;
       
   218     hbFont.klass = &hb_fontClass;
   227     hbFont.klass = &hb_fontClass;
   219     hbFont.userData = face;
   228     hbFont.userData = face;
   220     hbFont.x_ppem  = face->size->metrics.x_ppem;
   229     hbFont.x_ppem  = face->size->metrics.x_ppem;
   221     hbFont.y_ppem  = face->size->metrics.y_ppem;
   230     hbFont.y_ppem  = face->size->metrics.y_ppem;
   222     hbFont.x_scale = face->size->metrics.x_scale;
   231     hbFont.x_scale = face->size->metrics.x_scale;
   223     hbFont.y_scale = face->size->metrics.y_scale;
   232     hbFont.y_scale = face->size->metrics.y_scale;
   224 
   233 
   225     HB_ShaperItem shaper_item;
       
   226     shaper_item.kerning_applied = false;
   234     shaper_item.kerning_applied = false;
   227     shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData());
   235     shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData());
   228     shaper_item.stringLength = str.length();
   236     shaper_item.stringLength = str.length();
   229     shaper_item.item.script = script;
   237     shaper_item.item.script = script;
   230     shaper_item.item.pos = 0;
   238     shaper_item.item.pos = 0;
   235     shaper_item.face = hbFace;
   243     shaper_item.face = hbFace;
   236     shaper_item.num_glyphs = shaper_item.item.length;
   244     shaper_item.num_glyphs = shaper_item.item.length;
   237     shaper_item.glyphIndicesPresent = false;
   245     shaper_item.glyphIndicesPresent = false;
   238     shaper_item.initialGlyphCount = 0;
   246     shaper_item.initialGlyphCount = 0;
   239 
   247 
   240     QVarLengthArray<HB_Glyph> hb_glyphs(shaper_item.num_glyphs);
       
   241     QVarLengthArray<HB_GlyphAttributes> hb_attributes(shaper_item.num_glyphs);
       
   242     QVarLengthArray<HB_Fixed> hb_advances(shaper_item.num_glyphs);
       
   243     QVarLengthArray<HB_FixedPoint> hb_offsets(shaper_item.num_glyphs);
       
   244     QVarLengthArray<unsigned short> hb_logClusters(shaper_item.num_glyphs);
       
   245 
   248 
   246     while (1) {
   249     while (1) {
   247         hb_glyphs.resize(shaper_item.num_glyphs);
   250         hb_glyphs.resize(shaper_item.num_glyphs);
   248         hb_attributes.resize(shaper_item.num_glyphs);
   251         hb_attributes.resize(shaper_item.num_glyphs);
   249         hb_advances.resize(shaper_item.num_glyphs);
   252         hb_advances.resize(shaper_item.num_glyphs);
   261         shaper_item.offsets = hb_offsets.data();
   264         shaper_item.offsets = hb_offsets.data();
   262         shaper_item.log_clusters = hb_logClusters.data();
   265         shaper_item.log_clusters = hb_logClusters.data();
   263 
   266 
   264         if (HB_ShapeItem(&shaper_item))
   267         if (HB_ShapeItem(&shaper_item))
   265             break;
   268             break;
   266 
       
   267     }
   269     }
   268 
   270 
   269     HB_FreeFace(hbFace);
   271     HB_FreeFace(hbFace);
       
   272 }
       
   273 
       
   274 
       
   275 static bool decomposedShaping(FT_Face face, HB_Script script, const QChar &ch)
       
   276 {
       
   277     QString uc = QString().append(ch);
       
   278     Shaper shaper(face, script, uc);
       
   279 
       
   280     uc = uc.normalized(QString::NormalizationForm_D);
       
   281     Shaper decomposed(face, script, uc);
       
   282 
       
   283     if( shaper.shaper_item.num_glyphs != decomposed.shaper_item.num_glyphs )
       
   284         goto error;
       
   285 
       
   286     for (unsigned int i = 0; i < shaper.shaper_item.num_glyphs; ++i) {
       
   287         if ((shaper.shaper_item.glyphs[i]&0xffffff) != (decomposed.shaper_item.glyphs[i]&0xffffff))
       
   288             goto error;
       
   289     }
       
   290     return true;
       
   291  error:
       
   292     QString str = "";
       
   293     int i = 0;
       
   294     while (i < uc.length()) {
       
   295         str += QString("%1 ").arg(uc[i].unicode(), 4, 16);
       
   296         ++i;
       
   297     }
       
   298     qDebug("%s: decomposedShaping of char %4x failed\n    decomposedString: %s\n   nglyphs=%d, decomposed nglyphs %d",
       
   299            face->family_name,
       
   300            ch.unicode(), str.toLatin1().data(),
       
   301            shaper.shaper_item.num_glyphs,
       
   302            decomposed.shaper_item.num_glyphs);
       
   303 
       
   304     str = "";
       
   305     i = 0;
       
   306     while (i < shaper.shaper_item.num_glyphs) {
       
   307         str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16);
       
   308         ++i;
       
   309     }
       
   310     qDebug("    composed glyph result   = %s", str.toLatin1().constData());
       
   311     str = "";
       
   312     i = 0;
       
   313     while (i < decomposed.shaper_item.num_glyphs) {
       
   314         str += QString("%1 ").arg(decomposed.shaper_item.glyphs[i], 4, 16);
       
   315         ++i;
       
   316     }
       
   317     qDebug("    decomposed glyph result = %s", str.toLatin1().constData());
       
   318     return false;
       
   319 }
       
   320 
       
   321 struct ShapeTable {
       
   322     unsigned short unicode[16];
       
   323     unsigned short glyphs[16];
       
   324 };
       
   325 
       
   326 static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script)
       
   327 {
       
   328     Shaper shaper(face, script, QString::fromUtf16( s->unicode ));
   270 
   329 
   271     hb_uint32 nglyphs = 0;
   330     hb_uint32 nglyphs = 0;
   272     const unsigned short *g = s->glyphs;
   331     const unsigned short *g = s->glyphs;
   273     while ( *g ) {
   332     while ( *g ) {
   274 	nglyphs++;
   333 	nglyphs++;
   275 	g++;
   334 	g++;
   276     }
   335     }
   277 
   336 
   278     if( nglyphs != shaper_item.num_glyphs )
   337     if( nglyphs != shaper.shaper_item.num_glyphs )
   279 	goto error;
   338 	goto error;
   280 
   339 
   281     for (hb_uint32 i = 0; i < nglyphs; ++i) {
   340     for (hb_uint32 i = 0; i < nglyphs; ++i) {
   282 	if ((shaper_item.glyphs[i]&0xffffff) != s->glyphs[i])
   341         if ((shaper.shaper_item.glyphs[i]&0xffffff) != s->glyphs[i])
   283 	    goto error;
   342 	    goto error;
   284     }
   343     }
   285     return true;
   344     return true;
   286  error:
   345  error:
   287     str = "";
   346     QString str = "";
   288     const unsigned short *uc = s->unicode;
   347     const unsigned short *uc = s->unicode;
   289     while (*uc) {
   348     while (*uc) {
   290 	str += QString("%1 ").arg(*uc, 4, 16);
   349 	str += QString("%1 ").arg(*uc, 4, 16);
   291 	++uc;
   350 	++uc;
   292     }
   351     }
   293     qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d",
   352     qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d",
   294            face->family_name,
   353            face->family_name,
   295            str.toLatin1().constData(),
   354            str.toLatin1().constData(),
   296            shaper_item.num_glyphs, nglyphs);
   355            shaper.shaper_item.num_glyphs, nglyphs);
   297 
   356 
   298     str = "";
   357     str = "";
   299     hb_uint32 i = 0;
   358     hb_uint32 i = 0;
   300     while (i < shaper_item.num_glyphs) {
   359     while (i < shaper.shaper_item.num_glyphs) {
   301 	str += QString("%1 ").arg(shaper_item.glyphs[i], 4, 16);
   360         str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16);
   302 	++i;
   361 	++i;
   303     }
   362     }
   304     qDebug("    glyph result = %s", str.toLatin1().constData());
   363     qDebug("    glyph result = %s", str.toLatin1().constData());
   305     return false;
   364     return false;
   306 }
   365 }
       
   366 
       
   367 
       
   368 void tst_QScriptEngine::greek()
       
   369 {
       
   370     FT_Face face = loadFace("DejaVuSans.ttf");
       
   371     if (face) {
       
   372         for (int uc = 0x1f00; uc <= 0x1fff; ++uc) {
       
   373             QString str;
       
   374             str.append(uc);
       
   375             if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) {
       
   376                 //qDebug() << "skipping" << hex << uc;
       
   377                 continue;
       
   378             }
       
   379             if (uc == 0x1fc1 || uc == 0x1fed)
       
   380                 continue;
       
   381             QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) );
       
   382         }
       
   383         FT_Done_Face(face);
       
   384     } else {
       
   385         QSKIP("couln't find DejaVu Sans", SkipAll);
       
   386     }
       
   387 
       
   388 
       
   389     face = loadFace("SBL_grk.ttf");
       
   390     if (face) {
       
   391         for (int uc = 0x1f00; uc <= 0x1fff; ++uc) {
       
   392             QString str;
       
   393             str.append(uc);
       
   394             if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) {
       
   395                 //qDebug() << "skipping" << hex << uc;
       
   396                 continue;
       
   397             }
       
   398             if (uc == 0x1fc1 || uc == 0x1fed)
       
   399                 continue;
       
   400             QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) );
       
   401 
       
   402         }
       
   403 
       
   404         const ShapeTable shape_table [] = {
       
   405             { { 0x3b1, 0x300, 0x313, 0x0 },
       
   406               { 0xb8, 0x3d3, 0x3c7, 0x0 } },
       
   407             { { 0x3b1, 0x313, 0x300, 0x0 },
       
   408               { 0xd4, 0x0 } },
       
   409 
       
   410             { {0}, {0} }
       
   411         };
       
   412 
       
   413 
       
   414         const ShapeTable *s = shape_table;
       
   415         while (s->unicode[0]) {
       
   416             QVERIFY( shaping(face, s, HB_Script_Greek) );
       
   417             ++s;
       
   418         }
       
   419 
       
   420         FT_Done_Face(face);
       
   421     } else {
       
   422         QSKIP("couln't find DejaVu Sans", SkipAll);
       
   423     }
       
   424 }
       
   425 
   307 
   426 
   308 void tst_QScriptEngine::devanagari()
   427 void tst_QScriptEngine::devanagari()
   309 {
   428 {
   310     {
   429     {
   311         FT_Face face = loadFace("raghu.ttf");
   430         FT_Face face = loadFace("raghu.ttf");
  1009                   { 0x2ff, 0x0 } },
  1128                   { 0x2ff, 0x0 } },
  1010                 { { 0xd33, 0xd4d, 0xd33, 0x0 },
  1129                 { { 0xd33, 0xd4d, 0xd33, 0x0 },
  1011                   { 0x3f8, 0x0 } },
  1130                   { 0x3f8, 0x0 } },
  1012                 { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
  1131                 { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
  1013                   { 0x2ff, 0x0 } },
  1132                   { 0x2ff, 0x0 } },
       
  1133                 { { 0xd30, 0xd4d, 0x200d, 0xd35, 0xd4d, 0xd35, 0x0 },
       
  1134                   { 0xf3, 0x350, 0x0 } },
  1014 
  1135 
  1015                 { {0}, {0} }
  1136                 { {0}, {0} }
  1016             };
  1137             };
  1017 
  1138 
  1018 
  1139