diff --git a/libs/openFrameworks/graphics/ofTrueTypeFont.cpp b/libs/openFrameworks/graphics/ofTrueTypeFont.cpp index 9619daac9bb..f59ee8c2cd5 100644 --- a/libs/openFrameworks/graphics/ofTrueTypeFont.cpp +++ b/libs/openFrameworks/graphics/ofTrueTypeFont.cpp @@ -8,7 +8,7 @@ #include #ifdef TARGET_LINUX -#include + #include #endif #include FT_FREETYPE_H @@ -17,70 +17,70 @@ #include FT_TRIGONOMETRY_H using std::max; -using std::vector; -using std::string; using std::min; +using std::string; +using std::vector; -const ofUnicode::range ofUnicode::Space {32, 32}; -const ofUnicode::range ofUnicode::IdeographicSpace {0x3000, 0x3000}; -const ofUnicode::range ofUnicode::Latin {32, 0x007F}; -const ofUnicode::range ofUnicode::Latin1Supplement {32,0x00FF}; -const ofUnicode::range ofUnicode::LatinA {0x0100,0x017F}; -const ofUnicode::range ofUnicode::Greek {0x0370, 0x03FF}; -const ofUnicode::range ofUnicode::Cyrillic {0x0400, 0x04FF}; -const ofUnicode::range ofUnicode::Arabic {0x0600, 0x077F}; -const ofUnicode::range ofUnicode::ArabicSupplement {0x0750, 0x077F}; -const ofUnicode::range ofUnicode::ArabicExtendedA {0x08A0, 0x08FF}; -const ofUnicode::range ofUnicode::Devanagari {0x0900, 0x097F}; -const ofUnicode::range ofUnicode::HangulJamo {0x1100, 0x11FF}; -const ofUnicode::range ofUnicode::VedicExtensions {0x1CD0, 0x1CFF}; -const ofUnicode::range ofUnicode::LatinExtendedAdditional {0x1E00, 0x1EFF}; -const ofUnicode::range ofUnicode::GreekExtended {0x1F00, 0x1FFF}; -const ofUnicode::range ofUnicode::GeneralPunctuation {0x2000, 0x206F}; -const ofUnicode::range ofUnicode::SuperAndSubScripts {0x2070, 0x209F}; -const ofUnicode::range ofUnicode::CurrencySymbols {0x20A0, 0x20CF}; -const ofUnicode::range ofUnicode::LetterLikeSymbols {0x2100, 0x214F}; -const ofUnicode::range ofUnicode::NumberForms {0x2150, 0x218F}; -const ofUnicode::range ofUnicode::Arrows {0x2190, 0x21FF}; -const ofUnicode::range ofUnicode::MathOperators {0x2200, 0x22FF}; -const ofUnicode::range ofUnicode::MiscTechnical {0x2300, 0x23FF}; -const ofUnicode::range ofUnicode::BoxDrawing {0x2500, 0x257F}; -const ofUnicode::range ofUnicode::BlockElement {0x2580, 0x259F}; -const ofUnicode::range ofUnicode::GeometricShapes {0x25A0, 0x25FF}; -const ofUnicode::range ofUnicode::MiscSymbols {0x2600, 0x26FF}; -const ofUnicode::range ofUnicode::Dingbats {0x2700, 0x27BF}; -const ofUnicode::range ofUnicode::CJKSymbolAndPunctuation {0x3001, 0x303F}; -const ofUnicode::range ofUnicode::Hiragana {0x3040, 0x309F}; -const ofUnicode::range ofUnicode::Katakana {0x30A0, 0x30FF}; -const ofUnicode::range ofUnicode::HangulCompatJamo {0x3130, 0x318F}; -const ofUnicode::range ofUnicode::KatakanaPhoneticExtensions {0x31F0, 0x31FF}; -const ofUnicode::range ofUnicode::CJKLettersAndMonths {0x3200, 0x32FF}; -const ofUnicode::range ofUnicode::CJKUnified {0x4E00, 0x9FD5}; -const ofUnicode::range ofUnicode::DevanagariExtended {0xA8E0, 0xA8FF}; -const ofUnicode::range ofUnicode::HangulExtendedA {0xA960, 0xA97F}; -const ofUnicode::range ofUnicode::HangulSyllables {0xAC00, 0xD7AF}; -const ofUnicode::range ofUnicode::HangulExtendedB {0xD7B0, 0xD7FF}; -const ofUnicode::range ofUnicode::AlphabeticPresentationForms {0xFB00, 0xFB4F}; -const ofUnicode::range ofUnicode::ArabicPresFormsA {0xFB50, 0xFDFF}; -const ofUnicode::range ofUnicode::ArabicPresFormsB {0xFE70, 0xFEFF}; -const ofUnicode::range ofUnicode::KatakanaHalfAndFullwidthForms {0xFF00, 0xFFEF}; -const ofUnicode::range ofUnicode::KanaSupplement {0x1B000, 0x1B0FF}; -const ofUnicode::range ofUnicode::RumiNumericalSymbols {0x10E60, 0x10E7F}; -const ofUnicode::range ofUnicode::ArabicMath {0x1EE00, 0x1EEFF}; -const ofUnicode::range ofUnicode::MiscSymbolsAndPictographs {0x1F300, 0x1F5FF}; -const ofUnicode::range ofUnicode::Emoticons {0x1F601, 0x1F64F}; -const ofUnicode::range ofUnicode::TransportAndMap {0x1F680, 0x1F6FF}; -const ofUnicode::range ofUnicode::EnclosedCharacters {0x24C2, 0x1F251}; -const ofUnicode::range ofUnicode::Uncategorized {0x00A9, 0x1F5FF}; -const ofUnicode::range ofUnicode::AdditionalEmoticons {0x1F600, 0x1F636}; -const ofUnicode::range ofUnicode::AdditionalTransportAndMap {0x1F681, 0x1F6C5}; -const ofUnicode::range ofUnicode::OtherAdditionalSymbols {0x1F30D, 0x1F567}; -const ofUnicode::range ofUnicode::UppercaseLatin {65, 90}; -const ofUnicode::range ofUnicode::LowercaseLatin {97, 122}; -const ofUnicode::range ofUnicode::Braces {123, 127}; -const ofUnicode::range ofUnicode::Numbers {48, 57}; -const ofUnicode::range ofUnicode::Symbols {33, 47}; -const ofUnicode::range ofUnicode::GenericSymbols {58, 64}; +const ofUnicode::range ofUnicode::Space { 32, 32 }; +const ofUnicode::range ofUnicode::IdeographicSpace { 0x3000, 0x3000 }; +const ofUnicode::range ofUnicode::Latin { 32, 0x007F }; +const ofUnicode::range ofUnicode::Latin1Supplement { 32, 0x00FF }; +const ofUnicode::range ofUnicode::LatinA { 0x0100, 0x017F }; +const ofUnicode::range ofUnicode::Greek { 0x0370, 0x03FF }; +const ofUnicode::range ofUnicode::Cyrillic { 0x0400, 0x04FF }; +const ofUnicode::range ofUnicode::Arabic { 0x0600, 0x077F }; +const ofUnicode::range ofUnicode::ArabicSupplement { 0x0750, 0x077F }; +const ofUnicode::range ofUnicode::ArabicExtendedA { 0x08A0, 0x08FF }; +const ofUnicode::range ofUnicode::Devanagari { 0x0900, 0x097F }; +const ofUnicode::range ofUnicode::HangulJamo { 0x1100, 0x11FF }; +const ofUnicode::range ofUnicode::VedicExtensions { 0x1CD0, 0x1CFF }; +const ofUnicode::range ofUnicode::LatinExtendedAdditional { 0x1E00, 0x1EFF }; +const ofUnicode::range ofUnicode::GreekExtended { 0x1F00, 0x1FFF }; +const ofUnicode::range ofUnicode::GeneralPunctuation { 0x2000, 0x206F }; +const ofUnicode::range ofUnicode::SuperAndSubScripts { 0x2070, 0x209F }; +const ofUnicode::range ofUnicode::CurrencySymbols { 0x20A0, 0x20CF }; +const ofUnicode::range ofUnicode::LetterLikeSymbols { 0x2100, 0x214F }; +const ofUnicode::range ofUnicode::NumberForms { 0x2150, 0x218F }; +const ofUnicode::range ofUnicode::Arrows { 0x2190, 0x21FF }; +const ofUnicode::range ofUnicode::MathOperators { 0x2200, 0x22FF }; +const ofUnicode::range ofUnicode::MiscTechnical { 0x2300, 0x23FF }; +const ofUnicode::range ofUnicode::BoxDrawing { 0x2500, 0x257F }; +const ofUnicode::range ofUnicode::BlockElement { 0x2580, 0x259F }; +const ofUnicode::range ofUnicode::GeometricShapes { 0x25A0, 0x25FF }; +const ofUnicode::range ofUnicode::MiscSymbols { 0x2600, 0x26FF }; +const ofUnicode::range ofUnicode::Dingbats { 0x2700, 0x27BF }; +const ofUnicode::range ofUnicode::CJKSymbolAndPunctuation { 0x3001, 0x303F }; +const ofUnicode::range ofUnicode::Hiragana { 0x3040, 0x309F }; +const ofUnicode::range ofUnicode::Katakana { 0x30A0, 0x30FF }; +const ofUnicode::range ofUnicode::HangulCompatJamo { 0x3130, 0x318F }; +const ofUnicode::range ofUnicode::KatakanaPhoneticExtensions { 0x31F0, 0x31FF }; +const ofUnicode::range ofUnicode::CJKLettersAndMonths { 0x3200, 0x32FF }; +const ofUnicode::range ofUnicode::CJKUnified { 0x4E00, 0x9FD5 }; +const ofUnicode::range ofUnicode::DevanagariExtended { 0xA8E0, 0xA8FF }; +const ofUnicode::range ofUnicode::HangulExtendedA { 0xA960, 0xA97F }; +const ofUnicode::range ofUnicode::HangulSyllables { 0xAC00, 0xD7AF }; +const ofUnicode::range ofUnicode::HangulExtendedB { 0xD7B0, 0xD7FF }; +const ofUnicode::range ofUnicode::AlphabeticPresentationForms { 0xFB00, 0xFB4F }; +const ofUnicode::range ofUnicode::ArabicPresFormsA { 0xFB50, 0xFDFF }; +const ofUnicode::range ofUnicode::ArabicPresFormsB { 0xFE70, 0xFEFF }; +const ofUnicode::range ofUnicode::KatakanaHalfAndFullwidthForms { 0xFF00, 0xFFEF }; +const ofUnicode::range ofUnicode::KanaSupplement { 0x1B000, 0x1B0FF }; +const ofUnicode::range ofUnicode::RumiNumericalSymbols { 0x10E60, 0x10E7F }; +const ofUnicode::range ofUnicode::ArabicMath { 0x1EE00, 0x1EEFF }; +const ofUnicode::range ofUnicode::MiscSymbolsAndPictographs { 0x1F300, 0x1F5FF }; +const ofUnicode::range ofUnicode::Emoticons { 0x1F601, 0x1F64F }; +const ofUnicode::range ofUnicode::TransportAndMap { 0x1F680, 0x1F6FF }; +const ofUnicode::range ofUnicode::EnclosedCharacters { 0x24C2, 0x1F251 }; +const ofUnicode::range ofUnicode::Uncategorized { 0x00A9, 0x1F5FF }; +const ofUnicode::range ofUnicode::AdditionalEmoticons { 0x1F600, 0x1F636 }; +const ofUnicode::range ofUnicode::AdditionalTransportAndMap { 0x1F681, 0x1F6C5 }; +const ofUnicode::range ofUnicode::OtherAdditionalSymbols { 0x1F30D, 0x1F567 }; +const ofUnicode::range ofUnicode::UppercaseLatin { 65, 90 }; +const ofUnicode::range ofUnicode::LowercaseLatin { 97, 122 }; +const ofUnicode::range ofUnicode::Braces { 123, 127 }; +const ofUnicode::range ofUnicode::Numbers { 48, 57 }; +const ofUnicode::range ofUnicode::Symbols { 33, 47 }; +const ofUnicode::range ofUnicode::GenericSymbols { 58, 64 }; const std::initializer_list ofAlphabet::Emoji { ofUnicode::Space, @@ -156,16 +156,16 @@ const std::initializer_list ofAlphabet::Cyrillic { ofUnicode::Cyrillic }; -const ofTrueTypeFont::glyphProps ofTrueTypeFont::invalidProps{ +const ofTrueTypeFont::glyphProps ofTrueTypeFont::invalidProps { 0, 0, 0, 0, - 0,0, - 0,0,0,0, + 0, 0, + 0, 0, 0, 0, 0, - 0.0f,0.0f, - 0.0f,0.0f,0.0f,0.0f + 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f }; const size_t TAB_WIDTH = 4; /// Number of spaces per tab @@ -176,7 +176,7 @@ static bool librariesInitialized = false; static FT_Library library; //-------------------------------------------------------- -void ofTrueTypeShutdown(){ +void ofTrueTypeShutdown() { #ifdef TARGET_LINUX // This crashes if gtk was called at any time cause pango leaks // its fc related objects: https://github.com/openframeworks/openFrameworks/issues/5061 @@ -185,35 +185,35 @@ void ofTrueTypeShutdown(){ } //-------------------------------------------------------- -static ofPath makeContoursForCharacter(FT_Face face){ +static ofPath makeContoursForCharacter(FT_Face face) { ofPath charOutlines; charOutlines.setUseShapeColor(false); charOutlines.setPolyWindingMode(OF_POLY_WINDING_NONZERO); - auto moveTo = [](const FT_Vector*to, void * userData){ - ofPath * charOutlines = static_cast(userData); + auto moveTo = [](const FT_Vector * to, void * userData) { + ofPath * charOutlines = static_cast(userData); charOutlines->moveTo(ofTrueTypeFont::int26p6_to_dbl(to->x), ofTrueTypeFont::int26p6_to_dbl(-to->y)); return 0; }; - auto lineTo = [](const FT_Vector*to, void * userData){ - ofPath * charOutlines = static_cast(userData); + auto lineTo = [](const FT_Vector * to, void * userData) { + ofPath * charOutlines = static_cast(userData); charOutlines->lineTo(ofTrueTypeFont::int26p6_to_dbl(to->x), ofTrueTypeFont::int26p6_to_dbl(-to->y)); return 0; }; - auto conicTo = [](const FT_Vector*cp, const FT_Vector*to, void * userData){ - ofPath * charOutlines = static_cast(userData); + auto conicTo = [](const FT_Vector * cp, const FT_Vector * to, void * userData) { + ofPath * charOutlines = static_cast(userData); auto lastP = charOutlines->getCommands().back().to; charOutlines->quadBezierTo(lastP, { ofTrueTypeFont::int26p6_to_dbl(cp->x), ofTrueTypeFont::int26p6_to_dbl(-cp->y) }, - {ofTrueTypeFont::int26p6_to_dbl(to->x), ofTrueTypeFont::int26p6_to_dbl(-to->y)}); + { ofTrueTypeFont::int26p6_to_dbl(to->x), ofTrueTypeFont::int26p6_to_dbl(-to->y) }); return 0; }; - auto cubicTo = [](const FT_Vector*cp1, const FT_Vector*cp2, const FT_Vector*to, void * userData){ - ofPath * charOutlines = static_cast(userData); - charOutlines->bezierTo({ofTrueTypeFont::int26p6_to_dbl(cp1->x), ofTrueTypeFont::int26p6_to_dbl(-cp1->y)}, - {ofTrueTypeFont::int26p6_to_dbl(cp2->x), ofTrueTypeFont::int26p6_to_dbl(-cp2->y)}, - {ofTrueTypeFont::int26p6_to_dbl(to->x), ofTrueTypeFont::int26p6_to_dbl(-to->y)}); + auto cubicTo = [](const FT_Vector * cp1, const FT_Vector * cp2, const FT_Vector * to, void * userData) { + ofPath * charOutlines = static_cast(userData); + charOutlines->bezierTo({ ofTrueTypeFont::int26p6_to_dbl(cp1->x), ofTrueTypeFont::int26p6_to_dbl(-cp1->y) }, + { ofTrueTypeFont::int26p6_to_dbl(cp2->x), ofTrueTypeFont::int26p6_to_dbl(-cp2->y) }, + { ofTrueTypeFont::int26p6_to_dbl(to->x), ofTrueTypeFont::int26p6_to_dbl(-to->y) }); return 0; }; - FT_Outline_Funcs funcs{ + FT_Outline_Funcs funcs { moveTo, lineTo, conicTo, @@ -233,13 +233,13 @@ static ofPath makeContoursForCharacter(FT_Face face){ #include //------------------------------------------------------------------ -static string osxFontPathByName(const string& fontname){ +static string osxFontPathByName(const string & fontname) { CFStringRef targetName = CFStringCreateWithCString(nullptr, fontname.c_str(), kCFStringEncodingUTF8); CTFontDescriptorRef targetDescriptor = CTFontDescriptorCreateWithNameAndSize(targetName, 0.0); - CFURLRef targetURL = (CFURLRef) CTFontDescriptorCopyAttribute(targetDescriptor, kCTFontURLAttribute); + CFURLRef targetURL = (CFURLRef)CTFontDescriptorCopyAttribute(targetDescriptor, kCTFontURLAttribute); string fontPath = ""; - if(targetURL) { + if (targetURL) { UInt8 buffer[PATH_MAX]; CFURLGetFileSystemRepresentation(targetURL, true, buffer, PATH_MAX); fontPath = string((char *)buffer); @@ -254,20 +254,20 @@ static string osxFontPathByName(const string& fontname){ #endif #ifdef TARGET_WIN32 -#include + #include // font font face -> file name name mapping // FIXME: second -> fs::path static std::unordered_map fonts_table; // read font linking information from registry, and store in std::map //------------------------------------------------------------------ -void initWindows(){ +void initWindows() { LONG l_ret; - const wchar_t *Fonts = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; + const wchar_t * Fonts = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; HKEY key_ft; l_ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, Fonts, 0, KEY_QUERY_VALUE, &key_ft); - if (l_ret != ERROR_SUCCESS){ + if (l_ret != ERROR_SUCCESS) { ofLogError("ofTrueTypeFont") << "initWindows(): couldn't find fonts registery key"; return; } @@ -275,26 +275,25 @@ void initWindows(){ DWORD value_count; DWORD max_data_len; wchar_t value_name[2048]; - BYTE *value_data; - + BYTE * value_data; // get font_file_name -> font_face mapping from the "Fonts" registry key l_ret = RegQueryInfoKeyW(key_ft, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &value_count, nullptr, &max_data_len, nullptr, nullptr); - if(l_ret != ERROR_SUCCESS){ + if (l_ret != ERROR_SUCCESS) { ofLogError("ofTrueTypeFont") << "initWindows(): couldn't query registery for fonts"; return; } // no font installed - if (value_count == 0){ + if (value_count == 0) { ofLogError("ofTrueTypeFont") << "initWindows(): couldn't find any fonts in registery"; return; } // max_data_len is in BYTE value_data = static_cast(HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, max_data_len)); - if(value_data == nullptr) return; + if (value_data == nullptr) return; char value_name_char[2048]; char value_data_char[2048]; @@ -302,65 +301,63 @@ void initWindows(){ char fontsPath[2048]; SHGetKnownFolderIDList(FOLDERID_Fonts, 0, nullptr, &ppidl); SHGetPathFromIDList(ppidl,&fontsPath);*/ - string fontsDir = getenv ("windir"); + string fontsDir = getenv("windir"); fontsDir += "\\Fonts\\"; - for (DWORD i = 0; i < value_count; ++i) - { - DWORD name_len = 2048; - DWORD data_len = max_data_len; - - l_ret = RegEnumValueW(key_ft, i, value_name, &name_len, nullptr, nullptr, value_data, &data_len); - if(l_ret != ERROR_SUCCESS){ - ofLogError("ofTrueTypeFont") << "initWindows(): couldn't read registry key for font type"; - continue; - } + for (DWORD i = 0; i < value_count; ++i) { + DWORD name_len = 2048; + DWORD data_len = max_data_len; + + l_ret = RegEnumValueW(key_ft, i, value_name, &name_len, nullptr, nullptr, value_data, &data_len); + if (l_ret != ERROR_SUCCESS) { + ofLogError("ofTrueTypeFont") << "initWindows(): couldn't read registry key for font type"; + continue; + } - wcstombs(value_name_char,value_name,2048); - wcstombs(value_data_char,reinterpret_cast(value_data),2048); - string curr_face = value_name_char; - string font_file = value_data_char; - curr_face = curr_face.substr(0, curr_face.find('(') - 1); - curr_face = ofToLower(curr_face); - fonts_table[curr_face] = fontsDir + font_file; + wcstombs(value_name_char, value_name, 2048); + wcstombs(value_data_char, reinterpret_cast(value_data), 2048); + string curr_face = value_name_char; + string font_file = value_data_char; + curr_face = curr_face.substr(0, curr_face.find('(') - 1); + curr_face = ofToLower(curr_face); + fonts_table[curr_face] = fontsDir + font_file; } - HeapFree(GetProcessHeap(), 0, value_data); l_ret = RegCloseKey(key_ft); } - -static string winFontPathByName(const string & fontname){ +static string winFontPathByName(const string & fontname) { return fonts_table[fontname]; } #endif #ifdef TARGET_LINUX //------------------------------------------------------------------ -static string linuxFontPathByName(const string & fontname){ +static string linuxFontPathByName(const string & fontname) { + string filename; - FcPattern * pattern = FcNameParse((const FcChar8*)fontname.c_str()); - FcBool ret = FcConfigSubstitute(0,pattern,FcMatchPattern); - if(!ret){ + FcPattern * pattern = FcNameParse((const FcChar8 *)fontname.c_str()); + FcBool ret = FcConfigSubstitute(0, pattern, FcMatchPattern); + if (!ret) { ofLogError() << "linuxFontPathByName(): couldn't find font file or system font with name \"" << fontname << "\""; return ""; } FcDefaultSubstitute(pattern); FcResult result; - FcPattern * fontMatch=nullptr; - fontMatch = FcFontMatch(0,pattern,&result); + FcPattern * fontMatch = nullptr; + fontMatch = FcFontMatch(0, pattern, &result); - if(!fontMatch){ + if (!fontMatch) { ofLogError() << "linuxFontPathByName(): couldn't match font file or system font with name \"" << fontname << "\""; FcPatternDestroy(fontMatch); FcPatternDestroy(pattern); return ""; } - FcChar8 *file; - if (FcPatternGetString (fontMatch, FC_FILE, 0, &file) == FcResultMatch){ - filename = (const char*)file; - }else{ + FcChar8 * file; + if (FcPatternGetString(fontMatch, FC_FILE, 0, &file) == FcResultMatch) { + filename = (const char *)file; + } else { ofLogError() << "linuxFontPathByName(): couldn't find font match for \"" << fontname << "\""; FcPatternDestroy(fontMatch); FcPatternDestroy(pattern); @@ -373,6 +370,7 @@ static string linuxFontPathByName(const string & fontname){ #endif //----------------------------------------------------------- + // FIXME: it seems first parameter is string because it represents the font name only static bool loadFontFace(const of::filesystem::path & _fontname, FT_Face & face, of::filesystem::path & filename, int index){ auto fontname = _fontname; @@ -383,26 +381,26 @@ static bool loadFontFace(const of::filesystem::path & _fontname, FT_Face & face, // FIXME: fs::path in input and output filename = linuxFontPathByName(_fontname.string()); #elif defined(TARGET_OSX) - if(fontname==OF_TTF_SANS){ + if (fontname == OF_TTF_SANS) { fontname = "Helvetica Neue"; - #if MAC_OS_X_VERSION_10_13 && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - fontID = 0; - #else - fontID = 4; - #endif - }else if(fontname==OF_TTF_SERIF){ + #if MAC_OS_X_VERSION_10_13 && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 + fontID = 0; + #else + fontID = 4; + #endif + } else if (fontname == OF_TTF_SERIF) { fontname = "Times New Roman"; - }else if(fontname==OF_TTF_MONO){ + } else if (fontname == OF_TTF_MONO) { fontname = "Menlo Regular"; } // FIXME: fs::path in input and output filename = osxFontPathByName(fontname.string()); #elif defined(TARGET_WIN32) - if(fontname==OF_TTF_SANS){ + if (fontname == OF_TTF_SANS) { fontname = "Arial"; - }else if(fontname==OF_TTF_SERIF){ + } else if (fontname == OF_TTF_SERIF) { fontname = "Times New Roman"; - }else if(fontname==OF_TTF_MONO){ + } else if (fontname == OF_TTF_MONO) { fontname = "Courier New"; } // FIXME: fs::path in input and output @@ -415,6 +413,7 @@ static bool loadFontFace(const of::filesystem::path & _fontname, FT_Face & face, ofLogVerbose("ofTrueTypeFont") << "loadFontFace(): " << fontname << " not a file in data loading system font from " << filename; } FT_Error err; + err = FT_New_Face( library, ofPathToString(filename).c_str(), fontID, &face ); if (err) { // simple error table in lieu of full table (see fterrors.h) @@ -428,28 +427,27 @@ static bool loadFontFace(const of::filesystem::path & _fontname, FT_Face & face, } //-------------------------------------------------------- -void ofTrueTypeFont::setGlobalDpi(int newDpi){ +void ofTrueTypeFont::setGlobalDpi(int newDpi) { ttfGlobalDpi = newDpi; } - #if defined(TARGET_ANDROID) -#include "ofxAndroidUtils.h" + #include "ofxAndroidUtils.h" #endif //------------------------------------------------------------------ -bool ofTrueTypeFont::initLibraries(){ - if(!librariesInitialized){ +bool ofTrueTypeFont::initLibraries() { + if (!librariesInitialized) { FT_Error err; - err = FT_Init_FreeType( &library ); + err = FT_Init_FreeType(&library); - if (err){ + if (err) { ofLogError("ofTrueTypeFont") << "loadFont(): couldn't initialize Freetype lib: FT_Error " << err; return false; } #ifdef TARGET_LINUX FcBool result = FcInit(); - if(!result){ + if (!result) { return false; } #endif @@ -461,11 +459,10 @@ bool ofTrueTypeFont::initLibraries(){ return true; } - //------------------------------------------------------------------ ofTrueTypeFont::ofTrueTypeFont() -:settings("",0){ - bLoadedOk = false; + : settings("", 0) { + bLoadedOk = false; letterSpacing = 1; spaceSize = 1; fontUnitScale = 1; @@ -476,20 +473,20 @@ ofTrueTypeFont::ofTrueTypeFont() } //------------------------------------------------------------------ -ofTrueTypeFont::~ofTrueTypeFont(){ - #if defined(TARGET_ANDROID) - ofRemoveListener(ofxAndroidEvents().unloadGL,this,&ofTrueTypeFont::unloadTextures); - ofRemoveListener(ofxAndroidEvents().reloadGL,this,&ofTrueTypeFont::reloadTextures); - #endif +ofTrueTypeFont::~ofTrueTypeFont() { +#if defined(TARGET_ANDROID) + ofRemoveListener(ofxAndroidEvents().unloadGL, this, &ofTrueTypeFont::unloadTextures); + ofRemoveListener(ofxAndroidEvents().reloadGL, this, &ofTrueTypeFont::reloadTextures); +#endif } //------------------------------------------------------------------ -ofTrueTypeFont::ofTrueTypeFont(const ofTrueTypeFont& mom) -:settings(mom.settings){ +ofTrueTypeFont::ofTrueTypeFont(const ofTrueTypeFont & mom) + : settings(mom.settings) { #if defined(TARGET_ANDROID) - if(mom.isLoaded()){ - ofAddListener(ofxAndroidEvents().unloadGL,this,&ofTrueTypeFont::unloadTextures); - ofAddListener(ofxAndroidEvents().reloadGL,this,&ofTrueTypeFont::reloadTextures); + if (mom.isLoaded()) { + ofAddListener(ofxAndroidEvents().unloadGL, this, &ofTrueTypeFont::unloadTextures); + ofAddListener(ofxAndroidEvents().reloadGL, this, &ofTrueTypeFont::reloadTextures); } #endif bLoadedOk = mom.bLoadedOk; @@ -515,12 +512,12 @@ ofTrueTypeFont::ofTrueTypeFont(const ofTrueTypeFont& mom) } //------------------------------------------------------------------ -ofTrueTypeFont & ofTrueTypeFont::operator=(const ofTrueTypeFont& mom){ - if(this == &mom) return *this; +ofTrueTypeFont & ofTrueTypeFont::operator=(const ofTrueTypeFont & mom) { + if (this == &mom) return *this; #if defined(TARGET_ANDROID) - if(mom.isLoaded()){ - ofAddListener(ofxAndroidEvents().unloadGL,this,&ofTrueTypeFont::unloadTextures); - ofAddListener(ofxAndroidEvents().reloadGL,this,&ofTrueTypeFont::reloadTextures); + if (mom.isLoaded()) { + ofAddListener(ofxAndroidEvents().unloadGL, this, &ofTrueTypeFont::unloadTextures); + ofAddListener(ofxAndroidEvents().reloadGL, this, &ofTrueTypeFont::reloadTextures); } #endif settings = mom.settings; @@ -549,12 +546,12 @@ ofTrueTypeFont & ofTrueTypeFont::operator=(const ofTrueTypeFont& mom){ } //------------------------------------------------------------------ -ofTrueTypeFont::ofTrueTypeFont(ofTrueTypeFont&& mom) -:settings(std::move(mom.settings)){ +ofTrueTypeFont::ofTrueTypeFont(ofTrueTypeFont && mom) + : settings(std::move(mom.settings)) { #if defined(TARGET_ANDROID) - if(mom.isLoaded()){ - ofAddListener(ofxAndroidEvents().unloadGL,this,&ofTrueTypeFont::unloadTextures); - ofAddListener(ofxAndroidEvents().reloadGL,this,&ofTrueTypeFont::reloadTextures); + if (mom.isLoaded()) { + ofAddListener(ofxAndroidEvents().unloadGL, this, &ofTrueTypeFont::unloadTextures); + ofAddListener(ofxAndroidEvents().reloadGL, this, &ofTrueTypeFont::reloadTextures); } #endif bLoadedOk = mom.bLoadedOk; @@ -580,12 +577,12 @@ ofTrueTypeFont::ofTrueTypeFont(ofTrueTypeFont&& mom) } //------------------------------------------------------------------ -ofTrueTypeFont & ofTrueTypeFont::operator=(ofTrueTypeFont&& mom){ - if(this == &mom) return *this; +ofTrueTypeFont & ofTrueTypeFont::operator=(ofTrueTypeFont && mom) { + if (this == &mom) return *this; #if defined(TARGET_ANDROID) - if(mom.isLoaded()){ - ofAddListener(ofxAndroidEvents().unloadGL,this,&ofTrueTypeFont::unloadTextures); - ofAddListener(ofxAndroidEvents().reloadGL,this,&ofTrueTypeFont::reloadTextures); + if (mom.isLoaded()) { + ofAddListener(ofxAndroidEvents().unloadGL, this, &ofTrueTypeFont::unloadTextures); + ofAddListener(ofxAndroidEvents().reloadGL, this, &ofTrueTypeFont::reloadTextures); } #endif bLoadedOk = mom.bLoadedOk; @@ -612,14 +609,14 @@ ofTrueTypeFont & ofTrueTypeFont::operator=(ofTrueTypeFont&& mom){ } //----------------------------------------------------------- -void ofTrueTypeFont::unloadTextures(){ - if(!bLoadedOk) return; +void ofTrueTypeFont::unloadTextures() { + if (!bLoadedOk) return; texAtlas.clear(); } //----------------------------------------------------------- -void ofTrueTypeFont::reloadTextures(){ - if(bLoadedOk) load(settings); +void ofTrueTypeFont::reloadTextures() { + if (bLoadedOk) load(settings); } //----------------------------------------------------------- @@ -628,66 +625,66 @@ bool ofTrueTypeFont::loadFont(string filename, int fontSize, bool bAntiAliased, } //----------------------------------------------------------- -ofTrueTypeFont::glyph ofTrueTypeFont::loadGlyph(uint32_t utf8) const{ +ofTrueTypeFont::glyph ofTrueTypeFont::loadGlyph(uint32_t utf8) const { glyph aGlyph; - auto err = FT_Load_Glyph( face.get(), FT_Get_Char_Index( face.get(), utf8 ), settings.antialiased ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_DEFAULT ); - if(err){ + auto err = FT_Load_Glyph(face.get(), FT_Get_Char_Index(face.get(), utf8), settings.antialiased ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_DEFAULT); + if (err) { ofLogError("ofTrueTypeFont") << "loadFont(): FT_Load_Glyph failed for utf8 code " << utf8 << ": FT_Error " << err; return aGlyph; } - if (settings.antialiased) FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); - else FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO); - + if (settings.antialiased) + FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + else + FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO); // ------------------------- // info about the character: - aGlyph.props.glyph = utf8; - aGlyph.props.height = int26p6_to_dbl(face->glyph->metrics.height); - aGlyph.props.width = int26p6_to_dbl(face->glyph->metrics.width); - aGlyph.props.bearingX = int26p6_to_dbl(face->glyph->metrics.horiBearingX); - aGlyph.props.bearingY = int26p6_to_dbl(face->glyph->metrics.horiBearingY); - aGlyph.props.xmin = face->glyph->bitmap_left; - aGlyph.props.xmax = aGlyph.props.xmin + aGlyph.props.width; - aGlyph.props.ymin = -face->glyph->bitmap_top; - aGlyph.props.ymax = aGlyph.props.ymin + aGlyph.props.height; - aGlyph.props.advance = int26p6_to_dbl(face->glyph->metrics.horiAdvance); - aGlyph.props.tW = aGlyph.props.width; - aGlyph.props.tH = aGlyph.props.height; - - FT_Bitmap& bitmap= face->glyph->bitmap; - int width = bitmap.width; + aGlyph.props.glyph = utf8; + aGlyph.props.height = int26p6_to_dbl(face->glyph->metrics.height); + aGlyph.props.width = int26p6_to_dbl(face->glyph->metrics.width); + aGlyph.props.bearingX = int26p6_to_dbl(face->glyph->metrics.horiBearingX); + aGlyph.props.bearingY = int26p6_to_dbl(face->glyph->metrics.horiBearingY); + aGlyph.props.xmin = face->glyph->bitmap_left; + aGlyph.props.xmax = aGlyph.props.xmin + aGlyph.props.width; + aGlyph.props.ymin = -face->glyph->bitmap_top; + aGlyph.props.ymax = aGlyph.props.ymin + aGlyph.props.height; + aGlyph.props.advance = int26p6_to_dbl(face->glyph->metrics.horiAdvance); + aGlyph.props.tW = aGlyph.props.width; + aGlyph.props.tH = aGlyph.props.height; + + FT_Bitmap & bitmap = face->glyph->bitmap; + int width = bitmap.width; int height = bitmap.rows; - if(width==0 || height==0) return aGlyph; + if (width == 0 || height == 0) return aGlyph; // Allocate Memory For The Texture Data. aGlyph.pixels.allocate(width, height, OF_PIXELS_GRAY_ALPHA); //-------------------------------- clear data: - aGlyph.pixels.set(0,255); // every luminance pixel = 255 - aGlyph.pixels.set(1,0); - + aGlyph.pixels.set(0, 255); // every luminance pixel = 255 + aGlyph.pixels.set(1, 0); - if (settings.antialiased == true){ + if (settings.antialiased == true) { ofPixels bitmapPixels; - bitmapPixels.setFromExternalPixels(bitmap.buffer,bitmap.width,bitmap.rows,OF_PIXELS_GRAY); - aGlyph.pixels.setChannel(1,bitmapPixels); + bitmapPixels.setFromExternalPixels(bitmap.buffer, bitmap.width, bitmap.rows, OF_PIXELS_GRAY); + aGlyph.pixels.setChannel(1, bitmapPixels); } else { //----------------------------------- // true type packs monochrome info in a // 1-bit format, hella funky // here we unpack it: - unsigned char *src = bitmap.buffer; - for(unsigned int j=0; j (loadFace,FT_Done_Face); + face = std::shared_ptr(loadFace, FT_Done_Face); - if(settings.ranges.empty()){ + if (settings.ranges.empty()) { settings.ranges.push_back(ofUnicode::Latin1Supplement); } int border = 1; - - FT_Set_Char_Size( face.get(), dbl_to_int26p6(settings.fontSize), dbl_to_int26p6(settings.fontSize), settings.dpi, settings.dpi); + FT_Set_Char_Size(face.get(), dbl_to_int26p6(settings.fontSize), dbl_to_int26p6(settings.fontSize), settings.dpi, settings.dpi); fontUnitScale = (float(settings.fontSize * settings.dpi)) / (72 * face->units_per_EM); lineHeight = face->height * fontUnitScale; ascenderHeight = face->ascender * fontUnitScale; descenderHeight = face->descender * fontUnitScale; glyphBBox.set(face->bbox.xMin * fontUnitScale, - face->bbox.yMin * fontUnitScale, - (face->bbox.xMax - face->bbox.xMin) * fontUnitScale, - (face->bbox.yMax - face->bbox.yMin) * fontUnitScale); + face->bbox.yMin * fontUnitScale, + (face->bbox.xMax - face->bbox.xMin) * fontUnitScale, + (face->bbox.yMax - face->bbox.yMin) * fontUnitScale); //--------------- initialize character info and textures auto nGlyphs = std::accumulate(settings.ranges.begin(), settings.ranges.end(), 0u, - [](uint32_t acc, ofUnicode::range range){ - return acc + range.getNumGlyphs(); - }); + [](uint32_t acc, ofUnicode::range range) { + return acc + range.getNumGlyphs(); + }); cps.resize(nGlyphs); - if(settings.contours){ + if (settings.contours) { charOutlines.resize(nGlyphs); charOutlinesNonVFlipped.resize(nGlyphs); charOutlinesContour.resize(nGlyphs); charOutlinesNonVFlippedContour.resize(nGlyphs); - }else{ + } else { charOutlines.resize(1); } vector all_glyphs; - uint32_t areaSum=0; + uint32_t areaSum = 0; //--------------------- load each char ----------------------- auto i = 0u; - for(auto & range: settings.ranges){ - for (uint32_t g = range.begin; g <= range.end; g++, i++){ + for (auto & range : settings.ranges) { + for (uint32_t g = range.begin; g <= range.end; g++, i++) { all_glyphs.push_back(loadGlyph(g)); - all_glyphs[i].props.characterIndex = i; + all_glyphs[i].props.characterIndex = i; glyphIndexMap[g] = i; cps[i] = all_glyphs[i].props; - areaSum += (cps[i].tW+border*2)*(cps[i].tH+border*2); + areaSum += (cps[i].tW + border * 2) * (cps[i].tH + border * 2); - if(settings.contours){ - if(printVectorInfo){ + if (settings.contours) { + if (printVectorInfo) { string str; - ofUTF8Append(str,g); - ofLogNotice("ofTrueTypeFont") << "character " << str; + ofUTF8Append(str, g); + ofLogNotice("ofTrueTypeFont") << "character " << str; } //int character = i + NUM_CHARACTER_TO_START; - charOutlines[i] = makeContoursForCharacter( face.get() ); + charOutlines[i] = makeContoursForCharacter(face.get()); charOutlinesContour[i] = charOutlines[i]; charOutlinesContour[i].setFilled(false); charOutlinesContour[i].setStrokeWidth(1); charOutlinesNonVFlipped[i] = charOutlines[i]; - charOutlinesNonVFlipped[i].translate({0,cps[i].height,0.f}); - charOutlinesNonVFlipped[i].scale(1,-1); + charOutlinesNonVFlipped[i].translate({ 0, cps[i].height, 0.f }); + charOutlinesNonVFlipped[i].scale(1, -1); charOutlinesNonVFlippedContour[i] = charOutlines[i]; charOutlinesNonVFlippedContour[i].setFilled(false); charOutlinesNonVFlippedContour[i].setStrokeWidth(1); - - if(settings.simplifyAmt>0){ + if (settings.simplifyAmt > 0) { charOutlines[i].simplify(settings.simplifyAmt); charOutlinesNonVFlipped[i].simplify(settings.simplifyAmt); charOutlinesContour[i].simplify(settings.simplifyAmt); @@ -812,9 +807,11 @@ bool ofTrueTypeFont::load(const ofTrueTypeFontSettings & _settings){ } vector sortedCopy = cps; - sort(sortedCopy.begin(),sortedCopy.end(),[](const ofTrueTypeFont::glyphProps & c1, const ofTrueTypeFont::glyphProps & c2){ - if(c1.tH == c2.tH) return c1.tW > c2.tW; - else return c1.tH > c2.tH; + sort(sortedCopy.begin(), sortedCopy.end(), [](const ofTrueTypeFont::glyphProps & c1, const ofTrueTypeFont::glyphProps & c2) { + if (c1.tH == c2.tH) + return c1.tW > c2.tW; + else + return c1.tH > c2.tH; }); // pack in a texture, algorithm to calculate min w/h from @@ -822,74 +819,70 @@ bool ofTrueTypeFont::load(const ofTrueTypeFontSettings & _settings){ //ofLogNotice("ofTrueTypeFont") << "loadFont(): areaSum: " << areaSum bool packed = false; - float alpha = logf(areaSum)*1.44269f; + float alpha = logf(areaSum) * 1.44269f; int w; int h; - while(!packed){ - w = std::pow(2,std::floor((alpha/2.f) + 0.5f)); // there doesn't seem to be a round in cmath for windows. - //w = std::pow(2, std::round(alpha/2.f)); - h = w;//std::pow(2, std::round(alpha - std::round(alpha/2.f))); - int x=0; - int y=0; - auto maxRowHeight = sortedCopy[0].tH + border*2; + while (!packed) { + w = std::pow(2, std::floor((alpha / 2.f) + 0.5f)); // there doesn't seem to be a round in cmath for windows. + //w = pow(2,round(alpha/2.f)); + h = w; //pow(2,round(alpha - round(alpha/2.f))); + int x = 0; + int y = 0; + auto maxRowHeight = sortedCopy[0].tH + border * 2; packed = true; - for(auto & glyph: sortedCopy){ - if(x+glyph.tW + border*2>w){ + for (auto & glyph : sortedCopy) { + if (x + glyph.tW + border * 2 > w) { x = 0; y += maxRowHeight; - maxRowHeight = glyph.tH + border*2; - if(y + maxRowHeight > h){ + maxRowHeight = glyph.tH + border * 2; + if (y + maxRowHeight > h) { alpha++; packed = false; break; } } - x+= glyph.tW + border*2; + x += glyph.tW + border * 2; } - } - - ofPixels atlasPixelsLuminanceAlpha; - atlasPixelsLuminanceAlpha.allocate(w,h,OF_PIXELS_GRAY_ALPHA); - atlasPixelsLuminanceAlpha.set(0,255); - atlasPixelsLuminanceAlpha.set(1,0); - - - float x=0; - float y=0; - auto maxRowHeight = sortedCopy[0].tH + border*2.0; - for(auto & glyph: sortedCopy){ + atlasPixelsLuminanceAlpha.allocate(w, h, OF_PIXELS_GRAY_ALPHA); + atlasPixelsLuminanceAlpha.set(0, 255); + atlasPixelsLuminanceAlpha.set(1, 0); + + float x = 0; + float y = 0; + auto maxRowHeight = sortedCopy[0].tH + border * 2.0; + for (auto & glyph : sortedCopy) { ofPixels & charPixels = all_glyphs[glyph.characterIndex].pixels; - if(x+glyph.tW + border*2>w){ + if (x + glyph.tW + border * 2 > w) { x = 0; y += maxRowHeight; - maxRowHeight = glyph.tH + border*2.0; + maxRowHeight = glyph.tH + border * 2.0; } - cps[glyph.characterIndex].t1 = float(x + border)/float(w); - cps[glyph.characterIndex].v1 = float(y + border)/float(h); - cps[glyph.characterIndex].t2 = float(cps[glyph.characterIndex].tW + x + border)/float(w); - cps[glyph.characterIndex].v2 = float(cps[glyph.characterIndex].tH + y + border)/float(h); - charPixels.pasteInto(atlasPixelsLuminanceAlpha,x+border,y+border); - x+= glyph.tW + border*2.0; + cps[glyph.characterIndex].t1 = float(x + border) / float(w); + cps[glyph.characterIndex].v1 = float(y + border) / float(h); + cps[glyph.characterIndex].t2 = float(cps[glyph.characterIndex].tW + x + border) / float(w); + cps[glyph.characterIndex].v2 = float(cps[glyph.characterIndex].tH + y + border) / float(h); + charPixels.pasteInto(atlasPixelsLuminanceAlpha, x + border, y + border); + x += glyph.tW + border * 2.0; } int maxSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); - if(w > maxSize || h > maxSize){ + if (w > maxSize || h > maxSize) { ofLogError("ofTruetypeFont") << "Trying to allocate texture of " << w << "x" << h << " which is bigger than supported in current platform: " << maxSize; return false; - }else{ - texAtlas.allocate(atlasPixelsLuminanceAlpha,false); + } else { + texAtlas.allocate(atlasPixelsLuminanceAlpha, false); texAtlas.setRGToRGBASwizzles(true); - if(settings.antialiased && settings.fontSize>20){ - texAtlas.setTextureMinMagFilter(GL_LINEAR,GL_LINEAR); - }else{ - texAtlas.setTextureMinMagFilter(GL_NEAREST,GL_NEAREST); + if (settings.antialiased && settings.fontSize > 20) { + texAtlas.setTextureMinMagFilter(GL_LINEAR, GL_LINEAR); + } else { + texAtlas.setTextureMinMagFilter(GL_NEAREST, GL_NEAREST); } texAtlas.loadData(atlasPixelsLuminanceAlpha); bLoadedOk = true; @@ -898,22 +891,22 @@ bool ofTrueTypeFont::load(const ofTrueTypeFontSettings & _settings){ } //----------------------------------------------------------- -bool ofTrueTypeFont::isLoaded() const{ +bool ofTrueTypeFont::isLoaded() const { return bLoadedOk; } //----------------------------------------------------------- -bool ofTrueTypeFont::isAntiAliased() const{ +bool ofTrueTypeFont::isAntiAliased() const { return settings.antialiased; } //----------------------------------------------------------- -bool ofTrueTypeFont::hasFullCharacterSet() const{ +bool ofTrueTypeFont::hasFullCharacterSet() const { return true; } //----------------------------------------------------------- -int ofTrueTypeFont::getSize() const{ +int ofTrueTypeFont::getSize() const { return settings.fontSize; } @@ -923,7 +916,7 @@ void ofTrueTypeFont::setLineHeight(float _newLineHeight) { } //----------------------------------------------------------- -float ofTrueTypeFont::getLineHeight() const{ +float ofTrueTypeFont::getLineHeight() const { return lineHeight; } @@ -948,7 +941,7 @@ void ofTrueTypeFont::setLetterSpacing(float _newletterSpacing) { } //----------------------------------------------------------- -float ofTrueTypeFont::getLetterSpacing() const{ +float ofTrueTypeFont::getLetterSpacing() const { return letterSpacing; } @@ -958,53 +951,53 @@ void ofTrueTypeFont::setSpaceSize(float _newspaceSize) { } //----------------------------------------------------------- -float ofTrueTypeFont::getSpaceSize() const{ +float ofTrueTypeFont::getSpaceSize() const { return spaceSize; } //------------------------------------------------------------------ -ofPath ofTrueTypeFont::getCharacterAsPoints(uint32_t character, bool vflip, bool filled) const{ - if( settings.contours == false ){ +ofPath ofTrueTypeFont::getCharacterAsPoints(uint32_t character, bool vflip, bool filled) const { + if (settings.contours == false) { ofLogError("ofxTrueTypeFont") << "getCharacterAsPoints(): contours not created, call loadFont() with makeContours set to true"; return ofPath(); } - if (!isValidGlyph(character)){ + if (!isValidGlyph(character)) { return ofPath(); } - if(vflip){ - if(filled){ + if (vflip) { + if (filled) { return charOutlines[indexForGlyph(character)]; - }else{ + } else { return charOutlinesContour[indexForGlyph(character)]; } - }else{ - if(filled){ + } else { + if (filled) { return charOutlinesNonVFlipped[indexForGlyph(character)]; - }else{ + } else { return charOutlinesNonVFlippedContour[indexForGlyph(character)]; } } } //----------------------------------------------------------- -void ofTrueTypeFont::drawChar(uint32_t c, float x, float y, bool vFlipped) const{ +void ofTrueTypeFont::drawChar(uint32_t c, float x, float y, bool vFlipped) const { - if (!isValidGlyph(c)){ + if (!isValidGlyph(c)) { //ofLogError("ofTrueTypeFont") << "drawChar(): char " << c + NUM_CHARACTER_TO_START << " not allocated: line " << __LINE__ << " in " << __FILE__; return; } auto props = getGlyphProperties(c); - float xmin = props.xmin+x; - float ymin = props.ymin; - float xmax = props.xmax+x; - float ymax = props.ymax; + float xmin = props.xmin + x; + float ymin = props.ymin; + float xmax = props.xmax + x; + float ymax = props.ymax; - if(!vFlipped){ - ymin *= -1.0; - ymax *= -1.0; + if (!vFlipped) { + ymin *= -1.0; + ymax *= -1.0; } ymin += y; @@ -1012,150 +1005,147 @@ void ofTrueTypeFont::drawChar(uint32_t c, float x, float y, bool vFlipped) const ofIndexType firstIndex = stringQuads.getVertices().size(); - stringQuads.addVertex(glm::vec3(xmin,ymin,0.f)); - stringQuads.addVertex(glm::vec3(xmax,ymin,0.f)); - stringQuads.addVertex(glm::vec3(xmax,ymax,0.f)); - stringQuads.addVertex(glm::vec3(xmin,ymax,0.f)); + stringQuads.addVertex(glm::vec3(xmin, ymin, 0.f)); + stringQuads.addVertex(glm::vec3(xmax, ymin, 0.f)); + stringQuads.addVertex(glm::vec3(xmax, ymax, 0.f)); + stringQuads.addVertex(glm::vec3(xmin, ymax, 0.f)); - stringQuads.addTexCoord(glm::vec2(props.t1,props.v1)); - stringQuads.addTexCoord(glm::vec2(props.t2,props.v1)); - stringQuads.addTexCoord(glm::vec2(props.t2,props.v2)); - stringQuads.addTexCoord(glm::vec2(props.t1,props.v2)); + stringQuads.addTexCoord(glm::vec2(props.t1, props.v1)); + stringQuads.addTexCoord(glm::vec2(props.t2, props.v1)); + stringQuads.addTexCoord(glm::vec2(props.t2, props.v2)); + stringQuads.addTexCoord(glm::vec2(props.t1, props.v2)); stringQuads.addIndex(firstIndex); - stringQuads.addIndex(firstIndex+1); - stringQuads.addIndex(firstIndex+2); - stringQuads.addIndex(firstIndex+2); - stringQuads.addIndex(firstIndex+3); + stringQuads.addIndex(firstIndex + 1); + stringQuads.addIndex(firstIndex + 2); + stringQuads.addIndex(firstIndex + 2); + stringQuads.addIndex(firstIndex + 3); stringQuads.addIndex(firstIndex); - - } //----------------------------------------------------------- -double ofTrueTypeFont::getKerning(uint32_t leftC, uint32_t rightC) const{ - if(FT_HAS_KERNING( face )){ +double ofTrueTypeFont::getKerning(uint32_t leftC, uint32_t rightC) const { + if (FT_HAS_KERNING(face)) { FT_Vector kerning; FT_Get_Kerning(face.get(), FT_Get_Char_Index(face.get(), leftC), FT_Get_Char_Index(face.get(), rightC), FT_KERNING_UNFITTED, &kerning); return int26p6_to_dbl(kerning.x); - }else{ + } else { return 0.0; } } -void ofTrueTypeFont::iterateString(const string & str, float x, float y, bool vFlipped, std::function f) const{ - glm::vec2 pos(x,y); +void ofTrueTypeFont::iterateString(const string & str, float x, float y, bool vFlipped, std::function f) const { + glm::vec2 pos(x, y); - float newLineDirection = 1; - if(!vFlipped){ + float newLineDirection = 1; + if (!vFlipped) { // this would align multiline texts to the last line when vflip is disabled //int lines = ofStringTimesInString(c,"\n"); //Y = lines*lineHeight; newLineDirection = -1; } - float directionX = settings.direction == OF_TTF_LEFT_TO_RIGHT?1:-1; + float directionX = settings.direction == OF_TTF_LEFT_TO_RIGHT ? 1 : -1; uint32_t prevC = 0; - for(auto c: ofUTF8Iterator(str)){ - try{ + for (auto c : ofUTF8Iterator(str)) { + try { if (c == '\n') { - pos.y += lineHeight*newLineDirection; - pos.x = x ; //reset X Pos back to zero + pos.y += lineHeight * newLineDirection; + pos.x = x; //reset X Pos back to zero prevC = 0; } else if (c == '\t') { - if (settings.direction == OF_TTF_LEFT_TO_RIGHT){ + if (settings.direction == OF_TTF_LEFT_TO_RIGHT) { f(c, pos); pos.x += getGlyphProperties(' ').advance * spaceSize * TAB_WIDTH * directionX; - } else{ + } else { pos.x += getGlyphProperties(' ').advance * spaceSize * TAB_WIDTH * directionX; f(c, pos); } prevC = c; - }else if(c == ' '){ + } else if (c == ' ') { pos.x += getGlyphProperties(' ').advance * spaceSize * directionX; f(c, pos); prevC = c; - }else if(isValidGlyph(c)) { + } else if (isValidGlyph(c)) { const auto & props = getGlyphProperties(c); - if(prevC>0){ - if(settings.direction == OF_TTF_LEFT_TO_RIGHT){ + if (prevC > 0) { + if (settings.direction == OF_TTF_LEFT_TO_RIGHT) { pos.x += getKerning(prevC, c); - }else{ + } else { pos.x += getKerning(c, prevC); } } - if(settings.direction == OF_TTF_LEFT_TO_RIGHT){ - f(c,pos); - pos.x += props.advance * directionX; + if (settings.direction == OF_TTF_LEFT_TO_RIGHT) { + f(c, pos); + pos.x += props.advance * directionX; pos.x += getGlyphProperties(' ').advance * (letterSpacing - 1.f) * directionX; - }else{ - pos.x += props.advance * directionX; + } else { + pos.x += props.advance * directionX; pos.x += getGlyphProperties(' ').advance * (letterSpacing - 1.f) * directionX; - f(c,pos); + f(c, pos); } prevC = c; } - }catch(...){ + } catch (...) { break; } } } //----------------------------------------------------------- -void ofTrueTypeFont::setDirection(ofTrueTypeFontDirection direction){ +void ofTrueTypeFont::setDirection(ofTrueTypeFontDirection direction) { settings.direction = direction; } //----------------------------------------------------------- -vector ofTrueTypeFont::getStringAsPoints(const string & str, bool vflip, bool filled) const{ +vector ofTrueTypeFont::getStringAsPoints(const string & str, bool vflip, bool filled) const { vector shapes; - if (!bLoadedOk){ + if (!bLoadedOk) { ofLogError("ofxTrueTypeFont") << "getStringAsPoints(): font not allocated: line " << __LINE__ << " in " << __FILE__; return shapes; }; - iterateString(str,0,0,vflip,[&](uint32_t c, glm::vec2 pos){ - shapes.push_back(getCharacterAsPoints(c,vflip,filled)); - shapes.back().translate(glm::vec3{pos, 0.f}); + iterateString(str, 0, 0, vflip, [&](uint32_t c, glm::vec2 pos) { + shapes.push_back(getCharacterAsPoints(c, vflip, filled)); + shapes.back().translate(glm::vec3 { pos, 0.f }); }); return shapes; - } -bool ofTrueTypeFont::isValidGlyph(uint32_t glyph) const{ +bool ofTrueTypeFont::isValidGlyph(uint32_t glyph) const { //return glyphIndexMap.find(glyph) != glyphIndexMap.end(); return std::any_of(settings.ranges.begin(), settings.ranges.end(), - [&](ofUnicode::range range){ + [&](ofUnicode::range range) { return glyph >= range.begin && glyph <= range.end; - }); + }); } -size_t ofTrueTypeFont::indexForGlyph(uint32_t glyph) const{ +size_t ofTrueTypeFont::indexForGlyph(uint32_t glyph) const { return glyphIndexMap.find(glyph)->second; } -const ofTrueTypeFont::glyphProps & ofTrueTypeFont::getGlyphProperties(uint32_t glyph) const{ - if(isValidGlyph(glyph)){ +const ofTrueTypeFont::glyphProps & ofTrueTypeFont::getGlyphProperties(uint32_t glyph) const { + if (isValidGlyph(glyph)) { return cps[indexForGlyph(glyph)]; - }else{ + } else { return invalidProps; } } //----------------------------------------------------------- -void ofTrueTypeFont::drawCharAsShape(uint32_t c, float x, float y, bool vFlipped, bool filled) const{ - if(vFlipped){ - if(filled){ - charOutlines[indexForGlyph(c)].draw(x,y); - }else{ - charOutlinesContour[indexForGlyph(c)].draw(x,y); +void ofTrueTypeFont::drawCharAsShape(uint32_t c, float x, float y, bool vFlipped, bool filled) const { + if (vFlipped) { + if (filled) { + charOutlines[indexForGlyph(c)].draw(x, y); + } else { + charOutlinesContour[indexForGlyph(c)].draw(x, y); } - }else{ - if(filled){ - charOutlinesNonVFlipped[indexForGlyph(c)].draw(x,y); - }else{ - charOutlinesNonVFlippedContour[indexForGlyph(c)].draw(x,y); + } else { + if (filled) { + charOutlinesNonVFlipped[indexForGlyph(c)].draw(x, y); + } else { + charOutlinesNonVFlippedContour[indexForGlyph(c)].draw(x, y); } } } @@ -1180,10 +1170,10 @@ float ofTrueTypeFont::stringWidth(const string& c) const{ } //----------------------------------------------------------- -ofRectangle ofTrueTypeFont::getStringBoundingBox(const string& c, float x, float y, bool vflip) const{ +ofRectangle ofTrueTypeFont::getStringBoundingBox(const string & c, float x, float y, bool vflip) const { - if ( c.empty() ){ - return ofRectangle( x, y, 0.f, 0.f); + if (c.empty()) { + return ofRectangle(x, y, 0.f, 0.f); } float minX = x; @@ -1192,11 +1182,11 @@ ofRectangle ofTrueTypeFont::getStringBoundingBox(const string& c, float x, float // Calculate bounding box by iterating over glyph properties // Meaning of props can be deduced from illustration at top of: // https://www.freetype.org/freetype2/docs/tutorial/step2.html - // + // // We deliberately not generate a mesh and iterate over its // vertices, as this would not correctly return spacing for // blank characters. - + float w = 0; iterateString( c, x, y, vflip, [&]( uint32_t c, glm::vec2 pos ){ auto props = getGlyphProperties( c ); @@ -1220,7 +1210,7 @@ ofRectangle ofTrueTypeFont::getStringBoundingBox(const string& c, float x, float } else{ minY = min( minY, pos.y - ( props.ymax) ); maxY = max( maxY, pos.y - ( props.ymin ) ); - } + } }); float height = maxY - minY; @@ -1229,104 +1219,110 @@ ofRectangle ofTrueTypeFont::getStringBoundingBox(const string& c, float x, float } //----------------------------------------------------------- -float ofTrueTypeFont::stringHeight(const string& c) const{ - ofRectangle rect = getStringBoundingBox(c, 0,0); +float ofTrueTypeFont::stringHeight(const string & c) const { + ofRectangle rect = getStringBoundingBox(c, 0, 0); return rect.height; } //----------------------------------------------------------- -void ofTrueTypeFont::createStringMesh(const string& str, float x, float y, bool vflip) const{ - iterateString(str,x,y,vflip,[&](uint32_t c, glm::vec2 pos){ +void ofTrueTypeFont::createStringMesh(const string & str, float x, float y, bool vflip) const { + iterateString(str, x, y, vflip, [&](uint32_t c, glm::vec2 pos) { drawChar(c, pos.x, pos.y, vflip); }); } //----------------------------------------------------------- -const ofMesh & ofTrueTypeFont::getStringMesh(const string& c, float x, float y, bool vFlipped) const{ +const ofMesh & ofTrueTypeFont::getStringMesh(const string & c, float x, float y, bool vFlipped) const { stringQuads.clear(); - createStringMesh(c,x,y,vFlipped); + createStringMesh(c, x, y, vFlipped); return stringQuads; } //----------------------------------------------------------- -const ofTexture & ofTrueTypeFont::getFontTexture() const{ +const ofTexture & ofTrueTypeFont::getFontTexture() const { return texAtlas; } //----------------------------------------------------------- -glm::vec2 ofTrueTypeFont::getFirstGlyphPosForTexture(const string & str, bool vflip) const{ - if(!str.empty()){ - try{ +glm::vec2 ofTrueTypeFont::getFirstGlyphPosForTexture(const string & str, bool vflip) const { + if (!str.empty()) { + try { auto c = *ofUTF8Iterator(str).begin(); - if(settings.direction == OF_TTF_LEFT_TO_RIGHT){ + if (settings.direction == OF_TTF_LEFT_TO_RIGHT) { if (c != '\n') { auto g = loadGlyph(c); - return {-float(g.props.xmin), getLineHeight() + g.props.ymin + getDescenderHeight()}; + return { -float(g.props.xmin), getLineHeight() + g.props.ymin + getDescenderHeight() }; } - }else{ + } else { int width = 0; int lineWidth = 0; - iterateString(str, 0, 0, vflip, [&](uint32_t c, ofVec2f){ - try{ + iterateString(str, 0, 0, vflip, [&](uint32_t c, ofVec2f) { + try { if (c != '\n') { auto g = loadGlyph(c); - if(c == '\t')lineWidth += g.props.advance + getGlyphProperties(' ').advance * spaceSize * TAB_WIDTH; - else if(c == ' ')lineWidth += g.props.advance + getGlyphProperties(' ').advance * spaceSize; - else if(isValidGlyph(c))lineWidth += g.props.advance + getGlyphProperties(' ').advance * (letterSpacing - 1.f); + if (c == '\t') + lineWidth += g.props.advance + getGlyphProperties(' ').advance * spaceSize * TAB_WIDTH; + else if (c == ' ') + lineWidth += g.props.advance + getGlyphProperties(' ').advance * spaceSize; + else if (isValidGlyph(c)) + lineWidth += g.props.advance + getGlyphProperties(' ').advance * (letterSpacing - 1.f); width = max(width, lineWidth); - }else{ + } else { lineWidth = 0; } - }catch(...){ + } catch (...) { } }); if (c != '\n') { auto g = loadGlyph(c); - return {width - float(g.props.xmin), getLineHeight() + g.props.ymin + getDescenderHeight()}; - }else{ - return {float(width), 0.0}; + return { width - float(g.props.xmin), getLineHeight() + g.props.ymin + getDescenderHeight() }; + } else { + return { float(width), 0.0 }; } } - }catch(...){} + } catch (...) { } } - return {0.f, 0.f}; + return { 0.f, 0.f }; } //----------------------------------------------------------- -ofTexture ofTrueTypeFont::getStringTexture(const string& str, bool vflip) const{ +ofTexture ofTrueTypeFont::getStringTexture(const string & str, bool vflip) const { vector glyphs; vector glyphPositions; float height = 0; float width = 0; float lineWidth = 0; - iterateString(str, 0, 0, vflip, [&](uint32_t c, ofVec2f pos){ - try{ + iterateString(str, 0, 0, vflip, [&](uint32_t c, ofVec2f pos) { + try { if (c != '\n') { auto g = loadGlyph(c); - - if (c == '\t'){ + + if (c == '\t') { auto temp = loadGlyph(' '); glyphs.push_back(temp); - }else{ + } else { glyphs.push_back(g); } - + int x = pos.x + g.props.xmin; int y = pos.y; glyphPositions.emplace_back(x, y); - - if(c == '\t')lineWidth += g.props.advance + getGlyphProperties(' ').advance * spaceSize * TAB_WIDTH; - else if(c == ' ')lineWidth += g.props.advance + getGlyphProperties(' ').advance * spaceSize; - else if(isValidGlyph(c))lineWidth += g.props.advance + getGlyphProperties(' ').advance * (letterSpacing - 1.f); - + + if (c == '\t') + lineWidth += g.props.advance + getGlyphProperties(' ').advance * spaceSize * TAB_WIDTH; + else if (c == ' ') + lineWidth += g.props.advance + getGlyphProperties(' ').advance * spaceSize; + else if (isValidGlyph(c)) + lineWidth += g.props.advance + getGlyphProperties(' ').advance * (letterSpacing - 1.f); + width = max(width, lineWidth); y += g.props.ymax; height = max(height, y + getLineHeight()); - }else{ + } else { lineWidth = 0; } - }catch(...){ + } catch (...) { } }); @@ -1334,17 +1330,17 @@ ofTexture ofTrueTypeFont::getStringTexture(const string& str, bool vflip) const{ ofPixels totalPixels; totalPixels.allocate(width, height, OF_PIXELS_GRAY_ALPHA); //-------------------------------- clear data: - totalPixels.set(0,255); // every luminance pixel = 255 - totalPixels.set(1,0); + totalPixels.set(0, 255); // every luminance pixel = 255 + totalPixels.set(1, 0); size_t i = 0; - for(auto & g: glyphs){ - if(settings.direction == OF_TTF_LEFT_TO_RIGHT){ - g.pixels.blendInto(totalPixels, glyphPositions[i].x, glyphPositions[i].y + getLineHeight() + g.props.ymin + getDescenderHeight() ); - }else{ - g.pixels.blendInto(totalPixels, width-glyphPositions[i].x, glyphPositions[i].y + getLineHeight() + g.props.ymin + getDescenderHeight() ); + for (auto & g : glyphs) { + if (settings.direction == OF_TTF_LEFT_TO_RIGHT) { + g.pixels.blendInto(totalPixels, glyphPositions[i].x, glyphPositions[i].y + getLineHeight() + g.props.ymin + getDescenderHeight()); + } else { + g.pixels.blendInto(totalPixels, width - glyphPositions[i].x, glyphPositions[i].y + getLineHeight() + g.props.ymin + getDescenderHeight()); } i++; - if(i==glyphPositions.size()){ + if (i == glyphPositions.size()) { break; } } @@ -1353,36 +1349,35 @@ ofTexture ofTrueTypeFont::getStringTexture(const string& str, bool vflip) const{ } //----------------------------------------------------------- -void ofTrueTypeFont::drawString(const string & c, float x, float y) const{ +void ofTrueTypeFont::drawString(const string & c, float x, float y) const { - if (!bLoadedOk){ + if (!bLoadedOk) { ofLogError("ofTrueTypeFont") << "drawString(): font not allocated"; return; } - ofGetCurrentRenderer()->drawString(*this,c,x,y); - + ofGetCurrentRenderer()->drawString(*this, c, x, y); } //----------------------------------------------------------- -void ofTrueTypeFont::drawStringAsShapes(const string& str, float x, float y) const{ - if (!bLoadedOk){ +void ofTrueTypeFont::drawStringAsShapes(const string & str, float x, float y) const { + if (!bLoadedOk) { ofLogError("ofTrueTypeFont") << "drawStringAsShapes(): font not allocated: line " << __LINE__ << " in " << __FILE__; return; }; //----------------------- error checking - if (!settings.contours){ + if (!settings.contours) { ofLogError("ofTrueTypeFont") << "drawStringAsShapes(): contours not created for this font, call loadFont() with makeContours set to true"; return; } - iterateString(str,x,y,true,[&](uint32_t c, glm::vec2 pos){ + iterateString(str, x, y, true, [&](uint32_t c, glm::vec2 pos) { drawCharAsShape(c, pos.x, pos.y, ofIsVFlipped(), ofGetStyle().bFill); }); } //----------------------------------------------------------- -std::size_t ofTrueTypeFont::getNumCharacters() const{ +std::size_t ofTrueTypeFont::getNumCharacters() const { return cps.size(); } diff --git a/libs/openFrameworks/graphics/ofTrueTypeFont.h b/libs/openFrameworks/graphics/ofTrueTypeFont.h index 83366158865..9cd7d4c6c7b 100644 --- a/libs/openFrameworks/graphics/ofTrueTypeFont.h +++ b/libs/openFrameworks/graphics/ofTrueTypeFont.h @@ -148,9 +148,20 @@ struct ofTrueTypeFontSettings { ofTrueTypeFontDirection direction = OF_TTF_LEFT_TO_RIGHT; std::vector ranges; - ofTrueTypeFontSettings(const of::filesystem::path & name, int size) + ofTrueTypeFontSettings(const of::filesystem::path & name, int size, bool _antialiased = true, + bool fullCharacterSet = true, bool _contours = false, float _simplifyAmt = 0.0f, int _dpi = 0) : fontName(name) - , fontSize(size) { } + , fontSize(size) + , antialiased(_antialiased) + , contours(_contours) + , simplifyAmt(_simplifyAmt) + , dpi(_dpi) { + if (fullCharacterSet) { + ranges = { ofUnicode::Latin1Supplement }; + } else { + ranges = { ofUnicode::Latin }; + } + } void addRanges(std::initializer_list alphabet) { ranges.insert(ranges.end(), alphabet); @@ -176,6 +187,12 @@ class ofTrueTypeFont { ofTrueTypeFont(ofTrueTypeFont && mom); ofTrueTypeFont & operator=(ofTrueTypeFont && mom); + ofTrueTypeFont(const of::filesystem::path & filename, int fontsize, bool antiAliased = true, + bool fullCharacterSet = true, bool makeContours = false, float simplifyAmt = 0.0f, int dpi = 0) + : ofTrueTypeFont() { + load(filename, fontsize, antiAliased, fullCharacterSet, makeContours, simplifyAmt, dpi); + } + /// \name Load Font /// \{ @@ -338,7 +355,7 @@ class ofTrueTypeFont { /// \param x X position of returned rectangle. /// \param y Y position of returned rectangle. /// \returns the bounding box of a string as a rectangle. - ofRectangle getStringBoundingBox(const std::string & s, float x, float y, bool vflip = true) const; + ofRectangle getStringBoundingBox(const std::string & s, float x = 0, float y = 0, bool vflip = true) const; /// \} /// \name Drawing @@ -349,6 +366,9 @@ class ofTrueTypeFont { /// \param x X position of string /// \param y Y position of string void drawString(const std::string & s, float x, float y) const; + void drawString(const std::string & s, glm::vec2 pos) const { + drawString(s, pos.x, pos.y); + } /// \brief Draws the string as if it was geometrical shapes. /// @@ -357,6 +377,9 @@ class ofTrueTypeFont { /// \param x X position of shapes /// \param y Y position of shapes void drawStringAsShapes(const std::string & s, float x, float y) const; + void drawStringAsShapes(const std::string & s, glm::vec2 pos) const { + drawStringAsShapes(s, pos.x, pos.y); + } ofPath getCharacterAsPoints(uint32_t character, bool vflip = true, bool filled = true) const; std::vector getStringAsPoints(const std::string & str, bool vflip = true, bool filled = true) const;