From d6a64cca2a4de3f5212ffe1e8d51633194c14db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 Jun 2013 18:05:46 +0200 Subject: [PATCH] Text: interface for font converters. Provides a way to convert font into different format (either with or without contents of associated glyph cache) or import/export glyph cache (i.e. to avoid recreating it from scratch every time). --- modules/FindMagnum.cmake | 7 + src/Text/AbstractFontConverter.cpp | 216 ++++++++++++++ src/Text/AbstractFontConverter.h | 313 ++++++++++++++++++++ src/Text/CMakeLists.txt | 2 + src/Text/Test/AbstractFontConverterTest.cpp | 202 +++++++++++++ src/Text/Test/CMakeLists.txt | 1 + src/Text/Test/testConfigure.h.cmake | 1 + src/Text/Text.h | 1 + 8 files changed, 743 insertions(+) create mode 100644 src/Text/AbstractFontConverter.cpp create mode 100644 src/Text/AbstractFontConverter.h create mode 100644 src/Text/Test/AbstractFontConverterTest.cpp diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 9b72c8fec..a3b33eb85 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -8,6 +8,7 @@ # MAGNUM_INCLUDE_DIRS - Root include dir and include dirs of # dependencies # MAGNUM_PLUGINS_FONT_DIR - Directory with font plugins +# MAGNUM_PLUGINS_FONTCONVERTER_DIR - Directory with font converter plugins # MAGNUM_PLUGINS_IMAGECONVERTER_DIR - Directory with image converter plugins # MAGNUM_PLUGINS_IMPORTER_DIR - Directory with importer plugins # This command will try to find only the base library, not the optional @@ -63,6 +64,8 @@ # MAGNUM_PLUGINS_INSTALL_DIR - Plugin installation directory # MAGNUM_PLUGINS_FONT_INSTALL_DIR - Font plugin installation # directory +# MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR - Font converter plugin +# installation directory # MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR - Image converter plugin # installation directory # MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR - Importer plugin installation @@ -318,6 +321,7 @@ endif() set(MAGNUM_LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) set(MAGNUM_PLUGINS_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum) set(MAGNUM_PLUGINS_FONT_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/importers) set(MAGNUM_CMAKE_MODULE_INSTALL_DIR ${CMAKE_ROOT}/Modules) @@ -329,6 +333,7 @@ mark_as_advanced(FORCE MAGNUM_LIBRARY_INSTALL_DIR MAGNUM_PLUGINS_INSTALL_DIR MAGNUM_PLUGINS_FONT_INSTALL_DIR + MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR MAGNUM_CMAKE_MODULE_INSTALL_DIR @@ -338,10 +343,12 @@ mark_as_advanced(FORCE # Plugin directories if(NOT WIN32) set(MAGNUM_PLUGINS_FONT_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fonts) + set(MAGNUM_PLUGINS_FONTCONVERTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMPORTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/importers) else() set(MAGNUM_PLUGINS_FONT_DIR fonts) + set(MAGNUM_PLUGINS_FONTCONVERTER_DIR fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR imageconverters) set(MAGNUM_PLUGINS_IMPORTER_DIR importers) endif() diff --git a/src/Text/AbstractFontConverter.cpp b/src/Text/AbstractFontConverter.cpp new file mode 100644 index 000000000..ebd8cca31 --- /dev/null +++ b/src/Text/AbstractFontConverter.cpp @@ -0,0 +1,216 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + 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 +#include +#include +#include + +#include "AbstractFontConverter.h" + +namespace Magnum { namespace Text { + +AbstractFontConverter::AbstractFontConverter() = default; + +AbstractFontConverter::AbstractFontConverter(PluginManager::AbstractManager* manager, std::string plugin): PluginManager::AbstractPlugin(manager, std::move(plugin)) {} + +std::vector>> AbstractFontConverter::exportFontToData(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::string& characters) const { + CORRADE_ASSERT(features() >= (Feature::ExportFont|Feature::ConvertData), + "Text::AbstractFontConverter::exportFontToData(): feature not supported", {}); + + return doExportFontToData(font, cache, filename, Utility::Unicode::utf32(characters)); +} + +std::vector>> AbstractFontConverter::doExportFontToData(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::u32string& characters) const { + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::exportFontToData(): feature advertised but not implemented", {}); + + std::vector>> out; + out.emplace_back(filename, std::move(doExportFontToSingleData(font, cache, characters))); + return std::move(out); +} + +Containers::Array AbstractFontConverter::exportFontToSingleData(AbstractFont* const font, GlyphCache* const cache, const std::string& characters) const { + CORRADE_ASSERT(features() >= (Feature::ExportFont|Feature::ConvertData), + "Text::AbstractFontConverter::exportFontToSingleData(): feature not supported", nullptr); + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::exportFontToSingleData(): the format is not single-file", nullptr); + + return doExportFontToSingleData(font, cache, Utility::Unicode::utf32(characters)); +} + +Containers::Array AbstractFontConverter::doExportFontToSingleData(AbstractFont*, GlyphCache*, const std::u32string&) const { + CORRADE_ASSERT(false, + "Text::AbstractFontConverter::exportFontToSingleData(): feature advertised but not implemented", nullptr); +} + +bool AbstractFontConverter::exportFontToFile(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::string& characters) const { + CORRADE_ASSERT(features() & Feature::ExportFont, + "Text::AbstractFontConverter::exportFontToFile(): feature not supported", false); + + return doExportFontToFile(font, cache, filename, Utility::Unicode::utf32(characters)); +} + +bool AbstractFontConverter::doExportFontToFile(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::u32string& characters) const { + CORRADE_ASSERT(features() & Feature::ConvertData, + "Text::AbstractFontConverter::exportFontToFile(): not implemented", false); + + /* Export all data */ + const auto data = doExportFontToData(font, cache, filename, characters); + for(const auto& d: data) { + /* Open file */ + std::ofstream out(d.first.data(), std::ios::binary); + if(!out.good()) { + Error() << "Text::AbstractFontConverter::exportFontToFile(): cannot write to file" << d.first; + return false; + } + + /* Write data, close */ + out.write(reinterpret_cast(d.second.begin()), d.second.size()); + } + + return true; +} + +std::vector>> AbstractFontConverter::exportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const { + CORRADE_ASSERT(features() >= (Feature::ExportGlyphCache|Feature::ConvertData), + "Text::AbstractFontConverter::exportGlyphCacheToData(): feature not supported", {}); + + return doExportGlyphCacheToData(cache, filename); +} + +std::vector>> AbstractFontConverter::doExportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const { + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::exportGlyphCacheToData(): feature advertised but not implemented", {}); + + std::vector>> out; + out.emplace_back(filename, std::move(doExportGlyphCacheToSingleData(cache))); + return std::move(out); +} + +Containers::Array AbstractFontConverter::exportGlyphCacheToSingleData(GlyphCache* cache) const { + CORRADE_ASSERT(features() >= (Feature::ExportGlyphCache|Feature::ConvertData), + "Text::AbstractFontConverter::exportGlyphCacheToSingleData(): feature not supported", nullptr); + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::exportGlyphCacheToSingleData(): the format is not single-file", nullptr); + + return doExportGlyphCacheToSingleData(cache); +} + +Containers::Array AbstractFontConverter::doExportGlyphCacheToSingleData(GlyphCache*) const { + CORRADE_ASSERT(false, + "Text::AbstractFontConverter::exportGlyphCacheToSingleData(): feature advertised but not implemented", nullptr); +} + +bool AbstractFontConverter::exportGlyphCacheToFile(GlyphCache* cache, const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ExportGlyphCache, + "Text::AbstractFontConverter::exportGlyphCacheToFile(): feature not supported", false); + + return doExportGlyphCacheToFile(cache, filename); +} + +bool AbstractFontConverter::doExportGlyphCacheToFile(GlyphCache* cache, const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ConvertData, + "Text::AbstractFontConverter::exportGlyphCacheToFile(): not implemented", false); + + /* Export all data */ + const auto data = doExportGlyphCacheToData(cache, filename); + for(const auto& d: data) { + /* Open file */ + std::ofstream out(d.first.data(), std::ios::binary); + if(!out.good()) { + Error() << "Text::AbstractFontConverter::exportGlyphCacheToFile(): cannot write to file" << d.first; + return false; + } + + /* Write data, close */ + out.write(reinterpret_cast(d.second.begin()), d.second.size()); + } + + return true; +} + +GlyphCache* AbstractFontConverter::importGlyphCacheFromData(const std::vector>>& data) const { + CORRADE_ASSERT(features() >= (Feature::ImportGlyphCache|Feature::ConvertData), + "Text::AbstractFontConverter::importGlyphCacheFromData(): feature not supported", nullptr); + CORRADE_ASSERT(!data.empty(), + "Text::AbstractFontConverter::importGlyphCacheFromData(): no data passed", nullptr); + + return doImportGlyphCacheFromData(data); +} + +GlyphCache* AbstractFontConverter::doImportGlyphCacheFromData(const std::vector>>& data) const { + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::importGlyphCacheFromData(): feature advertised but not implemented", nullptr); + CORRADE_ASSERT(data.size() == 1, + "Text::AbstractFontConverter::importGlyphCacheFromData(): expected just one file for single-file format", nullptr); + + return doImportGlyphCacheFromSingleData(data[0].second); +} + +GlyphCache* AbstractFontConverter::importGlyphCacheFromSingleData(Containers::ArrayReference data) const { + CORRADE_ASSERT(features() >= (Feature::ImportGlyphCache|Feature::ConvertData), + "Text::AbstractFontConverter::importGlyphCacheFromSingleData(): feature not supported", nullptr); + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::importGlyphCacheFromSingleData(): the format is not single-file", nullptr); + + return doImportGlyphCacheFromSingleData(data); +} + +GlyphCache* AbstractFontConverter::doImportGlyphCacheFromSingleData(Containers::ArrayReference) const { + CORRADE_ASSERT(false, + "Text::AbstractFontConverter::importGlyphCacheFromSingleData(): feature advertised but not implemented", nullptr); +} + +GlyphCache* AbstractFontConverter::importGlyphCacheFromFile(const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ImportGlyphCache, + "Text::AbstractFontConverter::importGlyphCacheFromFile(): feature not supported", nullptr); + + return doImportGlyphCacheFromFile(filename); +} + +GlyphCache* AbstractFontConverter::doImportGlyphCacheFromFile(const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ConvertData && !(features() & Feature::MultiFile), + "Text::AbstractFontConverter::importGlyphCacheFromFile(): not implemented", nullptr); + + /* Open file */ + std::ifstream in(filename.data(), std::ios::binary); + if(!in.good()) { + Error() << "Trade::AbstractFontConverter::importGlyphCacheFromFile(): cannot open file" << filename; + return nullptr; + } + + /* Create array to hold file contents */ + in.seekg(0, std::ios::end); + Containers::Array data(in.tellg()); + + /* Read data, close */ + in.seekg(0, std::ios::beg); + in.read(reinterpret_cast(data.begin()), data.size()); + in.close(); + + return doImportGlyphCacheFromSingleData(data); +} + +}} diff --git a/src/Text/AbstractFontConverter.h b/src/Text/AbstractFontConverter.h new file mode 100644 index 000000000..97ff78d08 --- /dev/null +++ b/src/Text/AbstractFontConverter.h @@ -0,0 +1,313 @@ +#ifndef Magnum_Text_AbstractFontConverter_h +#define Magnum_Text_AbstractFontConverter_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + 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. +*/ + +/** @file + * @brief Class Magnum::Text::AbstractFontConverter + */ + +#include + +#include "Magnum.h" +#include "Text/Text.h" +#include "Text/magnumTextVisibility.h" + +namespace Magnum { namespace Text { + +/** +@brief Base for font converter plugins + +Provides functionality for converting arbitrary font to different format. + +@section AbstractFontConverter-subclassing Subclassing + +Plugin implements doFeatures() and one or more of `exportTo*()` / `importFrom*()` +functions based on what features are supported. + +You don't need to do most of the redundant sanity checks, these things are +checked by the implementation: + +- Functions `doExportFontTo*()` are called only if @ref Feature "Feature::ExportFont" + is supported, functions `doExportGlyphCacheTo*()` are called only if + @ref Feature "Feature::ExportGlyphCache" is supported. +- Functions `doImportGlyphCacheFrom*()` are called only if + @ref Feature "Feature::ImportGlyphCache" is supported. +- Functions `doExport*To*Data()` and `doImport*From*Data()` are called only + if @ref Feature "Feature::ConvertData" is supported. +- Function `doImport*FromData()` is called only if there is at least one data + array passed. +*/ +class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPlugin { + CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Text.AbstractFontConverter/0.1") + + public: + /** + * @brief Features supported by this converter + * + * @see Features, features() + */ + enum class Feature: UnsignedByte { + /** + * Exporting font using exportToFile() or exportToData() + * @see @ref Feature "Feature::ConvertData" + */ + ExportFont = 1 << 0, + + /** + * Export glyph cache using exportToFile() or exportToData() + * @see @ref Feature "Feature::ConvertData" + */ + ExportGlyphCache = 1 << 1, + + /** + * Import glyph cache using importFromFile() or importFromData() + * @see @ref Feature "Feature::ConvertData" + */ + ImportGlyphCache = 1 << 2, + + /** Convert from/to data using exportToData() or importFromData() */ + ConvertData = 1 << 4, + + /** + * The format is multi-file, thus exportToSingleData() and + * importFromSingleData() convenience functions cannot be used. + */ + MultiFile = 1 << 5 + }; + + /** + * @brief Features supported by this converter + * + * @see features() + */ + typedef Containers::EnumSet Features; + + /** @brief Default constructor */ + explicit AbstractFontConverter(); + + /** @brief Plugin manager constructor */ + explicit AbstractFontConverter(PluginManager::AbstractManager* manager, std::string plugin); + + /** @brief Features supported by this converter */ + Features features() const { return doFeatures(); } + + /** + * @brief Export font to raw data + * @param font Opened font + * @param cache Populated glyph cache + * @param filename Output filename + * @param characters Characters to export + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ExportFont" is supported. Returns pairs of + * filename and data on success, empty vector otherwise. All data will + * be sharing common basename derived from @p filename. If the plugin + * doesn't have @ref Feature "Feature::MultiFile", only one pair is + * returned, thus using exportFontToSingleData() might be more convenient + * in that case. + * @see features(), exportFontToFile(), exportGlyphCacheToData() + */ + std::vector>> exportFontToData(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::string& characters) const; + + /** + * @brief Export font to single raw data + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ExportFont" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile". Returns data on + * success, zero-sized array otherwise. See exportFontToData() for + * more information. + * @see features(), exportFontToFile(), importFromSingleData() + */ + Containers::Array exportFontToSingleData(AbstractFont* font, GlyphCache* cache, const std::string& characters) const; + + /** + * @brief Export font to file + * + * Available only if @ref Feature "Feature::ExportFont" is supported. + * If the plugin has @ref Feature "Feature::MultiFile", the function + * will create more than one file in given path, all sharing common + * basename derived from @p filename. Returns `true` on success, + * `false` otherwise. See exportFontToData() for more information. + * @see features(), exportFontToData(), exportGlyphCacheToFile() + */ + bool exportFontToFile(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::string& characters) const; + + /** + * @brief Export glyph cache to raw data + * @param cache Populated glyph cache + * @param filename Output filename + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ExportGlyphCache" is supported. Returns pairs + * of filename and data on success, empty vector otherwise. All data + * will be sharing common basename derived from @p filename. If the + * plugin doesn't have @ref Feature "Feature::MultiFile", only one pair + * is returned, thus using exportGlyphCacheToSingleData() might be more + * convenient in that case. + * + * All glyphs from given cache will be exported. If you want to export + * smaller subset, fill the cache with less characters. + * @see features(), exportGlyphCacheToFile(), exportFontToData() + */ + std::vector>> exportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const; + + /** + * @brief Export glyph cache to single raw data + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ExportGlyphCache" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile". Returns data on + * success, zero-sized array otherwise. See exportGlyphCacheToData() + * for more information. + * @see features(), exportGlyphCacheToFile(), importGlyphCacheFromSingleData() + */ + Containers::Array exportGlyphCacheToSingleData(GlyphCache* cache) const; + + /** + * @brief Export glyph cache to file + * + * Available only if @ref Feature "Feature::ExportGlyphCache" is + * supported. If the plugin has @ref Feature "Feature::MultiFile", the + * function will create more than one file in given path, all sharing + * common basename derived from @p filename. Returns `true` on success, + * `false` otherwise. + * @see features(), exportGlyphCacheToData(), exportFontToFile() + */ + bool exportGlyphCacheToFile(GlyphCache* cache, const std::string& filename) const; + + /** + * @brief Import glyph cache from raw data + * @param data Pairs of filename and file data + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ImportGlyphCache" is supported. Returns + * imported cache on success, `nullptr` otherwise. If the plugin + * doesn't have @ref Feature "Feature::MultiFile", only one file is + * needed, thus using convertToSingleData() might be more convenient in + * that case. + * @see features(), importFromFile(), exportToData() + */ + GlyphCache* importGlyphCacheFromData(const std::vector>>& data) const; + + /** + * @brief Import glyph cache from single raw data + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ImportGlyphCache" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile". Returns imported + * cache on success, `nullptr` otherwise. See importFromData() for + * multi-file conversion. + * @see features(), importFromFile(), exportToSingleData() + */ + GlyphCache* importGlyphCacheFromSingleData(Containers::ArrayReference data) const; + + /** + * @brief Import glyph cache from file + * + * Available only if @ref Feature "Feature::ImportGlyphCache" is + * supported. 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 imported cache on + * success, `nullptr` otherwise. + * @see features(), importFromData(), exportToFile() + */ + GlyphCache* importGlyphCacheFromFile(const std::string& filename) const; + + #ifndef DOXYGEN_GENERATING_OUTPUT + private: + #else + protected: + #endif + /** @brief Implementation for features() */ + virtual Features doFeatures() const = 0; + + /** + * @brief Implementation for exportFontToData() + * + * If the plugin doesn't have @ref Feature "Feature::MultiFile", + * default implementation calls doExportFontToSingleData(). + */ + virtual std::vector>> doExportFontToData(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::u32string& characters) const; + + /** @brief Implementation for exportFontToSingleData() */ + virtual Containers::Array doExportFontToSingleData(AbstractFont* font, GlyphCache* cache, const std::u32string& characters) const; + + /** + * @brief Implementation for exportFontToFile() + * + * If @ref Feature "Feature::ConvertData" is supported, default + * implementation calls doExportFontToData() and saves the result to + * given file(s). + */ + virtual bool doExportFontToFile(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::u32string& characters) const; + + /** + * @brief Implementation for exportGlyphCacheToData() + * + * If the plugin doesn't have @ref Feature "Feature::MultiFile", + * default implementation calls doExportGlyphCacheToSingleData(). + */ + virtual std::vector>> doExportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const; + + /** @brief Implementation for exportGlyphCacheToSingleData() */ + virtual Containers::Array doExportGlyphCacheToSingleData(GlyphCache* cache) const; + + /** + * @brief Implementation for exportGlyphCacheToFile() + * + * If @ref Feature "Feature::ConvertData" is supported, default + * implementation calls doExportGlyphCacheToData() and saves the result + * to given file(s). + */ + virtual bool doExportGlyphCacheToFile(GlyphCache* cache, const std::string& filename) const; + + /** + * @brief Implementation for importGlyphCacheFromData() + * + * If the plugin doesn't have @ref Feature "Feature::MultiFile", + * default implementation calls doImportGlyphCacheFromSingleData(). + */ + virtual GlyphCache* doImportGlyphCacheFromData(const std::vector>>& data) const; + + /** @brief Implementation for importGlyphCacheFromSingleData() */ + virtual GlyphCache* doImportGlyphCacheFromSingleData(Containers::ArrayReference data) const; + + /** + * @brief Implementation for importGlyphCacheFromFile() + * + * If @ref Feature "Feature::ConvertData" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile", default + * implementation opens the file and calls doImportGlyphCacheFromSingleData() + * with its contents. + */ + virtual GlyphCache* doImportGlyphCacheFromFile(const std::string& filename) const; +}; + +CORRADE_ENUMSET_OPERATORS(AbstractFontConverter::Features) + +}} + +#endif diff --git a/src/Text/CMakeLists.txt b/src/Text/CMakeLists.txt index b7e7f30a2..daed39f76 100644 --- a/src/Text/CMakeLists.txt +++ b/src/Text/CMakeLists.txt @@ -24,11 +24,13 @@ set(MagnumText_SRCS AbstractFont.cpp + AbstractFontConverter.cpp DistanceFieldGlyphCache.cpp GlyphCache.cpp TextRenderer.cpp) set(MagnumText_HEADERS AbstractFont.h + AbstractFontConverter.h DistanceFieldGlyphCache.h GlyphCache.h Text.h diff --git a/src/Text/Test/AbstractFontConverterTest.cpp b/src/Text/Test/AbstractFontConverterTest.cpp new file mode 100644 index 000000000..b5c9cf380 --- /dev/null +++ b/src/Text/Test/AbstractFontConverterTest.cpp @@ -0,0 +1,202 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + 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 +#include +#include +#include + +#include "Text/AbstractFontConverter.h" + +#include "testConfigure.h" + +namespace Magnum { namespace Text { namespace Test { + +class AbstractFontConverterTest: public TestSuite::Tester { + public: + explicit AbstractFontConverterTest(); + + void exportFontToSingleData(); + void exportFontToFile(); + + void exportGlyphCacheToSingleData(); + void exportGlyphCacheToFile(); + + void importGlyphCacheFromSingleData(); + void importGlyphCacheFromFile(); +}; + +AbstractFontConverterTest::AbstractFontConverterTest() { + addTests({&AbstractFontConverterTest::exportFontToSingleData, + &AbstractFontConverterTest::exportFontToFile, + + &AbstractFontConverterTest::exportGlyphCacheToSingleData, + &AbstractFontConverterTest::exportGlyphCacheToFile, + + &AbstractFontConverterTest::importGlyphCacheFromSingleData, + &AbstractFontConverterTest::importGlyphCacheFromFile}); +} + +void AbstractFontConverterTest::exportFontToSingleData() { + class SingleDataExporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportFont; } + + Containers::Array doExportFontToSingleData(AbstractFont*, GlyphCache*, const std::u32string&) const override { + Containers::Array data(1); + data[0] = 0xee; + return std::move(data); + } + }; + + /* doExportFontToData() should call doExportFontToSingleData() */ + SingleDataExporter exporter; + auto ret = exporter.exportFontToData(nullptr, nullptr, "font.out", {}); + CORRADE_COMPARE(ret.size(), 1); + CORRADE_COMPARE(ret[0].first, "font.out"); + CORRADE_COMPARE(ret[0].second.size(), 1); + CORRADE_COMPARE(ret[0].second[0], 0xee); +} + +void AbstractFontConverterTest::exportFontToFile() { + class DataExporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportFont|Feature::MultiFile; } + + std::vector>> doExportFontToData(AbstractFont*, GlyphCache*, const std::string& filename, const std::u32string&) const override { + Containers::Array file(1); + file[0] = 0xf0; + + Containers::Array data(2); + data[0] = 0xfe; + data[1] = 0xed; + + std::vector>> out; + out.emplace_back(filename, std::move(file)); + out.emplace_back(filename + ".data", std::move(data)); + return std::move(out); + } + }; + + /* Remove previous files */ + Utility::Directory::rm(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out")); + Utility::Directory::rm(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out.data")); + + /* doExportToFile() should call doExportToData() */ + DataExporter exporter; + bool exported = exporter.exportFontToFile(nullptr, nullptr, Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out"), {}); + CORRADE_VERIFY(exported); + CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out"), + "\xf0", TestSuite::Compare::FileToString); + CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out.data"), + "\xfe\xed", TestSuite::Compare::FileToString); +} + +void AbstractFontConverterTest::exportGlyphCacheToSingleData() { + class SingleDataExporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportGlyphCache; } + + Containers::Array doExportGlyphCacheToSingleData(GlyphCache*) const override { + Containers::Array data(1); + data[0] = 0xee; + return std::move(data); + } + }; + + /* doExportGlyphCacheToData() should call doExportGlyphCacheToSingleData() */ + SingleDataExporter exporter; + auto ret = exporter.exportGlyphCacheToData(nullptr, "font.out"); + CORRADE_COMPARE(ret.size(), 1); + CORRADE_COMPARE(ret[0].first, "font.out"); + CORRADE_COMPARE(ret[0].second.size(), 1); + CORRADE_COMPARE(ret[0].second[0], 0xee); +} + +void AbstractFontConverterTest::exportGlyphCacheToFile() { + class DataExporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportGlyphCache|Feature::MultiFile; } + + std::vector>> doExportGlyphCacheToData(GlyphCache*, const std::string& filename) const override { + Containers::Array file(1); + file[0] = 0xf0; + + Containers::Array data(2); + data[0] = 0xfe; + data[1] = 0xed; + + std::vector>> out; + out.emplace_back(filename, std::move(file)); + out.emplace_back(filename + ".data", std::move(data)); + return std::move(out); + } + }; + + /* Remove previous files */ + Utility::Directory::rm(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out")); + Utility::Directory::rm(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out.data")); + + /* doExportGlyphCacheToFile() should call doExportGlyphCacheToData() */ + DataExporter exporter; + bool exported = exporter.exportGlyphCacheToFile(nullptr, Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out")); + CORRADE_VERIFY(exported); + CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out"), + "\xf0", TestSuite::Compare::FileToString); + CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out.data"), + "\xfe\xed", TestSuite::Compare::FileToString); +} + +namespace { + +class SingleGlyphCacheDataImporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ImportGlyphCache; } + + GlyphCache* doImportGlyphCacheFromSingleData(const Containers::ArrayReference data) const override { + if(data.size() == 1 && data[0] == 0xa5) return reinterpret_cast(0xdeadbeef); + return nullptr; + } +}; + +} + +void AbstractFontConverterTest::importGlyphCacheFromSingleData() { + /* doImportFromData() should call doImportFromSingleData() */ + SingleGlyphCacheDataImporter importer; + const unsigned char data[] = {0xa5}; + GlyphCache* cache = importer.importGlyphCacheFromData({{{}, data}}); + CORRADE_COMPARE(cache, reinterpret_cast(0xdeadbeef)); +} + +void AbstractFontConverterTest::importGlyphCacheFromFile() { + /* doImportFromFile() should call doImportFromSingleData() */ + SingleGlyphCacheDataImporter importer; + GlyphCache* cache = importer.importGlyphCacheFromFile(Utility::Directory::join(TEXT_TEST_DIR, "data.bin")); + CORRADE_COMPARE(cache, reinterpret_cast(0xdeadbeef)); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Text::Test::AbstractFontConverterTest) diff --git a/src/Text/Test/CMakeLists.txt b/src/Text/Test/CMakeLists.txt index e190c7785..ad0affe81 100644 --- a/src/Text/Test/CMakeLists.txt +++ b/src/Text/Test/CMakeLists.txt @@ -28,3 +28,4 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testConfigure.h.cmake include_directories(${CMAKE_CURRENT_BINARY_DIR}) corrade_add_test(TextAbstractFontTest AbstractFontTest.cpp LIBRARIES Magnum MagnumText) +corrade_add_test(TextAbstractFontConverterTest AbstractFontConverterTest.cpp LIBRARIES Magnum MagnumText) diff --git a/src/Text/Test/testConfigure.h.cmake b/src/Text/Test/testConfigure.h.cmake index 0cfeafcbe..62d5a8037 100644 --- a/src/Text/Test/testConfigure.h.cmake +++ b/src/Text/Test/testConfigure.h.cmake @@ -23,3 +23,4 @@ */ #define TEXT_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}" +#define TEXT_TEST_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}" diff --git a/src/Text/Text.h b/src/Text/Text.h index aae3a8ea5..e0b51c2cf 100644 --- a/src/Text/Text.h +++ b/src/Text/Text.h @@ -35,6 +35,7 @@ namespace Magnum { namespace Text { class AbstractFont; +class AbstractFontConverter; class AbstractLayouter; class DistanceFieldGlyphCache; class GlyphCache;