diff options
-rw-r--r-- | vcl/inc/font/OpenTypeFeatureStrings.hrc | 1 | ||||
-rw-r--r-- | vcl/qa/cppunit/FontFeatureTest.cxx | 61 | ||||
-rw-r--r-- | vcl/source/font/FeatureCollector.cxx | 69 |
3 files changed, 124 insertions, 7 deletions
diff --git a/vcl/inc/font/OpenTypeFeatureStrings.hrc b/vcl/inc/font/OpenTypeFeatureStrings.hrc index a8bb5473fafb..aebdda1d2743 100644 --- a/vcl/inc/font/OpenTypeFeatureStrings.hrc +++ b/vcl/inc/font/OpenTypeFeatureStrings.hrc @@ -97,6 +97,7 @@ #define STR_FONT_FEATURE_ID_VRT2 NC_("STR_FONT_FEATURE_ID_VRT2", "Vertical Alternates and Rotation") #define STR_FONT_FEATURE_ID_VRTR NC_("STR_FONT_FEATURE_ID_VRTR", "Vertical Alternates for Rotation") #define STR_FONT_FEATURE_ID_ZERO NC_("STR_FONT_FEATURE_ID_ZERO", "Slashed Zero") +#define STR_FONT_FEATURE_PARAM_NONE NC_("STR_FONT_FEATURE_PARAM_NONE", "None") #endif // INCLUDED_VCL_INC_STRINGS_HRC diff --git a/vcl/qa/cppunit/FontFeatureTest.cxx b/vcl/qa/cppunit/FontFeatureTest.cxx index 103870306bcf..9858c96e0a9f 100644 --- a/vcl/qa/cppunit/FontFeatureTest.cxx +++ b/vcl/qa/cppunit/FontFeatureTest.cxx @@ -27,11 +27,13 @@ public: void testGetFontFeaturesGraphite(); void testGetFontFeaturesOpenType(); + void testGetFontFeaturesOpenTypeEnum(); void testParseFeature(); CPPUNIT_TEST_SUITE(FontFeatureTest); CPPUNIT_TEST(testGetFontFeaturesGraphite); CPPUNIT_TEST(testGetFontFeaturesOpenType); + CPPUNIT_TEST(testGetFontFeaturesOpenTypeEnum); CPPUNIT_TEST(testParseFeature); CPPUNIT_TEST_SUITE_END(); }; @@ -169,6 +171,65 @@ void FontFeatureTest::testGetFontFeaturesOpenType() #endif // HAVE_MORE_FONTS } +void FontFeatureTest::testGetFontFeaturesOpenTypeEnum() +{ +#if HAVE_MORE_FONTS + ScopedVclPtrInstance<VirtualDevice> aVDev(*Application::GetDefaultDevice(), + DeviceFormat::DEFAULT, DeviceFormat::DEFAULT); + aVDev->SetOutputSizePixel(Size(10, 10)); + + OUString aFontName("Reem Kufi"); + CPPUNIT_ASSERT(aVDev->IsFontAvailable(aFontName)); + + vcl::Font aFont = aVDev->GetFont(); + aFont.SetFamilyName(aFontName); + aFont.SetWeight(FontWeight::WEIGHT_NORMAL); + aFont.SetItalic(FontItalic::ITALIC_NORMAL); + aFont.SetWidthType(FontWidth::WIDTH_NORMAL); + aVDev->SetFont(aFont); + + std::vector<vcl::font::Feature> rFontFeatures; + CPPUNIT_ASSERT(aVDev->GetFontFeatures(rFontFeatures)); + + OUString aFeaturesString; + for (vcl::font::Feature const& rFeature : rFontFeatures) + aFeaturesString += vcl::font::featureCodeAsString(rFeature.m_nCode) + " "; + + CPPUNIT_ASSERT_EQUAL(size_t(10), rFontFeatures.size()); + + CPPUNIT_ASSERT_EQUAL(OUString("aalt case cv01 cv02 cv03 frac ordn sups " + "zero kern "), + aFeaturesString); + + // Check aalt feature + { + vcl::font::Feature& rFeature = rFontFeatures[0]; + CPPUNIT_ASSERT_EQUAL(vcl::font::featureCode("aalt"), rFeature.m_nCode); + + vcl::font::FeatureDefinition& rFeatureDefinition = rFeature.m_aDefinition; + CPPUNIT_ASSERT_EQUAL(vcl::font::featureCode("aalt"), rFeatureDefinition.getCode()); + CPPUNIT_ASSERT(!rFeatureDefinition.getDescription().isEmpty()); + CPPUNIT_ASSERT_EQUAL(vcl::font::FeatureParameterType::ENUM, rFeatureDefinition.getType()); + + CPPUNIT_ASSERT_EQUAL(size_t(3), rFeatureDefinition.getEnumParameters().size()); + + vcl::font::FeatureParameter const& rParameter1 = rFeatureDefinition.getEnumParameters()[0]; + CPPUNIT_ASSERT_EQUAL(uint32_t(0), rParameter1.getCode()); + CPPUNIT_ASSERT(!rParameter1.getDescription().isEmpty()); + + vcl::font::FeatureParameter const& rParameter2 = rFeatureDefinition.getEnumParameters()[1]; + CPPUNIT_ASSERT_EQUAL(uint32_t(1), rParameter2.getCode()); + CPPUNIT_ASSERT(!rParameter2.getDescription().isEmpty()); + + vcl::font::FeatureParameter const& rParameter3 = rFeatureDefinition.getEnumParameters()[2]; + CPPUNIT_ASSERT_EQUAL(uint32_t(2), rParameter3.getCode()); + CPPUNIT_ASSERT(!rParameter2.getDescription().isEmpty()); + } + + aVDev.disposeAndClear(); +#endif // HAVE_MORE_FONTS +} + void FontFeatureTest::testParseFeature() { { // No font features specified diff --git a/vcl/source/font/FeatureCollector.cxx b/vcl/source/font/FeatureCollector.cxx index 8b506f7dfd47..c792d50d7e17 100644 --- a/vcl/source/font/FeatureCollector.cxx +++ b/vcl/source/font/FeatureCollector.cxx @@ -11,6 +11,9 @@ #include <font/OpenTypeFeatureDefinitionList.hxx> #include <i18nlangtag/languagetag.hxx> +#include <font/OpenTypeFeatureStrings.hrc> +#include <svdata.hxx> + #include <hb-ot.h> #include <hb-graphite2.h> @@ -123,24 +126,76 @@ void FeatureCollector::collectForTable(hb_tag_t aTableTag) rFeature.m_nCode = aFeatureTag; FeatureDefinition aDefinition = OpenTypeFeatureDefinitionList().getDefinition(aFeatureTag); - - if (OpenTypeFeatureDefinitionListPrivate::isSpecialFeatureCode(aFeatureTag)) + std::vector<vcl::font::FeatureParameter> aParameters{ + { 0, VclResId(STR_FONT_FEATURE_PARAM_NONE) } + }; + + unsigned int nFeatureIdx; + if (hb_ot_layout_language_find_feature(m_pHbFace, aTableTag, 0, + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, aFeatureTag, + &nFeatureIdx)) { - unsigned int nFeatureIdx; - if (hb_ot_layout_language_find_feature(m_pHbFace, aTableTag, 0, - HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, aFeatureTag, - &nFeatureIdx)) + if (OpenTypeFeatureDefinitionListPrivate::isSpecialFeatureCode(aFeatureTag)) { + // ssXX and cvXX can have name ID defined for them, check for + // them and use as appropriate. hb_ot_name_id_t aLabelID; + hb_ot_name_id_t aFirstParameterID; + unsigned nNamedParameters; if (hb_ot_layout_feature_get_name_ids(m_pHbFace, aTableTag, nFeatureIdx, &aLabelID, - nullptr, nullptr, nullptr, nullptr)) + nullptr, nullptr, &nNamedParameters, + &aFirstParameterID)) { OString sLanguage = m_rLanguageTag.getBcp47().toUtf8(); OUString sLabel = getName(m_pHbFace, aLabelID, sLanguage); if (!sLabel.isEmpty()) aDefinition = vcl::font::FeatureDefinition(aFeatureTag, sLabel); + + // cvXX features can have parameters name IDs, check for + // them and populate feature parameters as appropriate. + for (unsigned i = 0; i < nNamedParameters; i++) + { + hb_ot_name_id_t aNameID = aFirstParameterID + i; + OUString sName = getName(m_pHbFace, aNameID, sLanguage); + if (!sName.isEmpty()) + aParameters.emplace_back(uint32_t(i), sName); + } + } + } + + // Collect lookups in this feature, and input glyphs for each + // lookup, and calculate the max number of alternates they have. + unsigned int nLookups = hb_ot_layout_feature_get_lookups( + m_pHbFace, aTableTag, nFeatureIdx, 0, nullptr, nullptr); + std::vector<unsigned int> aLookups(nLookups); + hb_ot_layout_feature_get_lookups(m_pHbFace, aTableTag, nFeatureIdx, 0, &nLookups, + aLookups.data()); + unsigned int nAlternates = 0; + for (unsigned int nLookupIdx : aLookups) + { + hb_set_t* aGlyphs = hb_set_create(); + hb_ot_layout_lookup_collect_glyphs(m_pHbFace, aTableTag, nLookupIdx, nullptr, + aGlyphs, nullptr, nullptr); + hb_codepoint_t nGlyphIdx = HB_SET_VALUE_INVALID; + while (hb_set_next(aGlyphs, &nGlyphIdx)) + { + nAlternates = std::max( + nAlternates, hb_ot_layout_lookup_get_glyph_alternates( + m_pHbFace, nLookupIdx, nGlyphIdx, 0, nullptr, nullptr)); } } + + // Append the alternates to the feature parameters, keeping any + // existing ones calculated from cvXX features above. + for (unsigned int i = aParameters.size() - 1; i < nAlternates; i++) + aParameters.emplace_back(uint32_t(i + 1), OUString::number(i + 1)); + + if (aParameters.size() > 1) + { + aDefinition = vcl::font::FeatureDefinition( + aFeatureTag, aDefinition.getDescription(), + vcl::font::FeatureParameterType::ENUM, std::move(aParameters), 0); + } } if (aDefinition) |