2 * Copyright (C) 2022 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
12 #include "FileItemList.h"
13 #include "StringUtils.h"
15 #include "filesystem/Directory.h"
16 #include "filesystem/File.h"
17 #include "filesystem/SpecialProtocol.h"
18 #include "utils/CharsetConverter.h"
19 #include "utils/log.h"
23 #include FT_FREETYPE_H
24 #include FT_SFNT_NAMES_H
25 #include FT_TRUETYPE_IDS_H
27 using namespace XFILE
;
29 namespace KODI::UTILS::FONT
34 // \brief Get font family from SFNT table entries
35 std::string
GetFamilyNameFromSfnt(FT_Face face
)
37 std::string familyName
;
39 for (FT_UInt index
= 0; index
< FT_Get_Sfnt_Name_Count(face
); ++index
)
42 if (FT_Get_Sfnt_Name(face
, index
, &name
) != 0)
44 CLog::LogF(LOGWARNING
, "Failed to get SFNT name at index {}", index
);
48 // Get the unicode font family name (format-specific interface)
49 // In properties there may be one or more font family names encoded for each platform.
50 // NOTE: we give preference to MS/APPLE platform data, then fallback to MAC
51 // because has been found some fonts that provide names not convertible MAC text to UTF8
52 if (name
.name_id
== TT_NAME_ID_FONT_FAMILY
)
54 const std::string nameEnc
{reinterpret_cast<const char*>(name
.string
), name
.string_len
};
56 if (name
.platform_id
== TT_PLATFORM_MICROSOFT
||
57 name
.platform_id
== TT_PLATFORM_APPLE_UNICODE
)
59 if (name
.language_id
!= TT_MAC_LANGID_ENGLISH
&&
60 name
.language_id
!= TT_MS_LANGID_ENGLISH_UNITED_STATES
&&
61 name
.language_id
!= TT_MS_LANGID_ENGLISH_UNITED_KINGDOM
)
64 if (CCharsetConverter::utf16BEtoUTF8(nameEnc
, familyName
))
65 break; // Stop here to prefer the name given with this platform
67 CLog::LogF(LOGERROR
, "Failed to convert the font name string encoded as \"UTF-16BE\"");
69 else if (name
.platform_id
== TT_PLATFORM_MACINTOSH
&& familyName
.empty())
71 if (name
.language_id
!= TT_MAC_LANGID_ENGLISH
|| name
.encoding_id
!= TT_MAC_ID_ROMAN
)
74 if (!CCharsetConverter::MacintoshToUTF8(nameEnc
, familyName
))
75 CLog::LogF(LOGERROR
, "Failed to convert the font name string encoded as \"macintosh\"");
79 CLog::LogF(LOGERROR
, "Unsupported font SFNT name platform \"{}\"", name
.platform_id
);
85 } // unnamed namespace
87 bool GetFontFamilyNames(const std::vector
<uint8_t>& buffer
, std::set
<std::string
>& familyNames
)
89 FT_Library m_library
{nullptr};
90 FT_Init_FreeType(&m_library
);
93 CLog::LogF(LOGERROR
, "Unable to initialize freetype library");
98 args
.flags
= FT_OPEN_MEMORY
;
99 args
.memory_base
= reinterpret_cast<const FT_Byte
*>(buffer
.data());
100 args
.memory_size
= static_cast<FT_Long
>(buffer
.size());
103 FT_Long numInstances
{0};
104 FT_Long faceIndex
{0};
105 FT_Long instanceIndex
{0};
107 // Iterate over all font faces, files like .ttc (TrueType Collection) contains multiple fonts
110 FT_Long idx
= (instanceIndex
<< 16) + faceIndex
;
111 FT_Face face
{nullptr};
113 FT_Error error
= FT_Open_Face(m_library
, &args
, idx
, &face
);
116 CLog::LogF(LOGERROR
, "Failed to open font face at index {} error code {}", idx
, error
);
120 std::string familyName
= GetFamilyNameFromSfnt(face
);
121 if (familyName
.empty())
123 CLog::LogF(LOGWARNING
, "Failed to get the unicode family name for \"{}\", fallback to ASCII",
125 // ASCII font family name may differ from the unicode one, use this as fallback only
126 familyName
= std::string
{face
->family_name
};
127 if (familyName
.empty())
129 CLog::LogF(LOGERROR
, "Family name missing in the font");
134 // We use the "set" container to avoid duplicate names that can happens
135 // for example when a font have each style on different files
136 familyNames
.insert(familyName
);
138 numFaces
= face
->num_faces
;
139 numInstances
= face
->style_flags
>> 16;
143 if (instanceIndex
< numInstances
)
151 } while (faceIndex
< numFaces
);
153 FT_Done_FreeType(m_library
);
157 bool GetFontFamilyNames(const std::string
& filepath
, std::set
<std::string
>& familyNames
)
159 std::vector
<uint8_t> buffer
;
160 if (filepath
.empty())
163 if (XFILE::CFile().LoadFile(filepath
, buffer
) <= 0)
165 CLog::LogF(LOGERROR
, "Failed to load file {}", filepath
);
168 return GetFontFamilyNames(buffer
, familyNames
);
171 std::string
GetFontFamily(std::vector
<uint8_t>& buffer
)
173 FT_Library m_library
{nullptr};
174 FT_Init_FreeType(&m_library
);
177 CLog::LogF(LOGERROR
, "Unable to initialize freetype library");
181 // Load the font face
183 std::string familyName
;
184 if (FT_New_Memory_Face(m_library
, reinterpret_cast<const FT_Byte
*>(buffer
.data()), buffer
.size(),
187 familyName
= GetFamilyNameFromSfnt(face
);
188 if (familyName
.empty())
190 CLog::LogF(LOGWARNING
, "Failed to get the unicode family name for \"{}\", fallback to ASCII",
192 // ASCII font family name may differ from the unicode one, use this as fallback only
193 familyName
= std::string
{face
->family_name
};
194 if (familyName
.empty())
195 CLog::LogF(LOGERROR
, "Family name missing in the font");
200 CLog::LogF(LOGERROR
, "Failed to process font memory buffer");
204 FT_Done_FreeType(m_library
);
208 std::string
GetFontFamily(const std::string
& filepath
)
210 std::vector
<uint8_t> buffer
;
211 if (filepath
.empty())
213 if (XFILE::CFile().LoadFile(filepath
, buffer
) <= 0)
215 CLog::LogF(LOGERROR
, "Failed to load file {}", filepath
);
218 return GetFontFamily(buffer
);
221 bool IsSupportedFontExtension(const std::string
& filepath
)
223 return URIUtils::HasExtension(filepath
, SUPPORTED_EXTENSIONS_MASK
);
226 void ClearTemporaryFonts()
228 if (!CDirectory::Exists(FONTPATH::TEMP
))
232 CDirectory::GetDirectory(FONTPATH::TEMP
, items
, SUPPORTED_EXTENSIONS_MASK
,
233 DIR_FLAG_NO_FILE_DIRS
| DIR_FLAG_BYPASS_CACHE
| DIR_FLAG_GET_HIDDEN
);
234 for (const auto& item
: items
)
236 if (item
->m_bIsFolder
)
239 CFile::Delete(item
->GetPath());
243 std::string
FONTPATH::GetSystemFontPath(const std::string
& filename
)
245 std::string fontPath
=
246 URIUtils::AddFileToFolder(CSpecialProtocol::TranslatePath(FONTPATH::SYSTEM
), filename
);
247 if (XFILE::CFile::Exists(fontPath
))
249 return CSpecialProtocol::TranslatePath(fontPath
);
252 CLog::LogF(LOGERROR
, "Could not find application system font {}", filename
);
256 } // namespace KODI::UTILS::FONT