From 9e1ab590f19190a4e9b38fbf2ed8d3bce9d81870 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 20 Aug 2022 15:07:23 +0400 Subject: [PATCH] Cleanup fonts loading --- apps/openmw/mwgui/windowmanagerimp.cpp | 6 +- components/fontloader/fontloader.cpp | 204 ++++++++++----------- components/fontloader/fontloader.hpp | 12 +- docs/source/reference/modding/font.rst | 12 +- files/data/fonts/DejaVuLGCSansMono.omwfont | 3 +- files/data/fonts/OMWAyembedt.omwfont | 3 +- files/data/fonts/Pelagiad.omwfont | 3 +- files/openmw.cfg | 6 +- files/openmw.cfg.local | 6 +- 9 files changed, 115 insertions(+), 140 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 42e2cc54d7..1ab256ba8d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -208,7 +208,7 @@ namespace MWGui // Load fonts mFontLoader = std::make_unique(encoding, resourceSystem->getVFS(), mScalingFactor); - mFontLoader->loadBitmapFonts(); + mFontLoader->loadFonts(); //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); @@ -236,7 +236,6 @@ namespace MWGui MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); MyGUI::FactoryManager::getInstance().registerFactory("Resource", "AutoSizedResourceSkin"); MyGUI::ResourceManager::getInstance().load("core.xml"); - mFontLoader->loadTrueTypeFonts(); bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI"); mKeyboardNavigation = std::make_unique(); @@ -1175,9 +1174,6 @@ namespace MWGui for (WindowBase* window : mWindows) window->onResChange(x, y); - // We should reload TrueType fonts to fit new resolution - mFontLoader->loadTrueTypeFonts(); - // TODO: check if any windows are now off-screen and move them back if so } diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 4d727653cb..e575b8cbc0 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -163,37 +164,109 @@ namespace Gui mEncoding = encoding; MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); - MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = MyGUI::newDelegate(this, &FontLoader::loadFontFromXml); + MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = MyGUI::newDelegate(this, &FontLoader::overrideLineHeight); } - void FontLoader::loadBitmapFonts() + void FontLoader::loadFonts() { - for (const auto& path : mVFS->getRecursiveDirectoryIterator("Fonts/")) - { - if (Misc::getFileExtension(path) == "fnt") - loadBitmapFont(path); - } + std::string defaultFont = Fallback::Map::getString("Fonts_Font_0"); + std::string scrollFont = Fallback::Map::getString("Fonts_Font_2"); + loadFont(defaultFont, "DefaultFont"); + loadFont(scrollFont, "ScrollFont"); + loadFont("DejaVuLGCSansMono", "MonoFont"); // We need to use a TrueType monospace font to display debug texts properly. + + // Use our TrueType fonts as a fallback. + if (!MyGUI::ResourceManager::getInstance().isExist("DefaultFont") && !Misc::StringUtils::ciEqual(defaultFont, "Pelagiad")) + loadFont("Pelagiad", "DefaultFont"); + if (!MyGUI::ResourceManager::getInstance().isExist("ScrollFont") && !Misc::StringUtils::ciEqual(scrollFont, "OMWAyembedt")) + loadFont("OMWAyembedt", "ScrollFont"); } - void FontLoader::loadTrueTypeFonts() + void FontLoader::loadFont(const std::string& fileName, const std::string& fontId) { + if (mVFS->exists("fonts/" + fileName + ".fnt")) + loadBitmapFont(fileName + ".fnt", fontId); + else if (mVFS->exists("fonts/" + fileName + ".omwfont")) + loadTrueTypeFont(fileName + ".omwfont", fontId); + else + Log(Debug::Error) << "Font '" << fileName << "' is not found."; + } + + void FontLoader::loadTrueTypeFont(const std::string& fileName, const std::string& fontId) + { + Log(Debug::Info) << "Loading font file " << fileName; + osgMyGUI::DataManager* dataManager = dynamic_cast(&osgMyGUI::DataManager::getInstance()); if (!dataManager) { - Log(Debug::Error) << "Can not load TrueType fonts: osgMyGUI::DataManager is not available."; + Log(Debug::Error) << "Can not load TrueType font " << fontId << ": osgMyGUI::DataManager is not available."; return; } std::string oldDataPath = dataManager->getDataPath(""); dataManager->setResourcePath("fonts"); + std::unique_ptr dataStream(dataManager->getData(fileName)); + + MyGUI::xml::Document xmlDocument; + xmlDocument.open(dataStream.get()); + MyGUI::xml::ElementPtr root = xmlDocument.getRoot(); + + MyGUI::xml::ElementEnumerator resourceNode = root->getElementEnumerator(); + bool valid = false; + if (resourceNode.next("Resource")) + { + std::string type = resourceNode->findAttribute("type"); + valid = (type == "ResourceTrueTypeFont"); + } - for (const auto& path : mVFS->getRecursiveDirectoryIterator("Fonts/")) + if (valid == false) { - if (Misc::getFileExtension(path) == "omwfont") - MyGUI::ResourceManager::getInstance().load(std::string(Misc::getFileName(path))); + dataManager->setResourcePath(oldDataPath); + Log(Debug::Error) << "Can not load TrueType font " << fontId << ": " << fileName << " is invalid."; + return; } + // For TrueType fonts we should override Size and Resolution properties + // to allow to configure font size via config file, without need to edit XML files. + // Also we should take UI scaling factor in account. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::clamp(resolution, 50, 125) * mScalingFactor; + + MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild("Property"); + sizeNode->addAttribute("key", "Size"); + sizeNode->addAttribute("value", std::to_string(mFontHeight)); + + MyGUI::ResourceTrueTypeFont* font = static_cast( + MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceTrueTypeFont")); + font->deserialization(resourceNode.current(), MyGUI::Version(3,2,0)); + font->setResourceName(fontId); + MyGUI::ResourceManager::getInstance().addResource(font); + + float currentX = Settings::Manager::getInt("resolution x", "Video"); + float currentY = Settings::Manager::getInt("resolution y", "Video"); + // TODO: read size from openmw_layout.xml somehow + // TODO: it may be worth to take in account resolution change, but it is not safe to replace used assets + float heightScale = (currentY / 520); + float widthScale = (currentX / 600); + float uiScale = std::min(widthScale, heightScale); + resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::clamp(resolution, 50, 125) * uiScale; + resolutionNode->setAttribute("value", std::to_string(resolution)); + + MyGUI::ResourceTrueTypeFont* bookFont = static_cast( + MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceTrueTypeFont")); + bookFont->deserialization(resourceNode.current(), MyGUI::Version(3,2,0)); + bookFont->setResourceName("Journalbook " + fontId); + MyGUI::ResourceManager::getInstance().addResource(bookFont); + dataManager->setResourcePath(oldDataPath); + + if (resourceNode.next("Resource")) + Log(Debug::Warning) << "Font file " << fileName << " contains multiple Resource entries, only first one will be used."; } typedef struct @@ -216,9 +289,11 @@ namespace Gui float ascent; } GlyphInfo; - void FontLoader::loadBitmapFont(const std::string &fileName) + void FontLoader::loadBitmapFont(const std::string &fileName, const std::string& fontId) { - Files::IStreamPtr file = mVFS->get(fileName); + Log(Debug::Info) << "Loading font file " << fileName; + + Files::IStreamPtr file = mVFS->get("fonts/" + fileName); float fontSize; file->read((char*)&fontSize, sizeof(fontSize)); @@ -254,7 +329,7 @@ namespace Gui file.reset(); // Create the font texture - std::string bitmapFilename = "Fonts/" + std::string(name) + ".tex"; + std::string bitmapFilename = "fonts/" + std::string(name) + ".tex"; Files::IStreamPtr bitmapFile = mVFS->get(bitmapFilename); @@ -285,8 +360,7 @@ namespace Gui MyGUI::xml::Document xmlDocument; MyGUI::xml::ElementPtr root = xmlDocument.createRoot("ResourceManualFont"); - std::string baseName(Misc::stemFile(fileName)); - root->addAttribute("name", getInternalFontName(baseName)); + root->addAttribute("name", fontId); MyGUI::xml::ElementPtr defaultHeight = root->createChild("Property"); defaultHeight->addAttribute("key", "DefaultHeight"); @@ -444,48 +518,21 @@ namespace Gui MyGUI::ResourceManualFont* bookFont = static_cast( MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); - mFonts.push_back(bookFont); bookFont->deserialization(root, MyGUI::Version(3,2,0)); - bookFont->setResourceName("Journalbook " + getInternalFontName(baseName)); + bookFont->setResourceName("Journalbook " + fontId); MyGUI::ResourceManager::getInstance().addResource(font); MyGUI::ResourceManager::getInstance().addResource(bookFont); } - void FontLoader::loadFontFromXml(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) + void FontLoader::overrideLineHeight(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) { MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator(); - bool createCopy = false; while (resourceNode.next("Resource")) { - std::string type, name; - resourceNode->findAttribute("type", type); - resourceNode->findAttribute("name", name); - - if (name.empty()) - continue; - - if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) - { - createCopy = true; - - // For TrueType fonts we should override Size and Resolution properties - // to allow to configure font size via config file, without need to edit XML files. - // Also we should take UI scaling factor in account. - int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); - resolution = std::clamp(resolution, 50, 125) * mScalingFactor; - - MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property"); - resolutionNode->addAttribute("key", "Resolution"); - resolutionNode->addAttribute("value", std::to_string(resolution)); - - MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild("Property"); - sizeNode->addAttribute("key", "Size"); - sizeNode->addAttribute("value", std::to_string(mFontHeight)); + std::string type = resourceNode->findAttribute("type"); - resourceNode->setAttribute("name", getInternalFontName(name)); - } - else if (Misc::StringUtils::ciEqual(type, "ResourceSkin") || + if (Misc::StringUtils::ciEqual(type, "ResourceSkin") || Misc::StringUtils::ciEqual(type, "AutoSizedResourceSkin")) { // We should adjust line height for MyGUI widgets depending on font size @@ -496,47 +543,6 @@ namespace Gui } MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); - - if (createCopy) - { - std::unique_ptr copy{_node->createCopy()}; - - MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator(); - while (copyFont.next("Resource")) - { - std::string type, name; - copyFont->findAttribute("type", type); - copyFont->findAttribute("name", name); - - if (name.empty()) - continue; - - if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) - { - // Since the journal and books use the custom scaling factor depending on resolution, - // setup separate fonts with different Resolution to fit these windows. - // These fonts have an internal prefix. - int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); - resolution = std::clamp(resolution, 50, 125); - - float currentX = Settings::Manager::getInt("resolution x", "Video"); - float currentY = Settings::Manager::getInt("resolution y", "Video"); - // TODO: read size from openmw_layout.xml somehow - float heightScale = (currentY / 520); - float widthScale = (currentX / 600); - float uiScale = std::min(widthScale, heightScale); - resolution *= uiScale; - - MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild("Property"); - resolutionNode->addAttribute("key", "Resolution"); - resolutionNode->addAttribute("value", std::to_string(resolution)); - - copyFont->setAttribute("name", "Journalbook " + getInternalFontName(name)); - } - } - - MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy.get(), _file, _version); - } } int FontLoader::getFontHeight() @@ -544,26 +550,6 @@ namespace Gui return mFontHeight; } - std::string FontLoader::getInternalFontName(const std::string& name) - { - const std::string lowerName = Misc::StringUtils::lowerCase(name); - - if (lowerName == Misc::StringUtils::lowerCase(Fallback::Map::getString("Fonts_Font_0"))) - return "DefaultFont"; - if (lowerName == Misc::StringUtils::lowerCase(Fallback::Map::getString("Fonts_Font_2"))) - return "ScrollFont"; - if (lowerName == "dejavusansmono") - return "MonoFont"; // We need to use a TrueType monospace font to display debug texts properly. - - // Use our TrueType fonts as a fallback. - if (!MyGUI::ResourceManager::getInstance().isExist("DefaultFont") && name == "pelagiad") - return "DefaultFont"; - if (!MyGUI::ResourceManager::getInstance().isExist("ScrollFont") && name == "ayembedt") - return "ScrollFont"; - - return name; - } - std::string FontLoader::getFontForFace(const std::string& face) { const std::string lowerFace = Misc::StringUtils::lowerCase(face); diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp index 474869e431..3a2151787b 100644 --- a/components/fontloader/fontloader.hpp +++ b/components/fontloader/fontloader.hpp @@ -27,10 +27,9 @@ namespace Gui public: FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor); - void loadBitmapFonts (); - void loadTrueTypeFonts (); + void loadFonts(); - void loadFontFromXml(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); + void overrideLineHeight(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); int getFontHeight(); @@ -42,11 +41,10 @@ namespace Gui int mFontHeight; float mScalingFactor; - std::vector mFonts; + void loadFont(const std::string& fontName, const std::string& fontId); - std::string getInternalFontName(const std::string& name); - - void loadBitmapFont (const std::string& fileName); + void loadBitmapFont (const std::string& fileName, const std::string& fontId); + void loadTrueTypeFont(const std::string& fileName, const std::string& fontId); FontLoader(const FontLoader&); void operator=(const FontLoader&); diff --git a/docs/source/reference/modding/font.rst b/docs/source/reference/modding/font.rst index d92abafda2..8b3be358e6 100644 --- a/docs/source/reference/modding/font.rst +++ b/docs/source/reference/modding/font.rst @@ -3,8 +3,8 @@ Fonts Default UI font and font used in magic scrolls are defined in ``openmw.cfg``: - fallback=Fonts_Font_0,pelagiad - fallback=Fonts_Font_2,ayembedt + fallback=Fonts_Font_0,Pelagiad + fallback=Fonts_Font_2,OMWAyembedt When there are no ``Fonts_Font_*`` lines in user's ``openmw.cfg``, built-in TrueType fonts are used. Font used by console and another debug windows is not configurable (so ``Fonts_Font_1`` is unused). @@ -33,13 +33,11 @@ Unlike vanilla Morrowind, OpenMW directly supports TrueType (``.ttf``) fonts. Th OpenMW has build-in TrueType fonts: Pelagiad, OMWAyembedt and DejaVuLGCSansMono, which are used by default. TrueType fonts are configured via ``openmw.cfg`` too: - fallback=Fonts_Font_0,pelagiad - fallback=Fonts_Font_2,ayembedt + fallback=Fonts_Font_0,Pelagiad + fallback=Fonts_Font_2,OMWAyembedt In this example, OpenMW will scan ``Fonts`` folder in data directories for ``.omwfont`` files. -These files are XML files wich schema used by MyGUI. OpenMW uses files which ``name`` tag matches ``openmw.cfg`` entries: - - +These files are XML files with schema provided by MyGUI. OpenMW uses ``.omwfont`` files which name (without extension) matches ``openmw.cfg`` entries. It is also possible to adjust the font size and resolution via ``settings.cfg`` file:: diff --git a/files/data/fonts/DejaVuLGCSansMono.omwfont b/files/data/fonts/DejaVuLGCSansMono.omwfont index 8bd6f50174..5e6726c2fb 100644 --- a/files/data/fonts/DejaVuLGCSansMono.omwfont +++ b/files/data/fonts/DejaVuLGCSansMono.omwfont @@ -1,6 +1,5 @@ - - + diff --git a/files/data/fonts/OMWAyembedt.omwfont b/files/data/fonts/OMWAyembedt.omwfont index 25659da215..5abf514e24 100644 --- a/files/data/fonts/OMWAyembedt.omwfont +++ b/files/data/fonts/OMWAyembedt.omwfont @@ -1,6 +1,5 @@ - - + diff --git a/files/data/fonts/Pelagiad.omwfont b/files/data/fonts/Pelagiad.omwfont index e06a233b3a..2fe2bcf942 100644 --- a/files/data/fonts/Pelagiad.omwfont +++ b/files/data/fonts/Pelagiad.omwfont @@ -1,6 +1,5 @@ - - + diff --git a/files/openmw.cfg b/files/openmw.cfg index 00c1435212..fb4cf70f5f 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -71,9 +71,9 @@ fallback=Water_UnderwaterColor,012,030,037 fallback=Water_UnderwaterColorWeight,0.85 # fonts -fallback=Fonts_Font_0,pelagiad -fallback=Fonts_Font_1,dejavusansmono -fallback=Fonts_Font_2,ayembedt +fallback=Fonts_Font_0,Pelagiad +fallback=Fonts_Font_1,DejaVuLGCSansMono +fallback=Fonts_Font_2,OMWAyembedt fallback=FontColor_color_normal,202,165,96 fallback=FontColor_color_normal_over,223,201,159 fallback=FontColor_color_normal_pressed,243,237,221 diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index 44525dc486..cb05d5f22a 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -71,9 +71,9 @@ fallback=Water_UnderwaterColor,012,030,037 fallback=Water_UnderwaterColorWeight,0.85 # fonts -fallback=Fonts_Font_0,pelagiad -fallback=Fonts_Font_1,dejavusansmono -fallback=Fonts_Font_2,ayembedt +fallback=Fonts_Font_0,Pelagiad +fallback=Fonts_Font_1,DejaVuLGCSansMono +fallback=Fonts_Font_2,OMWAyembedt fallback=FontColor_color_normal,202,165,96 fallback=FontColor_color_normal_over,223,201,159 fallback=FontColor_color_normal_pressed,243,237,221