diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2010-04-14 15:15:49 (GMT) |
---|---|---|
committer | Lars Knoll <lars.knoll@nokia.com> | 2010-04-14 15:23:36 (GMT) |
commit | f3a0bd8827f5d0de575b0f8c044b4bf65405b69c (patch) | |
tree | 22f45a4e20f1a743374be8eb3219e5abbd85cc0d /src/3rdparty/harfbuzz | |
parent | 8cf08f2640e31615bba0cc61544642855dc1275e (diff) | |
download | Qt-f3a0bd8827f5d0de575b0f8c044b4bf65405b69c.zip Qt-f3a0bd8827f5d0de575b0f8c044b4bf65405b69c.tar.gz Qt-f3a0bd8827f5d0de575b0f8c044b4bf65405b69c.tar.bz2 |
merge commit c638ddc70f6a8196f2c8b11808ab01510233a0ee from harfbuzz:
commit c638ddc70f6a8196f2c8b11808ab01510233a0ee
Author: Lars Knoll <lars.knoll@nokia.com>
Date: Wed Apr 14 17:01:49 2010 +0200
Fix a bug in malayalam shaping
See http://bugreports.qt.nokia.com/browse/QTBUG-1887.
We were not finding the base character correctly in the case
where the syllable contained a ZWJ.
In addition, the indic OT specs require us to also apply the 'loca',
'cjct' and 'calt' features. They seem to be mostly unused by todays
fonts, but we should better apply them anyways.
Task-number: QTBUG-1887
Reviewed-by: Eskil
Diffstat (limited to 'src/3rdparty/harfbuzz')
-rw-r--r-- | src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp | 23 | ||||
-rw-r--r-- | src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h | 55 | ||||
-rw-r--r-- | src/3rdparty/harfbuzz/tests/shaping/main.cpp | 165 |
3 files changed, 193 insertions, 50 deletions
diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp b/src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp index 3c9df93..4d8418b 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp +++ b/src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp @@ -1107,6 +1107,7 @@ static inline void splitMatra(unsigned short *reordered, int matra, int &len) #ifndef NO_OPENTYPE static const HB_OpenTypeFeature indic_features[] = { + { HB_MAKE_TAG('l', 'o', 'c', 'a'), LocaProperty }, { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty }, { HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty }, { HB_MAKE_TAG('n', 'u', 'k', 't'), NuktaProperty }, @@ -1115,12 +1116,14 @@ static const HB_OpenTypeFeature indic_features[] = { { HB_MAKE_TAG('b', 'l', 'w', 'f'), BelowFormProperty }, { HB_MAKE_TAG('h', 'a', 'l', 'f'), HalfFormProperty }, { HB_MAKE_TAG('p', 's', 't', 'f'), PostFormProperty }, + { HB_MAKE_TAG('c', 'j', 'c', 't'), ConjunctFormProperty }, { HB_MAKE_TAG('v', 'a', 't', 'u'), VattuProperty }, { HB_MAKE_TAG('p', 'r', 'e', 's'), PreSubstProperty }, { HB_MAKE_TAG('b', 'l', 'w', 's'), BelowSubstProperty }, { HB_MAKE_TAG('a', 'b', 'v', 's'), AboveSubstProperty }, { HB_MAKE_TAG('p', 's', 't', 's'), PostSubstProperty }, { HB_MAKE_TAG('h', 'a', 'l', 'n'), HalantProperty }, + { HB_MAKE_TAG('c', 'a', 'l', 't'), IndicCaltProperty }, { 0, 0 } }; #endif @@ -1148,6 +1151,8 @@ static QString propertiesToString(int properties) { QString res; properties = ~properties; + if (properties & LocaProperty) + res += "Loca "; if (properties & CcmpProperty) res += "Ccmp "; if (properties & InitProperty) @@ -1168,6 +1173,8 @@ static QString propertiesToString(int properties) res += "HalfForm "; if (properties & PostFormProperty) res += "PostForm "; + if (properties & ConjunctFormProperty) + res += "PostForm "; if (properties & VattuProperty) res += "Vattu "; if (properties & PreSubstProperty) @@ -1182,6 +1189,8 @@ static QString propertiesToString(int properties) res += "Halant "; if (properties & CligProperty) res += "Clig "; + if (properties & IndicCaltProperty) + res += "Calt "; return res; } #endif @@ -1296,10 +1305,15 @@ static bool indic_shape_syllable(HB_Bool openType, HB_ShaperItem *item, bool inv } int skipped = 0; Position pos = Post; - for (i = len-1; i > base; i--) { + for (i = len-1; i >= base; i--) { if (position[i] != Consonant && (position[i] != Control || script == HB_Script_Kannada)) continue; + if (i < len-1 && position[i] == Control && position[i+1] == Consonant) { + base = i+1; + break; + } + Position charPosition = indic_position(uc[i]); if (pos == Post && charPosition == Post) { pos = Post; @@ -1545,16 +1559,20 @@ static bool indic_shape_syllable(HB_Bool openType, HB_ShaperItem *item, bool inv // features we should always apply for (i = 0; i < len; ++i) - properties[i] = ~(CcmpProperty + properties[i] = ~(LocaProperty + | CcmpProperty | NuktaProperty | VattuProperty + | ConjunctFormProperty | PreSubstProperty | BelowSubstProperty | AboveSubstProperty | PostSubstProperty | HalantProperty + | IndicCaltProperty | PositioningProperties); + // Loca always applies // Ccmp always applies // Init if (item->item.pos == 0 @@ -1611,6 +1629,7 @@ static bool indic_shape_syllable(HB_Bool openType, HB_ShaperItem *item, bool inv // abvs always applies // psts always applies // halant always applies + // calt always applies #ifdef INDIC_DEBUG // { diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h b/src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h index 80bccf8..2fce7df 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h +++ b/src/3rdparty/harfbuzz/src/harfbuzz-shaper-private.h @@ -57,34 +57,37 @@ typedef enum } HB_CombiningClass; typedef enum { - CcmpProperty = 0x1, - InitProperty = 0x2, - IsolProperty = 0x4, - FinaProperty = 0x8, - MediProperty = 0x10, - RligProperty = 0x20, - CaltProperty = 0x40, - LigaProperty = 0x80, - DligProperty = 0x100, - CswhProperty = 0x200, - MsetProperty = 0x400, + LocaProperty = 0x1, + CcmpProperty = 0x2, + InitProperty = 0x4, + IsolProperty = 0x8, + FinaProperty = 0x10, + MediProperty = 0x20, + RligProperty = 0x40, + CaltProperty = 0x80, + LigaProperty = 0x100, + DligProperty = 0x200, + CswhProperty = 0x400, + MsetProperty = 0x800, /* used by indic and myanmar shaper */ - NuktaProperty = 0x4, - AkhantProperty = 0x8, - RephProperty = 0x10, - PreFormProperty = 0x20, - BelowFormProperty = 0x40, - AboveFormProperty = 0x80, - HalfFormProperty = 0x100, - PostFormProperty = 0x200, - VattuProperty = 0x400, - PreSubstProperty = 0x800, - BelowSubstProperty = 0x1000, - AboveSubstProperty = 0x2000, - PostSubstProperty = 0x4000, - HalantProperty = 0x8000, - CligProperty = 0x10000 + NuktaProperty = 0x8, + AkhantProperty = 0x10, + RephProperty = 0x20, + PreFormProperty = 0x40, + BelowFormProperty = 0x80, + AboveFormProperty = 0x100, + HalfFormProperty = 0x200, + PostFormProperty = 0x400, + ConjunctFormProperty = 0x800, + VattuProperty = 0x1000, + PreSubstProperty = 0x2000, + BelowSubstProperty = 0x4000, + AboveSubstProperty = 0x8000, + PostSubstProperty = 0x10000, + HalantProperty = 0x20000, + CligProperty = 0x40000, + IndicCaltProperty = 0x80000 } HB_OpenTypeProperty; diff --git a/src/3rdparty/harfbuzz/tests/shaping/main.cpp b/src/3rdparty/harfbuzz/tests/shaping/main.cpp index 827ac30..28f8e07 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,66 @@ static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) if (HB_ShapeItem(&shaper_item)) break; - } HB_FreeFace(hbFace); +} + + +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; +} + +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 +334,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 +352,78 @@ 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)) ); + } + FT_Done_Face(face); + } else { + QSKIP("couln't find DejaVu Sans", SkipAll); + } + + + face = loadFace("SBL_grk.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)) ); + + } + + const ShapeTable shape_table [] = { + { { 0x3b1, 0x300, 0x313, 0x0 }, + { 0xb8, 0x3d3, 0x3c7, 0x0 } }, + { { 0x3b1, 0x313, 0x300, 0x0 }, + { 0xd4, 0x0 } }, + + { {0}, {0} } + }; + + + const ShapeTable *s = shape_table; + while (s->unicode[0]) { + QVERIFY( shaping(face, s, HB_Script_Greek) ); + ++s; + } + + FT_Done_Face(face); + } else { + QSKIP("couln't find DejaVu Sans", SkipAll); + } +} + + void tst_QScriptEngine::devanagari() { { @@ -1011,6 +1130,8 @@ void tst_QScriptEngine::malayalam() { 0x3f8, 0x0 } }, { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 }, { 0x2ff, 0x0 } }, + { { 0xd30, 0xd4d, 0x200d, 0xd35, 0xd4d, 0xd35, 0x0 }, + { 0xf3, 0x350, 0x0 } }, { {0}, {0} } }; |