From 1c73379ed8483dcf71c5ccfc459c2c22516a9aef Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 8 Apr 2023 01:04:37 +0200 Subject: [PATCH] Fix subtitle font handling with in-memory fonts from SMPTE (#2509). Previously we would fail to make a font available if it came from a SMPTE MXF. In that case we have a memory buffer containing the TTF/OTF file but no file; here we add a hack/workaround so that in-memory font files can be used by FontConfig. --- src/lib/font_config.cc | 43 +++++++++++++++++++++++++++++++---- src/lib/font_config.h | 9 ++++++-- src/lib/render_text.cc | 17 ++------------ src/lib/util.cc | 2 +- src/tools/dcpomatic.cc | 3 +++ src/tools/dcpomatic_player.cc | 9 ++++++++ 6 files changed, 61 insertions(+), 22 deletions(-) diff --git a/src/lib/font_config.cc b/src/lib/font_config.cc index 40b9770c7..d4a442fc1 100644 --- a/src/lib/font_config.cc +++ b/src/lib/font_config.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2021 Carl Hetherington + Copyright (C) 2014-2023 Carl Hetherington This file is part of DCP-o-matic. @@ -21,12 +21,15 @@ #include "dcpomatic_assert.h" #include "dcpomatic_log.h" +#include "font.h" #include "font_config.h" +#include "util.h" #include #include #include +using std::shared_ptr; using std::string; using boost::optional; @@ -41,14 +44,38 @@ FontConfig::FontConfig() } +FontConfig::~FontConfig() +{ + for (auto file: _temp_files) { + boost::system::error_code ec; + boost::filesystem::remove(file, ec); + } +} + + string -FontConfig::make_font_available(boost::filesystem::path font_file) +FontConfig::make_font_available(shared_ptr font) { - auto existing = _available_fonts.find(font_file); + auto existing = _available_fonts.find(font->id()); if (existing != _available_fonts.end()) { return existing->second; } + boost::filesystem::path font_file = default_font_file(); + if (font->file()) { + font_file = *font->file(); + } else if (font->data()) { + /* This font only exists in memory (so far) but FontConfig doesn't have an API to add a font + * from a memory buffer (AFAICS). + * https://gitlab.freedesktop.org/fontconfig/fontconfig/-/issues/12 + * As a workaround, write the font data to a temporary file and use that. + */ + font_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + _temp_files.push_back(font_file); + font->data()->write(font_file); + } + + /* Make this font available to DCP-o-matic */ optional font_name; FcConfigAppFontAddFile (_config, reinterpret_cast(font_file.string().c_str())); @@ -80,7 +107,7 @@ FontConfig::make_font_available(boost::filesystem::path font_file) DCPOMATIC_ASSERT(font_name); - _available_fonts[font_file] = *font_name; + _available_fonts[font->id()] = *font_name; FcConfigBuildFonts(_config); return *font_name; @@ -135,3 +162,11 @@ FontConfig::instance() return _instance; } + +void +FontConfig::drop() +{ + delete _instance; + _instance = nullptr; +} + diff --git a/src/lib/font_config.h b/src/lib/font_config.h index 6dadbae8a..edcb4be36 100644 --- a/src/lib/font_config.h +++ b/src/lib/font_config.h @@ -31,14 +31,19 @@ class FontConfig public: static FontConfig* instance(); - std::string make_font_available(boost::filesystem::path font_file); + std::string make_font_available(std::shared_ptr font); boost::optional system_font_with_name(std::string name); + static void drop(); + private: FontConfig(); + ~FontConfig(); FcConfig* _config = nullptr; - std::map _available_fonts; + std::map _available_fonts; + + std::vector _temp_files; static FontConfig* _instance; }; diff --git a/src/lib/render_text.cc b/src/lib/render_text.cc index 33e0c6a89..1a0672262 100644 --- a/src/lib/render_text.cc +++ b/src/lib/render_text.cc @@ -171,19 +171,6 @@ create_surface (shared_ptr image) } -static string -setup_font(shared_ptr font) -{ - auto font_file = default_font_file (); - - if (font && font->file()) { - font_file = *font->file(); - } - - return FontConfig::instance()->make_font_available(font_file); -} - - static float calculate_fade_factor (StringText const& first, DCPTime time, int frame_rate) { @@ -314,7 +301,7 @@ setup_layout(list subtitles, dcp::Size target, DCPTime time, int fra DCPOMATIC_ASSERT(!subtitles.empty()); auto const& first = subtitles.front(); - auto const font_name = setup_font(first.font); + auto const font_name = FontConfig::instance()->make_font_available(first.font); auto const fade_factor = calculate_fade_factor(first, time, frame_rate); auto const markup = marked_up(subtitles, target.height, fade_factor, font_name); auto layout = create_layout(font_name, markup); @@ -492,7 +479,7 @@ FontMetrics::get(StringText const& subtitle) return iter; } - auto const font_name = setup_font(subtitle.font); + auto const font_name = FontConfig::instance()->make_font_available(subtitle.font); auto copy = subtitle; copy.set_text("Qypjg"); auto layout = create_layout(font_name, marked_up({copy}, _target_height, 1, font_name)); diff --git a/src/lib/util.cc b/src/lib/util.cc index 20a965781..8c5700598 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -466,7 +466,7 @@ LIBDCP_ENABLE_WARNINGS optional(), false, false, false, dcp::Colour(), 42, 1, dcp::Time(), dcp::Time(), 0, dcp::HAlign::CENTER, 0, dcp::VAlign::CENTER, 0, dcp::Direction::LTR, "Hello dolly", dcp::Effect::NONE, dcp::Colour(), dcp::Time(), dcp::Time(), 0 ); - subs.push_back(StringText(ss, 0, {}, dcp::SubtitleStandard::SMPTE_2014)); + subs.push_back(StringText(ss, 0, make_shared("foo"), dcp::SubtitleStandard::SMPTE_2014)); render_text (subs, dcp::Size(640, 480), DCPTime(), 24); #endif diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index d40976094..5b43e510c 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -74,6 +74,7 @@ #include "lib/exceptions.h" #include "lib/ffmpeg_encoder.h" #include "lib/film.h" +#include "lib/font_config.h" #include "lib/hints.h" #include "lib/job_manager.h" #include "lib/kdm_with_metadata.h" @@ -1191,6 +1192,8 @@ private: /* Also stop hearing about analytics-related stuff */ _analytics_message_connection.disconnect (); + FontConfig::drop(); + ev.Skip (); } diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc index 68460fe68..5ce02b1ea 100644 --- a/src/tools/dcpomatic_player.cc +++ b/src/tools/dcpomatic_player.cc @@ -47,6 +47,7 @@ #include "lib/ffmpeg_content.h" #include "lib/file_log.h" #include "lib/film.h" +#include "lib/font_config.h" #include "lib/image.h" #include "lib/image_jpeg.h" #include "lib/image_png.h" @@ -244,6 +245,8 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_timing, this), ID_tools_timing); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_system_information, this), ID_tools_system_information); + Bind(wxEVT_CLOSE_WINDOW, boost::bind(&DOMFrame::close, this, _1)); + if (Config::instance()->player_mode() == Config::PLAYER_MODE_DUAL) { auto pc = new PlaylistControls (_overall_panel, _viewer); _controls = pc; @@ -313,6 +316,12 @@ public: _viewer.stop(); } + void close(wxCloseEvent& ev) + { + FontConfig::drop(); + ev.Skip(); + } + void setup_main_sizer (Config::PlayerMode mode) { _main_sizer->Detach(_viewer.panel()); -- 2.30.2