// ============================================================== // This file is part of Glest Shared Library (www.glest.org) // // Copyright (C) 2001-2007 Martio Figueroa // // You can redistribute this code and/or modify it under // the terms of the GNU General Public License as published // by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version // ============================================================== #include "font.h" #include #include #include "conversion.h" #ifdef USE_FTGL #include "font_textFTGL.h" #include #include using namespace Shared::Graphics::Gl; #endif // If your compiler has a version older than 2.4.1 of fontconfig, you can tell cmake to // disable trying to use fontconfig via passing this to cmake: // -DWANT_FONTCONFIG=Off #ifdef HAVE_FONTCONFIG #include #endif #include "util.h" #include "platform_common.h" #include "platform_util.h" #ifdef HAVE_FRIBIDI #include #endif #include #include "leak_dumper.h" using namespace std; using namespace Shared::Util; using namespace Shared::PlatformCommon; namespace Shared { namespace Graphics { // Init statics int Font::charCount = 256; std::string Font::fontTypeName = "Times New Roman"; bool Font::fontIsMultibyte = false; bool Font::forceLegacyFonts = false; bool Font::fontIsRightToLeft = false; bool Font::fontSupportMixedRightToLeft = false; // This value is used to scale the font text rendering // in 3D render mode float Font::scaleFontValue = 0.80f; // This value is used for centering font text vertically (height) float Font::scaleFontValueCenterHFactor = 4.0f; int Font::baseSize = 3; int Font::faceResolution = 72; string Font::langHeightText = "yW"; // void Font::resetToDefaults() { Font::charCount = 256; Font::fontTypeName = "Times New Roman"; Font::fontIsMultibyte = false; Font::fontIsRightToLeft = false; Font::fontSupportMixedRightToLeft = false; // This value is used to scale the font text rendering // in 3D render mode Font::scaleFontValue = 0.80f; // This value is used for centering font text vertically (height) Font::scaleFontValueCenterHFactor = 4.0f; Font::baseSize = 3; Font::faceResolution = 72; Font::langHeightText = "yW"; #if defined(WIN32) string newEnvValue = "MEGAGLEST_FONT="; _putenv(newEnvValue.c_str()); newEnvValue = "MEGAGLEST_FONT_FAMILY="; _putenv(newEnvValue.c_str()); #else unsetenv("MEGAGLEST_FONT"); unsetenv("MEGAGLEST_FONT_FAMILY"); #endif } // ===================================================== // class FontMetrics // ===================================================== FontMetrics::FontMetrics(Text *textHandler) { this->textHandler = textHandler; //SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] this->textHandler = [%p] Owner = [%p]\n", __FILE__, __FUNCTION__, __LINE__, this->textHandler,this); this->widths = new float[Font::charCount]; this->height = 0; for (int i = 0; i < Font::charCount; ++i) { widths[i] = 0; } } FontMetrics::~FontMetrics() { delete[] widths; widths = NULL; } void FontMetrics::setTextHandler(Text *textHandler) { this->textHandler = textHandler; //SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] this->textHandler = [%p] Owner = [%p]\n", __FILE__, __FUNCTION__, __LINE__, this->textHandler, this); } Text * FontMetrics::getTextHandler() { return this->textHandler; } float FontMetrics::getTextWidth(const string &str) { string longestLine = ""; size_t found = str.find("\n"); if (found == string::npos) { longestLine = str; } else { vector lineTokens; Tokenize(str, lineTokens, "\n"); if (lineTokens.empty() == false) { for (unsigned int i = 0; i < lineTokens.size(); ++i) { string currentStr = lineTokens[i]; if (currentStr.length() > longestLine.length()) { longestLine = currentStr; } } } } if (textHandler != NULL) { return (textHandler->Advance(longestLine.c_str()) * Font::scaleFontValue); } else { float width = 0.f; for (unsigned int i = 0; i < longestLine.size() && (int) i < Font::charCount; ++i) { if (longestLine[i] >= Font::charCount) { string sError = "str[i] >= Font::charCount, [" + longestLine + "] i = " + uIntToStr(i); throw megaglest_runtime_error(sError); } //Treat 2 byte characters as spaces if (longestLine[i] < 0) { width += (widths[97]); // This is the letter a which is a normal wide character and good to use for spacing } else { width += widths[(int) longestLine[i]]; } } return width; } } float FontMetrics::getHeight(const string &str) const { if (textHandler != NULL) { return (textHandler->LineHeight(str.c_str())); } else { return height; } } string FontMetrics::wordWrapText(string text, int maxWidth) { // Strip newlines from source replaceAll(text, "\n", " \n "); // Get all words (space separated text) vector words; Tokenize(text, words, " "); string wrappedText = ""; float lineWidth = 0.0f; for (unsigned int i = 0; i < words.size(); ++i) { string word = words[i]; if (word == "\n") { wrappedText += word; lineWidth = 0; } else { float wordWidth = this->getTextWidth(word); if (lineWidth + wordWidth > maxWidth) { wrappedText += "\n"; lineWidth = 0; } lineWidth += this->getTextWidth(word + " "); wrappedText += word + " "; } } return wrappedText; } // =============================================== // class Font // =============================================== Font::Font(FontTextHandlerType type) { if (GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { throw megaglest_runtime_error("Loading graphics in headless server mode not allowed!"); } inited = false; this->type = fontTypeName; width = 400; size = 10; this->textHandler = NULL; //SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] this->textHandler = [%p] Owner = [%p]\n", __FILE__, __FUNCTION__, __LINE__, this->textHandler, this); #if defined(USE_FTGL) if (Font::forceLegacyFonts == false) { try { { TextFTGL::faceResolution = Font::faceResolution; TextFTGL::langHeightText = Font::langHeightText; this->textHandler = NULL; this->textHandler = new TextFTGL(type); //SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] this->textHandler = [%p] Owner = [%p]\n", __FILE__, __FUNCTION__, __LINE__, this->textHandler, this); TextFTGL::faceResolution = Font::faceResolution; TextFTGL::langHeightText = Font::langHeightText; } metrics.setTextHandler(this->textHandler); } catch (exception &ex) { SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] Error [%s]\n", __FILE__, __FUNCTION__, __LINE__, ex.what()); textHandler = NULL; //SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] this->textHandler = [%p] Owner = [%p]\n", __FILE__, __FUNCTION__, __LINE__, this->textHandler, this); } } #endif } Font::~Font() { if (this->textHandler) { delete this->textHandler; } this->textHandler = NULL; metrics.setTextHandler(this->textHandler); //SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] this->textHandler = [%p] Owner = [%p]\n", __FILE__, __FUNCTION__, __LINE__, this->textHandler, this); } string Font::getType() const { return this->type; } void Font::setType(string typeX11, string typeGeneric, string typeGenericFamily) { if (textHandler) { try { this->type = typeGeneric; textHandler->init(typeGeneric, typeGenericFamily, textHandler->GetFaceSize()); metrics.setTextHandler(this->textHandler); } catch (exception &ex) { SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] Error [%s]\n", __FILE__, __FUNCTION__, __LINE__, ex.what()); textHandler = NULL; //SystemFlags::OutputDebug(SystemFlags::debugError, "In [%s::%s Line: %d] this->textHandler = [%p] Owner = [%p]\n", __FILE__, __FUNCTION__, __LINE__, this->textHandler, this); } } if (textHandler == NULL) { this->type = typeX11; } } void Font::setWidth(int width) { this->width = width; } int Font::getWidth() const { return width; } int Font::getSize() const { if (textHandler) { return textHandler->GetFaceSize(); } else { return size; } } void Font::setSize(int size) { if (textHandler) { return textHandler->SetFaceSize(size); } else { this->size = size; } } bool is_non_ASCII(const int &c) { return (c < 0) || (c >= 128); } //bool is_ASCII(const int &c) { // return !is_non_ASCII(c); //} void Font::bidi_cvt(string &str_) { #ifdef HAVE_FRIBIDI //const bool debugFribidi = false; //if(debugFribidi == true) printf("BEFORE: [%s]\n",str_.c_str()); string new_value = ""; bool hasSoftNewLines = false; bool hasHardNewLines = false; vector lines; if (str_.find("\\n") != str_.npos) { Tokenize(str_, lines, "\\n"); hasSoftNewLines = true; } else if (str_.find("\n") != str_.npos) { Tokenize(str_, lines, "\n"); hasHardNewLines = true; } else { lines.push_back(str_); } //if(debugFribidi == true) printf("Lines: %d\n",(int)lines.size()); for (int lineIndex = 0; lineIndex < (int) lines.size(); ++lineIndex) { if (lineIndex > 0) { if (hasSoftNewLines == true) { new_value += "\\n"; } else if (hasHardNewLines == true) { new_value += "\n"; } } str_ = lines[lineIndex]; //if(debugFribidi == true) printf("Line: %d [%s] Font::fontSupportMixedRightToLeft = %d\n",lineIndex,str_.c_str(),Font::fontSupportMixedRightToLeft); vector words; if (Font::fontSupportMixedRightToLeft == true) { if (str_.find(" ") != str_.npos) { Tokenize(str_, words, " "); } else { words.push_back(str_); } } else { words.push_back(str_); } vector wordList; wordList.reserve(words.size()); vector nonASCIIWordList; nonASCIIWordList.reserve(words.size()); for (int wordIndex = 0; wordIndex < (int) words.size(); ++wordIndex) { str_ = words[wordIndex]; //if(debugFribidi == true) printf("Word: %d [%s]\n",wordIndex,str_.c_str()); //FriBidi C string holding the original text (that is probably with logical hebrew) //FriBidiChar *logical = NULL; //FriBidi C string for the output text (that should be visual hebrew) //FriBidiChar *visual = NULL; //FriBidiStrIndex *ltov = NULL; //FriBidiStrIndex *vtol = NULL; //C string holding the originall text (not nnecessarily as unicode) //char *ip = NULL; //C string for the output text (not necessarily as unicode) //char *op = NULL; //Size to allocate for the char arrays int size = (int) str_.size() + 2; //Allocate memory: //It's probably way too much, but at least it's not too little FriBidiChar *logical = new FriBidiChar[size * 3]; FriBidiChar *visual = new FriBidiChar[size * 3]; char *ip = new char[size * 3]; char *op = new char[size * 3]; FriBidiStrIndex *ltov = new FriBidiStrIndex[size * 3]; FriBidiStrIndex *vtol = new FriBidiStrIndex[size * 3]; FriBidiCharType base; size_t len; //A bool type to see if conversion succeded fribidi_boolean log2vis; //Holds information telling fribidi to use UTF-8 FriBidiCharSet char_set_num; char_set_num = fribidi_parse_charset("UTF-8"); //Copy the given to string into the ip string strcpy(ip, str_.c_str()); //Find length of originall text len = strlen(ip); //Insert ip to logical as unicode (and find it's size now) len = fribidi_charset_to_unicode(char_set_num, ip, (FriBidiStrIndex) len, logical); base = FRIBIDI_TYPE_ON; //printf("STRIPPED: [%s]\n",str_.c_str()); //Convert logical text to visual log2vis = fribidi_log2vis(logical, (FriBidiStrIndex) len, &base, visual, ltov, vtol, NULL); bool is_converted = false; //If convertion was successful if (log2vis) { //Remove bidi marks (that we don't need) from the output text len = fribidi_remove_bidi_marks(visual, (FriBidiStrIndex) len, ltov, vtol, NULL); //Convert unicode string back to the encoding the input string was in fribidi_unicode_to_charset(char_set_num, visual, (FriBidiStrIndex) len, op); if (string(op) != str_) { is_converted = true; } //Insert the output string into the result str_ = op; //if(debugFribidi == true) printf("LOG2VIS: [%s]\n",str_.c_str()); } //printf("AFTER: [%s]\n",str_.c_str()); //Free allocated memory delete[] ltov; delete[] vtol; delete[] visual; delete[] logical; delete[] ip; delete[] op; if (Font::fontSupportMixedRightToLeft == true) { if (is_converted == true) { nonASCIIWordList.push_back(str_); if (wordIndex + 1 == (int) words.size()) { if (nonASCIIWordList.size() > 1) { std::reverse(nonASCIIWordList.begin(), nonASCIIWordList.end()); copy(nonASCIIWordList.begin(), nonASCIIWordList.end(), std::inserter(wordList, wordList.begin())); } else { if (wordList.empty() == false) { copy(nonASCIIWordList.begin(), nonASCIIWordList.end(), std::inserter(wordList, wordList.begin() + wordList.size())); } else { wordList = nonASCIIWordList; } } } } else { if (nonASCIIWordList.size() > 1) { std::reverse(nonASCIIWordList.begin(), nonASCIIWordList.end()); } copy(nonASCIIWordList.begin(), nonASCIIWordList.end(), std::inserter(wordList, wordList.begin())); nonASCIIWordList.clear(); wordList.push_back(str_); } } else { wordList.push_back(str_); } } //if(debugFribidi == true) printf("Building New Line: %d [%s]\n",lineIndex,new_value.c_str()); for (int wordIndex = 0; wordIndex < (int) wordList.size(); ++wordIndex) { //if(debugFribidi == true) printf("wordIndex: %d [%s]\n",wordIndex,wordList[wordIndex].c_str()); if (wordIndex > 0) { new_value += " "; } new_value += wordList[wordIndex]; } //if(debugFribidi == true) printf("New Line: %d [%s]\n",lineIndex,new_value.c_str()); } str_ = new_value; //if(debugFribidi == true) printf("NEW: [%s]\n",str_.c_str()); #endif } // =============================================== // class Font2D // =============================================== Font2D::Font2D(FontTextHandlerType type) : Font(type) { } // =============================================== // class Font3D // =============================================== Font3D::Font3D(FontTextHandlerType type) : Font(type) { depth = 10.f; } string findFontFamily(const char* font, const char *fontFamily) { string resultFile = ""; // If your compiler has a version older than 2.4.1 of fontconfig, you can tell cmake to // disable trying to use fontconfig via passing this to cmake: // -DWANT_FONTCONFIG=Off #ifdef HAVE_FONTCONFIG // Get default font via fontconfig if (!font && FcInit() && fontFamily) { FcResult result; FcFontSet *fs; FcPattern* pat; FcPattern *match; /* TRANSLATORS: If using the FTGL backend, this should be the font name of a font that contains all the Unicode characters in use in your translation. */ //pat = FcNameParse((FcChar8 *)"Gothic Uralic"); pat = FcNameParse((FcChar8 *) fontFamily); FcConfigSubstitute(0, pat, FcMatchPattern); //FcPatternDel(pat, FC_WEIGHT); //FcPatternAddInteger(pat, FC_WEIGHT, FC_WEIGHT_BOLD); FcDefaultSubstitute(pat); fs = FcFontSetCreate(); match = FcFontMatch(0, pat, &result); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("Trying fontconfig for fontfamily [%s]\n", fontFamily); if (match) FcFontSetAdd(fs, match); if (pat) FcPatternDestroy(pat); if (fs) { FcChar8* file; if (FcPatternGetString(fs->fonts[0], FC_FILE, 0, &file) == FcResultMatch) { resultFile = (const char*) file; } FcFontSetDestroy(fs); } // If your compiler has a version older than 2.4.1 of fontconfig, you can tell cmake to // disable trying to use fontconfig via passing this to cmake: // -DWANT_FONTCONFIG=Off FcFini(); } else { if (SystemFlags::VERBOSE_MODE_ENABLED) printf("******************* FONT CONFIG will not be called font [%s] fontFamily [%s]!\n", (font != NULL ? font : "null"), (fontFamily != NULL ? fontFamily : "null")); } #else if (SystemFlags::VERBOSE_MODE_ENABLED) printf("******************* NO FONT CONFIG ENABLED!\n"); #endif return resultFile; } void CHECK_FONT_PATH(const char *filename, const char *fontFamily, const char **font, const char **path) { *path = filename; if (*font == NULL && *path != NULL && strlen(*path) > 0 && fileExists(*path) == true) { *font = strdup(*path); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("#1 candidate font file exists [%s]\n", (*font != NULL ? *font : "null")); } if (SystemFlags::VERBOSE_MODE_ENABLED) printf("#1 Searching for font file [%s] result [%s]\n", (*path != NULL ? *path : "null"), (*font != NULL ? *font : "null")); if (*font == NULL && fontFamily != NULL && strlen(fontFamily) > 0) { if (SystemFlags::VERBOSE_MODE_ENABLED) printf("#2 Searching for font [%s] family [%s]\n", (*font != NULL ? *font : "null"), fontFamily); string fileFound = findFontFamily(*font, fontFamily); if (fileFound != "") { //*path = fileFound.c_str(); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("#2 candidate font file found [%s]\n", fileFound.c_str()); if (fileFound.length() > 0 && fileExists(fileFound) == true) { if (*font) free((void*) *font); *font = strdup(fileFound.c_str()); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("#2 candidate font file has been set[%s]\n", (*font != NULL ? *font : "null")); } *path = NULL; } if (SystemFlags::VERBOSE_MODE_ENABLED) printf("#2 Searching for font family [%s] result [%s]\n", fontFamily, (*font != NULL ? *font : "null")); } } const char* findFont(const char *firstFontToTry, const char *firstFontFamilyToTry) { const char* font = NULL; const char* path = NULL; string tryFont = ""; if (firstFontToTry != NULL || firstFontFamilyToTry != NULL) { if (firstFontToTry != NULL && strlen(firstFontToTry) > 0) { tryFont = firstFontToTry; #ifdef WIN32 replaceAll(tryFont, "/", "\\"); #endif CHECK_FONT_PATH(tryFont.c_str(), firstFontFamilyToTry, &font, &path); } else { CHECK_FONT_PATH(NULL, firstFontFamilyToTry, &font, &path); } } // Get user-specified font path string megaglest_font = safeCharPtrCopy(getenv("MEGAGLEST_FONT"), 8095); string megaglest_font_family = safeCharPtrCopy(getenv("MEGAGLEST_FONT_FAMILY"), 8095); if (megaglest_font != "" || megaglest_font_family != "") { if (megaglest_font != "") { tryFont = megaglest_font; if (Text::DEFAULT_FONT_PATH_ABSOLUTE != "") { tryFont = Text::DEFAULT_FONT_PATH_ABSOLUTE + "/" + extractFileFromDirectoryPath(tryFont); } #ifdef WIN32 replaceAll(tryFont, "/", "\\"); #endif CHECK_FONT_PATH(tryFont.c_str(), megaglest_font_family.c_str(), &font, &path); } else { CHECK_FONT_PATH(NULL, megaglest_font_family.c_str(), &font, &path); } } string data_path = Text::DEFAULT_FONT_PATH; string defaultFont = data_path + "data/core/fonts/LinBiolinum_RB.ttf";//LinBiolinum_Re-0.6.4.ttf if (Text::DEFAULT_FONT_PATH_ABSOLUTE != "") { data_path = Text::DEFAULT_FONT_PATH; defaultFont = data_path + "/LinBiolinum_RB.ttf";//LinBiolinum_Re-0.6.4.ttf } tryFont = defaultFont; #ifdef WIN32 replaceAll(tryFont, "/", "\\"); #endif CHECK_FONT_PATH(tryFont.c_str(), "Linux Biolinum O:style=Bold", &font, &path); #ifdef FONT_PATH // Get distro-specified font path CHECK_FONT_PATH(FONT_PATH, NULL, &font, &path); #endif CHECK_FONT_PATH("/usr/share/fonts/truetype/uralic/gothub__.ttf", "Gothic Uralic:style=Regular", &font, &path); // Check a couple of common paths for Gothic Uralic/bold as a last resort // Debian /* TRANSLATORS: If using the FTGL backend, this should be the path of a bold font that contains all the Unicode characters in use in your translation. If the font is available in Debian it should be the Debian path. */ CHECK_FONT_PATH("/usr/share/fonts/truetype/uralic/gothub__.ttf", "Gothic Uralic:style=Regular", &font, &path); /* TRANSLATORS: If using the FTGL backend, this should be the path of a font that contains all the Unicode characters in use in your translation. If the font is available in Debian it should be the Debian path. */ CHECK_FONT_PATH("/usr/share/fonts/truetype/uralic/gothu___.ttf", "Gothic Uralic:style=Regular", &font, &path); // Mandrake /* TRANSLATORS: If using the FTGL backend, this should be the path of a bold font that contains all the Unicode characters in use in your translation. If the font is available in Mandrake it should be the Mandrake path. */ CHECK_FONT_PATH("/usr/share/fonts/TTF/uralic/GOTHUB__.TTF", "Gothic Uralic:style=Bold", &font, &path); /* TRANSLATORS: If using the FTGL backend, this should be the path of a font that contains all the Unicode characters in use in your translation. If the font is available in Mandrake it should be the Mandrake path. */ CHECK_FONT_PATH("/usr/share/fonts/TTF/uralic/GOTHU___.TTF", "Gothic Uralic:style=Regular", &font, &path); // Check the non-translated versions of the above CHECK_FONT_PATH("/usr/share/fonts/truetype/uralic/gothub__.ttf", "Gothic Uralic:style=Regular", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/truetype/uralic/gothu___.ttf", "Gothic Uralic:style=Regular", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/TTF/uralic/GOTHUB__.TTF", "Gothic Uralic:style=Regular", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/TTF/uralic/GOTHU___.TTF", "Gothic Uralic:style=Regular", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/truetype/linux-libertine/LinLibertine_Re.ttf", "Linux Libertine O:style=Regular", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/truetype/freefont/FreeSerif.ttf", "FreeSerif", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/truetype/freefont/FreeSans.ttf", "FreeSans", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/truetype/freefont/FreeMono.ttf", "FreeMono", &font, &path); //openSUSE CHECK_FONT_PATH("/usr/share/fonts/truetype/LinBiolinum_RB.otf", "Bold", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/truetype/FreeSerif.ttf", "FreeSerif", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/truetype/FreeSans.ttf", "FreeSans", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/truetype/FreeMono.ttf", "FreeMono", &font, &path); // gentoo paths CHECK_FONT_PATH("/usr/share/fonts/freefont-ttf/FreeSerif.ttf", "FreeSerif", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/freefont-ttf/FreeSans.ttf", "FreeSans", &font, &path); CHECK_FONT_PATH("/usr/share/fonts/freefont-ttf/FreeMono.ttf", "FreeMono", &font, &path); #ifdef _WIN32 CHECK_FONT_PATH("c:\\windows\\fonts\\verdana.ttf", NULL, &font, &path); CHECK_FONT_PATH("c:\\windows\\fonts\\tahoma.ttf", NULL, &font, &path); CHECK_FONT_PATH("c:\\windows\\fonts\\arial.ttf", NULL, &font, &path); CHECK_FONT_PATH("\\windows\\fonts\\arial.ttf", NULL, &font, &path); #endif if (SystemFlags::VERBOSE_MODE_ENABLED) printf("Final selection of font file is [%s]\n", (font != NULL ? font : "null")); \ return font; } } }//end namespace