aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/resource.language.en_gb/resources/strings.po7
-rwxr-xr-xsystem/settings/settings.xml63
-rw-r--r--xbmc/Application.cpp23
-rw-r--r--xbmc/Util.cpp26
-rw-r--r--xbmc/Util.h1
-rw-r--r--xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp34
-rw-r--r--xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.cpp293
-rw-r--r--xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.h2
-rw-r--r--xbmc/cores/VideoPlayer/DVDSubtitles/SubtitlesStyle.h5
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayer.cpp3
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/OverlayRenderer.cpp2
-rw-r--r--xbmc/guilib/GUIFontManager.cpp187
-rw-r--r--xbmc/guilib/GUIFontManager.h33
-rw-r--r--xbmc/settings/SettingConditions.cpp3
-rw-r--r--xbmc/settings/Settings.cpp5
-rw-r--r--xbmc/settings/Settings.h2
-rw-r--r--xbmc/settings/SubtitlesSettings.cpp35
-rw-r--r--xbmc/settings/SubtitlesSettings.h11
-rw-r--r--xbmc/settings/windows/GUIControlSettings.cpp11
-rw-r--r--xbmc/threads/Lockables.h7
-rw-r--r--xbmc/utils/CMakeLists.txt2
-rw-r--r--xbmc/utils/FontUtils.cpp101
-rw-r--r--xbmc/utils/FontUtils.h70
-rw-r--r--xbmc/utils/StringUtils.h2
24 files changed, 663 insertions, 265 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index 40c0773c28..cc523d9101 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -2695,6 +2695,7 @@ msgid "Date added"
msgstr ""
#: xbmc/playlists/SmartPlaylist.cpp
+#: xbmc/settings/SubtitlesSettings.cpp
msgctxt "#571"
msgid "Default"
msgstr ""
@@ -22983,3 +22984,9 @@ msgstr ""
msgctxt "#39179"
msgid "The time in seconds for the video OSD to be automatically closed"
msgstr ""
+
+#. Progress text on splash screen, to build the font cache
+#: xbmc/Application.cpp
+msgctxt "#39180"
+msgid "Building font cache in progress - please wait"
+msgstr ""
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index 47940fca91..36b2f7fa47 100755
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -517,26 +517,23 @@
</constraints>
<control type="spinner" format="integer" delayed="true"/>
</setting>
- <setting id="subtitles.font" type="string" label="14089" help="36185">
+ <setting id="subtitles.fontname" type="string" label="14089" help="36185">
<level>1</level>
- <default>arial.ttf</default>
+ <default>DEFAULT</default>
<constraints>
- <options>fonts</options>
+ <options>subtitlesfonts</options>
</constraints>
<control type="list" format="string" />
</setting>
- <setting id="subtitles.charset" type="string" parent="subtitles.font" label="735" help="36189">
+ <setting id="subtitles.charset" type="string" parent="subtitles.fontname" label="735" help="36189">
<level>1</level>
<default>DEFAULT</default>
<constraints>
<options>charsets</options>
</constraints>
- <dependencies>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
- </dependencies>
<control type="list" format="string" />
</setting>
- <setting id="subtitles.fontsize" type="integer" parent="subtitles.font" label="289" help="36186">
+ <setting id="subtitles.fontsize" type="integer" parent="subtitles.fontname" label="289" help="36186">
<level>3</level>
<default>42</default>
<constraints>
@@ -545,11 +542,11 @@
<maximum>74</maximum>
</constraints>
<dependencies>
- <dependency type="update" setting="subtitles.font" />
+ <dependency type="update" setting="subtitles.fontname" />
</dependencies>
<control type="list" format="string" />
</setting>
- <setting id="subtitles.style" type="integer" parent="subtitles.font" label="736" help="36187">
+ <setting id="subtitles.style" type="integer" parent="subtitles.fontname" label="736" help="36187">
<level>3</level>
<default>0</default> <!-- FONT_STYLE_NORMAL -->
<constraints>
@@ -560,62 +557,40 @@
<option label="741">3</option> <!-- FONT_STYLE_BOLD | FONT_STYLE_ITALICS -->
</options>
</constraints>
- <dependencies>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
- </dependencies>
<control type="list" format="string" />
</setting>
- <setting id="subtitles.colorpick" type="string" parent="subtitles.font" label="737" help="36188">
+ <setting id="subtitles.colorpick" type="string" parent="subtitles.fontname" label="737" help="36188">
<level>3</level>
<default>FFFFFFFF</default> <!-- White -->
- <dependencies>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
- </dependencies>
<control type="colorbutton" />
</setting>
- <setting id="subtitles.opacity" type="integer" parent="subtitles.font" label="752" help="36295">
+ <setting id="subtitles.opacity" type="integer" parent="subtitles.fontname" label="752" help="36295">
<level>3</level>
<default>100</default>
- <dependencies>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
- </dependencies>
<control type="slider" format="percentage" range="0,100" />
</setting>
- <setting id="subtitles.bordersize" type="integer" parent="subtitles.font" label="39159">
+ <setting id="subtitles.bordersize" type="integer" parent="subtitles.fontname" label="39159">
<level>3</level>
<default>30</default>
<dependencies>
- <dependency type="enable">
- <and>
- <condition on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font"/>
- <condition setting="subtitles.backgroundtype" operator="!is">2</condition>
- </and>
- </dependency>
+ <dependency type="enable" setting="subtitles.backgroundtype" operator="!is">2</dependency>
</dependencies>
<control type="slider" format="percentage" range="0,100" />
</setting>
- <setting id="subtitles.bordercolorpick" type="string" parent="subtitles.font" label="39160">
+ <setting id="subtitles.bordercolorpick" type="string" parent="subtitles.fontname" label="39160">
<level>3</level>
<default>FF000000</default> <!-- Black -->
<dependencies>
- <dependency type="enable">
- <and>
- <condition on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font"/>
- <condition setting="subtitles.backgroundtype" operator="!is">2</condition>
- </and>
- </dependency>
+ <dependency type="enable" setting="subtitles.backgroundtype" operator="!is">2</dependency>
</dependencies>
<control type="colorbutton" />
</setting>
- <setting id="subtitles.blur" type="integer" parent="subtitles.font" label="39173">
+ <setting id="subtitles.blur" type="integer" parent="subtitles.fontname" label="39173">
<level>3</level>
<default>0</default>
- <dependencies>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
- </dependencies>
<control type="slider" format="percentage" range="0,100" />
</setting>
- <setting id="subtitles.backgroundtype" type="integer" parent="subtitles.font" label="39165" help="39169">
+ <setting id="subtitles.backgroundtype" type="integer" parent="subtitles.fontname" label="39165" help="39169">
<level>3</level>
<default>0</default>
<constraints>
@@ -626,9 +601,6 @@
<option label="39168">3</option> <!-- SUBTITLE_BACKGROUNDTYPE_SQUAREBOX -->
</options>
</constraints>
- <dependencies>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
- </dependencies>
<control type="list" format="integer" />
</setting>
<setting id="subtitles.bgcolorpick" type="string" parent="subtitles.backgroundtype" label="745" help="36228">
@@ -641,7 +613,6 @@
<condition setting="subtitles.backgroundtype">3</condition>
</or>
</dependency>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
</dependencies>
<control type="colorbutton" />
</setting>
@@ -655,7 +626,6 @@
<condition setting="subtitles.backgroundtype">3</condition>
</or>
</dependency>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
</dependencies>
<control type="slider" format="percentage" range="0,100" />
</setting>
@@ -669,7 +639,6 @@
<condition setting="subtitles.backgroundtype">2</condition>
</or>
</dependency>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
</dependencies>
<control type="colorbutton" />
</setting>
@@ -683,7 +652,6 @@
<condition setting="subtitles.backgroundtype">2</condition>
</or>
</dependency>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
</dependencies>
<control type="slider" format="percentage" range="0,100" />
</setting>
@@ -697,7 +665,6 @@
<condition setting="subtitles.backgroundtype">2</condition>
</or>
</dependency>
- <dependency type="enable" on="property" name="HasSubtitlesFontExtensions" setting="subtitles.font" />
</dependencies>
<control type="slider" format="percentage" range="0,100" />
</setting>
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index 4430a8dafc..27e17cfa42 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -640,6 +640,29 @@ bool CApplication::Initialize()
}
CServiceBroker::GetRenderSystem()->ShowSplash("");
+ // Initialize GUI font manager to build/update fonts cache
+ //! @todo Move GUIFontManager into service broker and drop the global reference
+ event.Reset();
+ GUIFontManager& guiFontManager = g_fontManager;
+ CJobManager::GetInstance().Submit([&guiFontManager, &event]() {
+ guiFontManager.Initialize();
+ event.Set();
+ });
+ localizedStr = g_localizeStrings.Get(39180);
+ iDots = 1;
+ while (!event.Wait(1000ms))
+ {
+ if (g_fontManager.IsUpdating())
+ CServiceBroker::GetRenderSystem()->ShowSplash(std::string(iDots, ' ') + localizedStr +
+ std::string(iDots, '.'));
+
+ if (iDots == 3)
+ iDots = 1;
+ else
+ ++iDots;
+ }
+ CServiceBroker::GetRenderSystem()->ShowSplash("");
+
// GUI depends on seek handler
m_appPlayer.GetSeekHandler().Configure();
diff --git a/xbmc/Util.cpp b/xbmc/Util.cpp
index 23f4c0428a..86aa9878d5 100644
--- a/xbmc/Util.cpp
+++ b/xbmc/Util.cpp
@@ -70,6 +70,7 @@
#include "settings/SettingsComponent.h"
#include "utils/Digest.h"
#include "utils/FileExtensionProvider.h"
+#include "utils/FontUtils.h"
#include "utils/LangCodeExpander.h"
#include "utils/StringUtils.h"
#include "utils/TimeUtils.h"
@@ -677,26 +678,6 @@ void CUtil::ClearSubtitles()
}
}
-void CUtil::ClearTempFonts()
-{
- const std::string searchPath = "special://home/media/Fonts/";
-
- if (!CDirectory::Exists(searchPath))
- return;
-
- CFileItemList items;
- CDirectory::GetDirectory(searchPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE | DIR_FLAG_GET_HIDDEN);
-
- for (const auto &item : items)
- {
- if (item->m_bIsFolder)
- continue;
-
- if (StringUtils::StartsWithNoCase(URIUtils::GetFileName(item->GetPath()), "tmp.font."))
- CFile::Delete(item->GetPath());
- }
-}
-
int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
{
int64_t n;
@@ -1023,11 +1004,6 @@ std::string CUtil::ValidatePath(const std::string &path, bool bFixDoubleSlashes
return result;
}
-bool CUtil::IsSupportedFontExtension(const std::string& fileName)
-{
- return URIUtils::HasExtension(fileName, ".ttf|.otf");
-}
-
void CUtil::SplitExecFunction(const std::string &execString, std::string &function, std::vector<std::string> &parameters)
{
std::string paramString;
diff --git a/xbmc/Util.h b/xbmc/Util.h
index 6c7166f733..07c238c58e 100644
--- a/xbmc/Util.h
+++ b/xbmc/Util.h
@@ -64,7 +64,6 @@ public:
static bool GetDirectoryName(const std::string& strFileName, std::string& strDescription);
static void GetDVDDriveIcon(const std::string& strPath, std::string& strIcon);
static void RemoveTempFiles();
- static void ClearTempFonts();
static void ClearSubtitles();
static void ScanForExternalSubtitles(const std::string& strMovie, std::vector<std::string>& vecSubtitles );
diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
index f957a9df76..ddbd3988ca 100644
--- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
@@ -25,6 +25,7 @@
#include "settings/SettingsComponent.h"
#include "threads/SingleLock.h"
#include "threads/SystemClock.h"
+#include "utils/FontUtils.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "utils/XTimeUtils.h"
@@ -1748,39 +1749,40 @@ CDemuxStream* CDVDDemuxFFmpeg::AddStream(int streamIdx)
}
case AVMEDIA_TYPE_ATTACHMENT:
{
- // mkv attachments. Only bothering with fonts for now.
+ // MKV attachments. Only bothering with fonts for now.
AVDictionaryEntry* attachmentMimetype =
av_dict_get(pStream->metadata, "mimetype", nullptr, 0);
if (pStream->codecpar->codec_id == AV_CODEC_ID_TTF ||
pStream->codecpar->codec_id == AV_CODEC_ID_OTF || AttachmentIsFont(attachmentMimetype))
{
- std::string fileName = "special://home/media/Fonts/";
- XFILE::CDirectory::Create(fileName);
+ // Temporary fonts are extracted to the temporary fonts path
+ //! @todo: temporary font file management should be completely
+ //! removed, by sending font data to the subtitle renderer and
+ //! using libass ass_add_font to add the fonts directly in memory.
+ std::string filePath{UTILS::FONT::FONTPATH::TEMP};
+ XFILE::CDirectory::Create(filePath);
+
AVDictionaryEntry* nameTag = av_dict_get(pStream->metadata, "filename", NULL, 0);
- if (!nameTag)
- {
- CLog::Log(LOGERROR, "{}: TTF attachment has no name", __FUNCTION__);
- }
- else
+ if (nameTag)
{
- // Note: libass only supports a single font directory to look for additional fonts
- // (c.f. ass_set_fonts_dir). To support both user defined fonts (those placed in
- // special://home/media/Fonts/) and fonts extracted by the demuxer, make it extract
- // fonts to the user directory with a known, easy to identify, prefix (tmp.font.*).
- fileName += "tmp.font." + CUtil::MakeLegalFileName(nameTag->value, LEGAL_WIN32_COMPAT);
+ filePath += CUtil::MakeLegalFileName(nameTag->value, LEGAL_WIN32_COMPAT);
XFILE::CFile file;
- if (pStream->codecpar->extradata && file.OpenForWrite(fileName))
+ if (pStream->codecpar->extradata && file.OpenForWrite(filePath))
{
if (file.Write(pStream->codecpar->extradata, pStream->codecpar->extradata_size) !=
pStream->codecpar->extradata_size)
{
file.Close();
- XFILE::CFile::Delete(fileName);
- CLog::Log(LOGDEBUG, "{}: Error saving font file \"{}\"", __FUNCTION__, fileName);
+ XFILE::CFile::Delete(filePath);
+ CLog::LogF(LOGDEBUG, "Error saving font file \"{}\"", filePath);
}
}
}
+ else
+ {
+ CLog::LogF(LOGERROR, "Attached font has no name");
+ }
}
stream = new CDemuxStream();
stream->type = STREAM_NONE;
diff --git a/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.cpp b/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.cpp
index 2ed41ffbdf..d96340bb5f 100644
--- a/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.cpp
+++ b/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.cpp
@@ -8,13 +8,18 @@
#include "DVDSubtitlesLibass.h"
+#include "FileItem.h"
#include "ServiceBroker.h"
+#include "Util.h"
#include "cores/VideoPlayer/Interface/TimingConstants.h"
+#include "filesystem/Directory.h"
#include "filesystem/File.h"
#include "filesystem/SpecialProtocol.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
+#include "settings/SubtitlesSettings.h"
#include "threads/SingleLock.h"
+#include "utils/FontUtils.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "utils/log.h"
@@ -32,27 +37,6 @@ constexpr int ASS_BORDER_STYLE_SQUARE_BOX = 4; // Square box + outline
constexpr int ASS_FONT_ENCODING_AUTO = -1;
-// Directory where user defined fonts are located (and where mkv fonts are extracted to)
-constexpr const char* userFontPath = "special://home/media/Fonts/";
-// Directory where Kodi bundled fonts (default ones like Arial or Teletext) are located
-constexpr const char* systemFontPath = "special://xbmc/media/Fonts/";
-
-std::string GetDefaultFontPath(std::string& font)
-{
- constexpr std::array<const char*, 2> fontSources{userFontPath, systemFontPath};
-
- for (const auto& path : fontSources)
- {
- auto fontPath = URIUtils::AddFileToFolder(path, font);
- if (XFILE::CFile::Exists(fontPath))
- {
- return CSpecialProtocol::TranslatePath(fontPath).c_str();
- }
- }
- CLog::Log(LOGERROR, "CDVDSubtitlesLibass: Could not find font {} in font sources", font);
- return "";
-}
-
// Convert RGB/ARGB to RGBA by also applying the opacity value
COLOR::Color ConvColor(COLOR::Color argbColor, int opacity = 100)
{
@@ -109,9 +93,58 @@ void CDVDSubtitlesLibass::Configure()
ass_set_margins(m_renderer, 0, 0, 0, 0);
ass_set_use_margins(m_renderer, 0);
- // libass uses fontconfig (system lib) by default in some platforms (e.g. linux/android) or as
- // a fallback for all platforms. It is not wrapped so translate the path before calling into libass
- ass_set_fonts_dir(m_library, CSpecialProtocol::TranslatePath(userFontPath).c_str());
+ // Libass uses system font provider (like fontconfig) by default in some
+ // platforms (e.g. linux/windows), on some other systems like android the
+ // font provider is currenlty not supported, then an user can add his
+ // additionals fonts only by using the user fonts folder.
+ ass_set_fonts_dir(m_library,
+ CSpecialProtocol::TranslatePath(UTILS::FONT::FONTPATH::USER).c_str());
+
+ // Load additional fonts into Libass memory
+ CFileItemList items;
+ // Get fonts from system directory
+ XFILE::CDirectory::GetDirectory(UTILS::FONT::FONTPATH::SYSTEM, items,
+ UTILS::FONT::SUPPORTED_EXTENSIONS_MASK,
+ XFILE::DIR_FLAG_NO_FILE_DIRS | XFILE::DIR_FLAG_NO_FILE_INFO);
+ // Get temporary fonts
+ XFILE::CDirectory::GetDirectory(UTILS::FONT::FONTPATH::TEMP, items,
+ UTILS::FONT::SUPPORTED_EXTENSIONS_MASK,
+ XFILE::DIR_FLAG_NO_FILE_DIRS | XFILE::DIR_FLAG_NO_FILE_INFO);
+ for (const auto& item : items)
+ {
+ if (item->m_bIsFolder)
+ continue;
+ const std::string filepath = item->GetPath();
+ const std::string fileName = item->GetLabel();
+ std::vector<uint8_t> buffer;
+ if (XFILE::CFile().LoadFile(filepath, buffer) <= 0)
+ {
+ CLog::LogF(LOGERROR, "Failed to load file {}", filepath);
+ continue;
+ }
+#if LIBASS_VERSION >= 0x01501000
+ ass_add_font(m_library, fileName.c_str(), reinterpret_cast<const char*>(buffer.data()),
+ static_cast<int>(buffer.size()));
+#else
+ ass_add_font(m_library, const_cast<char*>(fileName.c_str()),
+ reinterpret_cast<char*>(buffer.data()), static_cast<int>(buffer.size()));
+#endif
+ if (StringUtils::CompareNoCase(fileName, FONT::FONT_DEFAULT_FILENAME) == 0)
+ {
+ m_defaultFontFamilyName = FONT::GetFontFamily(buffer);
+ }
+ }
+ if (m_defaultFontFamilyName.empty())
+ {
+ CLog::LogF(LOGERROR,
+ "The application font {} is missing. The default subtitle font cannot be set.",
+ FONT::FONT_DEFAULT_FILENAME);
+ }
+
+ ass_set_fonts(m_renderer,
+ UTILS::FONT::FONTPATH::GetSystemFontPath(FONT::FONT_DEFAULT_FILENAME).c_str(),
+ m_defaultFontFamilyName.c_str(), ASS_FONTPROVIDER_AUTODETECT, nullptr, 1);
+
ass_set_font_scale(m_renderer, 1);
// Extract font must be set before loading ASS/SSA data,
@@ -277,14 +310,13 @@ void CDVDSubtitlesLibass::ApplyStyle(const std::shared_ptr<struct KODI::SUBTITLE
return;
}
- ConfigureFont((m_subtitleType == NATIVE && subStyle->assOverrideFont), subStyle->fontName);
-
// ASS_Style is a POD struct need to be initialized with {}
ASS_Style defaultStyle{};
ASS_Style* style = nullptr;
if (m_subtitleType == ADAPTED ||
- (m_subtitleType == NATIVE && subStyle->assOverrideStyles != OverrideStyles::DISABLED))
+ (m_subtitleType == NATIVE &&
+ (subStyle->assOverrideStyles != OverrideStyles::DISABLED || subStyle->assOverrideFont)))
{
m_currentDefaultStyleId = m_defaultKodiStyleId;
@@ -297,6 +329,7 @@ void CDVDSubtitlesLibass::ApplyStyle(const std::shared_ptr<struct KODI::SUBTITLE
style = &m_track->styles[m_currentDefaultStyleId];
}
+ free(style->Name);
style->Name = strdup("KodiDefault");
// Calculate the scale (influence ASS style properties)
@@ -304,7 +337,8 @@ void CDVDSubtitlesLibass::ApplyStyle(const std::shared_ptr<struct KODI::SUBTITLE
int playResY;
if (m_subtitleType == NATIVE &&
(subStyle->assOverrideStyles == OverrideStyles::STYLES ||
- subStyle->assOverrideStyles == OverrideStyles::STYLES_POSITIONS))
+ subStyle->assOverrideStyles == OverrideStyles::STYLES_POSITIONS ||
+ subStyle->assOverrideFont))
{
// With styles overridden the PlayResY will be changed to 288
playResY = 288;
@@ -316,105 +350,106 @@ void CDVDSubtitlesLibass::ApplyStyle(const std::shared_ptr<struct KODI::SUBTITLE
}
// It is mandatory set the FontName, the text is case sensitive
- style->FontName = strdup(subStyle->fontName.c_str());
-
- if (m_subtitleType != NATIVE || subStyle->assOverrideStyles != OverrideStyles::POSITIONS)
+ free(style->FontName);
+ if (subStyle->fontName == FONT_DEFAULT_FAMILYNAME)
+ style->FontName = strdup(m_defaultFontFamilyName.c_str());
+ else
+ style->FontName = strdup(subStyle->fontName.c_str());
+
+ // Configure the font properties
+ // FIXME: The font size need to be scaled to be shown in right PT size
+ style->FontSize = (subStyle->fontSize / 720) * playResY;
+ // Modifies the width/height of the font (1 = 100%)
+ style->ScaleX = 1.0;
+ style->ScaleY = 1.0;
+ // Extra space between characters causes the underlined
+ // text line to become more discontinuous (test on LibAss 15.1)
+ style->Spacing = 0;
+
+ // Set automatic paragraph direction (not VSFilter-compatible)
+ // to fix wrong RTL text direction when there are unicode chars
+ style->Encoding = ASS_FONT_ENCODING_AUTO;
+
+ bool isFontBold =
+ (subStyle->fontStyle == FontStyle::BOLD || subStyle->fontStyle == FontStyle::BOLD_ITALIC);
+ bool isFontItalic =
+ (subStyle->fontStyle == FontStyle::ITALIC || subStyle->fontStyle == FontStyle::BOLD_ITALIC);
+ style->Bold = isFontBold * -1;
+ style->Italic = isFontItalic * -1;
+
+ // Compute the font color, depending on the opacity
+ COLOR::Color subColor = ConvColor(subStyle->fontColor, subStyle->fontOpacity);
+ // Set default subtitles color
+ style->PrimaryColour = subColor;
+ // Set SecondaryColour may be used to prevent an onscreen collision
+ style->SecondaryColour = subColor;
+
+ // Configure the effects
+ double lineSpacing = 0.0;
+ if (subStyle->borderStyle == BorderStyle::OUTLINE ||
+ subStyle->borderStyle == BorderStyle::OUTLINE_NO_SHADOW)
{
- // Configure the font properties
- // FIXME: The font size need to be scaled to be shown in right PT size
- style->FontSize = (subStyle->fontSize / 720) * playResY;
- // Modifies the width/height of the font (1 = 100%)
- style->ScaleX = 1.0;
- style->ScaleY = 1.0;
- // Extra space between characters causes the underlined
- // text line to become more discontinuous (test on LibAss 15.1)
- style->Spacing = 0;
-
- // Set automatic paragraph direction (not VSFilter-compatible)
- // to fix wrong RTL text direction when there are unicode chars
- style->Encoding = ASS_FONT_ENCODING_AUTO;
-
- bool isFontBold =
- (subStyle->fontStyle == FontStyle::BOLD || subStyle->fontStyle == FontStyle::BOLD_ITALIC);
- bool isFontItalic = (subStyle->fontStyle == FontStyle::ITALIC ||
- subStyle->fontStyle == FontStyle::BOLD_ITALIC);
- style->Bold = isFontBold * -1;
- style->Italic = isFontItalic * -1;
-
- // Compute the font color, depending on the opacity
- COLOR::Color subColor = ConvColor(subStyle->fontColor, subStyle->fontOpacity);
- // Set default subtitles color
- style->PrimaryColour = subColor;
- // Set SecondaryColour may be used to prevent an onscreen collision
- style->SecondaryColour = subColor;
-
- // Configure the effects
- double lineSpacing = 0.0;
- if (subStyle->borderStyle == BorderStyle::OUTLINE ||
- subStyle->borderStyle == BorderStyle::OUTLINE_NO_SHADOW)
+ style->BorderStyle = ASS_BORDER_STYLE_OUTLINE;
+ style->Outline = (10.00 / 100 * subStyle->fontBorderSize) * scale;
+ style->OutlineColour = ConvColor(subStyle->fontBorderColor, subStyle->fontOpacity);
+ if (subStyle->borderStyle == BorderStyle::OUTLINE_NO_SHADOW)
{
- style->BorderStyle = ASS_BORDER_STYLE_OUTLINE;
- style->Outline = (10.00 / 100 * subStyle->fontBorderSize) * scale;
- style->OutlineColour = ConvColor(subStyle->fontBorderColor, subStyle->fontOpacity);
- if (subStyle->borderStyle == BorderStyle::OUTLINE_NO_SHADOW)
- {
- style->BackColour = ConvColor(COLOR::NONE, 0); // Set the shadow color
- style->Shadow = 0; // Set the shadow size
- }
- else
- {
- style->BackColour =
- ConvColor(subStyle->shadowColor, subStyle->shadowOpacity); // Set the shadow color
- style->Shadow = (10.00 / 100 * subStyle->shadowSize) * scale; // Set the shadow size
- }
+ style->BackColour = ConvColor(COLOR::NONE, 0); // Set the shadow color
+ style->Shadow = 0; // Set the shadow size
}
- else if (subStyle->borderStyle == BorderStyle::BOX)
+ else
{
- // This BorderStyle not support outline color/size
- style->BorderStyle = ASS_BORDER_STYLE_BOX;
- style->Outline = 4 * scale; // Space between the text and the box edges
- style->OutlineColour =
- ConvColor(subStyle->backgroundColor,
- subStyle->backgroundOpacity); // Set the background border color
style->BackColour =
- ConvColor(subStyle->shadowColor, subStyle->shadowOpacity); // Set the box shadow color
- style->Shadow = (10.00 / 100 * subStyle->shadowSize) * scale; // Set the box shadow size
- // By default a box overlaps the other, then we increase a bit the line spacing
- lineSpacing = 6.0;
- }
- else if (subStyle->borderStyle == BorderStyle::SQUARE_BOX)
- {
- // This BorderStyle not support shadow color/size
- style->BorderStyle = ASS_BORDER_STYLE_SQUARE_BOX;
- style->Outline = (10.00 / 100 * subStyle->fontBorderSize) * scale;
- style->OutlineColour = ConvColor(subStyle->fontBorderColor, subStyle->fontOpacity);
- style->BackColour = ConvColor(subStyle->backgroundColor, subStyle->backgroundOpacity);
- style->Shadow = 4 * scale; // Space between the text and the box edges
+ ConvColor(subStyle->shadowColor, subStyle->shadowOpacity); // Set the shadow color
+ style->Shadow = (10.00 / 100 * subStyle->shadowSize) * scale; // Set the shadow size
}
+ }
+ else if (subStyle->borderStyle == BorderStyle::BOX)
+ {
+ // This BorderStyle not support outline color/size
+ style->BorderStyle = ASS_BORDER_STYLE_BOX;
+ style->Outline = 4 * scale; // Space between the text and the box edges
+ style->OutlineColour =
+ ConvColor(subStyle->backgroundColor,
+ subStyle->backgroundOpacity); // Set the background border color
+ style->BackColour =
+ ConvColor(subStyle->shadowColor, subStyle->shadowOpacity); // Set the box shadow color
+ style->Shadow = (10.00 / 100 * subStyle->shadowSize) * scale; // Set the box shadow size
+ // By default a box overlaps the other, then we increase a bit the line spacing
+ lineSpacing = 6.0;
+ }
+ else if (subStyle->borderStyle == BorderStyle::SQUARE_BOX)
+ {
+ // This BorderStyle not support shadow color/size
+ style->BorderStyle = ASS_BORDER_STYLE_SQUARE_BOX;
+ style->Outline = (10.00 / 100 * subStyle->fontBorderSize) * scale;
+ style->OutlineColour = ConvColor(subStyle->fontBorderColor, subStyle->fontOpacity);
+ style->BackColour = ConvColor(subStyle->backgroundColor, subStyle->backgroundOpacity);
+ style->Shadow = 4 * scale; // Space between the text and the box edges
+ }
- ass_set_line_spacing(m_renderer, lineSpacing);
-
- style->Blur = (10.00 / 100 * subStyle->blur);
+ ass_set_line_spacing(m_renderer, lineSpacing);
- double marginLR = 20;
- if (opts.horizontalAlignment != HorizontalAlignment::DISABLED)
- {
- // If the subtitle text is aligned on the left or right
- // of the screen, we set an extra left/right margin
- marginLR += static_cast<double>(opts.frameWidth) / 10;
- }
+ style->Blur = (10.00 / 100 * subStyle->blur);
- // Set the margins (in pixel)
- style->MarginL = static_cast<int>(marginLR * scale);
- style->MarginR = static_cast<int>(marginLR * scale);
- // Vertical margin (direction depends on alignment)
- // to be set only when the video calibration position setting is not used
- if (opts.usePosition)
- style->MarginV = 0;
- else
- style->MarginV = static_cast<int>(subStyle->marginVertical * scale);
+ double marginLR = 20;
+ if (opts.horizontalAlignment != HorizontalAlignment::DISABLED)
+ {
+ // If the subtitle text is aligned on the left or right
+ // of the screen, we set an extra left/right margin
+ marginLR += static_cast<double>(opts.frameWidth) / 10;
}
+ // Set the margins (in pixel)
+ style->MarginL = static_cast<int>(marginLR * scale);
+ style->MarginR = static_cast<int>(marginLR * scale);
+ // Vertical margin (direction depends on alignment)
+ // to be set only when the video calibration position setting is not used
+ if (opts.usePosition)
+ style->MarginV = 0;
+ else
+ style->MarginV = static_cast<int>(subStyle->marginVertical * scale);
+
// Set the vertical alignment
if (subStyle->alignment == FontAlignment::TOP_LEFT ||
subStyle->alignment == FontAlignment::TOP_CENTER ||
@@ -467,42 +502,32 @@ void CDVDSubtitlesLibass::ConfigureAssOverride(
}
// Default behaviour, disable ASS embedded styles override (if has been changed)
- int stylesFlags = ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE;
-
+ int stylesFlags{ASS_OVERRIDE_DEFAULT};
if (style)
{
// Manage override cases with ASS embedded styles
if (subStyle->assOverrideStyles == OverrideStyles::STYLES)
{
- stylesFlags = ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS | ASS_OVERRIDE_BIT_FONT_NAME |
- ASS_OVERRIDE_BIT_COLORS | ASS_OVERRIDE_BIT_ATTRIBUTES |
+ stylesFlags = ASS_OVERRIDE_BIT_COLORS | ASS_OVERRIDE_BIT_ATTRIBUTES |
ASS_OVERRIDE_BIT_BORDER | ASS_OVERRIDE_BIT_MARGINS;
}
else if (subStyle->assOverrideStyles == OverrideStyles::STYLES_POSITIONS)
{
- stylesFlags = ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS | ASS_OVERRIDE_BIT_FONT_NAME |
- ASS_OVERRIDE_BIT_COLORS | ASS_OVERRIDE_BIT_ATTRIBUTES |
+ stylesFlags = ASS_OVERRIDE_BIT_COLORS | ASS_OVERRIDE_BIT_ATTRIBUTES |
ASS_OVERRIDE_BIT_BORDER | ASS_OVERRIDE_BIT_MARGINS | ASS_OVERRIDE_BIT_ALIGNMENT;
}
else if (subStyle->assOverrideStyles == OverrideStyles::POSITIONS)
{
stylesFlags = ASS_OVERRIDE_BIT_ALIGNMENT;
}
+ if (subStyle->assOverrideFont)
+ {
+ stylesFlags |= ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS | ASS_OVERRIDE_BIT_FONT_NAME;
+ }
ass_set_selective_style_override(m_renderer, style);
}
- ass_set_selective_style_override_enabled(m_renderer, stylesFlags);
-}
-void CDVDSubtitlesLibass::ConfigureFont(bool overrideFont, std::string fontName)
-{
- int fontProvider = ASS_FONTPROVIDER_AUTODETECT;
- std::string fontPath = GetDefaultFontPath(fontName);
- if ((m_subtitleType == ADAPTED || overrideFont) && !fontPath.empty())
- fontProvider = ASS_FONTPROVIDER_NONE;
- // Libass take in consideration of the default font specified only
- // as last resort so when the builtin list of fallbacks fails,
- // the be able to use our font we have to set ASS_FONTPROVIDER_NONE
- ass_set_fonts(m_renderer, fontPath.c_str(), fontName.c_str(), fontProvider, nullptr, 1);
+ ass_set_selective_style_override_enabled(m_renderer, stylesFlags);
}
ASS_Event* CDVDSubtitlesLibass::GetEvents()
diff --git a/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.h b/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.h
index 5df4a8cffd..82efffc3c7 100644
--- a/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.h
+++ b/xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.h
@@ -146,7 +146,6 @@ protected:
private:
void ConfigureAssOverride(const std::shared_ptr<struct KODI::SUBTITLES::style>& subStyle,
ASS_Style* style);
- void ConfigureFont(bool overrideFont, std::string fontName);
void ApplyStyle(const std::shared_ptr<struct KODI::SUBTITLES::style>& subStyle,
KODI::SUBTITLES::renderOpts opts);
@@ -162,4 +161,5 @@ private:
// default allocated style ID for the kodi user configured subtitle style
int m_defaultKodiStyleId{ASS_NO_ID};
bool m_drawWithinBlackBars{false};
+ std::string m_defaultFontFamilyName;
};
diff --git a/xbmc/cores/VideoPlayer/DVDSubtitles/SubtitlesStyle.h b/xbmc/cores/VideoPlayer/DVDSubtitles/SubtitlesStyle.h
index eb249f1ce0..86c0170186 100644
--- a/xbmc/cores/VideoPlayer/DVDSubtitles/SubtitlesStyle.h
+++ b/xbmc/cores/VideoPlayer/DVDSubtitles/SubtitlesStyle.h
@@ -21,7 +21,6 @@ constexpr double VIEWPORT_HEIGHT = 1080.0;
constexpr double VIEWPORT_WIDTH = 1920.0;
constexpr int MARGIN_VERTICAL = 30;
-
enum class HorizontalAlignment
{
DISABLED = 0,
@@ -69,8 +68,8 @@ enum class OverrideStyles
struct style
{
- std::string fontName;
- double fontSize;
+ std::string fontName; // Font family name
+ double fontSize; // Font size in PT
FontStyle fontStyle = FontStyle::NORMAL;
UTILS::COLOR::Color fontColor = UTILS::COLOR::WHITE;
int fontBorderSize = 15; // In %
diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp
index 62e52089ad..9ea97e9da7 100644
--- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp
+++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp
@@ -50,6 +50,7 @@
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "storage/MediaManager.h"
+#include "utils/FontUtils.h"
#include "utils/JobManager.h"
#include "utils/LangCodeExpander.h"
#include "utils/StreamDetails.h"
@@ -745,7 +746,7 @@ void CVideoPlayer::OnStartup()
m_CurrentTeletext.Clear();
m_CurrentRadioRDS.Clear();
- CUtil::ClearTempFonts();
+ UTILS::FONT::ClearTemporaryFonts();
}
bool CVideoPlayer::OpenInputStream()
diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/OverlayRenderer.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/OverlayRenderer.cpp
index 6c9667097a..f83a26a019 100644
--- a/xbmc/cores/VideoPlayer/VideoRenderers/OverlayRenderer.cpp
+++ b/xbmc/cores/VideoPlayer/VideoRenderers/OverlayRenderer.cpp
@@ -263,7 +263,7 @@ void CRenderer::CreateSubtitlesStyle()
m_overlayStyle = std::make_shared<KODI::SUBTITLES::style>();
const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
- m_overlayStyle->fontName = settings->GetString(CSettings::SETTING_SUBTITLES_FONT);
+ m_overlayStyle->fontName = settings->GetString(CSettings::SETTING_SUBTITLES_FONTNAME);
m_overlayStyle->fontSize = (double)settings->GetInt(CSettings::SETTING_SUBTITLES_FONTSIZE);
uint32_t fontStyleMask = settings->GetInt(CSettings::SETTING_SUBTITLES_STYLE) & FONT_STYLE_MASK;
diff --git a/xbmc/guilib/GUIFontManager.cpp b/xbmc/guilib/GUIFontManager.cpp
index e3e94bba40..3a22fcdf38 100644
--- a/xbmc/guilib/GUIFontManager.cpp
+++ b/xbmc/guilib/GUIFontManager.cpp
@@ -28,17 +28,48 @@
#include "filesystem/File.h"
#include "settings/lib/Setting.h"
#include "settings/lib/SettingDefinitions.h"
+#include "utils/FontUtils.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "utils/XMLUtils.h"
#include "utils/log.h"
+#include <algorithm>
+#include <set>
+
#ifdef TARGET_POSIX
#include "filesystem/SpecialProtocol.h"
#endif
using namespace ADDON;
+namespace
+{
+constexpr const char* XML_FONTCACHE_FILENAME = "fontcache.xml";
+
+bool LoadXMLData(const std::string& filepath, CXBMCTinyXML& xmlDoc)
+{
+ if (!XFILE::CFile::Exists(filepath))
+ {
+ CLog::LogF(LOGDEBUG, "Couldn't load '{}' the file don't exists", filepath);
+ return false;
+ }
+ if (!xmlDoc.LoadFile(filepath))
+ {
+ CLog::LogF(LOGERROR, "Couldn't load '{}'", filepath);
+ return false;
+ }
+ TiXmlElement* pRootElement = xmlDoc.RootElement();
+ if (!pRootElement || pRootElement->ValueStr() != "fonts")
+ {
+ CLog::LogF(LOGERROR, "Couldn't load '{}' XML content doesn't start with <fonts>", filepath);
+ return false;
+ }
+ return true;
+}
+} // unnamed namespace
+
+
GUIFontManager::GUIFontManager() = default;
GUIFontManager::~GUIFontManager()
@@ -383,29 +414,18 @@ void GUIFontManager::Clear()
void GUIFontManager::LoadFonts(const std::string& fontSet)
{
// Get the file to load fonts from:
- const std::string strPath = g_SkinInfo->GetSkinPath("Font.xml", &m_skinResolution);
- CLog::Log(LOGINFO, "GUIFontManager::{}: Loading fonts from '{}'", __func__, strPath);
+ const std::string filePath = g_SkinInfo->GetSkinPath("Font.xml", &m_skinResolution);
+ CLog::LogF(LOGINFO, "Loading fonts from '{}'", filePath);
CXBMCTinyXML xmlDoc;
- if (!xmlDoc.LoadFile(strPath))
- {
- CLog::Log(LOGERROR, "GUIFontManager::{}: Couldn't load '{}'", __func__, strPath);
+ if (!LoadXMLData(filePath, xmlDoc))
return;
- }
TiXmlElement* pRootElement = xmlDoc.RootElement();
- if (!pRootElement || pRootElement->ValueStr() != "fonts")
- {
- CLog::Log(LOGERROR, "GUIFontManager::{}: File {} doesn't start with <fonts>", __func__,
- strPath);
- return;
- }
-
// Resolve includes in Font.xml
g_SkinInfo->ResolveIncludes(pRootElement);
// take note of the first font available in case we can't load the one specified
std::string firstFont;
-
const TiXmlElement* pChild = pRootElement->FirstChildElement("fontset");
while (pChild)
{
@@ -434,8 +454,7 @@ void GUIFontManager::LoadFonts(const std::string& fontSet)
LoadFonts(firstFont);
}
else
- CLog::Log(LOGERROR, "GUIFontManager::{}: File '{}' doesn't have a valid <fontset>", __func__,
- strPath);
+ CLog::LogF(LOGERROR, "File '{}' doesn't have a valid <fontset>", filePath);
}
void GUIFontManager::LoadFonts(const TiXmlNode* fontNode)
@@ -508,19 +527,143 @@ void GUIFontManager::SettingOptionsFontsFiller(const SettingConstPtr& setting,
CFileItemList items;
// Find font files
- XFILE::CDirectory::GetDirectory("special://xbmc/media/Fonts/", itemsRoot, "",
- XFILE::DIR_FLAG_DEFAULTS);
- XFILE::CDirectory::GetDirectory("special://home/media/Fonts/", items, "",
- XFILE::DIR_FLAG_DEFAULTS);
+ XFILE::CDirectory::GetDirectory(UTILS::FONT::FONTPATH::SYSTEM, itemsRoot,
+ UTILS::FONT::SUPPORTED_EXTENSIONS_MASK,
+ XFILE::DIR_FLAG_NO_FILE_DIRS | XFILE::DIR_FLAG_NO_FILE_INFO);
+ XFILE::CDirectory::GetDirectory(UTILS::FONT::FONTPATH::USER, items,
+ UTILS::FONT::SUPPORTED_EXTENSIONS_MASK,
+ XFILE::DIR_FLAG_NO_FILE_DIRS | XFILE::DIR_FLAG_NO_FILE_INFO);
for (auto itItem = itemsRoot.rbegin(); itItem != itemsRoot.rend(); ++itItem)
items.AddFront(*itItem, 0);
for (const auto& item : items)
{
- if (!item->m_bIsFolder && CUtil::IsSupportedFontExtension(item->GetLabel()))
+ if (item->m_bIsFolder)
+ continue;
+
+ list.emplace_back(item->GetLabel(), item->GetLabel());
+ }
+}
+
+void GUIFontManager::Initialize()
+{
+ CSingleLock lock(m_critSection);
+ LoadUserFonts();
+}
+
+void GUIFontManager::LoadUserFonts()
+{
+ if (!XFILE::CDirectory::Exists(UTILS::FONT::FONTPATH::USER))
+ return;
+
+ CLog::LogF(LOGDEBUG, "Updating user fonts cache...");
+ CXBMCTinyXML xmlDoc;
+ std::string userFontCacheFilepath =
+ URIUtils::AddFileToFolder(UTILS::FONT::FONTPATH::USER, XML_FONTCACHE_FILENAME);
+ if (LoadXMLData(userFontCacheFilepath, xmlDoc))
+ {
+ // Load in cache the fonts metadata previously stored in the XML
+ TiXmlElement* pRootElement = xmlDoc.RootElement();
+ if (pRootElement)
{
- list.emplace_back(item->GetLabel(), item->GetLabel());
+ const TiXmlNode* fontNode = pRootElement->FirstChild("font");
+ while (fontNode)
+ {
+ std::string filename;
+ std::string familyName;
+ XMLUtils::GetString(fontNode, "filename", filename);
+ XMLUtils::GetString(fontNode, "familyname", familyName);
+ m_userFontsCache.emplace_back(filename, familyName);
+ fontNode = fontNode->NextSibling("font");
+ }
}
}
+
+ bool isCacheChanged{false};
+ size_t previousCacheSize = m_userFontsCache.size();
+ CFileItemList dirItems;
+ // Get the current files list from user fonts folder
+ XFILE::CDirectory::GetDirectory(UTILS::FONT::FONTPATH::USER, dirItems,
+ UTILS::FONT::SUPPORTED_EXTENSIONS_MASK,
+ XFILE::DIR_FLAG_NO_FILE_DIRS | XFILE::DIR_FLAG_NO_FILE_INFO);
+ dirItems.SetFastLookup(true);
+
+ // Remove files that no longer exist from cache
+ auto it = m_userFontsCache.begin();
+ while (it != m_userFontsCache.end())
+ {
+ const std::string filePath = UTILS::FONT::FONTPATH::USER + (*it).m_filename;
+ if (!dirItems.Contains(filePath))
+ {
+ it = m_userFontsCache.erase(it);
+ }
+ else
+ {
+ auto item = dirItems.Get(filePath);
+ dirItems.Remove(item.get());
+ ++it;
+ }
+ }
+ isCacheChanged = previousCacheSize != m_userFontsCache.size();
+ previousCacheSize = m_userFontsCache.size();
+
+ // Add new files in cache and generate the metadata
+ //!@todo FIXME: this "for" loop should be replaced with the more performant
+ //! parallel execution std::for_each(std::execution::par, ...
+ //! to halving loading times of fonts list, maybe with C++17 with appropriate
+ //! fix to include parallel execution or future C++20
+ for (auto& item : dirItems)
+ {
+ std::string filepath = item->GetPath();
+ if (item->m_bIsFolder)
+ continue;
+
+ std::string familyName = UTILS::FONT::GetFontFamily(filepath);
+ if (!familyName.empty())
+ {
+ m_userFontsCache.emplace_back(item->GetLabel(), familyName);
+ }
+ }
+ isCacheChanged = isCacheChanged || previousCacheSize != m_userFontsCache.size();
+
+ // If the cache is changed save an updated XML cache file
+ if (isCacheChanged)
+ {
+ CXBMCTinyXML xmlDoc;
+ TiXmlDeclaration decl("1.0", "UTF-8", "yes");
+ xmlDoc.InsertEndChild(decl);
+ TiXmlElement xmlMainElement("fonts");
+ TiXmlNode* fontsNode = xmlDoc.InsertEndChild(xmlMainElement);
+ if (fontsNode)
+ {
+ for (auto& fontMetadata : m_userFontsCache)
+ {
+ TiXmlElement fontElement("font");
+ TiXmlNode* fontNode = fontsNode->InsertEndChild(fontElement);
+ XMLUtils::SetString(fontNode, "filename", fontMetadata.m_filename);
+ XMLUtils::SetString(fontNode, "familyname", fontMetadata.m_familyName);
+ }
+ if (!xmlDoc.SaveFile(userFontCacheFilepath))
+ CLog::LogF(LOGERROR, "Failed to save fonts cache file '{}'", userFontCacheFilepath);
+ }
+ else
+ {
+ CLog::LogF(LOGERROR, "Failed to create XML 'fonts' node");
+ }
+ }
+ CLog::LogF(LOGDEBUG, "Updating user fonts cache... DONE");
+}
+
+std::vector<std::string> GUIFontManager::GetUserFontsFamilyNames()
+{
+ // We ensure to have unique font family names and sorted alphabetically
+ // Duplicated family names can happens for example when a font have each style
+ // on different files
+ std::set<std::string, sortstringbyname> familyNames;
+ for (auto& fontMetadata : m_userFontsCache)
+ {
+ familyNames.insert(fontMetadata.m_familyName);
+ }
+ return std::vector<std::string>(familyNames.begin(), familyNames.end());
}
diff --git a/xbmc/guilib/GUIFontManager.h b/xbmc/guilib/GUIFontManager.h
index 88aed7bd62..5923517059 100644
--- a/xbmc/guilib/GUIFontManager.h
+++ b/xbmc/guilib/GUIFontManager.h
@@ -14,6 +14,8 @@
*/
#include "IMsgTargetCallback.h"
+#include "threads/CriticalSection.h"
+#include "threads/SingleLock.h"
#include "utils/ColorUtils.h"
#include "utils/GlobalsHandling.h"
#include "windowing/GraphicContext.h"
@@ -40,6 +42,17 @@ struct OrigFontInfo
bool border;
};
+struct FontMetadata
+{
+ FontMetadata(const std::string& filename, const std::string& familyName)
+ : m_filename{filename}, m_familyName{familyName}
+ {
+ }
+
+ std::string m_filename;
+ std::string m_familyName;
+};
+
/*!
\ingroup textures
\brief
@@ -50,6 +63,14 @@ public:
GUIFontManager();
~GUIFontManager() override;
+ /*!
+ * \brief Initialize the font manager.
+ * Checks that fonts cache are up to date, otherwise update it.
+ */
+ void Initialize();
+
+ bool IsUpdating() const { return m_critSection.IsLocked(); }
+
bool OnMessage(CGUIMessage& message) override;
void Unload(const std::string& strFontName);
@@ -81,6 +102,12 @@ public:
std::string& current,
void* data);
+ /*!
+ * \brief Get the list of user fonts as family names from cache
+ * \return The list of available fonts family names
+ */
+ std::vector<std::string> GetUserFontsFamilyNames();
+
protected:
void ReloadTTFFonts();
static void RescaleFontSizeAndAspect(CGraphicContext& context,
@@ -97,6 +124,12 @@ protected:
std::vector<OrigFontInfo> m_vecFontInfo;
RESOLUTION_INFO m_skinResolution;
bool m_canReload{true};
+
+private:
+ void LoadUserFonts();
+
+ mutable CCriticalSection m_critSection;
+ std::vector<FontMetadata> m_userFontsCache;
};
/*!
diff --git a/xbmc/settings/SettingConditions.cpp b/xbmc/settings/SettingConditions.cpp
index 8f5ec6a824..fb4201f114 100644
--- a/xbmc/settings/SettingConditions.cpp
+++ b/xbmc/settings/SettingConditions.cpp
@@ -25,6 +25,7 @@
#include "profiles/ProfileManager.h"
#include "settings/SettingAddon.h"
#include "settings/SettingsComponent.h"
+#include "utils/FontUtils.h"
#include "utils/StringUtils.h"
#include "windowing/WinSystem.h"
@@ -134,7 +135,7 @@ bool HasSubtitlesFontExtensions(const std::string& condition,
if (!settingStr)
return false;
- return CUtil::IsSupportedFontExtension(settingStr->GetValue());
+ return UTILS::FONT::IsSupportedFontExtension(settingStr->GetValue());
}
bool ProfileCanWriteDatabase(const std::string& condition,
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
index 02420efb89..ea9b359011 100644
--- a/xbmc/settings/Settings.cpp
+++ b/xbmc/settings/Settings.cpp
@@ -164,7 +164,7 @@ constexpr const char* CSettings::SETTING_SUBTITLES_PARSECAPTIONS;
constexpr const char* CSettings::SETTING_SUBTITLES_CAPTIONSALIGN;
constexpr const char* CSettings::SETTING_SUBTITLES_ALIGN;
constexpr const char* CSettings::SETTING_SUBTITLES_STEREOSCOPICDEPTH;
-constexpr const char* CSettings::SETTING_SUBTITLES_FONT;
+constexpr const char* CSettings::SETTING_SUBTITLES_FONTNAME;
constexpr const char* CSettings::SETTING_SUBTITLES_FONTSIZE;
constexpr const char* CSettings::SETTING_SUBTITLES_STYLE;
constexpr const char* CSettings::SETTING_SUBTITLES_COLOR;
@@ -788,6 +788,8 @@ void CSettings::InitializeOptionFillers()
#endif
GetSettingsManager()->RegisterSettingOptionsFiller("charsets", CCharsetConverter::SettingOptionsCharsetsFiller);
GetSettingsManager()->RegisterSettingOptionsFiller("fonts", GUIFontManager::SettingOptionsFontsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller(
+ "subtitlesfonts", SUBTITLES::CSubtitlesSettings::SettingOptionsSubtitleFontsFiller);
GetSettingsManager()->RegisterSettingOptionsFiller("languagenames", CLangInfo::SettingOptionsLanguageNamesFiller);
GetSettingsManager()->RegisterSettingOptionsFiller("refreshchangedelays", CDisplaySettings::SettingOptionsRefreshChangeDelaysFiller);
GetSettingsManager()->RegisterSettingOptionsFiller("refreshrates", CDisplaySettings::SettingOptionsRefreshRatesFiller);
@@ -832,6 +834,7 @@ void CSettings::UninitializeOptionFillers()
GetSettingsManager()->UnregisterSettingOptionsFiller("charsets");
GetSettingsManager()->UnregisterSettingOptionsFiller("fontheights");
GetSettingsManager()->UnregisterSettingOptionsFiller("fonts");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("subtitlesfonts");
GetSettingsManager()->UnregisterSettingOptionsFiller("languagenames");
GetSettingsManager()->UnregisterSettingOptionsFiller("refreshchangedelays");
GetSettingsManager()->UnregisterSettingOptionsFiller("refreshrates");
diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h
index 1227b306ee..c31fc60a82 100644
--- a/xbmc/settings/Settings.h
+++ b/xbmc/settings/Settings.h
@@ -143,7 +143,7 @@ public:
static constexpr auto SETTING_SUBTITLES_CAPTIONSALIGN = "subtitles.captionsalign";
static constexpr auto SETTING_SUBTITLES_ALIGN = "subtitles.align";
static constexpr auto SETTING_SUBTITLES_STEREOSCOPICDEPTH = "subtitles.stereoscopicdepth";
- static constexpr auto SETTING_SUBTITLES_FONT = "subtitles.font";
+ static constexpr auto SETTING_SUBTITLES_FONTNAME = "subtitles.fontname";
static constexpr auto SETTING_SUBTITLES_FONTSIZE = "subtitles.fontsize";
static constexpr auto SETTING_SUBTITLES_STYLE = "subtitles.style";
static constexpr auto SETTING_SUBTITLES_COLOR = "subtitles.colorpick";
diff --git a/xbmc/settings/SubtitlesSettings.cpp b/xbmc/settings/SubtitlesSettings.cpp
index 6414de7fd5..25742dd203 100644
--- a/xbmc/settings/SubtitlesSettings.cpp
+++ b/xbmc/settings/SubtitlesSettings.cpp
@@ -8,15 +8,23 @@
#include "SubtitlesSettings.h"
+#include "FileItem.h"
#include "ServiceBroker.h"
+#include "filesystem/Directory.h"
+#include "filesystem/File.h"
+#include "guilib/GUIFontManager.h"
+#include "guilib/LocalizeStrings.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "settings/lib/Setting.h"
+#include "utils/FontUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
using namespace KODI;
using namespace SUBTITLES;
-
CSubtitlesSettings::CSubtitlesSettings()
{
m_settings = CServiceBroker::GetSettingsComponent()->GetSettings();
@@ -25,7 +33,7 @@ CSubtitlesSettings::CSubtitlesSettings()
CSettings::SETTING_SUBTITLES_PARSECAPTIONS,
CSettings::SETTING_SUBTITLES_ALIGN,
CSettings::SETTING_SUBTITLES_STEREOSCOPICDEPTH,
- CSettings::SETTING_SUBTITLES_FONT,
+ CSettings::SETTING_SUBTITLES_FONTNAME,
CSettings::SETTING_SUBTITLES_FONTSIZE,
CSettings::SETTING_SUBTITLES_STYLE,
CSettings::SETTING_SUBTITLES_COLOR,
@@ -70,3 +78,26 @@ void CSubtitlesSettings::OnSettingChanged(const std::shared_ptr<const CSetting>&
SetChanged();
NotifyObservers(ObservableMessageSettingsChanged);
}
+
+void CSubtitlesSettings::SettingOptionsSubtitleFontsFiller(const SettingConstPtr& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ // From application system fonts folder we add the default font only
+ std::string defaultFontPath =
+ URIUtils::AddFileToFolder("special://xbmc/media/Fonts/", UTILS::FONT::FONT_DEFAULT_FILENAME);
+ if (XFILE::CFile::Exists(defaultFontPath))
+ {
+ std::string familyName = UTILS::FONT::GetFontFamily(defaultFontPath);
+ if (!familyName.empty())
+ {
+ list.emplace_back(g_localizeStrings.Get(571) + " " + familyName, FONT_DEFAULT_FAMILYNAME);
+ }
+ }
+ // Add additionals fonts from the user fonts folder
+ for (std::string familyName : g_fontManager.GetUserFontsFamilyNames())
+ {
+ list.emplace_back(familyName, familyName);
+ }
+}
diff --git a/xbmc/settings/SubtitlesSettings.h b/xbmc/settings/SubtitlesSettings.h
index ad7e41c783..0940c589a7 100644
--- a/xbmc/settings/SubtitlesSettings.h
+++ b/xbmc/settings/SubtitlesSettings.h
@@ -11,13 +11,19 @@
#include "settings/lib/ISettingCallback.h"
#include "utils/Observer.h"
+#include <memory>
+
class CSetting;
class CSettings;
+struct StringSettingOption;
namespace KODI
{
namespace SUBTITLES
{
+// This is a placeholder to keep the fontname setting valid
+// even if the default app font could be changed
+constexpr const char* FONT_DEFAULT_FAMILYNAME = "DEFAULT";
class CSubtitlesSettings : public ISettingCallback, public Observable
{
@@ -27,6 +33,11 @@ public:
// Inherited from ISettingCallback
void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ static void SettingOptionsSubtitleFontsFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data);
+
protected:
CSubtitlesSettings();
~CSubtitlesSettings() override;
diff --git a/xbmc/settings/windows/GUIControlSettings.cpp b/xbmc/settings/windows/GUIControlSettings.cpp
index 12aced38e7..eea6e76330 100644
--- a/xbmc/settings/windows/GUIControlSettings.cpp
+++ b/xbmc/settings/windows/GUIControlSettings.cpp
@@ -670,9 +670,8 @@ bool CGUIControlListSetting::OnClick()
if (!bAllowNewOption)
{
// Do not show dialog if
- // * there are no items to be chosen or
- // * only one value can be chosen and there are less than two items available
- if (!optionsValid || options.Size() <= 0 || (!control->CanMultiSelect() && options.Size() <= 1))
+ // there are no items to be chosen
+ if (!optionsValid || options.Size() <= 0)
return false;
dialog->Reset();
@@ -851,10 +850,8 @@ void CGUIControlListSetting::Update(bool fromControl, bool updateDisplayOnly)
if (!updateDisplayOnly)
{
// Disable the control if no items can be added and
- // * there are no items to be chosen
- // * only one value can be chosen and there are less than two items available
- if (!m_pButton->IsDisabled() && !bAllowNewOption &&
- (options.Size() <= 0 || (!control->CanMultiSelect() && options.Size() <= 1)))
+ // there are no items to be chosen
+ if (!m_pButton->IsDisabled() && !bAllowNewOption && (options.Size() <= 0))
m_pButton->SetEnabled(false);
}
}
diff --git a/xbmc/threads/Lockables.h b/xbmc/threads/Lockables.h
index 333d330229..28489f8a60 100644
--- a/xbmc/threads/Lockables.h
+++ b/xbmc/threads/Lockables.h
@@ -21,6 +21,7 @@ namespace XbmcThreads
* lock();
* try_lock();
* unlock();
+ * IsLocked();
*
* "Exitable" specifically means that, no matter how deep the recursion
* on the mutex/critical section, we can exit from it and then restore
@@ -50,6 +51,12 @@ namespace XbmcThreads
inline bool try_lock() { return mutex.try_lock() ? count++, true : false; }
inline void unlock() { count--; mutex.unlock(); }
+ /*!
+ * \brief Check if have a lock owned
+ * \return True if have a lock, otherwise false
+ */
+ inline bool IsLocked() const { return count > 0; }
+
/**
* This implements the "exitable" behavior mentioned above.
*/
diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt
index cb3a084741..201f2e992a 100644
--- a/xbmc/utils/CMakeLists.txt
+++ b/xbmc/utils/CMakeLists.txt
@@ -24,6 +24,7 @@ set(SOURCES ActorProtocol.cpp
Fanart.cpp
FileOperationJob.cpp
FileUtils.cpp
+ FontUtils.cpp
GroupUtils.cpp
HTMLUtil.cpp
HttpHeader.cpp
@@ -99,6 +100,7 @@ set(HEADERS ActorProtocol.h
Fanart.h
FileOperationJob.h
FileUtils.h
+ FontUtils.h
Geometry.h
GlobalsHandling.h
GroupUtils.h
diff --git a/xbmc/utils/FontUtils.cpp b/xbmc/utils/FontUtils.cpp
new file mode 100644
index 0000000000..c93b15e8fb
--- /dev/null
+++ b/xbmc/utils/FontUtils.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "FontUtils.h"
+
+#include "FileItem.h"
+#include "StringUtils.h"
+#include "URIUtils.h"
+#include "Util.h"
+#include "filesystem/Directory.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "utils/log.h"
+
+#include <ft2build.h>
+
+#include FT_FREETYPE_H
+
+using namespace XFILE;
+
+std::string UTILS::FONT::GetFontFamily(std::vector<uint8_t>& buffer)
+{
+ FT_Library m_library{nullptr};
+ FT_Init_FreeType(&m_library);
+ if (!m_library)
+ {
+ CLog::LogF(LOGERROR, "Unable to initialize freetype library");
+ return "";
+ }
+
+ // Load the font face
+ FT_Face face;
+ std::string familyName;
+ if (FT_New_Memory_Face(m_library, reinterpret_cast<const FT_Byte*>(buffer.data()), buffer.size(),
+ 0, &face) == 0)
+ {
+ familyName = std::string(face->family_name);
+ }
+ else
+ {
+ CLog::LogF(LOGERROR, "Failed to process font memory buffer");
+ }
+
+ FT_Done_Face(face);
+ FT_Done_FreeType(m_library);
+ return familyName;
+}
+
+std::string UTILS::FONT::GetFontFamily(const std::string& filepath)
+{
+ std::vector<uint8_t> buffer;
+ if (filepath.empty())
+ return "";
+ if (XFILE::CFile().LoadFile(filepath, buffer) <= 0)
+ {
+ CLog::LogF(LOGERROR, "Failed to load file {}", filepath);
+ return "";
+ }
+ return GetFontFamily(buffer);
+}
+
+bool UTILS::FONT::IsSupportedFontExtension(const std::string& filepath)
+{
+ return URIUtils::HasExtension(filepath, UTILS::FONT::SUPPORTED_EXTENSIONS_MASK);
+}
+
+void UTILS::FONT::ClearTemporaryFonts()
+{
+ if (!CDirectory::Exists(UTILS::FONT::FONTPATH::TEMP))
+ return;
+
+ CFileItemList items;
+ CDirectory::GetDirectory(UTILS::FONT::FONTPATH::TEMP, items,
+ UTILS::FONT::SUPPORTED_EXTENSIONS_MASK,
+ DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE | DIR_FLAG_GET_HIDDEN);
+ for (const auto& item : items)
+ {
+ if (item->m_bIsFolder)
+ continue;
+
+ CFile::Delete(item->GetPath());
+ }
+}
+
+std::string UTILS::FONT::FONTPATH::GetSystemFontPath(const std::string& filename)
+{
+ std::string fontPath = URIUtils::AddFileToFolder(
+ CSpecialProtocol::TranslatePath(UTILS::FONT::FONTPATH::SYSTEM), filename);
+ if (XFILE::CFile::Exists(fontPath))
+ {
+ return CSpecialProtocol::TranslatePath(fontPath);
+ }
+
+ CLog::LogF(LOGERROR, "Could not find application system font {}", filename);
+ return "";
+}
diff --git a/xbmc/utils/FontUtils.h b/xbmc/utils/FontUtils.h
new file mode 100644
index 0000000000..d4b92ddcee
--- /dev/null
+++ b/xbmc/utils/FontUtils.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace UTILS
+{
+namespace FONT
+{
+constexpr const char* SUPPORTED_EXTENSIONS_MASK = ".ttf|.otf";
+
+// The default application font
+constexpr const char* FONT_DEFAULT_FILENAME = "arial.ttf";
+
+namespace FONTPATH
+{
+// Directory where Kodi bundled fonts files are located
+constexpr const char* SYSTEM = "special://xbmc/media/Fonts/";
+// Directory where user defined fonts are located
+constexpr const char* USER = "special://home/media/Fonts/";
+// Temporary font path (where MKV fonts are extracted and temporarily stored)
+constexpr const char* TEMP = "special://temp/fonts/";
+
+/*!
+ * \brief Provided a font filename returns the complete path for the font in
+ * the system font folder (if it exists) or an empty string otherwise
+ * \param filename The font file name
+ * \return The path for the font or an empty string if the path does not exist
+ */
+std::string GetSystemFontPath(const std::string& filename);
+}; // namespace FONTPATH
+
+/*!
+ * \brief Get the font family name from a font file
+ * \param buffer The font data
+ * \return The font family name, otherwise empty if fails
+ */
+std::string GetFontFamily(std::vector<uint8_t>& buffer);
+
+/*!
+ * \brief Get the font family name from a font file
+ * \param filepath The path where read the font data
+ * \return The font family name, otherwise empty if fails
+ */
+std::string GetFontFamily(const std::string& filepath);
+
+/*!
+ * \brief Check if a filename have a supported font extension.
+ * \param filepath The font file path
+ * \return True if it has a supported extension, otherwise false
+ */
+bool IsSupportedFontExtension(const std::string& filepath);
+
+/*!
+ * \brief Removes all temporary fonts, e.g.those extract from MKV containers
+ * that are only available during playback
+ */
+void ClearTemporaryFonts();
+
+} // namespace FONT
+} // namespace UTILS
diff --git a/xbmc/utils/StringUtils.h b/xbmc/utils/StringUtils.h
index 9ff28e20d1..d30c64c5be 100644
--- a/xbmc/utils/StringUtils.h
+++ b/xbmc/utils/StringUtils.h
@@ -395,7 +395,7 @@ private:
struct sortstringbyname
{
- bool operator()(const std::string& strItem1, const std::string& strItem2)
+ bool operator()(const std::string& strItem1, const std::string& strItem2) const
{
return StringUtils::CompareNoCase(strItem1, strItem2) < 0;
}