Rearrange subtitle font management.
authorCarl Hetherington <cth@carlh.net>
Wed, 1 Jun 2022 11:03:38 +0000 (13:03 +0200)
committerCarl Hetherington <cth@carlh.net>
Tue, 7 Jun 2022 15:01:06 +0000 (17:01 +0200)
With this change each subtitle coming out of the player has a reference
to a dcpomatic::Font that belongs to the TextContent.  This hopefully
solves a few problems which all basically stemmed from the fact that
previously the decoders/player were deciding what the font ID in the
output DCP would be - they can't do that properly.

42 files changed:
src/lib/analyse_subtitles_job.cc
src/lib/content_text.h
src/lib/dcp_decoder.cc
src/lib/dcp_decoder.h
src/lib/dcp_encoder.cc
src/lib/dcp_subtitle_decoder.cc
src/lib/dcp_subtitle_decoder.h
src/lib/decoder.h
src/lib/font.cc
src/lib/font.h
src/lib/font_data.cc [deleted file]
src/lib/font_data.h [deleted file]
src/lib/font_id_map.cc [new file with mode: 0644]
src/lib/font_id_map.h [new file with mode: 0644]
src/lib/hints.cc
src/lib/player.cc
src/lib/player.h
src/lib/player_text.cc [deleted file]
src/lib/player_text.h
src/lib/reel_writer.cc
src/lib/reel_writer.h
src/lib/render_text.cc
src/lib/render_text.h
src/lib/string_text.h
src/lib/string_text_file.h
src/lib/string_text_file_content.cc
src/lib/string_text_file_decoder.cc
src/lib/string_text_file_decoder.h
src/lib/subtitle_encoder.cc
src/lib/subtitle_encoder.h
src/lib/text_content.cc
src/lib/text_content.h
src/lib/text_decoder.cc
src/lib/util.cc
src/lib/writer.cc
src/lib/writer.h
src/lib/wscript
src/wx/fonts_dialog.cc
test/dcp_subtitle_test.cc
test/render_subtitles_test.cc
test/srt_subtitle_test.cc
test/writer_test.cc

index 0b003427c54115af0b6771017c410af5b12d52d4..7f1b8ad04d9fe50cfe3ea6c041b89a7b95643407 100644 (file)
@@ -109,7 +109,7 @@ AnalyseSubtitlesJob::analyse (PlayerText text, TextType type)
        if (!text.string.empty()) {
                /* We can provide dummy values for time and frame rate here as they are only used to calculate fades */
                dcp::Size const frame = _film->frame_size();
-               for (auto i: render_text(text.string, text.fonts, frame, dcpomatic::DCPTime(), 24)) {
+               for (auto i: render_text(text.string, frame, dcpomatic::DCPTime(), 24)) {
                        dcpomatic::Rect<double> rect (
                                        double(i.position.x) / frame.width, double(i.position.y) / frame.height,
                                        double(i.image->size().width) / frame.width, double(i.image->size().height) / frame.height
index cd9d34bf90bce7de028a930b63d15e5316b6d830..06fb39cfce6c423166c465a07d4d59af5367ef7e 100644 (file)
@@ -26,6 +26,7 @@
 #include "bitmap_text.h"
 #include "dcpomatic_time.h"
 #include "rect.h"
+#include "string_text.h"
 #include "types.h"
 #include <dcp/subtitle_string.h>
 #include <vector>
@@ -74,12 +75,12 @@ public:
 class ContentStringText : public ContentText
 {
 public:
-       ContentStringText (dcpomatic::ContentTime f, std::vector<dcp::SubtitleString> s)
+       ContentStringText (dcpomatic::ContentTime f, std::vector<StringText> s)
                : ContentText (f)
                , subs (s)
        {}
 
-       std::vector<dcp::SubtitleString> subs;
+       std::vector<StringText> subs;
 };
 
 
index dfbef340601df21687144754d354e4539b33f1e6..636cc8ed2b3f1f8d937d65943083e322e600bf9e 100644 (file)
@@ -502,18 +502,3 @@ DCPDecoder::position () const
        return ContentTime::from_frames(_offset, _dcp_content->active_video_frame_rate(film())) + _next;
 }
 
-
-vector<FontData>
-DCPDecoder::fonts () const
-{
-       vector<FontData> data;
-       for (auto i: _reels) {
-               if (i->main_subtitle() && i->main_subtitle()->asset()) {
-                       for (auto const& j: i->main_subtitle()->asset()->font_data()) {
-                               data.push_back (FontData(j.first, j.second));
-                       }
-               }
-       }
-       return data;
-}
-
index 0882bfd097784295e299fdd155c46fe681b284cc..803c93a86f308c5cf2adddeac502977a2351a46b 100644 (file)
@@ -62,8 +62,6 @@ public:
        bool pass () override;
        void seek (dcpomatic::ContentTime t, bool accurate) override;
 
-       std::vector<dcpomatic::FontData> fonts () const override;
-
        std::string lazy_digest () const {
                return _lazy_digest;
        }
index f1c41253984f514563735b62580399711e4042f4..e3443c1ad83bcfa43073e2d933a2a40137ad9289 100644 (file)
@@ -104,18 +104,7 @@ DCPEncoder::go ()
        }
 
        if (_non_burnt_subtitles) {
-               auto fonts = _player->get_subtitle_fonts ();
-
-               if (fonts.size() > 1 && _film->interop()) {
-                       /* Interop will ignore second and subsequent <LoadFont>s so don't even
-                          write them as they upset some validators.
-                       */
-                       auto first = fonts.front();
-                       fonts.clear ();
-                       fonts.push_back (first);
-               }
-
-               _writer->write (fonts);
+               _writer->write(_player->get_subtitle_fonts());
        }
 
        while (!_player->pass ()) {}
index 940c8c75a3c6c2513dbe2b98fddb5f29f5fc54b1..83faa1e93f19fbed0d3094b378ff1b97942e9cbd 100644 (file)
@@ -51,43 +51,6 @@ DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr<const Film> film, shared_ptr<
                first = content_time_period(*_next).from;
        }
        text.push_back (make_shared<TextDecoder>(this, content->only_text(), first));
-
-       /* The fonts that are included in content's file; if it's interop there will be none
-        * (as the fonts are held in separate assets).
-        */
-       auto fonts_in_asset = c->font_data();
-
-       /* Fonts specified in the TextContent */
-       list<shared_ptr<dcpomatic::Font>> fonts_in_content;
-       for (auto i: content->text) {
-               auto this_fonts = i->fonts();
-               std::copy(this_fonts.begin(), this_fonts.end(), std::back_inserter(fonts_in_content));
-       }
-       fonts_in_content.sort();
-       fonts_in_content.unique();
-
-       /* Find a font for each <LoadFont> Node */
-       for (auto i: c->load_font_nodes()) {
-               bool done = false;
-               for (auto j: fonts_in_content) {
-                       if (j->id() == i->id && j->file()) {
-                               // One was specified in the content
-                               _fonts.push_back (FontData(i->id, dcp::ArrayData(*j->file())));
-                               done = true;
-                       }
-               }
-               if (!done) {
-                       if (fonts_in_asset.find(i->id) != fonts_in_asset.end()) {
-                               // One was included in the subtitle file
-                               _fonts.push_back (FontData(i->id, fonts_in_asset[i->id]));
-                               done = true;
-                       }
-               }
-               if (!done) {
-                       // Give up and add a default
-                       _fonts.push_back (FontData(i->id, dcp::ArrayData(default_font_file())));
-               }
-       }
 }
 
 
@@ -155,13 +118,6 @@ DCPSubtitleDecoder::content_time_period (shared_ptr<const dcp::Subtitle> s) cons
 }
 
 
-vector<dcpomatic::FontData>
-DCPSubtitleDecoder::fonts () const
-{
-       return _fonts;
-}
-
-
 /** @return time of first subtitle, if there is one */
 optional<ContentTime>
 DCPSubtitleDecoder::first () const
@@ -172,3 +128,4 @@ DCPSubtitleDecoder::first () const
 
        return ContentTime::from_seconds(_subtitles[0]->in().as_seconds());
 }
+
index d1237b2761c337a9b629b3baad7383e6b3e4ce19..4f5964c6fb0019e70b46abaca8b46705ebf642db 100644 (file)
@@ -21,7 +21,6 @@
 
 #include "text_decoder.h"
 #include "dcp_subtitle.h"
-#include "font_data.h"
 
 
 class DCPSubtitleContent;
@@ -35,8 +34,6 @@ public:
        bool pass () override;
        void seek (dcpomatic::ContentTime time, bool accurate) override;
 
-       std::vector<dcpomatic::FontData> fonts () const override;
-
        boost::optional<dcpomatic::ContentTime> first () const;
 
 private:
@@ -44,6 +41,4 @@ private:
 
        std::vector<std::shared_ptr<const dcp::Subtitle>> _subtitles;
        std::vector<std::shared_ptr<const dcp::Subtitle>>::const_iterator _next;
-
-       std::vector<dcpomatic::FontData> _fonts;
 };
index a672e8a10de0c090b8cf9fa520a007d46fe38a37..0324075a36d98a252013e29dad8df9a8cf6e0392 100644 (file)
@@ -30,7 +30,6 @@
 
 #include "dcpomatic_time.h"
 #include "film.h"
-#include "font_data.h"
 #include "types.h"
 #include "weak_film.h"
 #include <boost/utility.hpp>
@@ -70,10 +69,6 @@ public:
        virtual void seek (dcpomatic::ContentTime time, bool accurate);
 
        virtual dcpomatic::ContentTime position () const;
-
-       virtual std::vector<dcpomatic::FontData> fonts () const {
-               return {};
-       }
 };
 
 
index f750bfc88401b4a69939ea993aecf6ed35b97a0a..375427d93f71d57897da277f392b668dfce4f6d9 100644 (file)
@@ -28,6 +28,7 @@ LIBDCP_ENABLE_WARNINGS
 
 
 using std::string;
+using boost::optional;
 using namespace dcpomatic;
 
 
@@ -69,3 +70,19 @@ dcpomatic::operator!= (Font const & a, Font const & b)
 {
        return !(a == b);
 }
+
+
+optional<dcp::ArrayData>
+Font::data () const
+{
+       if (_data) {
+               return _data;
+       }
+
+       if (_file) {
+               return dcp::ArrayData(*_file);
+       }
+
+       return {};
+}
+
index a6bf61e340c445c25970e620c15ac1d489b7efa4..c1405d0f61c81d1695c95bca5d72009f87f63e43 100644 (file)
@@ -23,6 +23,7 @@
 #define DCPOMATIC_FONT_H
 
 
+#include <dcp/array_data.h>
 #include <libcxml/cxml.h>
 #include <boost/optional.hpp>
 #include <boost/signals2.hpp>
@@ -52,6 +53,10 @@ public:
                return _id;
        }
 
+       void set_id (std::string id) {
+               _id = id;
+       }
+
        boost::optional<boost::filesystem::path> file () const {
                return _file;
        }
@@ -61,11 +66,16 @@ public:
                Changed ();
        }
 
+       boost::optional<dcp::ArrayData> data() const;
+
        boost::signals2::signal<void()> Changed;
 
 private:
-       /** Font ID, used to describe it in the subtitle content */
+       /** Font ID, used to describe it in the subtitle content; could be either a
+        *  font family name or an ID from some DCP font XML.
+        */
        std::string _id;
+       boost::optional<dcp::ArrayData> _data;
        boost::optional<boost::filesystem::path> _file;
 };
 
diff --git a/src/lib/font_data.cc b/src/lib/font_data.cc
deleted file mode 100644 (file)
index 830b2da..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-    Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
-
-    This file is part of DCP-o-matic.
-
-    DCP-o-matic is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    DCP-o-matic is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#include "font.h"
-#include "font_data.h"
-#include "dcpomatic_assert.h"
-
-
-using std::shared_ptr;
-
-
-dcpomatic::FontData::FontData (shared_ptr<const Font> font)
-       : id (font->id())
-{
-       if (font->file()) {
-               data = dcp::ArrayData(font->file().get());
-       }
-}
-
-
-bool
-dcpomatic::operator== (FontData const& a, FontData const& b)
-{
-       if (a.id != b.id) {
-               return false;
-       }
-
-       return a.data == b.data;
-}
-
-
-bool
-dcpomatic::operator!= (FontData const& a, FontData const& b)
-{
-       return !(a == b);
-}
-
diff --git a/src/lib/font_data.h b/src/lib/font_data.h
deleted file mode 100644 (file)
index 7bd6d46..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-    Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
-
-    This file is part of DCP-o-matic.
-
-    DCP-o-matic is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    DCP-o-matic is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#ifndef DCPOMATIC_FONT_DATA_H
-#define DCPOMATIC_FONT_DATA_H
-
-
-#include <dcp/array_data.h>
-#include <boost/optional.hpp>
-#include <string>
-
-
-namespace dcpomatic {
-
-
-class Font;
-
-
-/** A font (TTF) file held as a block of data */
-class FontData
-{
-public:
-       FontData (std::shared_ptr<const Font> font);
-
-       FontData (std::string id_, dcp::ArrayData data_)
-               : id(id_)
-               , data(data_)
-       {}
-
-       /** <LoadFont> ID */
-       std::string id;
-       boost::optional<dcp::ArrayData> data;
-};
-
-
-extern bool operator== (FontData const& a, FontData const& b);
-extern bool operator!= (FontData const& a, FontData const& b);
-
-
-}
-
-
-#endif
diff --git a/src/lib/font_id_map.cc b/src/lib/font_id_map.cc
new file mode 100644 (file)
index 0000000..15654db
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    Copyright (C) 2022 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "dcpomatic_assert.h"
+#include "font.h"
+#include "font_id_map.h"
+
+
+using std::shared_ptr;
+using std::string;
+
+
+std::string
+FontIdMap::get(shared_ptr<dcpomatic::Font> font) const
+{
+       auto iter = _map.find(font);
+       DCPOMATIC_ASSERT(iter != _map.end());
+       return iter->second;
+}
+
+
+void
+FontIdMap::put(shared_ptr<dcpomatic::Font> font, string id)
+{
+       _map[font] = id;
+}
+
diff --git a/src/lib/font_id_map.h b/src/lib/font_id_map.h
new file mode 100644 (file)
index 0000000..e4acace
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+    Copyright (C) 2022 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef DCPOMATIC_FONT_ID_MAP_H
+#define DCPOMATIC_FONT_ID_MAP_H
+
+
+#include <map>
+#include <string>
+
+
+namespace dcpomatic {
+       class Font;
+}
+
+
+class FontIdMap
+{
+public:
+       std::string get(std::shared_ptr<dcpomatic::Font> font) const;
+       void put(std::shared_ptr<dcpomatic::Font> font, std::string id);
+
+       std::map<std::shared_ptr<dcpomatic::Font>, std::string> const& map() const {
+               return _map;
+       }
+
+private:
+       std::map<std::shared_ptr<dcpomatic::Font>, std::string> _map;
+
+};
+
+
+#endif
+
index 027510eca8015dafa55c9e38c32b04a261fd7fe4..4a5c984380a95af51c5e3fae1eeb4b2a548d06fa 100644 (file)
@@ -29,7 +29,6 @@
 #include "dcp_content_type.h"
 #include "film.h"
 #include "font.h"
-#include "font_data.h"
 #include "hints.h"
 #include "maths_util.h"
 #include "player.h"
@@ -419,6 +418,8 @@ try
        struct timeval last_pulse;
        gettimeofday (&last_pulse, 0);
 
+       _writer->write (player->get_subtitle_fonts());
+
        while (!player->pass()) {
 
                struct timeval now;
index 0f72ed8588dbbc7f3fa7861f64e415f81f532279..77193cb1ff148316eaf61663183a1aff85db9d0d 100644 (file)
@@ -477,18 +477,17 @@ Player::content_time_to_dcp (shared_ptr<const Piece> piece, ContentTime t) const
 }
 
 
-vector<FontData>
+vector<shared_ptr<Font>>
 Player::get_subtitle_fonts ()
 {
        boost::mutex::scoped_lock lm (_mutex);
 
-       vector<FontData> fonts;
-       for (auto i: _pieces) {
-               /* XXX: things may go wrong if there are duplicate font IDs
-                  with different font files.
-               */
-               auto f = i->decoder->fonts ();
-               copy (f.begin(), f.end(), back_inserter(fonts));
+       vector<shared_ptr<Font>> fonts;
+       for (auto piece: _pieces) {
+               for (auto text: piece->content->text) {
+                       auto text_fonts = text->fonts();
+                       copy (text_fonts.begin(), text_fonts.end(), back_inserter(fonts));
+               }
        }
 
        return fonts;
@@ -880,7 +879,7 @@ Player::open_subtitles_for_frame (DCPTime time) const
 
                /* String subtitles (rendered to an image) */
                if (!j.string.empty()) {
-                       auto s = render_text (j.string, j.fonts, _video_container_size, time, vfr);
+                       auto s = render_text(j.string, _video_container_size, time, vfr);
                        copy (s.begin(), s.end(), back_inserter (captions));
                }
        }
@@ -1191,8 +1190,7 @@ Player::plain_text_start (weak_ptr<Piece> weak_piece, weak_ptr<const TextContent
                }
 
                s.set_in (dcp::Time(from.seconds(), 1000));
-               ps.string.push_back (StringText (s, content->outline_width()));
-               ps.add_fonts (content->fonts ());
+               ps.string.push_back (s);
        }
 
        _active_texts[static_cast<int>(content->type())].add_from(weak_content, ps, from);
index 7d99c22c65ccca0448b73ec428b81572db8bc081..694ee70b77cca871eb83854a2b3a8c0a964da1f7 100644 (file)
@@ -45,10 +45,6 @@ namespace dcp {
        class ReelAsset;
 }
 
-namespace dcpomatic {
-       class FontData;
-}
-
 class AtmosContent;
 class AudioBuffers;
 class Content;
@@ -84,7 +80,7 @@ public:
        bool pass ();
        void seek (dcpomatic::DCPTime time, bool accurate);
 
-       std::vector<dcpomatic::FontData> get_subtitle_fonts ();
+       std::vector<std::shared_ptr<dcpomatic::Font>> get_subtitle_fonts ();
        std::list<ReferencedReelAsset> get_reel_assets ();
        dcp::Size video_container_size () const {
                boost::mutex::scoped_lock lm (_mutex);
diff --git a/src/lib/player_text.cc b/src/lib/player_text.cc
deleted file mode 100644 (file)
index 626f8fa..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-    Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
-
-    This file is part of DCP-o-matic.
-
-    DCP-o-matic is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    DCP-o-matic is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#include "font.h"
-#include "player_text.h"
-
-
-using std::list;
-using std::shared_ptr;
-using namespace dcpomatic;
-
-
-void
-PlayerText::add_fonts (list<shared_ptr<Font>> fonts_)
-{
-       for (auto i: fonts_) {
-               bool got = false;
-               for (auto j: fonts) {
-                       if (*i == *j) {
-                               got = true;
-                       }
-               }
-               if (!got) {
-                       fonts.push_back (i);
-               }
-       }
-}
index 36a63f3dab0322d42c93b5e52167e64eab84ffd3..38affd5ef1cc0953e8bee3ad87b1df8dcc8fc94f 100644 (file)
@@ -36,9 +36,6 @@ namespace dcpomatic {
 class PlayerText
 {
 public:
-       void add_fonts (std::list<std::shared_ptr<dcpomatic::Font>> fonts_);
-       std::list<std::shared_ptr<dcpomatic::Font>> fonts;
-
        /** BitmapTexts, with their rectangles transformed as specified by their content */
        std::list<BitmapText> bitmap;
        std::list<StringText> string;
index 2f28ef9d4e3ebe706bdd9c39c84b6ed299feb5aa..30650cf7de8cd291691da0c4a2353aa0b02c1c57 100644 (file)
@@ -26,7 +26,6 @@
 #include "dcpomatic_log.h"
 #include "digester.h"
 #include "film.h"
-#include "font_data.h"
 #include "image.h"
 #include "image_png.h"
 #include "job.h"
@@ -463,7 +462,8 @@ maybe_add_text (
        int reel_count,
        optional<string> content_summary,
        list<ReferencedReelAsset> const & refs,
-       vector<FontData> const & fonts,
+       FontIdMap const& fonts,
+       shared_ptr<dcpomatic::Font> chosen_interop_font,
        dcp::ArrayData default_font,
        shared_ptr<const Film> film,
        DCPTimePeriod period,
@@ -476,9 +476,17 @@ maybe_add_text (
        shared_ptr<Result> reel_asset;
 
        if (asset) {
-               /* Add the font to the subtitle content */
-               for (auto const& j: fonts) {
-                       asset->add_font (j.id, j.data.get_value_or(default_font));
+               if (film->interop()) {
+                       if (chosen_interop_font) {
+                               /* We only add one font, as Interop will ignore subsequent ones (and some validators will
+                                * complain if they are even present)
+                                */
+                               asset->add_font(fonts.get(chosen_interop_font), chosen_interop_font->data().get_value_or(default_font));
+                       }
+               } else {
+                       for (auto const& font: fonts.map()) {
+                               asset->add_font(font.second, font.first->data().get_value_or(default_font));
+                       }
                }
 
                if (auto interop = dynamic_pointer_cast<dcp::InteropSubtitleAsset>(asset)) {
@@ -638,7 +646,8 @@ void
 ReelWriter::create_reel_text (
        shared_ptr<dcp::Reel> reel,
        list<ReferencedReelAsset> const & refs,
-       vector<FontData> const& fonts,
+       FontIdMap const& fonts,
+       shared_ptr<dcpomatic::Font> chosen_interop_font,
        int64_t duration,
        boost::filesystem::path output_dcp,
        bool ensure_subtitles,
@@ -646,7 +655,7 @@ ReelWriter::create_reel_text (
        ) const
 {
        auto subtitle = maybe_add_text<dcp::ReelInteropSubtitleAsset, dcp::ReelSMPTESubtitleAsset, dcp::ReelSubtitleAsset> (
-               _subtitle_asset, duration, reel, _reel_index, _reel_count, _content_summary, refs, fonts, _default_font, film(), _period, output_dcp, _text_only
+               _subtitle_asset, duration, reel, _reel_index, _reel_count, _content_summary, refs, fonts, chosen_interop_font, _default_font, film(), _period, output_dcp, _text_only
                );
 
        if (subtitle) {
@@ -665,6 +674,7 @@ ReelWriter::create_reel_text (
                        _content_summary,
                        refs,
                        fonts,
+                       chosen_interop_font,
                        _default_font,
                        film(),
                        _period,
@@ -675,7 +685,7 @@ ReelWriter::create_reel_text (
 
        for (auto const& i: _closed_caption_assets) {
                auto a = maybe_add_text<dcp::ReelInteropClosedCaptionAsset, dcp::ReelSMPTEClosedCaptionAsset, dcp::ReelClosedCaptionAsset> (
-                       i.second, duration, reel, _reel_index, _reel_count, _content_summary, refs, fonts, _default_font, film(), _period, output_dcp, _text_only
+                       i.second, duration, reel, _reel_index, _reel_count, _content_summary, refs, fonts, chosen_interop_font, _default_font, film(), _period, output_dcp, _text_only
                        );
                DCPOMATIC_ASSERT (a);
                a->set_annotation_text (i.first.name);
@@ -697,6 +707,7 @@ ReelWriter::create_reel_text (
                        _content_summary,
                        refs,
                        fonts,
+                       chosen_interop_font,
                        _default_font,
                        film(),
                        _period,
@@ -742,7 +753,8 @@ ReelWriter::create_reel_markers (shared_ptr<dcp::Reel> reel) const
 shared_ptr<dcp::Reel>
 ReelWriter::create_reel (
        list<ReferencedReelAsset> const & refs,
-       vector<FontData> const & fonts,
+       FontIdMap const & fonts,
+       shared_ptr<dcpomatic::Font> chosen_interop_font,
        boost::filesystem::path output_dcp,
        bool ensure_subtitles,
        set<DCPTextTrack> ensure_closed_captions
@@ -764,7 +776,7 @@ ReelWriter::create_reel (
                create_reel_markers (reel);
        }
 
-       create_reel_text (reel, refs, fonts, duration, output_dcp, ensure_subtitles, ensure_closed_captions);
+       create_reel_text (reel, refs, fonts, chosen_interop_font, duration, output_dcp, ensure_subtitles, ensure_closed_captions);
 
        if (_atmos_asset) {
                reel->add (make_shared<dcp::ReelAtmosAsset>(_atmos_asset, 0));
@@ -880,7 +892,7 @@ ReelWriter::empty_text_asset (TextType type, optional<DCPTextTrack> track, bool
 
 
 void
-ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
+ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period, FontIdMap const& fonts)
 {
        shared_ptr<dcp::SubtitleAsset> asset;
 
@@ -918,7 +930,11 @@ ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track,
        for (auto i: subs.string) {
                i.set_in  (dcp::Time(period.from.seconds() - _period.from.seconds(), tcr));
                i.set_out (dcp::Time(period.to.seconds() - _period.from.seconds(), tcr));
-               asset->add (make_shared<dcp::SubtitleString>(i));
+               auto sub = make_shared<dcp::SubtitleString>(i);
+               if (type == TextType::OPEN_SUBTITLE) {
+                       sub->set_font(fonts.get(i.font));
+               }
+               asset->add(sub);
        }
 
        for (auto i: subs.bitmap) {
index d08b5b1f009b0e5a6f4424a96e2d5d8e9d4969aa..e578a481317b995984996f890536286e052ef2d4 100644 (file)
@@ -22,6 +22,7 @@
 #include "atmos_metadata.h"
 #include "dcp_text_track.h"
 #include "dcpomatic_time.h"
+#include "font_id_map.h"
 #include "player_text.h"
 #include "referenced_reel_asset.h"
 #include "types.h"
 #include <dcp/picture_asset_writer.h>
 
 
-namespace dcpomatic {
-       class FontData;
-}
-
 class AudioBuffers;
 class Film;
 class InfoFileHandle;
@@ -74,13 +71,14 @@ public:
        void fake_write (int size);
        void repeat_write (Frame frame, Eyes eyes);
        void write (std::shared_ptr<const AudioBuffers> audio);
-       void write (PlayerText text, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period);
+       void write (PlayerText text, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period, FontIdMap const& fonts);
        void write (std::shared_ptr<const dcp::AtmosFrame> atmos, AtmosMetadata metadata);
 
        void finish (boost::filesystem::path output_dcp);
        std::shared_ptr<dcp::Reel> create_reel (
                std::list<ReferencedReelAsset> const & refs,
-               std::vector<dcpomatic::FontData> const & fonts,
+               FontIdMap const & fonts,
+               std::shared_ptr<dcpomatic::Font> chosen_interop_font,
                boost::filesystem::path output_dcp,
                bool ensure_subtitles,
                std::set<DCPTextTrack> ensure_closed_captions
@@ -114,7 +112,8 @@ private:
        void create_reel_text (
                std::shared_ptr<dcp::Reel> reel,
                std::list<ReferencedReelAsset> const & refs,
-               std::vector<dcpomatic::FontData> const& fonts,
+               FontIdMap const& fonts,
+               std::shared_ptr<dcpomatic::Font> chosen_interop_font,
                int64_t duration,
                boost::filesystem::path output_dcp,
                bool ensure_subtitles,
index f1fce62b07dbac20c47a0169967634379ffe1b70..284e4fa88924ea97b027e5154a99ea159f88f76a 100644 (file)
@@ -171,14 +171,12 @@ create_surface (shared_ptr<Image> image)
 
 
 static string
-setup_font (StringText const& subtitle, list<shared_ptr<Font>> const& fonts)
+setup_font (StringText const& subtitle)
 {
        auto font_file = default_font_file ();
 
-       for (auto i: fonts) {
-               if (i->id() == subtitle.font() && i->file()) {
-                       font_file = i->file().get();
-               }
+       if (subtitle.font && subtitle.font->file()) {
+               font_file = *subtitle.font->file();
        }
 
        return FontConfig::instance()->make_font_available(font_file);
@@ -278,7 +276,7 @@ y_position (StringText const& first, int target_height, int layout_height)
  *  at the same time and with the same fade in/out.
  */
 static PositionImage
-render_line (list<StringText> subtitles, list<shared_ptr<Font>> fonts, dcp::Size target, DCPTime time, int frame_rate)
+render_line (list<StringText> subtitles, dcp::Size target, DCPTime time, int frame_rate)
 {
        /* XXX: this method can only handle italic / bold changes mid-line,
           nothing else yet.
@@ -287,7 +285,7 @@ render_line (list<StringText> subtitles, list<shared_ptr<Font>> fonts, dcp::Size
        DCPOMATIC_ASSERT (!subtitles.empty ());
        auto const& first = subtitles.front ();
 
-       auto const font_name = setup_font (first, fonts);
+       auto const font_name = setup_font (first);
        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 ();
@@ -375,21 +373,21 @@ render_line (list<StringText> subtitles, list<shared_ptr<Font>> fonts, dcp::Size
  *  @param frame_rate DCP frame rate.
  */
 list<PositionImage>
-render_text (list<StringText> subtitles, list<shared_ptr<Font>> fonts, dcp::Size target, DCPTime time, int frame_rate)
+render_text (list<StringText> subtitles, dcp::Size target, DCPTime time, int frame_rate)
 {
        list<StringText> pending;
        list<PositionImage> images;
 
        for (auto const& i: subtitles) {
                if (!pending.empty() && (i.v_align() != pending.back().v_align() || fabs(i.v_position() - pending.back().v_position()) > 1e-4)) {
-                       images.push_back (render_line (pending, fonts, target, time, frame_rate));
+                       images.push_back(render_line(pending, target, time, frame_rate));
                        pending.clear ();
                }
                pending.push_back (i);
        }
 
        if (!pending.empty()) {
-               images.push_back (render_line (pending, fonts, target, time, frame_rate));
+               images.push_back(render_line(pending,  target, time, frame_rate));
        }
 
        return images;
index 07a97bb4047ca90ca6fe5559b7b964e7f50e3a3c..6ff91dce1f98346fe09521a36833bb4de8020dfe 100644 (file)
@@ -28,6 +28,4 @@ namespace dcpomatic {
 }
 
 std::string marked_up (std::list<StringText> subtitles, int target_height, float fade_factor, std::string font_name);
-std::list<PositionImage> render_text (
-       std::list<StringText>, std::list<std::shared_ptr<dcpomatic::Font> > fonts, dcp::Size, dcpomatic::DCPTime, int
-       );
+std::list<PositionImage> render_text (std::list<StringText>, dcp::Size, dcpomatic::DCPTime, int);
index 887224d53eea3ceeb7e4fc8d83662799b1db9cd2..8c505f36a10d1b48f49279dfb4f1c5ca81c51402 100644 (file)
 #define DCPOMATIC_STRING_TEXT_H
 
 
+#include "font.h"
 #include <dcp/subtitle_string.h>
 
-/** A wrapper for SubtitleString which allows us to include settings that are not
 
- *  applicable to true DCP subtitles.  For example, we can set outline width for burn-in
- *  but this cannot be specified in DCP XML.
+/** A wrapper for SubtitleString which allows us to:
+ *
+ *    - include settings that are not applicable to true DCP subtitles.
+ *      For example, we can set outline width for burn-in but this cannot be specified in DCP XML.
+ *
+ *    - specify the font by referring to a Font object from the content we came from, rather than
+ *      having to use a DCP ID like in dcp::SubtitleString.
  */
 class StringText : public dcp::SubtitleString
 {
 public:
-       StringText (dcp::SubtitleString dcp_, int outline_width_)
+       StringText (dcp::SubtitleString dcp_, int outline_width_, std::shared_ptr<dcpomatic::Font> font_)
                : dcp::SubtitleString (dcp_)
                , outline_width (outline_width_)
+               , font (font_)
        {}
 
        int outline_width;
+       std::shared_ptr<dcpomatic::Font> font;
 };
 
 
index c6fdac1e8834af1238ab481c2eb31f2e76bdea8c..261c2ca6e95047b5b596e80f5e4b81b6662ad2b9 100644 (file)
@@ -44,6 +44,9 @@ public:
 
        boost::optional<dcpomatic::ContentTime> first () const;
        dcpomatic::ContentTime length () const;
+       std::vector<sub::Subtitle> const& subtitles() const {
+               return _subtitles;
+       }
 
 protected:
        std::vector<sub::Subtitle> _subtitles;
index 2ce343f2edb4d700eabff7c8c8916aa52bf1a4af..3750b0a5026a70db90b54dfdfe67ea2465fe6a62 100644 (file)
@@ -61,14 +61,29 @@ void
 StringTextFileContent::examine (shared_ptr<const Film> film, shared_ptr<Job> job)
 {
        Content::examine (film, job);
-       StringTextFile s (shared_from_this());
+       StringTextFile file (shared_from_this());
 
        /* Default to turning these subtitles on */
        only_text()->set_use (true);
 
+       std::set<string> names;
+       for (auto const& subtitle: file.subtitles()) {
+               for (auto const& line: subtitle.lines) {
+                       for (auto const& block: line.blocks) {
+                               names.insert(block.font.get_value_or(""));
+                       }
+               }
+       }
+
+       for (auto name: names) {
+               /* Make a font for each family name that somebody might later
+                * ask about.
+                */
+               only_text()->add_font(make_shared<Font>(name));
+       }
+
        boost::mutex::scoped_lock lm (_mutex);
-       _length = s.length ();
-       only_text()->add_font (make_shared<Font>(TEXT_FONT_ID));
+       _length = file.length();
 }
 
 
index d366bedc0fa84ebd578650eed2c18fb94ef012c6..d5f320a27f83ab83fa3c1705e8e9e0c0e3f1da3b 100644 (file)
@@ -91,17 +91,3 @@ StringTextFileDecoder::content_time_period (sub::Subtitle s) const
                ContentTime::from_seconds (s.to.all_as_seconds())
                );
 }
-
-
-vector<FontData>
-StringTextFileDecoder::fonts () const
-{
-       vector<FontData> data;
-       for (auto i: text) {
-               for (auto j: i->content()->fonts()) {
-                       data.push_back (FontData(j));
-               }
-       }
-       return data;
-}
-
index 5887f34c61f81fed1bdfaa9fc923273b4c122392..8fbf09608cd36b108d33e74b9a6703499c29583f 100644 (file)
@@ -34,8 +34,6 @@ public:
        void seek (dcpomatic::ContentTime time, bool accurate) override;
        bool pass () override;
 
-       std::vector<dcpomatic::FontData> fonts () const override;
-
 private:
        dcpomatic::ContentTimePeriod content_time_period (sub::Subtitle s) const;
 
index ba47e1231067f917e2ada7e160c3c2e21302aa94..2331eb52a8871241ba3ce58a87a3a99348a7aebf 100644 (file)
@@ -20,7 +20,6 @@
 
 
 #include "compose.hpp"
-#include "font_data.h"
 #include "job.h"
 #include "player.h"
 #include "subtitle_encoder.h"
@@ -121,7 +120,7 @@ SubtitleEncoder::go ()
 
                if (!_film->interop() || _include_font) {
                        for (auto j: _player->get_subtitle_fonts()) {
-                               i.first->add_font (j.id, _default_font);
+                               i.first->add_font(j->id(), j->data().get_value_or(_default_font));
                        }
                }
 
index b43cc268389e1ddbecfe19649692058d4a1b2dc5..a10e4ba47e09e27e5cb16296c9c1bc0ffde8804e 100644 (file)
@@ -54,7 +54,7 @@ public:
 private:
        void text (PlayerText subs, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period);
 
-       std::vector<std::pair<std::shared_ptr<dcp::SubtitleAsset>, boost::filesystem::path> > _assets;
+       std::vector<std::pair<std::shared_ptr<dcp::SubtitleAsset>, boost::filesystem::path>> _assets;
        std::vector<dcpomatic::DCPTimePeriod> _reels;
        bool _split_reels;
        bool _include_font;
index 1e9c609c9cafc92686f36cadad576d7aa6bf4f55..9c925cbcfb6bb0c6849fba9a947c66c70e7fa36b 100644 (file)
@@ -445,6 +445,7 @@ TextContent::identifier () const
 void
 TextContent::add_font (shared_ptr<Font> font)
 {
+       DCPOMATIC_ASSERT(!get_font(font->id()));
        _fonts.push_back (font);
        connect_to_fonts ();
 }
@@ -647,3 +648,19 @@ TextContent::take_settings_from (shared_ptr<const TextContent> c)
        set_language (c->_language);
        set_language_is_additional (c->_language_is_additional);
 }
+
+
+shared_ptr<dcpomatic::Font>
+TextContent::get_font(string id) const
+{
+       auto iter = std::find_if(_fonts.begin(), _fonts.end(), [&id](shared_ptr<dcpomatic::Font> font) {
+               return font->id() == id;
+       });
+
+       if (iter == _fonts.end()) {
+               return {};
+       }
+
+       return *iter;
+}
+
index 66bedecf51007c9e030cf3f01d3261ecc5eb5dfa..f46b6c6d0068733a1a17973fd6def36d5deab53f 100644 (file)
@@ -77,6 +77,7 @@ public:
        void take_settings_from (std::shared_ptr<const TextContent> c);
 
        void add_font (std::shared_ptr<dcpomatic::Font> font);
+       std::shared_ptr<dcpomatic::Font> get_font(std::string id) const;
 
        void set_use (bool);
        void set_burn (bool);
index c655fb5b58d5e5b2e208931f27d4ae0e753d9632..e3b7d995879b7f5dcc61b919b68db5fc6f86e567 100644 (file)
@@ -83,7 +83,7 @@ escape_text (string text)
 
 static
 void
-set_forced_appearance(shared_ptr<const TextContent> content, dcp::SubtitleString& subtitle)
+set_forced_appearance(shared_ptr<const TextContent> content, StringText& subtitle)
 {
        if (content->colour()) {
                subtitle.set_colour(*content->colour());
@@ -106,12 +106,16 @@ set_forced_appearance(shared_ptr<const TextContent> content, dcp::SubtitleString
 void
 TextDecoder::emit_plain_start (ContentTime from, vector<dcp::SubtitleString> subtitles)
 {
+       vector<StringText> string_texts;
+
        for (auto& subtitle: subtitles) {
-               subtitle.set_text(escape_text(subtitle.text()));
-               set_forced_appearance(content(), subtitle);
+               auto string_text = StringText(subtitle, content()->outline_width(), subtitle.font() ? content()->get_font(*subtitle.font()) : shared_ptr<Font>());
+               string_text.set_text(escape_text(string_text.text()));
+               set_forced_appearance(content(), string_text);
+               string_texts.push_back(string_text);
        }
 
-       PlainStart(ContentStringText(from, subtitles));
+       PlainStart(ContentStringText(from, string_texts));
        _position = from;
 }
 
@@ -143,7 +147,7 @@ TextDecoder::emit_plain_start (ContentTime from, sub::Subtitle const & sub_subti
                }
        }
 
-       vector<dcp::SubtitleString> dcp_subtitles;
+       vector<StringText> string_texts;
        for (auto line: sub_subtitle.lines) {
                for (auto block: line.blocks) {
 
@@ -233,46 +237,43 @@ TextDecoder::emit_plain_start (ContentTime from, sub::Subtitle const & sub_subti
                           content by the other emit_plain_start() above.
                        */
 
-                       dcp_subtitles.push_back(
-                               dcp::SubtitleString (
-                                       string(TEXT_FONT_ID),
-                                       block.italic,
-                                       block.bold,
-                                       block.underline,
-                                       block.colour.dcp(),
-                                       block.font_size.points (72 * 11),
-                                       1.0,
-                                       dcp::Time (from.seconds(), 1000),
-                                       /* XXX: hmm; this is a bit ugly (we don't know the to time yet) */
-                                       dcp::Time (),
-                                       h_position,
-                                       h_align,
-                                       v_position,
-                                       v_align,
-                                       dcp::Direction::LTR,
-                                       block.text,
-                                       dcp::Effect::NONE,
-                                       block.effect_colour.get_value_or(sub::Colour(0, 0, 0)).dcp(),
-                                       /* Hack: we should use subtitle.fade_up and subtitle.fade_down here
-                                          but the times of these often don't have a frame rate associated
-                                          with them so the sub::Time won't convert them to milliseconds without
-                                          throwing an exception.  Since only DCP subs fill those in (and we don't
-                                          use libsub for DCP subs) we can cheat by just putting 0 in here.
-                                       */
-                                       dcp::Time (),
-                                       dcp::Time (),
-                                       0
-                                       )
+                       auto dcp_subtitle = dcp::SubtitleString(
+                               optional<string>(),
+                               block.italic,
+                               block.bold,
+                               block.underline,
+                               block.colour.dcp(),
+                               block.font_size.points (72 * 11),
+                               1.0,
+                               dcp::Time (from.seconds(), 1000),
+                               /* XXX: hmm; this is a bit ugly (we don't know the to time yet) */
+                               dcp::Time (),
+                               h_position,
+                               h_align,
+                               v_position,
+                               v_align,
+                               dcp::Direction::LTR,
+                               escape_text(block.text),
+                               dcp::Effect::NONE,
+                               block.effect_colour.get_value_or(sub::Colour(0, 0, 0)).dcp(),
+                               /* Hack: we should use subtitle.fade_up and subtitle.fade_down here
+                                  but the times of these often don't have a frame rate associated
+                                  with them so the sub::Time won't convert them to milliseconds without
+                                  throwing an exception.  Since only DCP subs fill those in (and we don't
+                                  use libsub for DCP subs) we can cheat by just putting 0 in here.
+                               */
+                               dcp::Time (),
+                               dcp::Time (),
+                               0
                                );
-               }
-       }
 
-       for (auto& subtitle: dcp_subtitles) {
-               subtitle.set_text(escape_text(subtitle.text()));
-               set_forced_appearance(content(), subtitle);
+                       auto string_text = StringText(dcp_subtitle, content()->outline_width(), content()->get_font(block.font.get_value_or("")));
+                       set_forced_appearance(content(), string_text);
+                       string_texts.push_back(string_text);
+               }
        }
 
-       PlainStart(ContentStringText(from, dcp_subtitles));
+       PlainStart(ContentStringText(from, string_texts));
        _position = from;
 }
 
index 79730d3472f7584e6aaccf95b2f46712e07f421d..c964d853330ac5a7876f50919c22463fab54db17 100644 (file)
@@ -410,8 +410,8 @@ LIBDCP_ENABLE_WARNINGS
                optional<string>(), false, false, false, dcp::Colour(), 42, 1, dcp::Time(), dcp::Time(), 0, dcp::HAlign::CENTER, 0, dcp::VAlign::CENTER, dcp::Direction::LTR,
                "Hello dolly", dcp::Effect::NONE, dcp::Colour(), dcp::Time(), dcp::Time(), 0
                );
-       subs.push_back (StringText(ss, 0));
-       render_text (subs, list<shared_ptr<Font>>(), dcp::Size(640, 480), DCPTime(), 24);
+       subs.push_back (StringText(ss, 0, {}));
+       render_text (subs, dcp::Size(640, 480), DCPTime(), 24);
 #endif
 
        Ratio::setup_ratios ();
index 6e3821aa7c7d7d94ea624ced4fb637e2a653c7da..bc0c5bf878aaefa1aa1c67225070b8572aa1da00 100644 (file)
@@ -33,7 +33,6 @@
 #include "cross.h"
 #include "audio_buffers.h"
 #include "version.h"
-#include "font_data.h"
 #include "util.h"
 #include "reel_writer.h"
 #include "text_content.h"
@@ -44,6 +43,7 @@
 #include <cerrno>
 #include <iostream>
 #include <cfloat>
+#include <set>
 
 #include "i18n.h"
 
@@ -58,6 +58,7 @@ using std::make_shared;
 using std::max;
 using std::min;
 using std::shared_ptr;
+using std::set;
 using std::string;
 using std::vector;
 using std::weak_ptr;
@@ -605,7 +606,7 @@ Writer::finish (boost::filesystem::path output_dcp)
        /* Add reels */
 
        for (auto& i: _reels) {
-               cpl->add (i.create_reel(_reel_assets, _fonts, output_dcp, _have_subtitles, _have_closed_captions));
+               cpl->add (i.create_reel(_reel_assets, _fonts, _chosen_interop_font, output_dcp, _have_subtitles, _have_closed_captions));
        }
 
        /* Add metadata */
@@ -855,26 +856,79 @@ Writer::write (PlayerText text, TextType type, optional<DCPTextTrack> track, DCP
                period = back_off(period);
        }
 
-       (*reel)->write (text, type, track, period);
+       (*reel)->write(text, type, track, period, _fonts);
 }
 
 
 void
-Writer::write (vector<FontData> fonts)
+Writer::write (vector<shared_ptr<Font>> fonts)
 {
-       /* Just keep a list of unique fonts and we'll deal with them in ::finish */
+       if (fonts.empty()) {
+               return;
+       }
 
-       for (auto const& i: fonts) {
-               bool got = false;
-               for (auto& j: _fonts) {
-                       if (i == j) {
-                               got = true;
-                       }
+       /* Fonts may come in with empty IDs but we don't want to put those in the DCP */
+       auto fix_id = [](string id) {
+               return id.empty() ? "font" : id;
+       };
+
+       if (film()->interop()) {
+               /* Interop will ignore second and subsequent <LoadFont>s so we don't want to
+                * even write them as they upset some validators.  Set up _fonts so that every
+                * font used by any subtitle will be written with the same ID.
+                */
+               for (size_t i = 0; i < fonts.size(); ++i) {
+                       _fonts.put(fonts[i], fix_id(fonts[0]->id()));
                }
+               _chosen_interop_font = fonts[0];
+       } else {
+               set<string> used_ids;
+
+               /* Return the index of a _N at the end of a string, or string::npos */
+               auto underscore_number_position = [](string s) {
+                       auto last_underscore = s.find_last_of("_");
+                       if (last_underscore == string::npos) {
+                               return string::npos;
+                       }
+
+                       for (auto i = last_underscore + 1; i < s.size(); ++i) {
+                               if (!isdigit(s[i])) {
+                                       return string::npos;
+                               }
+                       }
+
+                       return last_underscore;
+               };
+
+               /* Write fonts to _fonts, changing any duplicate IDs so that they are unique */
+               for (auto font: fonts) {
+                       auto id = fix_id(font->id());
+                       if (used_ids.find(id) == used_ids.end()) {
+                               /* This ID is unique so we can just use it as-is */
+                               _fonts.put(font, id);
+                               used_ids.insert(id);
+                       } else {
+                               auto end = underscore_number_position(id);
+                               if (end == string::npos) {
+                                       /* This string has no _N suffix, so add one */
+                                       id += "_0";
+                                       end = underscore_number_position(id);
+                               }
+
+                               ++end;
 
-               if (!got) {
-                       _fonts.push_back (i);
+                               /* Increment the suffix until we find a unique one */
+                               auto number = dcp::raw_convert<int>(id.substr(end));
+                               while (used_ids.find(id) != used_ids.end()) {
+                                       ++number;
+                                       id = String::compose("%1_%2", id.substr(0, end - 1), number);
+                               }
+                               used_ids.insert(id);
+                       }
+                       _fonts.put(font, id);
                }
+
+               DCPOMATIC_ASSERT(_fonts.map().size() == used_ids.size());
        }
 }
 
@@ -976,7 +1030,7 @@ Writer::write_hanging_text (ReelWriter& reel)
        vector<HangingText> new_hanging_texts;
        for (auto i: _hanging_texts) {
                if (i.period.from == reel.period().from) {
-                       reel.write (i.text, i.type, i.track, i.period);
+                       reel.write (i.text, i.type, i.track, i.period, _fonts);
                } else {
                        new_hanging_texts.push_back (i);
                }
index 67172b0d4de94a8b655f23e5d8ed2c4554d35a27..1ae761dea83656073d4d03abb45c1ef6b8a9e5f7 100644 (file)
@@ -28,6 +28,7 @@
 #include "dcp_text_track.h"
 #include "dcpomatic_time.h"
 #include "exception_store.h"
+#include "font_id_map.h"
 #include "player_text.h"
 #include "types.h"
 #include "weak_film.h"
@@ -41,15 +42,14 @@ namespace dcp {
        class Data;
 }
 
-namespace dcpomatic {
-       class FontData;
-}
-
 class AudioBuffers;
 class Film;
 class Job;
 class ReelWriter;
 class ReferencedReelAsset;
+struct writer_disambiguate_font_ids1;
+struct writer_disambiguate_font_ids2;
+struct writer_disambiguate_font_ids3;
 
 
 struct QueueItem
@@ -115,7 +115,7 @@ public:
        void repeat (Frame, Eyes);
        void write (std::shared_ptr<const AudioBuffers>, dcpomatic::DCPTime time);
        void write (PlayerText text, TextType type, boost::optional<DCPTextTrack>, dcpomatic::DCPTimePeriod period);
-       void write (std::vector<dcpomatic::FontData> fonts);
+       void write (std::vector<std::shared_ptr<dcpomatic::Font>> fonts);
        void write (ReferencedReelAsset asset);
        void write (std::shared_ptr<const dcp::AtmosFrame> atmos, dcpomatic::DCPTime time, AtmosMetadata metadata);
        void finish (boost::filesystem::path output_dcp);
@@ -123,6 +123,10 @@ public:
        void set_encoder_threads (int threads);
 
 private:
+       friend struct ::writer_disambiguate_font_ids1;
+       friend struct ::writer_disambiguate_font_ids2;
+       friend struct ::writer_disambiguate_font_ids3;
+
        void thread ();
        void terminate_thread (bool);
        bool have_sequenced_image_at_queue_head ();
@@ -201,7 +205,11 @@ private:
 
        std::list<ReferencedReelAsset> _reel_assets;
 
-       std::vector<dcpomatic::FontData> _fonts;
+       FontIdMap _fonts;
+       /** If we are given many fonts, but we're making an Interop DCP, we'll choose a single
+        *  one that we'll use everywher.  This is that chosen font.
+        */
+       std::shared_ptr<dcpomatic::Font> _chosen_interop_font;
 
        /** true if any reel has any subtitles */
        bool _have_subtitles = false;
index 285ebfab55f045902ae068990314c4493b8534b7..6bab60d545d9ced24dcb7383df799b3b33ffa3fa 100644 (file)
@@ -114,8 +114,8 @@ sources = """
           film.cc
           filter.cc
           font.cc
-          font_data.cc
           font_config.cc
+          font_id_map.cc
           frame_interval_checker.cc
           frame_rate_change.cc
           guess_crop.cc
@@ -146,7 +146,6 @@ sources = """
           overlaps.cc
           pixel_quanta.cc
           player.cc
-          player_text.cc
           player_video.cc
           playlist.cc
           position_image.cc
index e81f4a853cd094a2ae8e71bde5a04fb91212dc74..4ca338709dc3332ccb8ab3816ce28bc48cd57080 100644 (file)
@@ -101,9 +101,11 @@ FontsDialog::setup ()
                wxListItem item;
                item.SetId (n);
                _fonts->InsertItem (item);
-               _fonts->SetItem (n, 0, std_to_wx (i->id ()));
+               auto const id = i->id().empty() ? _("Unspecified") : std_to_wx(i->id());
+               _fonts->SetItem(n, 0, id);
+               _fonts->SetItemData(n, i->id().empty());
                if (i->file()) {
-                       _fonts->SetItem (n, 1, i->file()->leaf().string ());
+                       _fonts->SetItem(n, 1, i->file()->leaf().string());
                }
                ++n;
        }
@@ -137,14 +139,8 @@ FontsDialog::edit_clicked ()
        }
 
        int const item = _fonts->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-       auto const id = wx_to_std (_fonts->GetItemText(item, 0));
-       shared_ptr<Font> font;
-       for (auto i: caption->fonts()) {
-               if (i->id() == id) {
-                       font = i;
-               }
-       }
-
+       auto const id = _fonts->GetItemData(item) ? "" : wx_to_std(_fonts->GetItemText(item, 0));
+       auto font = caption->get_font(id);
        if (!font) {
                return;
        }
index fb8888d0c6859edccdec7e665c1d97a49c9a5710..640f6510efd71e11b1448646bb2634467cdd9cda 100644 (file)
@@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE (test_font_override)
        film->set_interop(true);
 
        BOOST_REQUIRE_EQUAL(content->text.size(), 1U);
-       content->text.front()->add_font(make_shared<dcpomatic::Font>("theFontId", "test/data/Inconsolata-VF.ttf"));
+       content->text.front()->get_font("theFontId")->set_file("test/data/Inconsolata-VF.ttf");
 
        make_and_verify_dcp (film, { dcp::VerificationNote::Code::INVALID_STANDARD });
        check_file (subtitle_file(film).parent_path() / "font_0.ttf", "test/data/Inconsolata-VF.ttf");
index 5774c8e9af7e111dc4d5140eb68c39fc33627b2f..d850062c2a8893168e3a32e4dc546b61510d4509 100644 (file)
 
 */
 
+
 /** @file  test/render_text_test.cc
  *  @brief Check markup of subtitles for rendering.
  *  @ingroup feature
  */
 
+
 #include "lib/render_text.h"
+#include "lib/string_text.h"
 #include <dcp/subtitle_string.h>
 #include <boost/test/unit_test.hpp>
 
+
 static void
 add (std::list<StringText>& s, std::string text, bool italic, bool bold, bool underline)
 {
@@ -54,7 +58,8 @@ add (std::list<StringText>& s, std::string text, bool italic, bool bold, bool un
                                dcp::Time (),
                                0
                                ),
-                       2
+                       2,
+                       std::shared_ptr<dcpomatic::Font>()
                        )
                );
 }
index cd4ce3a05079184c60b8ad95d9a6f1704fd621ff..11b6b28b99cbe6875cfcea5fc597c36f10c8281d 100644 (file)
@@ -170,13 +170,14 @@ BOOST_AUTO_TEST_CASE (srt_subtitle_test5)
        film->set_name ("frobozz");
        film->set_interop (true);
        film->set_sequence (false);
-       auto content = make_shared<StringTextFileContent>("test/data/subrip2.srt");
-       content->only_text()->set_use (true);
-       content->only_text()->set_burn (false);
-       film->examine_and_add_content (content);
-       film->examine_and_add_content (content);
-       BOOST_REQUIRE (!wait_for_jobs());
-       content->set_position (film, DCPTime());
+       for (auto i = 0; i < 2; ++i) {
+               auto content = make_shared<StringTextFileContent>("test/data/subrip2.srt");
+               content->only_text()->set_use (true);
+               content->only_text()->set_burn (false);
+               film->examine_and_add_content (content);
+               BOOST_REQUIRE (!wait_for_jobs());
+               content->set_position (film, DCPTime());
+       }
        make_and_verify_dcp (film, {dcp::VerificationNote::Code::INVALID_STANDARD});
 
        check_dcp ("test/data/xml_subtitle_test2", film->dir (film->dcp_name ()));
index 1dfc75bfc024c588c7eef1b2db86cde3db64c4d2..e0df0610e51e0c27020a9e122b1adf48cd2a4296 100644 (file)
@@ -36,6 +36,7 @@
 
 using std::make_shared;
 using std::shared_ptr;
+using std::vector;
 
 
 BOOST_AUTO_TEST_CASE (test_write_odd_amount_of_silence)
@@ -101,3 +102,62 @@ BOOST_AUTO_TEST_CASE (interrupt_writer)
        cl.run ();
 }
 
+
+BOOST_AUTO_TEST_CASE (writer_disambiguate_font_ids1)
+{
+       auto film = new_test_film2("writer_disambiguate_font_ids1", {});
+       Writer writer(film, {});
+
+       auto fonts = vector<shared_ptr<dcpomatic::Font>> {
+               make_shared<dcpomatic::Font>("a"),
+               make_shared<dcpomatic::Font>("b"),
+               make_shared<dcpomatic::Font>("c")
+       };
+
+       writer.write(fonts);
+
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[0]), "a");
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[1]), "b");
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[2]), "c");
+}
+
+
+BOOST_AUTO_TEST_CASE (writer_disambiguate_font_ids2)
+{
+       auto film = new_test_film2("writer_disambiguate_font_ids2", {});
+       Writer writer(film, {});
+
+       auto fonts = vector<shared_ptr<dcpomatic::Font>> {
+               make_shared<dcpomatic::Font>("a"),
+               make_shared<dcpomatic::Font>("a"),
+               make_shared<dcpomatic::Font>("a")
+       };
+
+       writer.write(fonts);
+
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[0]), "a");
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[1]), "a_0");
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[2]), "a_1");
+}
+
+
+BOOST_AUTO_TEST_CASE (writer_disambiguate_font_ids3)
+{
+       auto film = new_test_film2("writer_disambiguate_font_ids3", {});
+       Writer writer(film, {});
+
+       auto fonts = vector<shared_ptr<dcpomatic::Font>> {
+               make_shared<dcpomatic::Font>("a_2"),
+               make_shared<dcpomatic::Font>("a_1"),
+               make_shared<dcpomatic::Font>("a_1"),
+               make_shared<dcpomatic::Font>("b")
+       };
+
+       writer.write(fonts);
+
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[1]), "a_1");
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[0]), "a_2");
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[2]), "a_3");
+       BOOST_CHECK_EQUAL(writer._fonts.get(fonts[3]), "b");
+}
+