2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "GUIFontManager.h"
22 #include "GraphicContext.h"
23 #include "GUIWindowManager.h"
24 #include "addons/Skin.h"
25 #include "GUIFontTTF.h"
27 #include "utils/XMLUtils.h"
28 #include "GUIControlFactory.h"
29 #include "filesystem/Directory.h"
30 #include "filesystem/File.h"
31 #include "filesystem/SpecialProtocol.h"
32 #include "settings/lib/Setting.h"
33 #include "utils/log.h"
34 #include "utils/URIUtils.h"
35 #include "utils/StringUtils.h"
36 #include "windowing/WindowingFactory.h"
43 GUIFontManager::GUIFontManager(void)
45 m_fontsetUnicode=false;
49 GUIFontManager::~GUIFontManager(void)
54 void GUIFontManager::RescaleFontSizeAndAspect(float *size, float *aspect, const RESOLUTION_INFO &sourceRes, bool preserveAspect)
56 // get the UI scaling constants so that we can scale our font sizes correctly
57 // as fonts aren't scaled at render time (due to aliasing) we must scale
58 // the size of the fonts before they are drawn to bitmaps
60 g_graphicsContext.GetGUIScaling(sourceRes, scaleX, scaleY);
64 // font always displayed in the aspect specified by the aspect parameter
65 *aspect /= g_graphicsContext.GetResInfo().fPixelRatio;
69 // font streched like the rest of the UI, aspect parameter being the original aspect
71 // adjust aspect ratio
72 *aspect *= sourceRes.fPixelRatio;
74 *aspect *= scaleY / scaleX;
80 static bool CheckFont(CStdString& strPath, const CStdString& newPath,
81 const CStdString& filename)
83 if (!XFILE::CFile::Exists(strPath))
85 strPath = URIUtils::AddFileToFolder(newPath,filename);
87 strPath = CSpecialProtocol::TranslatePathConvertCase(strPath);
95 CGUIFont* GUIFontManager::LoadTTF(const CStdString& strFontName, const CStdString& strFilename, color_t textColor, color_t shadowColor, const int iSize, const int iStyle, bool border, float lineSpacing, float aspect, const RESOLUTION_INFO *sourceRes, bool preserveAspect)
97 float originalAspect = aspect;
99 //check if font already exists
100 CGUIFont* pFont = GetFont(strFontName, false);
104 if (!sourceRes) // no source res specified, so assume the skin res
105 sourceRes = &m_skinResolution;
107 float newSize = (float)iSize;
108 RescaleFontSizeAndAspect(&newSize, &aspect, *sourceRes, preserveAspect);
110 // First try to load the font from the skin
112 if (!CURL::IsFullPath(strFilename))
114 strPath = URIUtils::AddFileToFolder(g_graphicsContext.GetMediaDir(), "fonts");
115 strPath = URIUtils::AddFileToFolder(strPath, strFilename);
118 strPath = strFilename;
121 strPath = CSpecialProtocol::TranslatePathConvertCase(strPath);
124 // Check if the file exists, otherwise try loading it from the global media dir
125 CStdString file = URIUtils::GetFileName(strFilename);
126 if (!CheckFont(strPath,"special://home/media/Fonts",file))
127 CheckFont(strPath,"special://xbmc/media/Fonts",file);
129 // check if we already have this font file loaded (font object could differ only by color or style)
130 CStdString TTFfontName = StringUtils::Format("%s_%f_%f%s", strFilename.c_str(), newSize, aspect, border ? "_border" : "");
132 CGUIFontTTFBase* pFontFile = GetFontFile(TTFfontName);
135 pFontFile = new CGUIFontTTF(TTFfontName);
136 bool bFontLoaded = pFontFile->Load(strPath, newSize, aspect, 1.0f, border);
142 // font could not be loaded - try Arial.ttf, which we distribute
143 if (strFilename != "arial.ttf")
145 CLog::Log(LOGERROR, "Couldn't load font name: %s(%s), trying to substitute arial.ttf", strFontName.c_str(), strFilename.c_str());
146 return LoadTTF(strFontName, "arial.ttf", textColor, shadowColor, iSize, iStyle, border, lineSpacing, originalAspect);
148 CLog::Log(LOGERROR, "Couldn't load font name:%s file:%s", strFontName.c_str(), strPath.c_str());
153 m_vecFontFiles.push_back(pFontFile);
156 // font file is loaded, create our CGUIFont
157 CGUIFont *pNewFont = new CGUIFont(strFontName, iStyle, textColor, shadowColor, lineSpacing, (float)iSize, pFontFile);
158 m_vecFonts.push_back(pNewFont);
160 // Store the original TTF font info in case we need to reload it in a different resolution
161 OrigFontInfo fontInfo;
162 fontInfo.size = iSize;
163 fontInfo.aspect = originalAspect;
164 fontInfo.fontFilePath = strPath;
165 fontInfo.fileName = strFilename;
166 fontInfo.sourceRes = *sourceRes;
167 fontInfo.preserveAspect = preserveAspect;
168 fontInfo.border = border;
169 m_vecFontInfo.push_back(fontInfo);
174 bool GUIFontManager::OnMessage(CGUIMessage &message)
176 if (message.GetMessage() != GUI_MSG_NOTIFY_ALL)
179 if (message.GetParam1() == GUI_MSG_RENDERER_LOST)
185 if (message.GetParam1() == GUI_MSG_RENDERER_RESET)
186 { // our device has been reset - we have to reload our ttf fonts, and send
187 // a message to controls that we have done so
189 g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE);
194 if (message.GetParam1() == GUI_MSG_WINDOW_RESIZE)
195 { // we need to reload our fonts
199 // no need to send a resize message, as this message will do the rounds
206 void GUIFontManager::ReloadTTFFonts(void)
208 if (!m_vecFonts.size())
209 return; // we haven't even loaded fonts in yet
211 for (unsigned int i = 0; i < m_vecFonts.size(); i++)
213 CGUIFont* font = m_vecFonts[i];
214 OrigFontInfo fontInfo = m_vecFontInfo[i];
216 float aspect = fontInfo.aspect;
217 float newSize = (float)fontInfo.size;
218 CStdString& strPath = fontInfo.fontFilePath;
219 CStdString& strFilename = fontInfo.fileName;
221 RescaleFontSizeAndAspect(&newSize, &aspect, fontInfo.sourceRes, fontInfo.preserveAspect);
223 CStdString TTFfontName = StringUtils::Format("%s_%f_%f%s", strFilename.c_str(), newSize, aspect, fontInfo.border ? "_border" : "");
224 CGUIFontTTFBase* pFontFile = GetFontFile(TTFfontName);
227 pFontFile = new CGUIFontTTF(TTFfontName);
228 if (!pFontFile || !pFontFile->Load(strPath, newSize, aspect, 1.0f, fontInfo.border))
231 // font could not be loaded
232 CLog::Log(LOGERROR, "Couldn't re-load font file:%s", strPath.c_str());
236 m_vecFontFiles.push_back(pFontFile);
239 font->SetFont(pFontFile);
243 void GUIFontManager::Unload(const CStdString& strFontName)
245 for (vector<CGUIFont*>::iterator iFont = m_vecFonts.begin(); iFont != m_vecFonts.end(); ++iFont)
247 if ((*iFont)->GetFontName().Equals(strFontName))
250 m_vecFonts.erase(iFont);
256 void GUIFontManager::FreeFontFile(CGUIFontTTFBase *pFont)
258 for (vector<CGUIFontTTFBase*>::iterator it = m_vecFontFiles.begin(); it != m_vecFontFiles.end(); ++it)
262 m_vecFontFiles.erase(it);
269 CGUIFontTTFBase* GUIFontManager::GetFontFile(const CStdString& strFileName)
271 for (int i = 0; i < (int)m_vecFontFiles.size(); ++i)
273 CGUIFontTTFBase* pFont = (CGUIFontTTFBase *)m_vecFontFiles[i];
274 if (pFont->GetFileName().Equals(strFileName))
280 CGUIFont* GUIFontManager::GetFont(const CStdString& strFontName, bool fallback /*= true*/)
282 for (int i = 0; i < (int)m_vecFonts.size(); ++i)
284 CGUIFont* pFont = m_vecFonts[i];
285 if (pFont->GetFontName().Equals(strFontName))
288 // fall back to "font13" if we have none
289 if (fallback && !strFontName.empty() && !strFontName.Equals("-") && !strFontName.Equals("font13"))
290 return GetFont("font13");
294 CGUIFont* GUIFontManager::GetDefaultFont(bool border)
296 // first find "font13" or "__defaultborder__"
297 unsigned int font13index = m_vecFonts.size();
298 CGUIFont *font13border = NULL;
299 for (unsigned int i = 0; i < m_vecFonts.size(); i++)
301 CGUIFont* font = m_vecFonts[i];
302 if (font->GetFontName() == "font13")
304 else if (font->GetFontName() == "__defaultborder__")
307 // no "font13" means no default font is found - use the first font found.
308 if (font13index == m_vecFonts.size())
310 if (m_vecFonts.empty())
319 CGUIFont *font13 = m_vecFonts[font13index];
320 OrigFontInfo fontInfo = m_vecFontInfo[font13index];
321 font13border = LoadTTF("__defaultborder__", fontInfo.fileName, 0xFF000000, 0, fontInfo.size, font13->GetStyle(), true, 1.0f, fontInfo.aspect, &fontInfo.sourceRes, fontInfo.preserveAspect);
325 return m_vecFonts[font13index];
328 void GUIFontManager::Clear()
330 for (int i = 0; i < (int)m_vecFonts.size(); ++i)
332 CGUIFont* pFont = m_vecFonts[i];
337 m_vecFontFiles.clear();
338 m_vecFontInfo.clear();
339 m_fontsetUnicode=false;
342 void GUIFontManager::LoadFonts(const CStdString& strFontSet)
345 if (!OpenFontFile(xmlDoc))
348 TiXmlElement* pRootElement = xmlDoc.RootElement();
349 const TiXmlNode *pChild = pRootElement->FirstChild();
351 // If there are no fontset's defined in the XML (old skin format) run in backward compatibility
352 // and ignore the fontset request
353 CStdString strValue = pChild->Value();
354 if (strValue == "fontset")
359 strValue = pChild->Value();
360 if (strValue == "fontset")
362 const char* idAttr = ((TiXmlElement*) pChild)->Attribute("id");
364 const char* unicodeAttr = ((TiXmlElement*) pChild)->Attribute("unicode");
366 if (foundTTF.empty() && idAttr != NULL && unicodeAttr != NULL && stricmp(unicodeAttr, "true") == 0)
369 // Check if this is the fontset that we want
370 if (idAttr != NULL && stricmp(strFontSet.c_str(), idAttr) == 0)
372 m_fontsetUnicode=false;
373 // Check if this is the a ttf fontset
374 if (unicodeAttr != NULL && stricmp(unicodeAttr, "true") == 0)
375 m_fontsetUnicode=true;
377 if (m_fontsetUnicode)
379 LoadFonts(pChild->FirstChild());
386 pChild = pChild->NextSibling();
389 // If no fontset was loaded
392 CLog::Log(LOGWARNING, "file doesnt have <fontset> with name '%s', defaulting to first fontset", strFontSet.c_str());
393 if (!foundTTF.empty())
399 CLog::Log(LOGERROR, "file doesnt have <fontset> in <fonts>, but rather %s", strValue.c_str());
404 void GUIFontManager::LoadFonts(const TiXmlNode* fontNode)
408 CStdString strValue = fontNode->Value();
409 if (strValue == "font")
411 const TiXmlNode *pNode = fontNode->FirstChild("name");
414 CStdString strFontName = pNode->FirstChild()->Value();
415 color_t shadowColor = 0;
416 color_t textColor = 0;
417 CGUIControlFactory::GetColor(fontNode, "shadow", shadowColor);
418 CGUIControlFactory::GetColor(fontNode, "color", textColor);
419 const TiXmlNode *pNode = fontNode->FirstChild("filename");
422 CStdString strFontFileName = pNode->FirstChild()->Value();
423 StringUtils::ToLower(strFontFileName);
424 if (strFontFileName.find(".ttf") != string::npos)
427 int iStyle = FONT_STYLE_NORMAL;
429 float lineSpacing = 1.0f;
431 XMLUtils::GetInt(fontNode, "size", iSize);
432 if (iSize <= 0) iSize = 20;
434 pNode = fontNode->FirstChild("style");
435 if (pNode && pNode->FirstChild())
437 vector<string> styles = StringUtils::Split(pNode->FirstChild()->ValueStr(), " ");
438 for (vector<string>::iterator i = styles.begin(); i != styles.end(); ++i)
441 iStyle |= FONT_STYLE_BOLD;
442 else if (*i == "italics")
443 iStyle |= FONT_STYLE_ITALICS;
444 else if (*i == "bolditalics") // backward compatibility
445 iStyle |= (FONT_STYLE_BOLD | FONT_STYLE_ITALICS);
446 else if (*i == "uppercase")
447 iStyle |= FONT_STYLE_UPPERCASE;
448 else if (*i == "lowercase")
449 iStyle |= FONT_STYLE_LOWERCASE;
453 XMLUtils::GetFloat(fontNode, "linespacing", lineSpacing);
454 XMLUtils::GetFloat(fontNode, "aspect", aspect);
456 LoadTTF(strFontName, strFontFileName, textColor, shadowColor, iSize, iStyle, false, lineSpacing, aspect);
462 fontNode = fontNode->NextSibling();
466 bool GUIFontManager::OpenFontFile(CXBMCTinyXML& xmlDoc)
468 // Get the file to load fonts from:
469 CStdString strPath = g_SkinInfo->GetSkinPath("Font.xml", &m_skinResolution);
470 CLog::Log(LOGINFO, "Loading fonts from %s", strPath.c_str());
472 // first try our preferred file
473 if ( !xmlDoc.LoadFile(strPath) )
475 CLog::Log(LOGERROR, "Couldn't load %s", strPath.c_str());
478 TiXmlElement* pRootElement = xmlDoc.RootElement();
480 CStdString strValue = pRootElement->Value();
481 if (strValue != CStdString("fonts"))
483 CLog::Log(LOGERROR, "file %s doesnt start with <fonts>", strPath.c_str());
490 bool GUIFontManager::GetFirstFontSetUnicode(CStdString& strFontSet)
494 // Load our font file
496 if (!OpenFontFile(xmlDoc))
499 TiXmlElement* pRootElement = xmlDoc.RootElement();
500 const TiXmlNode *pChild = pRootElement->FirstChild();
502 CStdString strValue = pChild->Value();
503 if (strValue == "fontset")
507 strValue = pChild->Value();
508 if (strValue == "fontset")
510 const char* idAttr = ((TiXmlElement*) pChild)->Attribute("id");
512 const char* unicodeAttr = ((TiXmlElement*) pChild)->Attribute("unicode");
514 // Check if this is a fontset with a ttf attribute set to true
515 if (unicodeAttr != NULL && stricmp(unicodeAttr, "true") == 0)
517 // This is the first ttf fontset
524 pChild = pChild->NextSibling();
527 // If no fontset was loaded
529 CLog::Log(LOGWARNING, "file doesnt have <fontset> with attribute unicode=\"true\"");
533 CLog::Log(LOGERROR, "file doesnt have <fontset> in <fonts>, but rather %s", strValue.c_str());
536 return !strFontSet.empty();
539 bool GUIFontManager::IsFontSetUnicode(const CStdString& strFontSet)
542 if (!OpenFontFile(xmlDoc))
545 TiXmlElement* pRootElement = xmlDoc.RootElement();
546 const TiXmlNode *pChild = pRootElement->FirstChild();
548 CStdString strValue = pChild->Value();
549 if (strValue == "fontset")
553 strValue = pChild->Value();
554 if (strValue == "fontset")
556 const char* idAttr = ((TiXmlElement*) pChild)->Attribute("id");
558 const char* unicodeAttr = ((TiXmlElement*) pChild)->Attribute("unicode");
560 // Check if this is the fontset that we want
561 if (idAttr != NULL && stricmp(strFontSet.c_str(), idAttr) == 0)
562 return (unicodeAttr != NULL && stricmp(unicodeAttr, "true") == 0);
566 pChild = pChild->NextSibling();
573 void GUIFontManager::SettingOptionsFontsFiller(const CSetting *setting, std::vector< std::pair<std::string, std::string> > &list, std::string ¤t)
576 CFileItemList items2;
579 XFILE::CDirectory::GetDirectory("special://home/media/Fonts/", items2);
581 if (XFILE::CDirectory::GetDirectory("special://xbmc/media/Fonts/", items))
583 items.Append(items2);
584 for (int i = 0; i < items.Size(); ++i)
586 CFileItemPtr pItem = items[i];
588 if (!pItem->m_bIsFolder
589 && URIUtils::HasExtension(pItem->GetLabel(), ".ttf"))
591 list.push_back(make_pair(pItem->GetLabel(), pItem->GetLabel()));