diff options
-rw-r--r-- | src/3rdparty/harfbuzz/src/Makefile.am | 3 | ||||
-rw-r--r-- | src/3rdparty/harfbuzz/src/harfbuzz-shaper-all.cpp | 1 | ||||
-rw-r--r-- | src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h | 1 | ||||
-rw-r--r-- | src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp | 2 | ||||
-rw-r--r-- | src/3rdparty/harfbuzz/tests/shaping/main.cpp | 127 | ||||
-rw-r--r-- | tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp | 75 |
6 files changed, 185 insertions, 24 deletions
diff --git a/src/3rdparty/harfbuzz/src/Makefile.am b/src/3rdparty/harfbuzz/src/Makefile.am index 2b0fb1d..51d0652 100644 --- a/src/3rdparty/harfbuzz/src/Makefile.am +++ b/src/3rdparty/harfbuzz/src/Makefile.am @@ -12,7 +12,8 @@ MAINSOURCES = \ harfbuzz-impl.c \ harfbuzz-open.c \ harfbuzz-shaper.cpp \ - harfbuzz-tibetan.c \ + harfbuzz-greek.c \ + harfbuzz-tibetan.c \ harfbuzz-khmer.c \ harfbuzz-indic.cpp \ harfbuzz-hebrew.c \ diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-shaper-all.cpp b/src/3rdparty/harfbuzz/src/harfbuzz-shaper-all.cpp index d2f902f..2dae501 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-shaper-all.cpp +++ b/src/3rdparty/harfbuzz/src/harfbuzz-shaper-all.cpp @@ -25,6 +25,7 @@ #include "harfbuzz-shaper.cpp" #include "harfbuzz-indic.cpp" extern "C" { +#include "harfbuzz-greek.c" #include "harfbuzz-tibetan.c" #include "harfbuzz-khmer.c" #include "harfbuzz-hebrew.c" diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h b/src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h index 80bccf8..11ed753 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h +++ b/src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h @@ -100,6 +100,7 @@ typedef struct { extern const HB_ScriptEngine hb_scriptEngines[]; extern HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item); +extern HB_Bool HB_GreekShape(HB_ShaperItem *shaper_item); extern HB_Bool HB_TibetanShape(HB_ShaperItem *shaper_item); extern HB_Bool HB_HebrewShape(HB_ShaperItem *shaper_item); extern HB_Bool HB_ArabicShape(HB_ShaperItem *shaper_item); diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp b/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp index bfc7bd4..4bc53c8 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp +++ b/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp @@ -587,7 +587,7 @@ const HB_ScriptEngine HB_ScriptEngines[] = { // Common { HB_BasicShape, 0}, // Greek - { HB_BasicShape, 0}, + { HB_GreekShape, 0}, // Cyrillic { HB_BasicShape, 0}, // Armenian diff --git a/src/3rdparty/harfbuzz/tests/shaping/main.cpp b/src/3rdparty/harfbuzz/tests/shaping/main.cpp index 827ac30..b48b0a9 100644 --- a/src/3rdparty/harfbuzz/tests/shaping/main.cpp +++ b/src/3rdparty/harfbuzz/tests/shaping/main.cpp @@ -136,13 +136,13 @@ HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 return HB_Err_Ok; } -void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics) +void hb_getGlyphMetrics(HB_Font, HB_Glyph, HB_GlyphMetrics *metrics) { // ### metrics->x = metrics->y = metrics->width = metrics->height = metrics->xOffset = metrics->yOffset = 0; } -HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric) +HB_Fixed hb_getFontMetric(HB_Font, HB_FontMetric ) { return 0; // #### } @@ -169,6 +169,8 @@ public slots: void initTestCase(); void cleanupTestCase(); private slots: + void greek(); + void devanagari(); void bengali(); void gurmukhi(); @@ -203,18 +205,25 @@ void tst_QScriptEngine::cleanupTestCase() FT_Done_FreeType(freetype); } -struct ShapeTable { - unsigned short unicode[16]; - unsigned short glyphs[16]; +class Shaper +{ +public: + Shaper(FT_Face face, HB_Script script, const QString &str); + + HB_FontRec hbFont; + HB_ShaperItem shaper_item; + QVarLengthArray<HB_Glyph> hb_glyphs; + QVarLengthArray<HB_GlyphAttributes> hb_attributes; + QVarLengthArray<HB_Fixed> hb_advances; + QVarLengthArray<HB_FixedPoint> hb_offsets; + QVarLengthArray<unsigned short> hb_logClusters; + }; -static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) +Shaper::Shaper(FT_Face face, HB_Script script, const QString &str) { - QString str = QString::fromUtf16( s->unicode ); - HB_Face hbFace = HB_NewFace(face, hb_getSFntTable); - HB_FontRec hbFont; hbFont.klass = &hb_fontClass; hbFont.userData = face; hbFont.x_ppem = face->size->metrics.x_ppem; @@ -222,7 +231,6 @@ static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) hbFont.x_scale = face->size->metrics.x_scale; hbFont.y_scale = face->size->metrics.y_scale; - HB_ShaperItem shaper_item; shaper_item.kerning_applied = false; shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData()); shaper_item.stringLength = str.length(); @@ -237,11 +245,6 @@ static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) shaper_item.glyphIndicesPresent = false; shaper_item.initialGlyphCount = 0; - QVarLengthArray<HB_Glyph> hb_glyphs(shaper_item.num_glyphs); - QVarLengthArray<HB_GlyphAttributes> hb_attributes(shaper_item.num_glyphs); - QVarLengthArray<HB_Fixed> hb_advances(shaper_item.num_glyphs); - QVarLengthArray<HB_FixedPoint> hb_offsets(shaper_item.num_glyphs); - QVarLengthArray<unsigned short> hb_logClusters(shaper_item.num_glyphs); while (1) { hb_glyphs.resize(shaper_item.num_glyphs); @@ -263,10 +266,68 @@ static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) if (HB_ShapeItem(&shaper_item)) break; - } HB_FreeFace(hbFace); +} + + +#if defined(Q_WS_X11) +static bool decomposedShaping(FT_Face face, HB_Script script, const QChar &ch) +{ + QString uc = QString().append(ch); + Shaper shaper(face, script, uc); + + uc = uc.normalized(QString::NormalizationForm_D); + Shaper decomposed(face, script, uc); + + if( shaper.shaper_item.num_glyphs != decomposed.shaper_item.num_glyphs ) + goto error; + + for (unsigned int i = 0; i < shaper.shaper_item.num_glyphs; ++i) { + if ((shaper.shaper_item.glyphs[i]&0xffffff) != (decomposed.shaper_item.glyphs[i]&0xffffff)) + goto error; + } + return true; + error: + QString str = ""; + int i = 0; + while (i < uc.length()) { + str += QString("%1 ").arg(uc[i].unicode(), 4, 16); + ++i; + } + qDebug("%s: decomposedShaping of char %4x failed\n decomposedString: %s\n nglyphs=%d, decomposed nglyphs %d", + face->family_name, + ch.unicode(), str.toLatin1().data(), + shaper.shaper_item.num_glyphs, + decomposed.shaper_item.num_glyphs); + + str = ""; + i = 0; + while (i < shaper.shaper_item.num_glyphs) { + str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16); + ++i; + } + qDebug(" composed glyph result = %s", str.toLatin1().constData()); + str = ""; + i = 0; + while (i < decomposed.shaper_item.num_glyphs) { + str += QString("%1 ").arg(decomposed.shaper_item.glyphs[i], 4, 16); + ++i; + } + qDebug(" decomposed glyph result = %s", str.toLatin1().constData()); + return false; +} +#endif + +struct ShapeTable { + unsigned short unicode[16]; + unsigned short glyphs[16]; +}; + +static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) +{ + Shaper shaper(face, script, QString::fromUtf16( s->unicode )); hb_uint32 nglyphs = 0; const unsigned short *g = s->glyphs; @@ -275,16 +336,16 @@ static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) g++; } - if( nglyphs != shaper_item.num_glyphs ) + if( nglyphs != shaper.shaper_item.num_glyphs ) goto error; for (hb_uint32 i = 0; i < nglyphs; ++i) { - if ((shaper_item.glyphs[i]&0xffffff) != s->glyphs[i]) + if ((shaper.shaper_item.glyphs[i]&0xffffff) != s->glyphs[i]) goto error; } return true; error: - str = ""; + QString str = ""; const unsigned short *uc = s->unicode; while (*uc) { str += QString("%1 ").arg(*uc, 4, 16); @@ -293,18 +354,40 @@ static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d", face->family_name, str.toLatin1().constData(), - shaper_item.num_glyphs, nglyphs); + shaper.shaper_item.num_glyphs, nglyphs); str = ""; hb_uint32 i = 0; - while (i < shaper_item.num_glyphs) { - str += QString("%1 ").arg(shaper_item.glyphs[i], 4, 16); + while (i < shaper.shaper_item.num_glyphs) { + str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16); ++i; } qDebug(" glyph result = %s", str.toLatin1().constData()); return false; } + +void tst_QScriptEngine::greek() +{ + FT_Face face = loadFace("DejaVuSans.ttf"); + if (face) { + for (int uc = 0x1f00; uc <= 0x1fff; ++uc) { + QString str; + str.append(uc); + if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) { + //qDebug() << "skipping" << hex << uc; + continue; + } + if (uc == 0x1fc1 || uc == 0x1fed) + continue; + QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) ); + } + } else { + QSKIP("couln't find DejaVu Sans", SkipAll); + } +} + + void tst_QScriptEngine::devanagari() { { diff --git a/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp b/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp index 6de3f59..841f5b9 100644 --- a/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp +++ b/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp @@ -99,6 +99,7 @@ private slots: void kannada(); void malayalam(); void sinhala(); + void greek(); void khmer(); void linearB(); @@ -998,6 +999,80 @@ void tst_QTextScriptEngine::linearB() #endif } +#if defined(Q_WS_X11) +static bool decomposedShaping( const QFont &f, const QChar &ch) +{ + QString str = QString().append(ch); + QTextLayout layout(str, f); + QTextEngine *e = layout.d; + e->itemize(); + e->shape(0); + + QTextLayout decomposed(str.normalized(QString::NormalizationForm_D), f); + QTextEngine *de = decomposed.d; + de->itemize(); + de->shape(0); + + if( e->layoutData->items[0].num_glyphs != de->layoutData->items[0].num_glyphs ) + goto error; + + for (int i = 0; i < e->layoutData->items[0].num_glyphs; ++i) { + if ((e->layoutData->glyphLayout.glyphs[i] & 0xffffff) != (de->layoutData->glyphLayout.glyphs[i] & 0xffffff)) + goto error; + } + return true; + error: + qDebug("%s: decomposedShaping of char %4x failed, nglyphs=%d, decomposed nglyphs %d", + f.family().toLatin1().constData(), + ch.unicode(), + e->layoutData->items[0].num_glyphs, + de->layoutData->items[0].num_glyphs); + + str = ""; + int i = 0; + while (i < e->layoutData->items[0].num_glyphs) { + str += QString("%1 ").arg(e->layoutData->glyphLayout.glyphs[i], 4, 16); + ++i; + } + qDebug(" composed glyph result = %s", str.toLatin1().constData()); + str = ""; + i = 0; + while (i < de->layoutData->items[0].num_glyphs) { + str += QString("%1 ").arg(de->layoutData->glyphLayout.glyphs[i], 4, 16); + ++i; + } + qDebug(" decomposed glyph result = %s", str.toLatin1().constData()); + return false; +} +#endif + + +void tst_QTextScriptEngine::greek() +{ +#if defined(Q_WS_X11) + { + if (QFontDatabase().families(QFontDatabase::Any).contains("DejaVu Sans")) { + QFont f("DejaVu Sans"); + for (int uc = 0x1f00; uc <= 0x1fff; ++uc) { + QString str; + str.append(uc); + if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) { + //qDebug() << "skipping" << hex << uc; + continue; + } + if (uc == 0x1fc1 || uc == 0x1fed) + continue; + QVERIFY( decomposedShaping(f, QChar(uc)) ); + } + } else { + QSKIP("couln't find DejaVu Sans", SkipAll); + } + } +#else + QSKIP("X11 specific test", SkipAll); +#endif +} + QTEST_MAIN(tst_QTextScriptEngine) #include "tst_qtextscriptengine.moc" |