summaryrefslogtreecommitdiffstats
path: root/vcl/source/font/FeatureCollector.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/source/font/FeatureCollector.cxx206
1 files changed, 206 insertions, 0 deletions
diff --git a/vcl/source/font/FeatureCollector.cxx b/vcl/source/font/FeatureCollector.cxx
new file mode 100644
index 0000000000..fd175eade0
--- /dev/null
+++ b/vcl/source/font/FeatureCollector.cxx
@@ -0,0 +1,206 @@
+/* -*- 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/OpenTypeFeatureDefinitionList.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <font/OpenTypeFeatureStrings.hrc>
+#include <svdata.hxx>
+
+#include <hb-ot.h>
+#include <hb-graphite2.h>
+
+namespace vcl::font
+{
+bool FeatureCollector::collectGraphite()
+{
+ gr_face* grFace = hb_graphite2_face_get_gr_face(m_pHbFace);
+
+ if (grFace == nullptr)
+ return false;
+
+ gr_uint16 nUILanguage = gr_uint16(m_rLanguageTag.getLanguageType());
+
+ gr_uint16 nNumberOfFeatures = gr_face_n_fref(grFace);
+ gr_feature_val* pfeatureValues
+ = gr_face_featureval_for_lang(grFace, 0); // shame we don't know which lang
+
+ 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 == 0) // illegal feature code - skip
+ continue;
+
+ gr_uint16 nValue = gr_fref_feature_value(pFeatureRef, pfeatureValues);
+ 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);
+
+ if (nNumberOfValues > 0)
+ {
+ for (gr_uint16 j = 0; j < nNumberOfValues; ++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)));
+ gr_uint16 nParamValue = gr_fref_value(pFeatureRef, j);
+ aParameters.emplace_back(sal_uInt32(nParamValue), sValueLabel);
+ gr_label_destroy(pValueLabel);
+ }
+
+ auto eFeatureParameterType = vcl::font::FeatureParameterType::ENUM;
+
+ // Check if the parameters are boolean
+ if (aParameters.size() == 2
+ && (aParameters[0].getDescription() == "True"
+ || aParameters[0].getDescription() == "False"))
+ {
+ eFeatureParameterType = vcl::font::FeatureParameterType::BOOL;
+ aParameters.clear();
+ }
+
+ m_rFontFeatures.emplace_back(nFeatureCode, vcl::font::FeatureType::Graphite);
+ vcl::font::Feature& rFeature = m_rFontFeatures.back();
+ rFeature.m_aDefinition
+ = vcl::font::FeatureDefinition(nFeatureCode, sLabel, eFeatureParameterType,
+ std::move(aParameters), int32_t(nValue));
+ }
+ }
+ gr_featureval_destroy(pfeatureValues);
+ return true;
+}
+
+void FeatureCollector::collectForTable(hb_tag_t aTableTag)
+{
+ unsigned int nFeatureCount
+ = hb_ot_layout_table_get_feature_tags(m_pHbFace, aTableTag, 0, nullptr, nullptr);
+ std::vector<hb_tag_t> aFeatureTags(nFeatureCount);
+ hb_ot_layout_table_get_feature_tags(m_pHbFace, aTableTag, 0, &nFeatureCount,
+ aFeatureTags.data());
+ aFeatureTags.resize(nFeatureCount);
+
+ for (hb_tag_t aFeatureTag : aFeatureTags)
+ {
+ if (OpenTypeFeatureDefinitionList().isRequired(aFeatureTag))
+ continue;
+
+ m_rFontFeatures.emplace_back();
+ vcl::font::Feature& rFeature = m_rFontFeatures.back();
+ rFeature.m_nCode = aFeatureTag;
+
+ FeatureDefinition aDefinition = OpenTypeFeatureDefinitionList().getDefinition(rFeature);
+ 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))
+ {
+ // 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, &nNamedParameters,
+ &aFirstParameterID))
+ {
+ OUString sLabel = m_pFace->GetName(NameID(aLabelID), m_rLanguageTag);
+ 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 = m_pFace->GetName(NameID(aNameID), m_rLanguageTag);
+ if (!sName.isEmpty())
+ aParameters.emplace_back(uint32_t(i + 1), sName);
+ else
+ aParameters.emplace_back(uint32_t(i + 1), OUString::number(i + 1));
+ }
+ }
+
+ unsigned int nAlternates = 0;
+ if (aTableTag == HB_OT_TAG_GSUB)
+ {
+ // 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());
+
+ hb_set_t* pGlyphs = hb_set_create();
+ for (unsigned int nLookupIdx : aLookups)
+ {
+ hb_set_clear(pGlyphs);
+ hb_ot_layout_lookup_collect_glyphs(m_pHbFace, aTableTag, nLookupIdx, nullptr,
+ pGlyphs, nullptr, nullptr);
+ hb_codepoint_t nGlyphIdx = HB_SET_VALUE_INVALID;
+ while (hb_set_next(pGlyphs, &nGlyphIdx))
+ {
+ nAlternates
+ = std::max(nAlternates,
+ hb_ot_layout_lookup_get_glyph_alternates(
+ m_pHbFace, nLookupIdx, nGlyphIdx, 0, nullptr, nullptr));
+ }
+ }
+ hb_set_destroy(pGlyphs);
+ }
+
+ // 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)
+ rFeature.m_aDefinition = aDefinition;
+ }
+}
+
+bool FeatureCollector::collect()
+{
+ gr_face* grFace = hb_graphite2_face_get_gr_face(m_pHbFace);
+
+ if (grFace)
+ {
+ return collectGraphite();
+ }
+ else
+ {
+ collectForTable(HB_OT_TAG_GSUB); // substitution
+ collectForTable(HB_OT_TAG_GPOS); // positioning
+ return true;
+ }
+}
+
+} // end namespace vcl::font
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */