vcl/inc/font/OpenTypeFeatureStrings.hrc | 1 vcl/qa/cppunit/FontFeatureTest.cxx | 61 ++++++++++++++++++++++++++++ vcl/source/font/FeatureCollector.cxx | 69 ++++++++++++++++++++++++++++---- 3 files changed, 124 insertions(+), 7 deletions(-)
New commits: commit 9ca3c6794ca1969f453ddaa66c4e02ac8c695cc6 Author: Khaled Hosny <[email protected]> AuthorDate: Wed Aug 24 07:54:15 2022 +0200 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Thu Aug 25 13:04:27 2022 +0200 FeatureCollector: Detect ENUM features in OpenType fonts A bit heavy handed, but the only way to detect the number of alternates a feature has is to get the maximum number of alternates for all lookups in all glyphs of this feature, which is what this code does. Additionally, for cvXX features, parameter names from the font are used when available. Change-Id: Ib7f7ea5e5288b47b03dac8703668ca6c4007f517 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138749 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <[email protected]> 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)
