diff options
Diffstat (limited to 'src/player/TextEngine.cpp')
-rw-r--r-- | src/player/TextEngine.cpp | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/src/player/TextEngine.cpp b/src/player/TextEngine.cpp new file mode 100644 index 0000000..226b0de --- /dev/null +++ b/src/player/TextEngine.cpp @@ -0,0 +1,313 @@ +// +// libavg - Media Playback Engine. +// Copyright (C) 2003-2014 Ulrich von Zadow +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// Current versions can be found at www.libavg.de +// + +#include "TextEngine.h" + +#include "../base/Logger.h" +#include "../base/OSHelper.h" +#include "../base/Exception.h" +#include "../base/FileHelper.h" +#include "../base/StringHelper.h" + +#include <algorithm> + +namespace avg { + +using namespace std; + +static void +text_subst_func_hint(FcPattern *pattern, gpointer data) +{ + FcPatternAddBool(pattern, FC_HINTING, true); + FcPatternAddInteger(pattern, FC_HINT_STYLE, FC_HINT_MEDIUM); + FcPatternAddInteger(pattern, FC_RGBA, FC_RGBA_NONE); + FcPatternAddBool(pattern, FC_ANTIALIAS, true); +} + +static void +text_subst_func_nohint(FcPattern *pattern, gpointer data) +{ + FcPatternAddBool(pattern, FC_HINTING, false); + FcPatternAddBool(pattern, FC_AUTOHINT, false); + FcPatternAddInteger(pattern, FC_HINT_STYLE, FC_HINT_NONE); + FcPatternAddInteger(pattern, FC_RGBA, FC_RGBA_NONE); + FcPatternAddBool(pattern, FC_ANTIALIAS, true); +} + +TextEngine& TextEngine::get(bool bHint) +{ + if (bHint) { + static TextEngine s_Instance(true); + return s_Instance; + } else { + static TextEngine s_Instance(false); + return s_Instance; + } +} + + +TextEngine::TextEngine(bool bHint) + : m_bHint(bHint) +{ + m_sFontDirs.push_back("fonts/"); + init(); +} + +TextEngine::~TextEngine() +{ + deinit(); +} + +void TextEngine::init() +{ + m_pFontMap = PANGO_FT2_FONT_MAP(pango_ft2_font_map_new()); + pango_ft2_font_map_set_resolution(m_pFontMap, 72, 72); + if (m_bHint) { + pango_ft2_font_map_set_default_substitute(m_pFontMap, text_subst_func_hint, + 0, 0); + } else { + pango_ft2_font_map_set_default_substitute(m_pFontMap, text_subst_func_nohint, + 0, 0); + } +#if PANGO_VERSION > PANGO_VERSION_ENCODE(1,22,0) + m_pPangoContext = pango_font_map_create_context(PANGO_FONT_MAP(m_pFontMap)); +#else + m_pPangoContext = pango_ft2_font_map_create_context(m_pFontMap); +#endif + + pango_context_set_language(m_pPangoContext, + pango_language_from_string ("en_US")); + pango_context_set_base_dir(m_pPangoContext, PANGO_DIRECTION_LTR); + + initFonts(); + + string sOldLang = ""; + getEnv("LC_CTYPE", sOldLang); + setEnv("LC_CTYPE", "en-us"); + pango_font_map_list_families(PANGO_FONT_MAP(m_pFontMap), &m_ppFontFamilies, + &m_NumFontFamilies); + setEnv("LC_CTYPE", sOldLang); + for (int i = 0; i < m_NumFontFamilies; ++i) { + m_sFonts.push_back(pango_font_family_get_name(m_ppFontFamilies[i])); + } + sort(m_sFonts.begin(), m_sFonts.end()); +} + +void TextEngine::deinit() +{ + g_object_unref(m_pFontMap); + g_free(m_ppFontFamilies); + g_object_unref(m_pPangoContext); + m_sFonts.clear(); +} + +void TextEngine::addFontDir(const std::string& sDir) +{ + deinit(); + m_sFontDirs.push_back(sDir); + init(); +} + +PangoContext * TextEngine::getPangoContext() +{ + return m_pPangoContext; +} + +const vector<string>& TextEngine::getFontFamilies() +{ + return m_sFonts; +} + +const vector<string>& TextEngine::getFontVariants(const string& sFontName) +{ + PangoFontFamily * pCurFamily = getFontFamily(sFontName); + PangoFontFace ** ppFaces; + int numFaces; + pango_font_family_list_faces (pCurFamily, &ppFaces, &numFaces); + static vector<string> sVariants; + for (int i = 0; i < numFaces; ++i) { + sVariants.push_back(pango_font_face_get_face_name(ppFaces[i])); + } + g_free(ppFaces); + return sVariants; +} + +PangoFontDescription * TextEngine::getFontDescription(const string& sFamily, + const string& sVariant) +{ + PangoFontDescription* pDescription; + FontDescriptionCache::iterator it; + it = m_FontDescriptionCache.find(pair<string, string>(sFamily, sVariant)); + if (it == m_FontDescriptionCache.end()) { + PangoFontFamily * pFamily; + bool bFamilyFound = true; + try { + pFamily = getFontFamily(sFamily); + } catch (Exception&) { + if (m_sFontsNotFound.find(sFamily) == m_sFontsNotFound.end()) { + AVG_LOG_WARNING("Could not find font face " << sFamily << + ". Using sans instead."); + m_sFontsNotFound.insert(sFamily); + } + bFamilyFound = false; + pFamily = getFontFamily("sans"); + } + PangoFontFace ** ppFaces; + int numFaces; + pango_font_family_list_faces(pFamily, &ppFaces, &numFaces); + PangoFontFace * pFace = 0; + if (sVariant == "") { + pFace = ppFaces[0]; + } else { + for (int i = 0; i < numFaces; ++i) { + if (equalIgnoreCase(pango_font_face_get_face_name(ppFaces[i]), sVariant)) { + pFace = ppFaces[i]; + } + } + } + if (!pFace) { + pFace = ppFaces[0]; + if (bFamilyFound) { + pair<string, string> variant(sFamily, sVariant); + if (m_VariantsNotFound.find(variant) == m_VariantsNotFound.end()) { + m_VariantsNotFound.insert(variant); + AVG_LOG_WARNING("Could not find font variant " + << sFamily << ":" << sVariant << ". Using " << + pango_font_face_get_face_name(pFace) << " instead."); + } + } + } + g_free(ppFaces); + pDescription = pango_font_face_describe(pFace); + m_FontDescriptionCache[pair<string, string>(sFamily, sVariant)] = + pDescription; + } else { + pDescription = it->second; + } + return pango_font_description_copy(pDescription); +} + +void GLibLogFunc(const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer unused_data) +{ +//TODO: Make this use correct AVG_LOG_LEVEL function +#ifndef WIN32 + string s = "Pango "; + if (log_level & G_LOG_LEVEL_ERROR) { + s += message; + AVG_LOG_ERROR(s); + return; + } else if (log_level & G_LOG_LEVEL_CRITICAL) { + s += message; + AVG_LOG_ERROR(s); + AVG_ASSERT(false); + } else if (log_level & G_LOG_LEVEL_WARNING) { + s += message; + AVG_LOG_WARNING(s); + return; + } else if (log_level & G_LOG_LEVEL_MESSAGE) { + s += (string("message: ") + message); + AVG_LOG_INFO(s); + return; + } else if (log_level & G_LOG_LEVEL_INFO) { + s += message; + AVG_LOG_INFO(s); + return; + } else if (log_level & G_LOG_LEVEL_DEBUG) { + s += message; + AVG_TRACE(Logger::category::NONE, Logger::severity::DEBUG, s); + return; + } + s += message; + AVG_LOG_WARNING(s); +#endif +} + +void TextEngine::initFonts() +{ + std::vector<std::string> fontConfPathPrefixList; +#ifndef WIN32 + fontConfPathPrefixList.push_back("/"); + fontConfPathPrefixList.push_back("/usr/local/"); + fontConfPathPrefixList.push_back("/opt/local/"); +#endif + fontConfPathPrefixList.push_back(getAvgLibPath()); + + std::string sFontConfPath; + for (size_t i = 0; i < fontConfPathPrefixList.size(); ++i) { + sFontConfPath = fontConfPathPrefixList[i] + "etc/fonts/fonts.conf"; + if (fileExists(sFontConfPath)) { + break; + } + } + + FcConfig * pConfig = FcConfigCreate(); + int ok = (int)FcConfigParseAndLoad(pConfig, + (const FcChar8 *)(sFontConfPath.c_str()), true); + checkFontError(ok, string("Font error: could not load config file ")+sFontConfPath); + ok = (int)FcConfigBuildFonts(pConfig); + checkFontError(ok, string("Font error: FcConfigBuildFonts failed.")); + ok = (int)FcConfigSetCurrent(pConfig); + checkFontError(ok, string("Font error: FcConfigSetCurrent failed.")); + for(std::vector<std::string>::const_iterator it = m_sFontDirs.begin(); + it != m_sFontDirs.end(); ++it) + { + ok = (int)FcConfigAppFontAddDir(pConfig, (const FcChar8 *)it->c_str()); + checkFontError(ok, string("Font error: FcConfigAppFontAddDir(" + + *it + ") failed.")); + } + /* + FcStrList * pCacheDirs = FcConfigGetCacheDirs(pConfig); + FcChar8 * pDir; + do { + pDir = FcStrListNext(pCacheDirs); + if (pDir) { + cerr << pDir << endl; + } + } while (pDir); + */ + g_log_set_default_handler(GLibLogFunc, 0); +} + +PangoFontFamily * TextEngine::getFontFamily(const string& sFamily) +{ + PangoFontFamily * pFamily = 0; + AVG_ASSERT(m_NumFontFamilies != 0); + for (int i=0; i<m_NumFontFamilies; ++i) { + if (equalIgnoreCase(pango_font_family_get_name(m_ppFontFamilies[i]), sFamily)) { + pFamily = m_ppFontFamilies[i]; + } + } + if (!pFamily) { + throw(Exception(AVG_ERR_INVALID_ARGS, + "getFontFamily: Font family "+sFamily+" not found.")); + } + return pFamily; +} + +void TextEngine::checkFontError(int ok, const string& sMsg) +{ + if (ok == 0) { + throw Exception(AVG_ERR_FONT_INIT_FAILED, sMsg); + } +} + +} |