vcl/inc/font/FeatureCollector.hxx | 6 +- vcl/inc/font/OpenTypeFeatureDefinitionList.hxx | 2 vcl/qa/cppunit/FontFeatureTest.cxx | 57 +++++++++++++++++++++++-- vcl/source/font/FeatureCollector.cxx | 46 +++++++++++++++++++- vcl/source/outdev/font.cxx | 4 - 5 files changed, 105 insertions(+), 10 deletions(-)
New commits: commit 19787042b1f139cb9c366801d283c6c0227e85e6 Author: Khaled Hosny <kha...@aliftype.com> AuthorDate: Wed Aug 24 00:26:10 2022 +0200 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Thu Aug 25 13:03:55 2022 +0200 FeatureCollector: Get feature labels for ssXX/cvXX features from the font If the font provides feature labels for ssXX/cvXX features, fetch and use them. Switches also to using the UI language not locale for finding labels as the UI language is what controls the UI localization. Change-Id: Ic7eeae9df25692e7f2924b2db65905666999c904 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138748 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/vcl/inc/font/FeatureCollector.hxx b/vcl/inc/font/FeatureCollector.hxx index 976a89fc8d86..ae784474e06d 100644 --- a/vcl/inc/font/FeatureCollector.hxx +++ b/vcl/inc/font/FeatureCollector.hxx @@ -22,14 +22,14 @@ class FeatureCollector private: hb_face_t* m_pHbFace; std::vector<vcl::font::Feature>& m_rFontFeatures; - LanguageType m_eLanguageType; + const LanguageTag& m_rLanguageTag; public: FeatureCollector(hb_face_t* pHbFace, std::vector<vcl::font::Feature>& rFontFeatures, - LanguageType eLanguageType) + const LanguageTag& rLanguageTag) : m_pHbFace(pHbFace) , m_rFontFeatures(rFontFeatures) - , m_eLanguageType(eLanguageType) + , m_rLanguageTag(rLanguageTag) { } diff --git a/vcl/inc/font/OpenTypeFeatureDefinitionList.hxx b/vcl/inc/font/OpenTypeFeatureDefinitionList.hxx index 52dbcfb5b9f0..aaa89ebe5d48 100644 --- a/vcl/inc/font/OpenTypeFeatureDefinitionList.hxx +++ b/vcl/inc/font/OpenTypeFeatureDefinitionList.hxx @@ -25,12 +25,12 @@ private: void init(); - static bool isSpecialFeatureCode(sal_uInt32 nFeatureCode); static FeatureDefinition handleSpecialFeatureCode(sal_uInt32 nFeatureCode); public: OpenTypeFeatureDefinitionListPrivate(); FeatureDefinition getDefinition(sal_uInt32 nFeatureCode); + static bool isSpecialFeatureCode(sal_uInt32 nFeatureCode); bool isRequired(sal_uInt32 nFeatureCode); }; diff --git a/vcl/qa/cppunit/FontFeatureTest.cxx b/vcl/qa/cppunit/FontFeatureTest.cxx index 91bcda29b74f..103870306bcf 100644 --- a/vcl/qa/cppunit/FontFeatureTest.cxx +++ b/vcl/qa/cppunit/FontFeatureTest.cxx @@ -25,16 +25,18 @@ public: { } - void testGetFontFeatures(); + void testGetFontFeaturesGraphite(); + void testGetFontFeaturesOpenType(); void testParseFeature(); CPPUNIT_TEST_SUITE(FontFeatureTest); - CPPUNIT_TEST(testGetFontFeatures); + CPPUNIT_TEST(testGetFontFeaturesGraphite); + CPPUNIT_TEST(testGetFontFeaturesOpenType); CPPUNIT_TEST(testParseFeature); CPPUNIT_TEST_SUITE_END(); }; -void FontFeatureTest::testGetFontFeatures() +void FontFeatureTest::testGetFontFeaturesGraphite() { #if HAVE_MORE_FONTS ScopedVclPtrInstance<VirtualDevice> aVDev(*Application::GetDefaultDevice(), @@ -118,6 +120,55 @@ void FontFeatureTest::testGetFontFeatures() #endif // HAVE_MORE_FONTS } +void FontFeatureTest::testGetFontFeaturesOpenType() +{ +#if HAVE_MORE_FONTS + ScopedVclPtrInstance<VirtualDevice> aVDev(*Application::GetDefaultDevice(), + DeviceFormat::DEFAULT, DeviceFormat::DEFAULT); + aVDev->SetOutputSizePixel(Size(10, 10)); + + OUString aFontName("Amiri"); + 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(17), rFontFeatures.size()); + + CPPUNIT_ASSERT_EQUAL(OUString("calt calt dnom liga numr pnum ss01 ss02 " + "ss03 ss04 ss05 ss06 ss07 ss08 kern kern " + "ss05 "), + aFeaturesString); + + // Check ss01 feature + { + vcl::font::Feature& rFeature = rFontFeatures[6]; + CPPUNIT_ASSERT_EQUAL(vcl::font::featureCode("ss01"), rFeature.m_nCode); + + vcl::font::FeatureDefinition& rFeatureDefinition = rFeature.m_aDefinition; + CPPUNIT_ASSERT_EQUAL(vcl::font::featureCode("ss01"), rFeatureDefinition.getCode()); + CPPUNIT_ASSERT_EQUAL(OUString("Low Baa dot following a Raa or Waw"), + rFeatureDefinition.getDescription()); + CPPUNIT_ASSERT_EQUAL(vcl::font::FeatureParameterType::BOOL, rFeatureDefinition.getType()); + + CPPUNIT_ASSERT_EQUAL(size_t(0), rFeatureDefinition.getEnumParameters().size()); + } + + 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 20f47dfdc502..8b506f7dfd47 100644 --- a/vcl/source/font/FeatureCollector.cxx +++ b/vcl/source/font/FeatureCollector.cxx @@ -9,6 +9,7 @@ #include <font/FeatureCollector.hxx> #include <font/OpenTypeFeatureDefinitionList.hxx> +#include <i18nlangtag/languagetag.hxx> #include <hb-ot.h> #include <hb-graphite2.h> @@ -22,7 +23,7 @@ bool FeatureCollector::collectGraphite() if (grFace == nullptr) return false; - gr_uint16 nUILanguage = gr_uint16(m_eLanguageType); + gr_uint16 nUILanguage = gr_uint16(m_rLanguageTag.getLanguageType()); gr_uint16 nNumberOfFeatures = gr_face_n_fref(grFace); gr_feature_val* pfeatureValues @@ -80,6 +81,29 @@ bool FeatureCollector::collectGraphite() return true; } +static OUString getName(hb_face_t* pHbFace, hb_ot_name_id_t aNameID, OString& rLanguage) +{ + auto aHbLang = hb_language_from_string(rLanguage.getStr(), rLanguage.getLength()); + auto nName = hb_ot_name_get_utf16(pHbFace, aNameID, aHbLang, nullptr, nullptr); + + if (!nName) + { + // Fallback to English if localized name is missing. + aHbLang = hb_language_from_string("en", 2); + nName = hb_ot_name_get_utf16(pHbFace, aNameID, aHbLang, nullptr, nullptr); + } + + OUString sName; + if (nName) + { + std::vector<uint16_t> aBuf(++nName); // make space for terminating NUL. + hb_ot_name_get_utf16(pHbFace, aNameID, aHbLang, &nName, aBuf.data()); + sName = OUString(reinterpret_cast<sal_Unicode*>(aBuf.data()), nName); + } + + return sName; +} + void FeatureCollector::collectForTable(hb_tag_t aTableTag) { unsigned int nFeatureCount @@ -99,6 +123,26 @@ void FeatureCollector::collectForTable(hb_tag_t aTableTag) rFeature.m_nCode = aFeatureTag; FeatureDefinition aDefinition = OpenTypeFeatureDefinitionList().getDefinition(aFeatureTag); + + if (OpenTypeFeatureDefinitionListPrivate::isSpecialFeatureCode(aFeatureTag)) + { + unsigned int nFeatureIdx; + if (hb_ot_layout_language_find_feature(m_pHbFace, aTableTag, 0, + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, aFeatureTag, + &nFeatureIdx)) + { + hb_ot_name_id_t aLabelID; + if (hb_ot_layout_feature_get_name_ids(m_pHbFace, aTableTag, nFeatureIdx, &aLabelID, + nullptr, nullptr, nullptr, nullptr)) + { + OString sLanguage = m_rLanguageTag.getBcp47().toUtf8(); + OUString sLabel = getName(m_pHbFace, aLabelID, sLanguage); + if (!sLabel.isEmpty()) + aDefinition = vcl::font::FeatureDefinition(aFeatureTag, sLabel); + } + } + } + if (aDefinition) rFeature.m_aDefinition = aDefinition; } diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx index e24d6ac014db..dc5ca50abaf9 100644 --- a/vcl/source/outdev/font.cxx +++ b/vcl/source/outdev/font.cxx @@ -170,9 +170,9 @@ bool OutputDevice::GetFontFeatures(std::vector<vcl::font::Feature>& rFontFeature if (!pHbFace) return false; - const LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); + const LanguageTag& rOfficeLanguage = Application::GetSettings().GetUILanguageTag(); - vcl::font::FeatureCollector aFeatureCollector(pHbFace, rFontFeatures, eOfficeLanguage); + vcl::font::FeatureCollector aFeatureCollector(pHbFace, rFontFeatures, rOfficeLanguage); aFeatureCollector.collect(); return true;