[Libreoffice-commits] core.git: include/vcl vcl/CppunitTest_vcl_fontfeature.mk vcl/inc vcl/Library_vcl.mk vcl/Module_vcl.mk vcl/qa vcl/source

Tomaž Vajngerl tomaz.vajngerl at collabora.co.uk
Sat Jun 16 12:29:13 UTC 2018


 include/vcl/font/Feature.hxx                     |   99 +++++++++++
 include/vcl/outdev.hxx                           |    6 
 vcl/CppunitTest_vcl_fontfeature.mk               |   49 +++++
 vcl/Library_vcl.mk                               |    3 
 vcl/Module_vcl.mk                                |    1 
 vcl/inc/font/FeatureCollector.hxx                |   52 ++++++
 vcl/inc/font/OpenTypeFeatureDefinitonList.hxx    |   47 +++++
 vcl/inc/font/OpenTypeFeatureStrings.hrc          |  184 +++++++++++++++++++++
 vcl/qa/cppunit/FontFeatureTest.cxx               |  110 ++++++++++++
 vcl/source/font/Feature.cxx                      |  119 +++++++++++++
 vcl/source/font/FeatureCollector.cxx             |  141 ++++++++++++++++
 vcl/source/font/OpenTypeFeatureDefinitonList.cxx |  199 +++++++++++++++++++++++
 vcl/source/outdev/font.cxx                       |   34 +++
 13 files changed, 1044 insertions(+)

New commits:
commit bda33ba2ce4d53214e1bd9ee62b05ced0525e8db
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Fri Jun 15 18:50:00 2018 +0200

    tdf#58941 implement retrieving font features and structures
    
    This adds the following:
    - Retrieving of features using HarfBuzz
    - Retrieving of feature definitions from graphite fonts
    - Adds description texts of OpenType features
    - Tests for retrieving features (Linux Libertine G graphite font)
      and making sure the features and definitions are what we
      expect.
    
    Change-Id: I51573fe086e59c6228cb546bbfac95ac53824c47
    Reviewed-on: https://gerrit.libreoffice.org/55892
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>

diff --git a/include/vcl/font/Feature.hxx b/include/vcl/font/Feature.hxx
new file mode 100644
index 000000000000..0fa8f6d5bb70
--- /dev/null
+++ b/include/vcl/font/Feature.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_VCL_FONT_FEATURE_HXX
+#define INCLUDED_VCL_FONT_FEATURE_HXX
+
+#include <vcl/dllapi.h>
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <memory>
+#include <vector>
+#include <unordered_map>
+
+namespace vcl
+{
+namespace font
+{
+constexpr sal_uInt32 featureCode(const char sFeature[4])
+{
+    return sFeature[0] << 24 | sFeature[1] << 16 | sFeature[2] << 8 | sFeature[3] << 0;
+}
+
+VCL_DLLPUBLIC OUString featureCodeAsString(sal_uInt32 nFeature);
+
+enum class FeatureParameterType
+{
+    BOOL,
+    ENUM
+};
+
+struct VCL_DLLPUBLIC FeatureParameter
+{
+private:
+    sal_uInt32 m_nCode;
+    OUString m_sDescription;
+    const char* m_pDescriptionID;
+
+public:
+    FeatureParameter(sal_uInt32 nCode, OUString aDescription);
+    FeatureParameter(sal_uInt32 nCode, const char* pDescriptionID);
+
+    sal_uInt32 getCode() const;
+    OUString getDescription() const;
+};
+
+class VCL_DLLPUBLIC FeatureDefinition
+{
+private:
+    sal_uInt32 m_nCode;
+    OUString m_sDescription;
+    const char* m_pDescriptionID;
+    FeatureParameterType m_eType;
+    // the index of the parameter defines the enum value, string is the description
+    std::vector<FeatureParameter> m_aEnumParameters;
+
+public:
+    FeatureDefinition();
+    FeatureDefinition(sal_uInt32 nCode, OUString const& rDescription,
+                      FeatureParameterType eType = FeatureParameterType::BOOL,
+                      std::vector<FeatureParameter> const& rEnumParameters
+                      = std::vector<FeatureParameter>{});
+    FeatureDefinition(sal_uInt32 nCode, const char* pDescriptionID);
+    FeatureDefinition(sal_uInt32 nCode, const char* pDescriptionID,
+                      std::vector<FeatureParameter> aEnumParameters);
+
+    const std::vector<FeatureParameter>& getEnumParameters() const;
+    OUString getDescription() const;
+    sal_uInt32 getCode() const;
+    FeatureParameterType getType() const;
+
+    operator bool() const;
+};
+
+struct VCL_DLLPUBLIC FeatureID
+{
+    sal_uInt32 m_aFeatureCode;
+    sal_uInt32 m_aScriptCode;
+    sal_uInt32 m_aLanguageCode;
+};
+
+struct VCL_DLLPUBLIC Feature
+{
+    FeatureID m_aID;
+    FeatureDefinition m_aDefinition;
+};
+
+} // end font namespace
+
+} // end vcl namespace
+
+#endif // INCLUDED_VCL_FONT_FEATURE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index 5cff1915b602..96fd4246a070 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -101,6 +101,9 @@ namespace vcl
     class TextLayoutCache;
     class Window;
     class FontInfo;
+    namespace font {
+        struct Feature;
+    }
 }
 
 namespace com { namespace sun { namespace star { namespace rendering {
@@ -1218,6 +1221,9 @@ public:
     bool                        GetFontCharMap( FontCharMapRef& rxFontCharMap ) const;
     bool                        GetFontCapabilities( vcl::FontCapabilities& rFontCapabilities ) const;
 
+    bool GetFontFeatures(std::vector<vcl::font::Feature>& rFontFeatures) const;
+
+
     /** Retrieve detailed font information in platform independent structure
 
         @param  nFallbacklevel      Fallback font level (0 = best matching font)
diff --git a/vcl/CppunitTest_vcl_fontfeature.mk b/vcl/CppunitTest_vcl_fontfeature.mk
new file mode 100644
index 000000000000..bb3b97106de1
--- /dev/null
+++ b/vcl/CppunitTest_vcl_fontfeature.mk
@@ -0,0 +1,49 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,vcl_fontfeature))
+
+$(eval $(call gb_CppunitTest_set_include,vcl_fontfeature,\
+    $$(INCLUDE) \
+    -I$(SRCDIR)/vcl/inc \
+))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,vcl_fontfeature, \
+	vcl/qa/cppunit/FontFeatureTest \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,vcl_fontfeature,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_libraries,vcl_fontfeature, \
+	comphelper \
+	cppu \
+	cppuhelper \
+	sal \
+	svt \
+	test \
+	tl \
+	tk \
+	unotest \
+	vcl \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,vcl_fontfeature))
+
+$(eval $(call gb_CppunitTest_use_ure,vcl_fontfeature))
+$(eval $(call gb_CppunitTest_use_vcl,vcl_fontfeature))
+
+$(eval $(call gb_CppunitTest_use_components,vcl_fontfeature,\
+	configmgr/source/configmgr \
+	i18npool/util/i18npool \
+	ucb/source/core/ucb1 \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,vcl_fontfeature))
+
+# vim: set noet sw=4 ts=4:
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index fd99be295c98..58fbfaaa8b56 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -403,6 +403,9 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/filter/wmf/wmf \
     vcl/source/filter/wmf/wmfexternal \
     vcl/source/filter/wmf/wmfwr \
+    vcl/source/font/Feature \
+    vcl/source/font/FeatureCollector \
+    vcl/source/font/OpenTypeFeatureDefinitonList \
     vcl/source/font/PhysicalFontCollection \
     vcl/source/font/PhysicalFontFace \
     vcl/source/font/PhysicalFontFamily \
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index a739d5988716..f2dfff71d862 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -192,6 +192,7 @@ $(eval $(call gb_Module_add_check_targets,vcl,\
 	CppunitTest_vcl_graphic_test \
 	CppunitTest_vcl_fontcharmap \
 	CppunitTest_vcl_font \
+	CppunitTest_vcl_fontfeature \
 	CppunitTest_vcl_fontmetric \
 	CppunitTest_vcl_complextext \
 	CppunitTest_vcl_filters_test \
diff --git a/vcl/inc/font/FeatureCollector.hxx b/vcl/inc/font/FeatureCollector.hxx
new file mode 100644
index 000000000000..4e918c48e334
--- /dev/null
+++ b/vcl/inc/font/FeatureCollector.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#ifndef INCLUDED_VCL_INC_FONT_FEATURECOLLECTOR_HXX
+#define INCLUDED_VCL_INC_FONT_FEATURECOLLECTOR_HXX
+
+#include <vcl/font/Feature.hxx>
+#include <o3tl/make_unique.hxx>
+#include <hb.h>
+
+namespace vcl
+{
+namespace font
+{
+class FeatureCollector
+{
+private:
+    hb_face_t* m_pHbFace;
+    std::vector<vcl::font::Feature>& m_rFontFeatures;
+
+public:
+    FeatureCollector(hb_face_t* pHbFace, std::vector<vcl::font::Feature>& rFontFeatures)
+        : m_pHbFace(pHbFace)
+        , m_rFontFeatures(rFontFeatures)
+    {
+    }
+
+private:
+    bool collectGraphiteFeatureDefinition(vcl::font::Feature& rFeature);
+
+    void collectForLanguage(hb_tag_t aTableTag, sal_uInt32 nScript, hb_tag_t aScriptTag,
+                            sal_uInt32 nLanguage, hb_tag_t aLanguageTag);
+
+    void collectForScript(hb_tag_t aTableTag, sal_uInt32 nScript, hb_tag_t aScriptTag);
+
+public:
+    void collectForTable(hb_tag_t aTableTag);
+};
+
+} // end namespace font
+} // end namespace vcl
+
+#endif // INCLUDED_VCL_INC_FONT_FEATURECOLLECTOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/font/OpenTypeFeatureDefinitonList.hxx b/vcl/inc/font/OpenTypeFeatureDefinitonList.hxx
new file mode 100644
index 000000000000..f606017d7412
--- /dev/null
+++ b/vcl/inc/font/OpenTypeFeatureDefinitonList.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_VCL_INC_FONT_OPENTYPEFEATUREDEFINITONLIST_HXX
+#define INCLUDED_VCL_INC_FONT_OPENTYPEFEATUREDEFINITONLIST_HXX
+
+#include <vcl/dllapi.h>
+#include <vcl/font/Feature.hxx>
+#include <rtl/instance.hxx>
+#include <memory>
+#include <vector>
+#include <unordered_map>
+
+namespace vcl
+{
+namespace font
+{
+class VCL_DLLPUBLIC OpenTypeFeatureDefinitonListPrivate
+{
+private:
+    std::vector<FeatureDefinition> m_aFeatureDefinition;
+    std::unordered_map<sal_uInt32, size_t> m_aCodeToIndex;
+
+    void init();
+
+public:
+    OpenTypeFeatureDefinitonListPrivate();
+    FeatureDefinition getDefinition(sal_uInt32 nFeatureCode);
+};
+
+class VCL_DLLPUBLIC OpenTypeFeatureDefinitonList
+    : public rtl::Static<OpenTypeFeatureDefinitonListPrivate, OpenTypeFeatureDefinitonList>
+{
+};
+
+} // end font namespace
+} // end vcl namespace
+
+#endif // INCLUDED_VCL_INC_FONT_OPENTYPEFEATUREDEFINITONLIST_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/font/OpenTypeFeatureStrings.hrc b/vcl/inc/font/OpenTypeFeatureStrings.hrc
new file mode 100644
index 000000000000..379a013b9f89
--- /dev/null
+++ b/vcl/inc/font/OpenTypeFeatureStrings.hrc
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_INC_FONT_OPENTYPEFEATRESTRINGS_HRC
+#define INCLUDED_VCL_INC_FONT_OPENTYPEFEATRESTRINGS_HRC
+
+#define NC_(Context, String) (Context "\004" u8##String)
+
+#define STR_FONT_FEATURE_ID_AALT          NC_("STR_FONT_FEATURE_ID_AALT", "Access All Alternates")
+#define STR_FONT_FEATURE_ID_ABVF          NC_("STR_FONT_FEATURE_ID_ABVF", "Above Base Forms")
+#define STR_FONT_FEATURE_ID_ABVM          NC_("STR_FONT_FEATURE_ID_ABVM", "Above Base Mark Positioning")
+#define STR_FONT_FEATURE_ID_ABVS          NC_("STR_FONT_FEATURE_ID_ABVS", "Above Base Substitutions")
+#define STR_FONT_FEATURE_ID_AFRC          NC_("STR_FONT_FEATURE_ID_AFRC", "Alternative (Vertical) Fractions")
+#define STR_FONT_FEATURE_ID_AKHN          NC_("STR_FONT_FEATURE_ID_AKHN", "Akhands")
+#define STR_FONT_FEATURE_ID_ALIG          NC_("STR_FONT_FEATURE_ID_ALIG", "Ancient Ligatures")
+#define STR_FONT_FEATURE_ID_BLWF          NC_("STR_FONT_FEATURE_ID_BLWF", "Below Base Forms")
+#define STR_FONT_FEATURE_ID_BLWM          NC_("STR_FONT_FEATURE_ID_BLWM", "Below Base Mark Positioning")
+#define STR_FONT_FEATURE_ID_BLWS          NC_("STR_FONT_FEATURE_ID_BLWS", "Below Base Substitutions")
+#define STR_FONT_FEATURE_ID_C2PC          NC_("STR_FONT_FEATURE_ID_C2PC", "Capitals to Petite Capitals")
+#define STR_FONT_FEATURE_ID_C2SC          NC_("STR_FONT_FEATURE_ID_C2SC", "Capitals to Small Capitals")
+#define STR_FONT_FEATURE_ID_CALT          NC_("STR_FONT_FEATURE_ID_CALT", "Contextual Alternates")
+#define STR_FONT_FEATURE_ID_CASE          NC_("STR_FONT_FEATURE_ID_CASE", "Case-Sensitive Forms")
+#define STR_FONT_FEATURE_ID_CCMP          NC_("STR_FONT_FEATURE_ID_CCMP", "Glyph Composition / Decomposition")
+#define STR_FONT_FEATURE_ID_CFAR          NC_("STR_FONT_FEATURE_ID_CFAR", "Conjunct Form After Ro")
+#define STR_FONT_FEATURE_ID_CJCT          NC_("STR_FONT_FEATURE_ID_CJCT", "Conjunct Forms")
+#define STR_FONT_FEATURE_ID_CLIG          NC_("STR_FONT_FEATURE_ID_CLIG", "Contextual Ligatures")
+#define STR_FONT_FEATURE_ID_CPCT          NC_("STR_FONT_FEATURE_ID_CPCT", "Centered CJK Punctuation")
+#define STR_FONT_FEATURE_ID_CPSP          NC_("STR_FONT_FEATURE_ID_CPSP", "Capital Spacing")
+#define STR_FONT_FEATURE_ID_CSWH          NC_("STR_FONT_FEATURE_ID_CSWH", "Contextual Swash")
+#define STR_FONT_FEATURE_ID_CURS          NC_("STR_FONT_FEATURE_ID_CURS", "Cursive Attachment")
+#define STR_FONT_FEATURE_ID_CV01          NC_("STR_FONT_FEATURE_ID_CV01", "Character Variant 1")
+#define STR_FONT_FEATURE_ID_CV02          NC_("STR_FONT_FEATURE_ID_CV02", "Character Variant 2")
+#define STR_FONT_FEATURE_ID_CV03          NC_("STR_FONT_FEATURE_ID_CV03", "Character Variant 3")
+#define STR_FONT_FEATURE_ID_CV04          NC_("STR_FONT_FEATURE_ID_CV04", "Character Variant 4")
+#define STR_FONT_FEATURE_ID_CV05          NC_("STR_FONT_FEATURE_ID_CV05", "Character Variant 5")
+#define STR_FONT_FEATURE_ID_CV06          NC_("STR_FONT_FEATURE_ID_CV06", "Character Variant 6")
+#define STR_FONT_FEATURE_ID_CV07          NC_("STR_FONT_FEATURE_ID_CV07", "Character Variant 7")
+#define STR_FONT_FEATURE_ID_CV08          NC_("STR_FONT_FEATURE_ID_CV08", "Character Variant 8")
+#define STR_FONT_FEATURE_ID_CV09          NC_("STR_FONT_FEATURE_ID_CV09", "Character Variant 9")
+#define STR_FONT_FEATURE_ID_CV10          NC_("STR_FONT_FEATURE_ID_CV10", "Character Variant 10")
+#define STR_FONT_FEATURE_ID_CV11          NC_("STR_FONT_FEATURE_ID_CV11", "Character Variant 11")
+#define STR_FONT_FEATURE_ID_DCAP          NC_("STR_FONT_FEATURE_ID_DCAP", "Drop Caps")
+#define STR_FONT_FEATURE_ID_DIST          NC_("STR_FONT_FEATURE_ID_DIST", "Distances")
+#define STR_FONT_FEATURE_ID_DLIG          NC_("STR_FONT_FEATURE_ID_DLIG", "Discretionary Ligatures")
+#define STR_FONT_FEATURE_ID_DNOM          NC_("STR_FONT_FEATURE_ID_DNOM", "Denominators")
+#define STR_FONT_FEATURE_ID_DPNG          NC_("STR_FONT_FEATURE_ID_DPNG", "Dipthongs (Obsolete)")
+#define STR_FONT_FEATURE_ID_DTLS          NC_("STR_FONT_FEATURE_ID_DTLS", "Dotless Forms")
+#define STR_FONT_FEATURE_ID_EXPT          NC_("STR_FONT_FEATURE_ID_EXPT", "Expert Forms")
+#define STR_FONT_FEATURE_ID_FALT          NC_("STR_FONT_FEATURE_ID_FALT", "Final Glyph on Line Alternates")
+#define STR_FONT_FEATURE_ID_FIN2          NC_("STR_FONT_FEATURE_ID_FIN2", "Terminal Forms #2")
+#define STR_FONT_FEATURE_ID_FIN3          NC_("STR_FONT_FEATURE_ID_FIN3", "Terminal Forms #3")
+#define STR_FONT_FEATURE_ID_FINA          NC_("STR_FONT_FEATURE_ID_FINA", "Terminal Forms")
+#define STR_FONT_FEATURE_ID_FLAC          NC_("STR_FONT_FEATURE_ID_FLAC", "Flattened Accent Over Capitals")
+#define STR_FONT_FEATURE_ID_FRAC          NC_("STR_FONT_FEATURE_ID_FRAC", "DIagonal Fractions")
+#define STR_FONT_FEATURE_ID_FRAC_PARAM_1  NC_("STR_FONT_FEATURE_ID_FRAC_PARAM_1", "Diagonal Fractions")
+#define STR_FONT_FEATURE_ID_FRAC_PARAM_2  NC_("STR_FONT_FEATURE_ID_FRAC_PARAM_2", "Nut Fractions")
+#define STR_FONT_FEATURE_ID_FWID          NC_("STR_FONT_FEATURE_ID_FWID", "Full Widths")
+#define STR_FONT_FEATURE_ID_HALF          NC_("STR_FONT_FEATURE_ID_HALF", "Half Forms")
+#define STR_FONT_FEATURE_ID_HALN          NC_("STR_FONT_FEATURE_ID_HALN", "Halant Forms")
+#define STR_FONT_FEATURE_ID_HALT          NC_("STR_FONT_FEATURE_ID_HALT", "Alternate Half Widths")
+#define STR_FONT_FEATURE_ID_HIST          NC_("STR_FONT_FEATURE_ID_HIST", "Historical Forms")
+#define STR_FONT_FEATURE_ID_HKNA          NC_("STR_FONT_FEATURE_ID_HKNA", "Horizontal Kana Alternates")
+#define STR_FONT_FEATURE_ID_HLIG          NC_("STR_FONT_FEATURE_ID_HLIG", "Historical Ligatures")
+#define STR_FONT_FEATURE_ID_HNGL          NC_("STR_FONT_FEATURE_ID_HNGL", "Hanja to Hangul")
+#define STR_FONT_FEATURE_ID_HOJO          NC_("STR_FONT_FEATURE_ID_HOJO", "Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms)")
+#define STR_FONT_FEATURE_ID_HWID          NC_("STR_FONT_FEATURE_ID_HWID", "Half Widths")
+#define STR_FONT_FEATURE_ID_INIT          NC_("STR_FONT_FEATURE_ID_INIT", "Initial Forms")
+#define STR_FONT_FEATURE_ID_ISOL          NC_("STR_FONT_FEATURE_ID_ISOL", "Isolated Forms")
+#define STR_FONT_FEATURE_ID_ITAL          NC_("STR_FONT_FEATURE_ID_ITAL", "Italics")
+#define STR_FONT_FEATURE_ID_JALT          NC_("STR_FONT_FEATURE_ID_JALT", "Justification Alternates")
+#define STR_FONT_FEATURE_ID_JP04          NC_("STR_FONT_FEATURE_ID_JP04", "JIS2004 Forms")
+#define STR_FONT_FEATURE_ID_JP78          NC_("STR_FONT_FEATURE_ID_JP78", "JIS78 Forms")
+#define STR_FONT_FEATURE_ID_JP83          NC_("STR_FONT_FEATURE_ID_JP83", "JIS83 Forms")
+#define STR_FONT_FEATURE_ID_JP90          NC_("STR_FONT_FEATURE_ID_JP90", "JIS90 Forms")
+#define STR_FONT_FEATURE_ID_KERN          NC_("STR_FONT_FEATURE_ID_KERN", "Horizontal Kerning")
+#define STR_FONT_FEATURE_ID_LFBD          NC_("STR_FONT_FEATURE_ID_LFBD", "Left Bounds")
+#define STR_FONT_FEATURE_ID_LIGA          NC_("STR_FONT_FEATURE_ID_LIGA", "Standard Ligatures")
+#define STR_FONT_FEATURE_ID_LJMO          NC_("STR_FONT_FEATURE_ID_LJMO", "Leading Jamo Forms")
+#define STR_FONT_FEATURE_ID_LNUM          NC_("STR_FONT_FEATURE_ID_LNUM", "Lining Figures")
+#define STR_FONT_FEATURE_ID_LOCL          NC_("STR_FONT_FEATURE_ID_LOCL", "Localized Forms")
+#define STR_FONT_FEATURE_ID_LTRA          NC_("STR_FONT_FEATURE_ID_LTRA", "Left to Right Alternates")
+#define STR_FONT_FEATURE_ID_LTRM          NC_("STR_FONT_FEATURE_ID_LTRM", "Left to Right Mirrored Forms")
+#define STR_FONT_FEATURE_ID_MARK          NC_("STR_FONT_FEATURE_ID_MARK", "Mark Positioning")
+#define STR_FONT_FEATURE_ID_MED2          NC_("STR_FONT_FEATURE_ID_MED2", "Medial Forms #2")
+#define STR_FONT_FEATURE_ID_MEDI          NC_("STR_FONT_FEATURE_ID_MEDI", "Medial Forms")
+#define STR_FONT_FEATURE_ID_MGRK          NC_("STR_FONT_FEATURE_ID_MGRK", "Mathematical Greek")
+#define STR_FONT_FEATURE_ID_MKMK          NC_("STR_FONT_FEATURE_ID_MKMK", "Mark to Mark Positioning")
+#define STR_FONT_FEATURE_ID_MSET          NC_("STR_FONT_FEATURE_ID_MSET", "Mark Positioning via Substitution")
+#define STR_FONT_FEATURE_ID_NALT          NC_("STR_FONT_FEATURE_ID_NALT", "Alternate Annotation Forms")
+#define STR_FONT_FEATURE_ID_NLCK          NC_("STR_FONT_FEATURE_ID_NLCK", "NLC Kanji Forms")
+#define STR_FONT_FEATURE_ID_NUKT          NC_("STR_FONT_FEATURE_ID_NUKT", "Nukta Forms")
+#define STR_FONT_FEATURE_ID_NUMR          NC_("STR_FONT_FEATURE_ID_NUMR", "Numerators")
+#define STR_FONT_FEATURE_ID_ONUM          NC_("STR_FONT_FEATURE_ID_ONUM", "Oldstyle Figures")
+#define STR_FONT_FEATURE_ID_OPBD          NC_("STR_FONT_FEATURE_ID_OPBD", "Optical Bounds")
+#define STR_FONT_FEATURE_ID_ORDN          NC_("STR_FONT_FEATURE_ID_ORDN", "Ordinals")
+#define STR_FONT_FEATURE_ID_ORNM          NC_("STR_FONT_FEATURE_ID_ORNM", "Ornaments")
+#define STR_FONT_FEATURE_ID_PALT          NC_("STR_FONT_FEATURE_ID_PALT", "Proportional Alternate Metrics")
+#define STR_FONT_FEATURE_ID_PCAP          NC_("STR_FONT_FEATURE_ID_PCAP", "Lowercase to Petite Capitals")
+#define STR_FONT_FEATURE_ID_PKNA          NC_("STR_FONT_FEATURE_ID_PKNA", "Proportional Kana")
+#define STR_FONT_FEATURE_ID_PNUM          NC_("STR_FONT_FEATURE_ID_PNUM", "Proportional Numbers")
+#define STR_FONT_FEATURE_ID_PREF          NC_("STR_FONT_FEATURE_ID_PREF", "Pre Base Forms")
+#define STR_FONT_FEATURE_ID_PRES          NC_("STR_FONT_FEATURE_ID_PRES", "Pre Base Substitutions")
+#define STR_FONT_FEATURE_ID_PSTF          NC_("STR_FONT_FEATURE_ID_PSTF", "Post Base Forms")
+#define STR_FONT_FEATURE_ID_PSTS          NC_("STR_FONT_FEATURE_ID_PSTS", "Post Base Substitutions")
+#define STR_FONT_FEATURE_ID_PWID          NC_("STR_FONT_FEATURE_ID_PWID", "Proportional Widths")
+#define STR_FONT_FEATURE_ID_QWID          NC_("STR_FONT_FEATURE_ID_QWID", "Quarter Widths")
+#define STR_FONT_FEATURE_ID_RAND          NC_("STR_FONT_FEATURE_ID_RAND", "Randomize")
+#define STR_FONT_FEATURE_ID_RCLT          NC_("STR_FONT_FEATURE_ID_RCLT", "Required Contextual Alternates")
+#define STR_FONT_FEATURE_ID_RKRF          NC_("STR_FONT_FEATURE_ID_RKRF", "Rakar Forms")
+#define STR_FONT_FEATURE_ID_RLIG          NC_("STR_FONT_FEATURE_ID_RLIG", "Required Ligatures")
+#define STR_FONT_FEATURE_ID_RPHF          NC_("STR_FONT_FEATURE_ID_RPHF", "Reph Forms")
+#define STR_FONT_FEATURE_ID_RTBD          NC_("STR_FONT_FEATURE_ID_RTBD", "Right Bounds")
+#define STR_FONT_FEATURE_ID_RTLA          NC_("STR_FONT_FEATURE_ID_RTLA", "Right to Left Alternates")
+#define STR_FONT_FEATURE_ID_RTLM          NC_("STR_FONT_FEATURE_ID_RTLM", "Right to Left Mirrored Forms")
+#define STR_FONT_FEATURE_ID_RUBY          NC_("STR_FONT_FEATURE_ID_RUBY", "Ruby Notation Forms")
+#define STR_FONT_FEATURE_ID_RVRN          NC_("STR_FONT_FEATURE_ID_RVRN", "Required Variation Alternates")
+#define STR_FONT_FEATURE_ID_SALT          NC_("STR_FONT_FEATURE_ID_SALT", "Stylistic Alternates")
+#define STR_FONT_FEATURE_ID_SINF          NC_("STR_FONT_FEATURE_ID_SINF", "Scientific Inferiors")
+#define STR_FONT_FEATURE_ID_SIZE          NC_("STR_FONT_FEATURE_ID_SIZE", "Optical Size")
+#define STR_FONT_FEATURE_ID_SMCP          NC_("STR_FONT_FEATURE_ID_SMCP", "Lowercase to Small Capitals")
+#define STR_FONT_FEATURE_ID_SMPL          NC_("STR_FONT_FEATURE_ID_SMPL", "Simplified Forms")
+#define STR_FONT_FEATURE_ID_SS01          NC_("STR_FONT_FEATURE_ID_SS01", "Stylistic Set 1")
+#define STR_FONT_FEATURE_ID_SS02          NC_("STR_FONT_FEATURE_ID_SS02", "Stylistic Set 2")
+#define STR_FONT_FEATURE_ID_SS03          NC_("STR_FONT_FEATURE_ID_SS03", "Stylistic Set 3")
+#define STR_FONT_FEATURE_ID_SS04          NC_("STR_FONT_FEATURE_ID_SS04", "Stylistic Set 4")
+#define STR_FONT_FEATURE_ID_SS05          NC_("STR_FONT_FEATURE_ID_SS05", "Stylistic Set 5")
+#define STR_FONT_FEATURE_ID_SS06          NC_("STR_FONT_FEATURE_ID_SS06", "Stylistic Set 6")
+#define STR_FONT_FEATURE_ID_SS07          NC_("STR_FONT_FEATURE_ID_SS07", "Stylistic Set 7")
+#define STR_FONT_FEATURE_ID_SS08          NC_("STR_FONT_FEATURE_ID_SS08", "Stylistic Set 8")
+#define STR_FONT_FEATURE_ID_SS09          NC_("STR_FONT_FEATURE_ID_SS09", "Stylistic Set 9")
+#define STR_FONT_FEATURE_ID_SS10          NC_("STR_FONT_FEATURE_ID_SS10", "Stylistic Set 10")
+#define STR_FONT_FEATURE_ID_SS11          NC_("STR_FONT_FEATURE_ID_SS11", "Stylistic Set 11")
+#define STR_FONT_FEATURE_ID_SS12          NC_("STR_FONT_FEATURE_ID_SS12", "Stylistic Set 12")
+#define STR_FONT_FEATURE_ID_SS13          NC_("STR_FONT_FEATURE_ID_SS13", "Stylistic Set 13")
+#define STR_FONT_FEATURE_ID_SS14          NC_("STR_FONT_FEATURE_ID_SS14", "Stylistic Set 14")
+#define STR_FONT_FEATURE_ID_SS15          NC_("STR_FONT_FEATURE_ID_SS15", "Stylistic Set 15")
+#define STR_FONT_FEATURE_ID_SS16          NC_("STR_FONT_FEATURE_ID_SS16", "Stylistic Set 16")
+#define STR_FONT_FEATURE_ID_SS17          NC_("STR_FONT_FEATURE_ID_SS17", "Stylistic Set 17")
+#define STR_FONT_FEATURE_ID_SS18          NC_("STR_FONT_FEATURE_ID_SS18", "Stylistic Set 18")
+#define STR_FONT_FEATURE_ID_SS19          NC_("STR_FONT_FEATURE_ID_SS19", "Stylistic Set 19")
+#define STR_FONT_FEATURE_ID_SS20          NC_("STR_FONT_FEATURE_ID_SS20", "Stylistic Set 20")
+#define STR_FONT_FEATURE_ID_SSTY          NC_("STR_FONT_FEATURE_ID_SSTY", "Script Style")
+#define STR_FONT_FEATURE_ID_STCH          NC_("STR_FONT_FEATURE_ID_STCH", "Stretching Glyph Decomposition")
+#define STR_FONT_FEATURE_ID_SUBS          NC_("STR_FONT_FEATURE_ID_SUBS", "Subscript")
+#define STR_FONT_FEATURE_ID_SUPS          NC_("STR_FONT_FEATURE_ID_SUPS", "Superscript")
+#define STR_FONT_FEATURE_ID_SWSH          NC_("STR_FONT_FEATURE_ID_SWSH", "Swash")
+#define STR_FONT_FEATURE_ID_TITL          NC_("STR_FONT_FEATURE_ID_TITL", "Titling")
+#define STR_FONT_FEATURE_ID_TJMO          NC_("STR_FONT_FEATURE_ID_TJMO", "Trailing Jamo Forms")
+#define STR_FONT_FEATURE_ID_TNAM          NC_("STR_FONT_FEATURE_ID_TNAM", "Traditional Name Forms")
+#define STR_FONT_FEATURE_ID_TNUM          NC_("STR_FONT_FEATURE_ID_TNUM", "Tabular Numbers")
+#define STR_FONT_FEATURE_ID_TRAD          NC_("STR_FONT_FEATURE_ID_TRAD", "Traditional Forms")
+#define STR_FONT_FEATURE_ID_TWID          NC_("STR_FONT_FEATURE_ID_TWID", "Third Widths")
+#define STR_FONT_FEATURE_ID_UNIC          NC_("STR_FONT_FEATURE_ID_UNIC", "Unicase")
+#define STR_FONT_FEATURE_ID_VALT          NC_("STR_FONT_FEATURE_ID_VALT", "Alternate Vertical Metrics")
+#define STR_FONT_FEATURE_ID_VATU          NC_("STR_FONT_FEATURE_ID_VATU", "Vattu Variants")
+#define STR_FONT_FEATURE_ID_VERT          NC_("STR_FONT_FEATURE_ID_VERT", "Vertical Alternatives")
+#define STR_FONT_FEATURE_ID_VHAL          NC_("STR_FONT_FEATURE_ID_VHAL", "Alternate Vertical Half Metrics")
+#define STR_FONT_FEATURE_ID_VJMO          NC_("STR_FONT_FEATURE_ID_VJMO", "Vowel Jamo Forms")
+#define STR_FONT_FEATURE_ID_VKNA          NC_("STR_FONT_FEATURE_ID_VKNA", "Vertical Kana Alternates")
+#define STR_FONT_FEATURE_ID_VKRN          NC_("STR_FONT_FEATURE_ID_VKRN", "Vertical Kerning")
+#define STR_FONT_FEATURE_ID_VPAL          NC_("STR_FONT_FEATURE_ID_VPAL", "Proportional Alternate Vertical Metrics")
+#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")
+
+#endif // INCLUDED_VCL_INC_STRINGS_HRC
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/qa/cppunit/FontFeatureTest.cxx b/vcl/qa/cppunit/FontFeatureTest.cxx
new file mode 100644
index 000000000000..0bb28e306139
--- /dev/null
+++ b/vcl/qa/cppunit/FontFeatureTest.cxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <vcl/font/Feature.hxx>
+
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+
+class FontFeatureTest : public test::BootstrapFixture
+{
+public:
+    FontFeatureTest()
+        : BootstrapFixture(true, false)
+    {
+    }
+
+    void testGetFontFeatures();
+
+    CPPUNIT_TEST_SUITE(FontFeatureTest);
+    CPPUNIT_TEST(testGetFontFeatures);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+void FontFeatureTest::testGetFontFeatures()
+{
+    ScopedVclPtrInstance<VirtualDevice> aVDev(*Application::GetDefaultDevice(),
+                                              DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
+    aVDev->SetOutputSizePixel(Size(10, 10));
+
+    vcl::Font aFont = aVDev->GetFont();
+    aFont.SetFamilyName("Linux Libertine G");
+    aVDev->SetFont(aFont);
+
+    std::vector<vcl::font::Feature> rFontFeatures;
+    CPPUNIT_ASSERT(aVDev->GetFontFeatures(rFontFeatures));
+
+    // We're interested only in defaults here
+    std::vector<vcl::font::Feature> rDefaultFontFeatures;
+    OUString aFeaturesString;
+    for (vcl::font::Feature const& rFeature : rFontFeatures)
+    {
+        if (rFeature.m_aID.m_aScriptCode == vcl::font::featureCode("DFLT")
+            && rFeature.m_aID.m_aLanguageCode == vcl::font::featureCode("dflt"))
+        {
+            rDefaultFontFeatures.push_back(rFeature);
+            aFeaturesString += vcl::font::featureCodeAsString(rFeature.m_aID.m_aFeatureCode) + " ";
+        }
+    }
+
+    CPPUNIT_ASSERT_EQUAL(size_t(31), rDefaultFontFeatures.size());
+
+    OUString aExpectedFeaturesString = "aalt c2sc case ccmp dlig fina frac hlig "
+                                       "liga lnum nalt onum pnum salt sinf smcp "
+                                       "ss01 ss02 ss03 ss04 ss05 ss06 sups tnum "
+                                       "zero cpsp kern lfbd mark mkmk rtbd ";
+    CPPUNIT_ASSERT_EQUAL(aExpectedFeaturesString, aFeaturesString);
+
+    // Check C2SC feature
+    {
+        vcl::font::Feature& rFeature = rDefaultFontFeatures[1];
+        CPPUNIT_ASSERT_EQUAL(vcl::font::featureCode("c2sc"), rFeature.m_aID.m_aFeatureCode);
+
+        vcl::font::FeatureDefinition& rFracFeatureDefinition = rFeature.m_aDefinition;
+        CPPUNIT_ASSERT_EQUAL(vcl::font::featureCode("c2sc"), rFracFeatureDefinition.getCode());
+        CPPUNIT_ASSERT(!rFracFeatureDefinition.getDescription().isEmpty());
+        CPPUNIT_ASSERT_EQUAL(vcl::font::FeatureParameterType::BOOL,
+                             rFracFeatureDefinition.getType());
+
+        CPPUNIT_ASSERT_EQUAL(size_t(0), rFracFeatureDefinition.getEnumParameters().size());
+    }
+
+    // Check FRAC feature
+    {
+        vcl::font::Feature& rFeature = rDefaultFontFeatures[6];
+        CPPUNIT_ASSERT_EQUAL(vcl::font::featureCode("frac"), rFeature.m_aID.m_aFeatureCode);
+
+        vcl::font::FeatureDefinition& rFracFeatureDefinition = rFeature.m_aDefinition;
+        CPPUNIT_ASSERT_EQUAL(vcl::font::featureCode("frac"), rFracFeatureDefinition.getCode());
+        CPPUNIT_ASSERT(!rFracFeatureDefinition.getDescription().isEmpty());
+        CPPUNIT_ASSERT_EQUAL(vcl::font::FeatureParameterType::ENUM,
+                             rFracFeatureDefinition.getType());
+
+        CPPUNIT_ASSERT_EQUAL(size_t(2), rFracFeatureDefinition.getEnumParameters().size());
+
+        vcl::font::FeatureParameter const& rParameter1
+            = rFracFeatureDefinition.getEnumParameters()[0];
+        CPPUNIT_ASSERT_EQUAL(sal_uInt32(1), rParameter1.getCode());
+        CPPUNIT_ASSERT(!rParameter1.getDescription().isEmpty());
+
+        vcl::font::FeatureParameter const& rParameter2
+            = rFracFeatureDefinition.getEnumParameters()[1];
+        CPPUNIT_ASSERT_EQUAL(sal_uInt32(2), rParameter2.getCode());
+        CPPUNIT_ASSERT(!rParameter2.getDescription().isEmpty());
+    }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(FontFeatureTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/font/Feature.cxx b/vcl/source/font/Feature.cxx
new file mode 100644
index 000000000000..1b7545b560b9
--- /dev/null
+++ b/vcl/source/font/Feature.cxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <vcl/font/Feature.hxx>
+#include <strings.hrc>
+#include <svdata.hxx>
+
+namespace vcl
+{
+namespace font
+{
+OUString featureCodeAsString(sal_uInt32 nFeature)
+{
+    std::vector<sal_Char> aString(5, 0);
+    aString[0] = sal_Char(nFeature >> 24 & 0xff);
+    aString[1] = sal_Char(nFeature >> 16 & 0xff);
+    aString[2] = sal_Char(nFeature >> 8 & 0xff);
+    aString[3] = sal_Char(nFeature >> 0 & 0xff);
+
+    return OStringToOUString(aString.data(), RTL_TEXTENCODING_ASCII_US);
+}
+
+// FeatureParameter
+
+FeatureParameter::FeatureParameter(sal_uInt32 nCode, OUString aDescription)
+    : m_nCode(nCode)
+    , m_sDescription(aDescription)
+    , m_pDescriptionID(nullptr)
+{
+}
+
+FeatureParameter::FeatureParameter(sal_uInt32 nCode, const char* pDescriptionID)
+    : m_nCode(nCode)
+    , m_pDescriptionID(pDescriptionID)
+{
+}
+
+OUString FeatureParameter::getDescription() const
+{
+    OUString aReturnString;
+
+    if (m_pDescriptionID)
+        aReturnString = VclResId(m_pDescriptionID);
+    else if (!m_sDescription.isEmpty())
+        aReturnString = m_sDescription;
+
+    return aReturnString;
+}
+
+sal_uInt32 FeatureParameter::getCode() const { return m_nCode; }
+
+// FeatureDefinition
+
+FeatureDefinition::FeatureDefinition()
+    : m_nCode(0)
+    , m_pDescriptionID(nullptr)
+    , m_eType(FeatureParameterType::BOOL)
+{
+}
+
+FeatureDefinition::FeatureDefinition(sal_uInt32 nCode, OUString const& rDescription,
+                                     FeatureParameterType eType,
+                                     std::vector<FeatureParameter> const& rEnumParameters)
+    : m_nCode(nCode)
+    , m_sDescription(rDescription)
+    , m_pDescriptionID(nullptr)
+    , m_eType(eType)
+    , m_aEnumParameters(rEnumParameters)
+{
+}
+
+FeatureDefinition::FeatureDefinition(sal_uInt32 nCode, const char* pDescriptionID)
+    : m_nCode(nCode)
+    , m_pDescriptionID(pDescriptionID)
+    , m_eType(FeatureParameterType::BOOL)
+{
+}
+
+FeatureDefinition::FeatureDefinition(sal_uInt32 nCode, const char* pDescriptionID,
+                                     std::vector<FeatureParameter> aEnumParameters)
+    : m_nCode(nCode)
+    , m_pDescriptionID(pDescriptionID)
+    , m_eType(FeatureParameterType::ENUM)
+    , m_aEnumParameters(aEnumParameters)
+{
+}
+
+const std::vector<FeatureParameter>& FeatureDefinition::getEnumParameters() const
+{
+    return m_aEnumParameters;
+}
+
+OUString FeatureDefinition::getDescription() const
+{
+    if (m_pDescriptionID)
+        return VclResId(m_pDescriptionID);
+    else if (!m_sDescription.isEmpty())
+        return m_sDescription;
+    else
+        return vcl::font::featureCodeAsString(m_nCode);
+}
+
+sal_uInt32 FeatureDefinition::getCode() const { return m_nCode; }
+
+FeatureParameterType FeatureDefinition::getType() const { return m_eType; }
+
+FeatureDefinition::operator bool() const { return m_nCode != 0; }
+
+} // end font namespace
+} // end vcl namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/font/FeatureCollector.cxx b/vcl/source/font/FeatureCollector.cxx
new file mode 100644
index 000000000000..1087a9311daf
--- /dev/null
+++ b/vcl/source/font/FeatureCollector.cxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <font/FeatureCollector.hxx>
+#include <font/OpenTypeFeatureDefinitonList.hxx>
+#include <o3tl/make_unique.hxx>
+
+#include <hb-ot.h>
+#include <hb-graphite2.h>
+
+namespace vcl
+{
+namespace font
+{
+bool FeatureCollector::collectGraphiteFeatureDefinition(vcl::font::Feature& rFeature)
+{
+    gr_face* grFace = hb_graphite2_face_get_gr_face(m_pHbFace);
+    if (grFace == nullptr)
+        return false;
+
+    bool bFound = false;
+
+    gr_uint16 nUILanguage = 0x0409;
+
+    gr_uint16 nNumberOfFeatures = gr_face_n_fref(grFace);
+    gr_feature_val* pFeatures = gr_face_featureval_for_lang(grFace, rFeature.m_aID.m_aLanguageCode);
+
+    if (pFeatures)
+    {
+        for (gr_uint16 i = 0; i < nNumberOfFeatures; ++i)
+        {
+            const gr_feature_ref* pFeatureRef = gr_face_fref(grFace, i);
+            gr_uint32 nFeatureCode = gr_fref_id(pFeatureRef);
+
+            if (nFeatureCode == rFeature.m_aID.m_aFeatureCode)
+            {
+                gr_uint32 nLabelLength = 0;
+                void* pLabel = gr_fref_label(pFeatureRef, &nUILanguage, gr_utf8, &nLabelLength);
+                OUString sLabel(OUString::createFromAscii(static_cast<char*>(pLabel)));
+                gr_label_destroy(pLabel);
+
+                std::vector<vcl::font::FeatureParameter> aParameters;
+                gr_uint16 nNumberOfValues = gr_fref_n_values(pFeatureRef);
+                for (gr_uint16 j = 0; j < nNumberOfValues; ++j)
+                {
+                    if (gr_fref_value(pFeatureRef, j))
+                    {
+                        gr_uint32 nValueLabelLength = 0;
+                        void* pValueLabel = gr_fref_value_label(pFeatureRef, j, &nUILanguage,
+                                                                gr_utf8, &nValueLabelLength);
+                        OUString sValueLabel(
+                            OUString::createFromAscii(static_cast<char*>(pValueLabel)));
+                        aParameters.emplace_back(sal_uInt32(j), sValueLabel);
+                        gr_label_destroy(pValueLabel);
+                    }
+                }
+
+                auto eFeatureParameterType = vcl::font::FeatureParameterType::ENUM;
+
+                // Check if the parameters are boolean
+                if (aParameters.size() == 1 && aParameters[0].getDescription() == "True")
+                {
+                    eFeatureParameterType = vcl::font::FeatureParameterType::BOOL;
+                    aParameters.clear();
+                }
+                rFeature.m_aDefinition = vcl::font::FeatureDefinition(
+                    rFeature.m_aID.m_aFeatureCode, sLabel, eFeatureParameterType, aParameters);
+                bFound = true;
+            }
+        }
+    }
+    gr_featureval_destroy(pFeatures);
+    return bFound;
+}
+
+void FeatureCollector::collectForLanguage(hb_tag_t aTableTag, sal_uInt32 nScript,
+                                          hb_tag_t aScriptTag, sal_uInt32 nLanguage,
+                                          hb_tag_t aLanguageTag)
+{
+    unsigned int nFeatureCount = hb_ot_layout_language_get_feature_tags(
+        m_pHbFace, aTableTag, nScript, nLanguage, 0, nullptr, nullptr);
+    std::vector<hb_tag_t> aFeatureTags(nFeatureCount);
+    hb_ot_layout_language_get_feature_tags(m_pHbFace, aTableTag, nScript, nLanguage, 0,
+                                           &nFeatureCount, aFeatureTags.data());
+    aFeatureTags.resize(nFeatureCount);
+
+    for (hb_tag_t aFeatureTag : aFeatureTags)
+    {
+        m_rFontFeatures.emplace_back();
+        vcl::font::Feature& rFeature = m_rFontFeatures.back();
+        rFeature.m_aID = { aFeatureTag, aScriptTag, aLanguageTag };
+        bool bResult = collectGraphiteFeatureDefinition(rFeature);
+        if (!bResult)
+        {
+            FeatureDefinition aDefinition
+                = OpenTypeFeatureDefinitonList::get().getDefinition(aFeatureTag);
+            if (aDefinition)
+            {
+                rFeature.m_aDefinition = vcl::font::FeatureDefinition(aDefinition);
+            }
+        }
+    }
+}
+
+void FeatureCollector::collectForScript(hb_tag_t aTableTag, sal_uInt32 nScript, hb_tag_t aScriptTag)
+{
+    collectForLanguage(aTableTag, nScript, aScriptTag, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
+                       HB_OT_TAG_DEFAULT_LANGUAGE);
+
+    unsigned int nLanguageCount
+        = hb_ot_layout_script_get_language_tags(m_pHbFace, aTableTag, nScript, 0, nullptr, nullptr);
+    std::vector<hb_tag_t> aLanguageTags(nLanguageCount);
+    hb_ot_layout_script_get_language_tags(m_pHbFace, aTableTag, nScript, 0, &nLanguageCount,
+                                          aLanguageTags.data());
+    aLanguageTags.resize(nLanguageCount);
+    for (sal_uInt32 nLanguage = 0; nLanguage < sal_uInt32(nLanguageCount); ++nLanguage)
+        collectForLanguage(aTableTag, nScript, aScriptTag, nLanguage, aLanguageTags[nLanguage]);
+}
+
+void FeatureCollector::collectForTable(hb_tag_t aTableTag)
+{
+    unsigned int nScriptCount
+        = hb_ot_layout_table_get_script_tags(m_pHbFace, aTableTag, 0, nullptr, nullptr);
+    std::vector<hb_tag_t> aScriptTags(nScriptCount);
+    hb_ot_layout_table_get_script_tags(m_pHbFace, aTableTag, 0, &nScriptCount, aScriptTags.data());
+    aScriptTags.resize(nScriptCount);
+
+    for (sal_uInt32 nScript = 0; nScript < sal_uInt32(nScriptCount); ++nScript)
+        collectForScript(aTableTag, nScript, aScriptTags[nScript]);
+}
+
+} // end namespace font
+} // end namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/font/OpenTypeFeatureDefinitonList.cxx b/vcl/source/font/OpenTypeFeatureDefinitonList.cxx
new file mode 100644
index 000000000000..17ee8268c741
--- /dev/null
+++ b/vcl/source/font/OpenTypeFeatureDefinitonList.cxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <font/OpenTypeFeatureDefinitonList.hxx>
+#include <font/OpenTypeFeatureStrings.hrc>
+#include <svdata.hxx>
+
+namespace vcl
+{
+namespace font
+{
+OpenTypeFeatureDefinitonListPrivate::OpenTypeFeatureDefinitonListPrivate() { init(); }
+
+void OpenTypeFeatureDefinitonListPrivate::init()
+{
+    m_aFeatureDefinition.assign({
+        { featureCode("aalt"), STR_FONT_FEATURE_ID_AALT },
+        { featureCode("abvf"), STR_FONT_FEATURE_ID_ABVF },
+        { featureCode("abvm"), STR_FONT_FEATURE_ID_ABVM },
+        { featureCode("abvs"), STR_FONT_FEATURE_ID_ABVS },
+        { featureCode("afrc"), STR_FONT_FEATURE_ID_AFRC },
+        { featureCode("akhn"), STR_FONT_FEATURE_ID_AKHN },
+        { featureCode("alig"), STR_FONT_FEATURE_ID_ALIG },
+        { featureCode("blwf"), STR_FONT_FEATURE_ID_BLWF },
+        { featureCode("blwm"), STR_FONT_FEATURE_ID_BLWM },
+        { featureCode("blws"), STR_FONT_FEATURE_ID_BLWS },
+        { featureCode("c2pc"), STR_FONT_FEATURE_ID_C2PC },
+        { featureCode("c2sc"), STR_FONT_FEATURE_ID_C2SC },
+        { featureCode("calt"), STR_FONT_FEATURE_ID_CALT },
+        { featureCode("case"), STR_FONT_FEATURE_ID_CASE },
+        { featureCode("ccmp"), STR_FONT_FEATURE_ID_CCMP },
+        { featureCode("cfar"), STR_FONT_FEATURE_ID_CFAR },
+        { featureCode("cjct"), STR_FONT_FEATURE_ID_CJCT },
+        { featureCode("clig"), STR_FONT_FEATURE_ID_CLIG },
+        { featureCode("cpct"), STR_FONT_FEATURE_ID_CPCT },
+        { featureCode("cpsp"), STR_FONT_FEATURE_ID_CPSP },
+        { featureCode("cswh"), STR_FONT_FEATURE_ID_CSWH },
+        { featureCode("curs"), STR_FONT_FEATURE_ID_CURS },
+        { featureCode("cv01"), STR_FONT_FEATURE_ID_CV01 },
+        { featureCode("cv02"), STR_FONT_FEATURE_ID_CV02 },
+        { featureCode("cv03"), STR_FONT_FEATURE_ID_CV03 },
+        { featureCode("cv04"), STR_FONT_FEATURE_ID_CV04 },
+        { featureCode("cv05"), STR_FONT_FEATURE_ID_CV05 },
+        { featureCode("cv06"), STR_FONT_FEATURE_ID_CV06 },
+        { featureCode("cv07"), STR_FONT_FEATURE_ID_CV07 },
+        { featureCode("cv08"), STR_FONT_FEATURE_ID_CV08 },
+        { featureCode("cv09"), STR_FONT_FEATURE_ID_CV09 },
+        { featureCode("dcap"), STR_FONT_FEATURE_ID_DCAP },
+        { featureCode("dist"), STR_FONT_FEATURE_ID_DIST },
+        { featureCode("dlig"), STR_FONT_FEATURE_ID_DLIG },
+        { featureCode("dnom"), STR_FONT_FEATURE_ID_DNOM },
+        { featureCode("dpng"), STR_FONT_FEATURE_ID_DPNG },
+        { featureCode("dtls"), STR_FONT_FEATURE_ID_DTLS },
+        { featureCode("expt"), STR_FONT_FEATURE_ID_EXPT },
+        { featureCode("falt"), STR_FONT_FEATURE_ID_FALT },
+        { featureCode("fin2"), STR_FONT_FEATURE_ID_FIN2 },
+        { featureCode("fin3"), STR_FONT_FEATURE_ID_FIN3 },
+        { featureCode("fina"), STR_FONT_FEATURE_ID_FINA },
+        { featureCode("flac"), STR_FONT_FEATURE_ID_FLAC },
+        { featureCode("frac"), STR_FONT_FEATURE_ID_FRAC,
+          std::vector<FeatureParameter>{ { 1, STR_FONT_FEATURE_ID_FRAC_PARAM_1 },
+                                         { 2, STR_FONT_FEATURE_ID_FRAC_PARAM_2 } } },
+        { featureCode("fwid"), STR_FONT_FEATURE_ID_FWID },
+        { featureCode("half"), STR_FONT_FEATURE_ID_HALF },
+        { featureCode("haln"), STR_FONT_FEATURE_ID_HALN },
+        { featureCode("halt"), STR_FONT_FEATURE_ID_HALT },
+        { featureCode("hist"), STR_FONT_FEATURE_ID_HIST },
+        { featureCode("hkna"), STR_FONT_FEATURE_ID_HKNA },
+        { featureCode("hlig"), STR_FONT_FEATURE_ID_HLIG },
+        { featureCode("hngl"), STR_FONT_FEATURE_ID_HNGL },
+        { featureCode("hojo"), STR_FONT_FEATURE_ID_HOJO },
+        { featureCode("hwid"), STR_FONT_FEATURE_ID_HWID },
+        { featureCode("init"), STR_FONT_FEATURE_ID_INIT },
+        { featureCode("isol"), STR_FONT_FEATURE_ID_ISOL },
+        { featureCode("ital"), STR_FONT_FEATURE_ID_ITAL },
+        { featureCode("jalt"), STR_FONT_FEATURE_ID_JALT },
+        { featureCode("jp78"), STR_FONT_FEATURE_ID_JP78 },
+        { featureCode("jp83"), STR_FONT_FEATURE_ID_JP83 },
+        { featureCode("jp90"), STR_FONT_FEATURE_ID_JP90 },
+        { featureCode("jp04"), STR_FONT_FEATURE_ID_JP04 },
+        { featureCode("kern"), STR_FONT_FEATURE_ID_KERN },
+        { featureCode("lfbd"), STR_FONT_FEATURE_ID_LFBD },
+        { featureCode("liga"), STR_FONT_FEATURE_ID_LIGA },
+        { featureCode("ljmo"), STR_FONT_FEATURE_ID_LJMO },
+        { featureCode("lnum"), STR_FONT_FEATURE_ID_LNUM },
+        { featureCode("locl"), STR_FONT_FEATURE_ID_LOCL },
+        { featureCode("ltra"), STR_FONT_FEATURE_ID_LTRA },
+        { featureCode("ltrm"), STR_FONT_FEATURE_ID_LTRM },
+        { featureCode("mark"), STR_FONT_FEATURE_ID_MARK },
+        { featureCode("med2"), STR_FONT_FEATURE_ID_MED2 },
+        { featureCode("medi"), STR_FONT_FEATURE_ID_MEDI },
+        { featureCode("mgrk"), STR_FONT_FEATURE_ID_MGRK },
+        { featureCode("mkmk"), STR_FONT_FEATURE_ID_MKMK },
+        { featureCode("mset"), STR_FONT_FEATURE_ID_MSET },
+        { featureCode("nalt"), STR_FONT_FEATURE_ID_NALT },
+        { featureCode("nlck"), STR_FONT_FEATURE_ID_NLCK },
+        { featureCode("nukt"), STR_FONT_FEATURE_ID_NUKT },
+        { featureCode("numr"), STR_FONT_FEATURE_ID_NUMR },
+        { featureCode("onum"), STR_FONT_FEATURE_ID_ONUM },
+        { featureCode("opbd"), STR_FONT_FEATURE_ID_OPBD },
+        { featureCode("ordn"), STR_FONT_FEATURE_ID_ORDN },
+        { featureCode("ornm"), STR_FONT_FEATURE_ID_ORNM },
+        { featureCode("palt"), STR_FONT_FEATURE_ID_PALT },
+        { featureCode("pcap"), STR_FONT_FEATURE_ID_PCAP },
+        { featureCode("pkna"), STR_FONT_FEATURE_ID_PKNA },
+        { featureCode("pnum"), STR_FONT_FEATURE_ID_PNUM },
+        { featureCode("pref"), STR_FONT_FEATURE_ID_PREF },
+        { featureCode("pres"), STR_FONT_FEATURE_ID_PRES },
+        { featureCode("pstf"), STR_FONT_FEATURE_ID_PSTF },
+        { featureCode("psts"), STR_FONT_FEATURE_ID_PSTS },
+        { featureCode("pwid"), STR_FONT_FEATURE_ID_PWID },
+        { featureCode("qwid"), STR_FONT_FEATURE_ID_QWID },
+        { featureCode("rand"), STR_FONT_FEATURE_ID_RAND },
+        { featureCode("rclt"), STR_FONT_FEATURE_ID_RCLT },
+        { featureCode("rkrf"), STR_FONT_FEATURE_ID_RKRF },
+        { featureCode("rlig"), STR_FONT_FEATURE_ID_RLIG },
+        { featureCode("rphf"), STR_FONT_FEATURE_ID_RPHF },
+        { featureCode("rtbd"), STR_FONT_FEATURE_ID_RTBD },
+        { featureCode("rtla"), STR_FONT_FEATURE_ID_RTLA },
+        { featureCode("rtlm"), STR_FONT_FEATURE_ID_RTLM },
+        { featureCode("ruby"), STR_FONT_FEATURE_ID_RUBY },
+        { featureCode("rvrn"), STR_FONT_FEATURE_ID_RVRN },
+        { featureCode("salt"), STR_FONT_FEATURE_ID_SALT },
+        { featureCode("sinf"), STR_FONT_FEATURE_ID_SINF },
+        { featureCode("size"), STR_FONT_FEATURE_ID_SIZE },
+        { featureCode("smcp"), STR_FONT_FEATURE_ID_SMCP },
+        { featureCode("smpl"), STR_FONT_FEATURE_ID_SMPL },
+        { featureCode("ss01"), STR_FONT_FEATURE_ID_SS01 },
+        { featureCode("ss02"), STR_FONT_FEATURE_ID_SS02 },
+        { featureCode("ss03"), STR_FONT_FEATURE_ID_SS03 },
+        { featureCode("ss04"), STR_FONT_FEATURE_ID_SS04 },
+        { featureCode("ss05"), STR_FONT_FEATURE_ID_SS05 },
+        { featureCode("ss06"), STR_FONT_FEATURE_ID_SS06 },
+        { featureCode("ss07"), STR_FONT_FEATURE_ID_SS07 },
+        { featureCode("ss08"), STR_FONT_FEATURE_ID_SS08 },
+        { featureCode("ss09"), STR_FONT_FEATURE_ID_SS09 },
+        { featureCode("ss10"), STR_FONT_FEATURE_ID_SS10 },
+        { featureCode("ss11"), STR_FONT_FEATURE_ID_SS11 },
+        { featureCode("ss12"), STR_FONT_FEATURE_ID_SS12 },
+        { featureCode("ss13"), STR_FONT_FEATURE_ID_SS13 },
+        { featureCode("ss14"), STR_FONT_FEATURE_ID_SS14 },
+        { featureCode("ss15"), STR_FONT_FEATURE_ID_SS15 },
+        { featureCode("ss16"), STR_FONT_FEATURE_ID_SS16 },
+        { featureCode("ss17"), STR_FONT_FEATURE_ID_SS17 },
+        { featureCode("ss18"), STR_FONT_FEATURE_ID_SS18 },
+        { featureCode("ss19"), STR_FONT_FEATURE_ID_SS19 },
+        { featureCode("ss20"), STR_FONT_FEATURE_ID_SS20 },
+        { featureCode("ssty"), STR_FONT_FEATURE_ID_SSTY },
+        { featureCode("stch"), STR_FONT_FEATURE_ID_STCH },
+        { featureCode("subs"), STR_FONT_FEATURE_ID_SUBS },
+        { featureCode("sups"), STR_FONT_FEATURE_ID_SUPS },
+        { featureCode("swsh"), STR_FONT_FEATURE_ID_SWSH },
+        { featureCode("titl"), STR_FONT_FEATURE_ID_TITL },
+        { featureCode("tjmo"), STR_FONT_FEATURE_ID_TJMO },
+        { featureCode("tnam"), STR_FONT_FEATURE_ID_TNAM },
+        { featureCode("tnum"), STR_FONT_FEATURE_ID_TNUM },
+        { featureCode("trad"), STR_FONT_FEATURE_ID_TRAD },
+        { featureCode("twid"), STR_FONT_FEATURE_ID_TWID },
+        { featureCode("unic"), STR_FONT_FEATURE_ID_UNIC },
+        { featureCode("valt"), STR_FONT_FEATURE_ID_VALT },
+        { featureCode("vatu"), STR_FONT_FEATURE_ID_VATU },
+        { featureCode("vert"), STR_FONT_FEATURE_ID_VERT },
+        { featureCode("vhal"), STR_FONT_FEATURE_ID_VHAL },
+        { featureCode("vjmo"), STR_FONT_FEATURE_ID_VJMO },
+        { featureCode("vkna"), STR_FONT_FEATURE_ID_VKNA },
+        { featureCode("vkrn"), STR_FONT_FEATURE_ID_VKRN },
+        { featureCode("vpal"), STR_FONT_FEATURE_ID_VPAL },
+        { featureCode("vrt2"), STR_FONT_FEATURE_ID_VRT2 },
+        { featureCode("vrtr"), STR_FONT_FEATURE_ID_VRTR },
+        { featureCode("zero"), STR_FONT_FEATURE_ID_ZERO },
+    });
+
+    for (size_t i = 0; i < m_aFeatureDefinition.size(); ++i)
+    {
+        m_aCodeToIndex.emplace(m_aFeatureDefinition[i].getCode(), i);
+    }
+}
+
+FeatureDefinition OpenTypeFeatureDefinitonListPrivate::getDefinition(sal_uInt32 nFeatureCode)
+{
+    if (m_aCodeToIndex.find(nFeatureCode) != m_aCodeToIndex.end())
+    {
+        size_t nIndex = m_aCodeToIndex.at(nFeatureCode);
+        return m_aFeatureDefinition[nIndex];
+    }
+    return FeatureDefinition();
+}
+
+} // end font namespace
+} // end vcl namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx
index 439a3df75c5b..2f9a7f7df570 100644
--- a/vcl/source/outdev/font.cxx
+++ b/vcl/source/outdev/font.cxx
@@ -24,6 +24,7 @@
 #include <vcl/print.hxx>
 #include <vcl/sysdata.hxx>
 #include <vcl/fontcharmap.hxx>
+#include <font/FeatureCollector.hxx>
 
 #include <sallayout.hxx>
 #include <salgdi.hxx>
@@ -162,6 +163,39 @@ bool OutputDevice::AddTempDevFont( const OUString& rFileURL, const OUString& rFo
     return true;
 }
 
+bool OutputDevice::GetFontFeatures(std::vector<vcl::font::Feature>& rFontFeatures) const
+{
+    if (mbNewFont)
+        ImplNewFont();
+
+    if (mbInitFont)
+        InitFont();
+
+    if (!mpFontInstance)
+        return false;
+
+    LogicalFontInstance* pFontInstance = mpFontInstance;
+
+    if (!pFontInstance)
+        return false;
+
+    hb_font_t* pHbFont = pFontInstance->GetHbFont();
+
+    if (!pHbFont)
+        return false;
+
+    hb_face_t* pHbFace = hb_font_get_face(pHbFont);
+
+    if (!pHbFace)
+        return false;
+
+    vcl::font::FeatureCollector aFeatureCollector(pHbFace, rFontFeatures);
+    aFeatureCollector.collectForTable(HB_OT_TAG_GSUB); // substitution
+    aFeatureCollector.collectForTable(HB_OT_TAG_GPOS); // positioning
+
+    return true;
+}
+
 FontMetric OutputDevice::GetFontMetric() const
 {
     FontMetric aMetric;


More information about the Libreoffice-commits mailing list