From 477b3109c75c52357ce2376ea3f7d3923892d049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 23 Apr 2024 17:08:17 -0700 Subject: [PATCH] Text: add queries for and search by glyph names to AbstractFont. --- doc/changelog.dox | 3 + src/Magnum/Text/AbstractFont.cpp | 20 ++++ src/Magnum/Text/AbstractFont.h | 48 +++++++- src/Magnum/Text/Test/AbstractFontTest.cpp | 134 ++++++++++++++++++++++ 4 files changed, 204 insertions(+), 1 deletion(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 1a3d16ff2..6246b0fa8 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -840,6 +840,9 @@ See also: - New @ref Text::AbstractFont::fillGlyphCache() overload taking a list of glyph IDs instead of a UTF-8 string to allow filling the glyph cache with glyphs that don't directly map to Unicode +- New @ref Text::AbstractFont::glyphName() and + @relativeref{Text::AbstractFont,glyphForName()} APIs for querying glyph + names and retrieving IDs for particular glyph names. - @ref Text::AbstractFont::fillGlyphCache() now returns a @cpp bool @ce to allow font plugin implementations to gracefully report failures diff --git a/src/Magnum/Text/AbstractFont.cpp b/src/Magnum/Text/AbstractFont.cpp index 692c21f84..78f6467e3 100644 --- a/src/Magnum/Text/AbstractFont.cpp +++ b/src/Magnum/Text/AbstractFont.cpp @@ -276,6 +276,26 @@ void AbstractFont::glyphIdsInto(const Containers::StridedArrayView1D& characters, const Containers::StridedArrayView1D& glyphs); + /** + * @brief Glyph name + * @m_since_latest + * + * Returns a name of the glyph in the font file, if present and + * supported by the implementation, or an empty string. Expects that a + * font is opened and @p glyph is less than @ref glyphCount(). + * @see @ref glyphForName() + */ + Containers::String glyphName(UnsignedInt glyph); + + /** + * @brief Glyph for given name + * @m_since_latest + * + * If the implementation supports querying glyphs by name and the name + * exists, returns a corresponding glyph ID, otherwise returns + * @cpp 0 @ce for an invalid glyph. Note that certain named glyphs can + * also map to glyph @cpp 0 @ce, in particular @cpp ".notdef" @ce in + * TTF and OTF fonts, the way to distinguish them is to ask for name of + * the zero glyph and compare. The returned index is guaranteed to be + * less than @ref glyphCount(). + */ + UnsignedInt glyphForName(Containers::StringView name); + /** * @brief Glyph size in pixels * @param glyph Glyph ID @@ -686,6 +715,23 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { */ virtual void doGlyphIdsInto(const Containers::StridedArrayView1D& characters, const Containers::StridedArrayView1D& glyphs) = 0; + /** + * @brief Implementation for @ref glyphName() + * @m_since_latest + * + * Default implementation returns an empty string. + */ + virtual Containers::String doGlyphName(UnsignedInt glyph); + + /** + * @brief Implementation for @ref glyphForName() + * @m_since_latest + * + * The implementation is expected to return a value smaller than + * @ref glyphCount(). Default implementation returns @cpp 0 @ce. + */ + virtual UnsignedInt doGlyphForName(Containers::StringView name); + /** * @brief Implementation for @ref glyphSize() * @m_since_latest diff --git a/src/Magnum/Text/Test/AbstractFontTest.cpp b/src/Magnum/Text/Test/AbstractFontTest.cpp index 0d31ee63d..cc0a948e2 100644 --- a/src/Magnum/Text/Test/AbstractFontTest.cpp +++ b/src/Magnum/Text/Test/AbstractFontTest.cpp @@ -86,6 +86,11 @@ struct AbstractFontTest: TestSuite::Tester { void glyphIdInvalidSize(); void glyphIdOutOfRange(); + void glyphName(); + void glyphNameNotImplemented(); + void glyphNameNoFont(); + void glyphNameOutOfRange(); + void glyphSizeAdvance(); void glyphSizeAdvanceNoFont(); void glyphSizeAdvanceOutOfRange(); @@ -155,6 +160,11 @@ AbstractFontTest::AbstractFontTest() { &AbstractFontTest::glyphIdInvalidSize, &AbstractFontTest::glyphIdOutOfRange, + &AbstractFontTest::glyphName, + &AbstractFontTest::glyphNameNotImplemented, + &AbstractFontTest::glyphNameNoFont, + &AbstractFontTest::glyphNameOutOfRange, + &AbstractFontTest::glyphSizeAdvance, &AbstractFontTest::glyphSizeAdvanceNoFont, &AbstractFontTest::glyphSizeAdvanceOutOfRange, @@ -938,6 +948,130 @@ void AbstractFontTest::glyphIdOutOfRange() { CORRADE_COMPARE(out.str(), "Text::AbstractFont::glyphIdsInto(): implementation-returned index 4 for character U+2345 out of range for 4 glyphs\n"); } +void AbstractFontTest::glyphName() { + struct MyFont: AbstractFont { + FontFeatures doFeatures() const override { + return FontFeature::OpenData; + } + bool doIsOpened() const override { return _opened; } + void doClose() override {} + + Properties doOpenData(Containers::ArrayView, Float) override { + _opened = true; + return {0.0f, 0.0f, 0.0f, 0.0f, 4}; + } + + void doGlyphIdsInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) override {} + Containers::String doGlyphName(UnsignedInt glyph) override { + return glyph == 3 ? "WHATEVER" : ""; + } + UnsignedInt doGlyphForName(Containers::StringView name) override { + return name == "whatever" ? 3 : 0; + } + Vector2 doGlyphSize(UnsignedInt) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doCreateShaper() override { return {}; } + + private: + bool _opened = false; + } font; + + /* Have to explicitly open in order to make glyphCount() non-zero */ + CORRADE_VERIFY(font.openData(nullptr, 0.0f)); + + CORRADE_COMPARE(font.glyphName(3), "WHATEVER"); + CORRADE_COMPARE(font.glyphForName("whatever"), 3); +} + +void AbstractFontTest::glyphNameNotImplemented() { + struct MyFont: AbstractFont { + FontFeatures doFeatures() const override { + return FontFeature::OpenData; + } + bool doIsOpened() const override { return _opened; } + void doClose() override {} + + Properties doOpenData(Containers::ArrayView, Float) override { + _opened = true; + return {0.0f, 0.0f, 0.0f, 0.0f, 4}; + } + + void doGlyphIdsInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) override {} + Vector2 doGlyphSize(UnsignedInt) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doCreateShaper() override { return {}; } + + private: + bool _opened = false; + } font; + + /* Have to explicitly open in order to make glyphCount() non-zero */ + CORRADE_VERIFY(font.openData(nullptr, 0.0f)); + + CORRADE_COMPARE(font.glyphName(3), ""); + CORRADE_COMPARE(font.glyphForName("whatever"), 0); +} + +void AbstractFontTest::glyphNameNoFont() { + CORRADE_SKIP_IF_NO_ASSERT(); + + struct MyFont: AbstractFont { + FontFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + void doGlyphIdsInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) override {} + Vector2 doGlyphSize(UnsignedInt) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doCreateShaper() override { return {}; } + } font; + + std::ostringstream out; + Error redirectError{&out}; + font.glyphName(0); + font.glyphForName(""); + CORRADE_COMPARE(out.str(), + "Text::AbstractFont::glyphName(): no font opened\n" + "Text::AbstractFont::glyphForName(): no font opened\n"); +} + +void AbstractFontTest::glyphNameOutOfRange() { + CORRADE_SKIP_IF_NO_ASSERT(); + + struct MyFont: AbstractFont { + FontFeatures doFeatures() const override { + return FontFeature::OpenData; + } + bool doIsOpened() const override { return _opened; } + void doClose() override {} + + Properties doOpenData(Containers::ArrayView, Float) override { + _opened = true; + return {0.0f, 0.0f, 0.0f, 0.0f, 4}; + } + + void doGlyphIdsInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) override {} + UnsignedInt doGlyphForName(Containers::StringView) override { return 4; } + Vector2 doGlyphSize(UnsignedInt) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doCreateShaper() override { return {}; } + + private: + bool _opened = false; + } font; + + /* Have to explicitly open in order to make glyphCount() non-zero */ + CORRADE_VERIFY(font.openData(nullptr, 0.0f)); + + std::ostringstream out; + Error redirectError{&out}; + font.glyphName(4); + font.glyphForName(""); + CORRADE_COMPARE(out.str(), + "Text::AbstractFont::glyphName(): index 4 out of range for 4 glyphs\n" + "Text::AbstractFont::glyphForName(): implementation-returned index 4 out of range for 4 glyphs\n"); +} + void AbstractFontTest::glyphSizeAdvance() { struct MyFont: AbstractFont { FontFeatures doFeatures() const override { return FontFeature::OpenData; }