Browse Source

Text: reworked AbstractFont plugin interface.

Implementation is moved into `do*()` private virtual functions, public
interface is doing additional sanity checks around them. Opening files
is by default done in base implementation, which loads the files into
memory and then calls function for opening raw data.

Added interface for opening multi-file fonts from raw data, default
implementation just calls single-file implementation.

Function for creating glyph cache converts the text from UTF-8 to UTF-32
and then calls the implementation, removing the burden from
reimplementing this in each plugin.

Added unit test for file and single data opening, bumped plugin version
to 0.2.
pull/278/head
Vladimír Vondruš 13 years ago
parent
commit
d655352485
  1. 96
      src/Text/AbstractFont.cpp
  2. 128
      src/Text/AbstractFont.h
  3. 4
      src/Text/CMakeLists.txt
  4. 118
      src/Text/Test/AbstractFontTest.cpp
  5. 30
      src/Text/Test/CMakeLists.txt
  6. 1
      src/Text/Test/data.bin
  7. 25
      src/Text/Test/testConfigure.h.cmake

96
src/Text/AbstractFont.cpp

@ -22,7 +22,11 @@
DEALINGS IN THE SOFTWARE.
*/
#include "Text/AbstractFont.h"
#include "AbstractFont.h"
#include <fstream>
#include <Containers/Array.h>
#include <Utility/Unicode.h>
namespace Magnum { namespace Text {
@ -30,6 +34,96 @@ AbstractFont::AbstractFont(): _size(0.0f) {}
AbstractFont::AbstractFont(PluginManager::AbstractManager* manager, std::string plugin): AbstractPlugin(manager, std::move(plugin)), _size(0.0f) {}
bool AbstractFont::openData(const std::vector<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& data, const Float size) {
CORRADE_ASSERT(features() & Feature::OpenData,
"Text::AbstractFont::openData(): feature not supported", false);
CORRADE_ASSERT(!data.empty(),
"Text::AbstractFont::openData(): no data passed", false);
close();
doOpenData(data, size);
return isOpened();
}
void AbstractFont::doOpenData(const std::vector<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& data, const Float size) {
CORRADE_ASSERT(!(features() & Feature::MultiFile),
"Text::AbstractFont::openData(): feature advertised but not implemented", );
CORRADE_ASSERT(data.size() == 1,
"Text::AbstractFont::openData(): expected just one file for single-file format", );
close();
doOpenSingleData(data[0].second, size);
}
bool AbstractFont::openSingleData(const Containers::ArrayReference<const unsigned char> data, const Float size) {
CORRADE_ASSERT(features() & Feature::OpenData,
"Text::AbstractFont::openSingleData(): feature not supported", false);
CORRADE_ASSERT(!(features() & Feature::MultiFile),
"Text::AbstractFont::openSingleData(): the format is not single-file", false);
close();
doOpenSingleData(data, size);
return isOpened();
}
void AbstractFont::doOpenSingleData(Containers::ArrayReference<const unsigned char>, Float) {
CORRADE_ASSERT(false, "Text::AbstractFont::openSingleData(): feature advertised but not implemented", );
}
bool AbstractFont::openFile(const std::string& filename, const Float size) {
close();
doOpenFile(filename, size);
return isOpened();
}
void AbstractFont::doOpenFile(const std::string& filename, const Float size) {
CORRADE_ASSERT(features() & Feature::OpenData && !(features() & Feature::MultiFile),
"Text::AbstractFont::openFile(): not implemented", );
/* Open file */
std::ifstream in(filename.data(), std::ios::binary);
if(!in.good()) {
Error() << "Trade::AbstractFont::openFile(): cannot open file" << filename;
return;
}
/* Create array to hold file contents */
in.seekg(0, std::ios::end);
Containers::Array<unsigned char> data(in.tellg());
/* Read data, close */
in.seekg(0, std::ios::beg);
in.read(reinterpret_cast<char*>(data.begin()), data.size());
in.close();
doOpenSingleData(data, size);
}
void AbstractFont::close() {
if(isOpened()) doClose();
}
void AbstractFont::createGlyphCache(GlyphCache* const cache, const std::string& characters) {
CORRADE_ASSERT(isOpened(), "Text::AbstractFont::createGlyphCache(): no font opened", );
/* Get glyph codes from characters */
std::u32string unicodeCharacters;
unicodeCharacters.reserve(characters.size()+1);
for(std::size_t i = 0; i != characters.size(); ) {
char32_t unicode;
std::tie(unicode, i) = Utility::Unicode::nextChar(characters, i);
unicodeCharacters.push_back(unicode);
}
doCreateGlyphCache(cache, unicodeCharacters);
}
AbstractLayouter* AbstractFont::layout(const GlyphCache* const cache, const Float size, const std::string& text) {
CORRADE_ASSERT(isOpened(), "Text::AbstractFont::layout(): no font opened", nullptr);
return doLayout(cache, size, text);
}
AbstractLayouter::AbstractLayouter(): _glyphCount(0) {}
AbstractLayouter::~AbstractLayouter() {}

128
src/Text/AbstractFont.h

@ -50,41 +50,93 @@ information. See TextRenderer for information about text rendering.
@section AbstractFont-subclassing Subclassing
Plugin implements functions open(), close(), createGlyphCache() and layout().
Plugin implements doFeatures(), doClose(), doCreateGlyphCache(), doLayout() and
one or more of `doOpen*()` functions.
You don't need to do most of the redundant sanity checks, these things are
checked by the implementation:
- Functions doOpenData(), doOpenSingleData() and doOpenFile() are called
after the previous file was closed, function doClose() is called only if
there is any file opened.
- Functions doOpenData() and doOpenSingleData() are called only if
@ref Feature "Feature::OpenData" is supported.
- All `do*()` implementations working on opened file are called only if
there is any file opened.
*/
class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin {
CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Text.AbstractFont/0.1")
CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Text.AbstractFont/0.2")
public:
/**
* @brief Features supported by this importer
*
* @see Features, features()
*/
enum class Feature: UnsignedByte {
/** Opening fonts from raw data using openData() */
OpenData = 1 << 0,
/**
* The format is multi-file, thus openSingleData() convenience
* function cannot be used.
*/
MultiFile = 1 << 1
};
/** @brief Set of features supported by this importer */
typedef Containers::EnumSet<Feature, UnsignedByte> Features;
/** @brief Default constructor */
explicit AbstractFont();
/** @brief Plugin manager constructor */
explicit AbstractFont(PluginManager::AbstractManager* manager, std::string plugin);
/** @brief Features supported by this font */
Features features() const { return doFeatures(); }
/** @brief Whether any file is opened */
bool isOpened() const { return doIsOpened(); }
/**
* @brief Open font from file
* @param filename Font file
* @brief Open font from raw data
* @param data Pairs of filename and file data
* @param size Font size
*
* Closes previous file, if it was opened, and tries to open given
* file. Available only if @ref Feature "Feature::OpenData" is
* supported. Returns `true` on success, `false` otherwise.
*/
bool openData(const std::vector<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& data, Float size);
/**
* @brief Open font from single data
* @param data File data
* @param size Font size
*
* Closes previous file, if it was opened, and tries to open given
* file. Returns `true` on success, `false` otherwise.
* file. Available only if @ref Feature "Feature::OpenData" is
* supported and the plugin doesn't have @ref Feature "Feature::MultiFile".
* Returns `true` on success, `false` otherwise.
*/
virtual bool open(const std::string& filename, Float size) = 0;
bool openSingleData(Containers::ArrayReference<const unsigned char> data, Float size);
/**
* @brief Open font from memory
* @param data Font data
* @param dataSize Font data size
* @brief Open font from file
* @param filename Font file
* @param size Font size
*
* Closes previous file, if it was opened, and tries to open given
* file. Returns `true` on success, `false` otherwise.
* file. If the plugin has @ref Feature "Feature::MultiFile", the
* function will use additional files in given path, all sharing common
* basename derived from @p filename. Returns `true` on success,
* `false` otherwise.
*/
virtual bool open(const unsigned char* data, std::size_t dataSize, Float size) = 0;
bool openFile(const std::string& filename, Float size);
/** @brief Close font */
virtual void close() = 0;
void close();
/** @brief Font size */
Float size() const { return _size; }
@ -96,17 +148,17 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin {
*
* Fills the cache with given characters.
*/
virtual void createGlyphCache(GlyphCache* cache, const std::string& characters) = 0;
void createGlyphCache(GlyphCache* cache, const std::string& characters);
/**
* @brief Layout the text using font own layouter
* @brief Layout the text using font's own layouter
* @param cache Glyph cache
* @param size Font size
* @param text %Text to layout
*
* @see createGlyphCache()
*/
virtual AbstractLayouter* layout(const GlyphCache* cache, Float size, const std::string& text) = 0;
AbstractLayouter* layout(const GlyphCache* cache, Float size, const std::string& text);
#ifdef DOXYGEN_GENERATING_OUTPUT
private:
@ -114,6 +166,52 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin {
protected:
#endif
Float _size;
#ifdef DOXYGEN_GENERATING_OUTPUT
protected:
#else
private:
#endif
/** @brief Implementation for features() */
virtual Features doFeatures() const = 0;
/** @brief Implementation for isOpened() */
virtual bool doIsOpened() const = 0;
/**
* @brief Implementation for openData()
*
* If the plugin doesn't have @ref Feature "Feature::MultiFile",
* default implementation calls doOpenSingleData().
*/
virtual void doOpenData(const std::vector<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& data, Float size);
/** @brief Implementation for openSingleData() */
virtual void doOpenSingleData(Containers::ArrayReference<const unsigned char> data, Float size);
/**
* @brief Implementation for openFile()
*
* If @ref Feature "Feature::OpenData" is supported and the plugin
* doesn't have @ref Feature "Feature::MultiFile", default
* implementation opens the file and calls doOpenSingleData() with its
* contents.
*/
virtual void doOpenFile(const std::string& filename, Float size);
/** @brief Implementation for close() */
virtual void doClose() = 0;
/**
* @brief Implementation for createGlyphCache()
*
* The string is converted from UTF-8 to UTF-32, unique characters are
* *not* removed.
*/
virtual void doCreateGlyphCache(GlyphCache* cache, const std::u32string& characters) = 0;
/** @brief Implementation for layout() */
virtual AbstractLayouter* doLayout(const GlyphCache* cache, Float size, const std::string& text) = 0;
};
/**

4
src/Text/CMakeLists.txt

@ -45,3 +45,7 @@ target_link_libraries(MagnumText Magnum MagnumTextureTools)
install(TARGETS MagnumText DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR})
install(FILES ${MagnumText_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Text)
if(BUILD_TESTS)
add_subdirectory(Test)
endif()

118
src/Text/Test/AbstractFontTest.cpp

@ -0,0 +1,118 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Containers/Array.h>
#include <TestSuite/Tester.h>
#include <Utility/Directory.h>
#include "Text/AbstractFont.h"
#include "testConfigure.h"
namespace Magnum { namespace Text { namespace Test {
class AbstractFontTest: public TestSuite::Tester {
public:
explicit AbstractFontTest();
void openSingleData();
void openFile();
void createGlyphCache();
};
AbstractFontTest::AbstractFontTest() {
addTests({&AbstractFontTest::openSingleData,
&AbstractFontTest::openFile,
&AbstractFontTest::createGlyphCache});
}
namespace {
class SingleDataFont: public Text::AbstractFont {
public:
Features doFeatures() const override { return Feature::OpenData; }
bool doIsOpened() const override { return opened; }
void doClose() override {}
void doOpenSingleData(const Containers::ArrayReference<const unsigned char> data, Float) override {
opened = (data.size() == 1 && data[0] == 0xa5);
}
void doCreateGlyphCache(GlyphCache*, const std::u32string&) override {}
AbstractLayouter* doLayout(const GlyphCache*, Float, const std::string&) {
return nullptr;
}
bool opened = false;
};
}
void AbstractFontTest::openSingleData() {
/* doOpenData() should call doOpenSingleData() */
SingleDataFont font;
const unsigned char data[] = {0xa5};
CORRADE_VERIFY(!font.isOpened());
font.openData({{{}, data}}, 3.0f);
CORRADE_VERIFY(font.isOpened());
}
void AbstractFontTest::openFile() {
/* doOpenFile() should call doOpenSingleData() */
SingleDataFont font;
CORRADE_VERIFY(!font.isOpened());
font.openFile(Utility::Directory::join(TEXT_TEST_DIR, "data.bin"), 3.0f);
CORRADE_VERIFY(font.isOpened());
}
void AbstractFontTest::createGlyphCache() {
class CachingFont: public Text::AbstractFont {
public:
std::u32string cacheCharacters;
private:
Features doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
void doCreateGlyphCache(GlyphCache*, const std::u32string& characters) override {
cacheCharacters = characters;
}
AbstractLayouter* doLayout(const GlyphCache*, Float, const std::string&) override {
return nullptr;
}
};
CachingFont font;
font.createGlyphCache(nullptr, "žluťoučký kůň");
CORRADE_COMPARE(font.cacheCharacters, U"\u017Elu\u0165ou\u010Dk\u00FD k\u016F\u0148");
}
}}}
CORRADE_TEST_MAIN(Magnum::Text::Test::AbstractFontTest)

30
src/Text/Test/CMakeLists.txt

@ -0,0 +1,30 @@
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testConfigure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/testConfigure.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
corrade_add_test(TextAbstractFontTest AbstractFontTest.cpp LIBRARIES Magnum MagnumText)

1
src/Text/Test/data.bin

@ -0,0 +1 @@
<EFBFBD>

25
src/Text/Test/testConfigure.h.cmake

@ -0,0 +1,25 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#define TEXT_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
Loading…
Cancel
Save