From 5a820bb8fae34591be5ac6d19a73461b9dab532a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 1 Jun 2022 13:03:38 +0200 Subject: [PATCH] Rearrange subtitle font management. 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. --- src/lib/analyse_subtitles_job.cc | 2 +- src/lib/content_text.h | 5 +- src/lib/dcp_decoder.cc | 15 ---- src/lib/dcp_decoder.h | 2 - src/lib/dcp_encoder.cc | 13 +--- src/lib/dcp_subtitle_decoder.cc | 45 +----------- src/lib/dcp_subtitle_decoder.h | 5 -- src/lib/decoder.h | 5 -- src/lib/font.cc | 17 +++++ src/lib/font.h | 12 +++- src/lib/font_data.h | 61 ---------------- src/lib/{player_text.cc => font_id_map.cc} | 32 ++++----- src/lib/{font_data.cc => font_id_map.h} | 41 +++++------ src/lib/hints.cc | 3 +- src/lib/player.cc | 20 +++--- src/lib/player.h | 6 +- src/lib/player_text.h | 3 - src/lib/reel_writer.cc | 40 +++++++---- src/lib/reel_writer.h | 13 ++-- src/lib/render_text.cc | 18 +++-- src/lib/render_text.h | 4 +- src/lib/string_text.h | 15 ++-- src/lib/string_text_file.h | 3 + src/lib/string_text_file_content.cc | 21 +++++- src/lib/string_text_file_decoder.cc | 14 ---- src/lib/string_text_file_decoder.h | 2 - src/lib/subtitle_encoder.cc | 3 +- src/lib/subtitle_encoder.h | 2 +- src/lib/text_content.cc | 17 +++++ src/lib/text_content.h | 1 + src/lib/text_decoder.cc | 83 +++++++++++----------- src/lib/util.cc | 4 +- src/lib/writer.cc | 82 +++++++++++++++++---- src/lib/writer.h | 20 ++++-- src/lib/wscript | 3 +- src/wx/fonts_dialog.cc | 16 ++--- test/dcp_subtitle_test.cc | 2 +- test/render_subtitles_test.cc | 7 +- test/srt_subtitle_test.cc | 15 ++-- test/writer_test.cc | 60 ++++++++++++++++ 40 files changed, 384 insertions(+), 348 deletions(-) delete mode 100644 src/lib/font_data.h rename src/lib/{player_text.cc => font_id_map.cc} (66%) rename src/lib/{font_data.cc => font_id_map.h} (57%) diff --git a/src/lib/analyse_subtitles_job.cc b/src/lib/analyse_subtitles_job.cc index 0b003427c..7f1b8ad04 100644 --- a/src/lib/analyse_subtitles_job.cc +++ b/src/lib/analyse_subtitles_job.cc @@ -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 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 diff --git a/src/lib/content_text.h b/src/lib/content_text.h index cd9d34bf9..06fb39cfc 100644 --- a/src/lib/content_text.h +++ b/src/lib/content_text.h @@ -26,6 +26,7 @@ #include "bitmap_text.h" #include "dcpomatic_time.h" #include "rect.h" +#include "string_text.h" #include "types.h" #include #include @@ -74,12 +75,12 @@ public: class ContentStringText : public ContentText { public: - ContentStringText (dcpomatic::ContentTime f, std::vector s) + ContentStringText (dcpomatic::ContentTime f, std::vector s) : ContentText (f) , subs (s) {} - std::vector subs; + std::vector subs; }; diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc index dfbef3406..636cc8ed2 100644 --- a/src/lib/dcp_decoder.cc +++ b/src/lib/dcp_decoder.cc @@ -502,18 +502,3 @@ DCPDecoder::position () const return ContentTime::from_frames(_offset, _dcp_content->active_video_frame_rate(film())) + _next; } - -vector -DCPDecoder::fonts () const -{ - vector 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; -} - diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h index 0882bfd09..803c93a86 100644 --- a/src/lib/dcp_decoder.h +++ b/src/lib/dcp_decoder.h @@ -62,8 +62,6 @@ public: bool pass () override; void seek (dcpomatic::ContentTime t, bool accurate) override; - std::vector fonts () const override; - std::string lazy_digest () const { return _lazy_digest; } diff --git a/src/lib/dcp_encoder.cc b/src/lib/dcp_encoder.cc index f1c412539..e3443c1ad 100644 --- a/src/lib/dcp_encoder.cc +++ b/src/lib/dcp_encoder.cc @@ -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 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 ()) {} diff --git a/src/lib/dcp_subtitle_decoder.cc b/src/lib/dcp_subtitle_decoder.cc index 940c8c75a..83faa1e93 100644 --- a/src/lib/dcp_subtitle_decoder.cc +++ b/src/lib/dcp_subtitle_decoder.cc @@ -51,43 +51,6 @@ DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr film, shared_ptr< first = content_time_period(*_next).from; } text.push_back (make_shared(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> 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 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 s) cons } -vector -DCPSubtitleDecoder::fonts () const -{ - return _fonts; -} - - /** @return time of first subtitle, if there is one */ optional DCPSubtitleDecoder::first () const @@ -172,3 +128,4 @@ DCPSubtitleDecoder::first () const return ContentTime::from_seconds(_subtitles[0]->in().as_seconds()); } + diff --git a/src/lib/dcp_subtitle_decoder.h b/src/lib/dcp_subtitle_decoder.h index d1237b276..4f5964c6f 100644 --- a/src/lib/dcp_subtitle_decoder.h +++ b/src/lib/dcp_subtitle_decoder.h @@ -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 fonts () const override; - boost::optional first () const; private: @@ -44,6 +41,4 @@ private: std::vector> _subtitles; std::vector>::const_iterator _next; - - std::vector _fonts; }; diff --git a/src/lib/decoder.h b/src/lib/decoder.h index a672e8a10..0324075a3 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -30,7 +30,6 @@ #include "dcpomatic_time.h" #include "film.h" -#include "font_data.h" #include "types.h" #include "weak_film.h" #include @@ -70,10 +69,6 @@ public: virtual void seek (dcpomatic::ContentTime time, bool accurate); virtual dcpomatic::ContentTime position () const; - - virtual std::vector fonts () const { - return {}; - } }; diff --git a/src/lib/font.cc b/src/lib/font.cc index f750bfc88..375427d93 100644 --- a/src/lib/font.cc +++ b/src/lib/font.cc @@ -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 +Font::data () const +{ + if (_data) { + return _data; + } + + if (_file) { + return dcp::ArrayData(*_file); + } + + return {}; +} + diff --git a/src/lib/font.h b/src/lib/font.h index a6bf61e34..c1405d0f6 100644 --- a/src/lib/font.h +++ b/src/lib/font.h @@ -23,6 +23,7 @@ #define DCPOMATIC_FONT_H +#include #include #include #include @@ -52,6 +53,10 @@ public: return _id; } + void set_id (std::string id) { + _id = id; + } + boost::optional file () const { return _file; } @@ -61,11 +66,16 @@ public: Changed (); } + boost::optional data() const; + boost::signals2::signal 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 _data; boost::optional _file; }; diff --git a/src/lib/font_data.h b/src/lib/font_data.h deleted file mode 100644 index 7bd6d4648..000000000 --- a/src/lib/font_data.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright (C) 2020-2021 Carl Hetherington - - 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 . - -*/ - - -#ifndef DCPOMATIC_FONT_DATA_H -#define DCPOMATIC_FONT_DATA_H - - -#include -#include -#include - - -namespace dcpomatic { - - -class Font; - - -/** A font (TTF) file held as a block of data */ -class FontData -{ -public: - FontData (std::shared_ptr font); - - FontData (std::string id_, dcp::ArrayData data_) - : id(id_) - , data(data_) - {} - - /** ID */ - std::string id; - boost::optional 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/player_text.cc b/src/lib/font_id_map.cc similarity index 66% rename from src/lib/player_text.cc rename to src/lib/font_id_map.cc index 626f8fac3..15654db6b 100644 --- a/src/lib/player_text.cc +++ b/src/lib/font_id_map.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2021 Carl Hetherington + Copyright (C) 2022 Carl Hetherington This file is part of DCP-o-matic. @@ -19,27 +19,27 @@ */ +#include "dcpomatic_assert.h" #include "font.h" -#include "player_text.h" +#include "font_id_map.h" -using std::list; using std::shared_ptr; -using namespace dcpomatic; +using std::string; + + +std::string +FontIdMap::get(shared_ptr font) const +{ + auto iter = _map.find(font); + DCPOMATIC_ASSERT(iter != _map.end()); + return iter->second; +} void -PlayerText::add_fonts (list> fonts_) +FontIdMap::put(shared_ptr font, string id) { - for (auto i: fonts_) { - bool got = false; - for (auto j: fonts) { - if (*i == *j) { - got = true; - } - } - if (!got) { - fonts.push_back (i); - } - } + _map[font] = id; } + diff --git a/src/lib/font_data.cc b/src/lib/font_id_map.h similarity index 57% rename from src/lib/font_data.cc rename to src/lib/font_id_map.h index 830b2da34..e4acace6d 100644 --- a/src/lib/font_data.cc +++ b/src/lib/font_id_map.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2020-2021 Carl Hetherington + Copyright (C) 2022 Carl Hetherington This file is part of DCP-o-matic. @@ -19,37 +19,34 @@ */ -#include "font.h" -#include "font_data.h" -#include "dcpomatic_assert.h" +#ifndef DCPOMATIC_FONT_ID_MAP_H +#define DCPOMATIC_FONT_ID_MAP_H -using std::shared_ptr; +#include +#include -dcpomatic::FontData::FontData (shared_ptr font) - : id (font->id()) -{ - if (font->file()) { - data = dcp::ArrayData(font->file().get()); - } +namespace dcpomatic { + class Font; } -bool -dcpomatic::operator== (FontData const& a, FontData const& b) +class FontIdMap { - if (a.id != b.id) { - return false; +public: + std::string get(std::shared_ptr font) const; + void put(std::shared_ptr font, std::string id); + + std::map, std::string> const& map() const { + return _map; } - return a.data == b.data; -} +private: + std::map, std::string> _map; +}; -bool -dcpomatic::operator!= (FontData const& a, FontData const& b) -{ - return !(a == b); -} + +#endif diff --git a/src/lib/hints.cc b/src/lib/hints.cc index 027510eca..4a5c98438 100644 --- a/src/lib/hints.cc +++ b/src/lib/hints.cc @@ -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; diff --git a/src/lib/player.cc b/src/lib/player.cc index 0f72ed858..77193cb1f 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -477,18 +477,17 @@ Player::content_time_to_dcp (shared_ptr piece, ContentTime t) const } -vector +vector> Player::get_subtitle_fonts () { boost::mutex::scoped_lock lm (_mutex); - vector 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> 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 weak_piece, weak_ptroutline_width())); - ps.add_fonts (content->fonts ()); + ps.string.push_back (s); } _active_texts[static_cast(content->type())].add_from(weak_content, ps, from); diff --git a/src/lib/player.h b/src/lib/player.h index 7d99c22c6..694ee70b7 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -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 get_subtitle_fonts (); + std::vector> get_subtitle_fonts (); std::list get_reel_assets (); dcp::Size video_container_size () const { boost::mutex::scoped_lock lm (_mutex); diff --git a/src/lib/player_text.h b/src/lib/player_text.h index 36a63f3da..38affd5ef 100644 --- a/src/lib/player_text.h +++ b/src/lib/player_text.h @@ -36,9 +36,6 @@ namespace dcpomatic { class PlayerText { public: - void add_fonts (std::list> fonts_); - std::list> fonts; - /** BitmapTexts, with their rectangles transformed as specified by their content */ std::list bitmap; std::list string; diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 2f28ef9d4..30650cf7d 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -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 content_summary, list const & refs, - vector const & fonts, + FontIdMap const& fonts, + shared_ptr chosen_interop_font, dcp::ArrayData default_font, shared_ptr film, DCPTimePeriod period, @@ -476,9 +476,17 @@ maybe_add_text ( shared_ptr 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(asset)) { @@ -638,7 +646,8 @@ void ReelWriter::create_reel_text ( shared_ptr reel, list const & refs, - vector const& fonts, + FontIdMap const& fonts, + shared_ptr 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 ( - _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 ( - 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 reel) const shared_ptr ReelWriter::create_reel ( list const & refs, - vector const & fonts, + FontIdMap const & fonts, + shared_ptr chosen_interop_font, boost::filesystem::path output_dcp, bool ensure_subtitles, set 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(_atmos_asset, 0)); @@ -880,7 +892,7 @@ ReelWriter::empty_text_asset (TextType type, optional track, bool void -ReelWriter::write (PlayerText subs, TextType type, optional track, DCPTimePeriod period) +ReelWriter::write (PlayerText subs, TextType type, optional track, DCPTimePeriod period, FontIdMap const& fonts) { shared_ptr asset; @@ -918,7 +930,11 @@ ReelWriter::write (PlayerText subs, TextType type, optional 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(i)); + auto sub = make_shared(i); + if (type == TextType::OPEN_SUBTITLE) { + sub->set_font(fonts.get(i.font)); + } + asset->add(sub); } for (auto i: subs.bitmap) { diff --git a/src/lib/reel_writer.h b/src/lib/reel_writer.h index d08b5b1f0..e578a4813 100644 --- a/src/lib/reel_writer.h +++ b/src/lib/reel_writer.h @@ -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" @@ -31,10 +32,6 @@ #include -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 audio); - void write (PlayerText text, TextType type, boost::optional track, dcpomatic::DCPTimePeriod period); + void write (PlayerText text, TextType type, boost::optional track, dcpomatic::DCPTimePeriod period, FontIdMap const& fonts); void write (std::shared_ptr atmos, AtmosMetadata metadata); void finish (boost::filesystem::path output_dcp); std::shared_ptr create_reel ( std::list const & refs, - std::vector const & fonts, + FontIdMap const & fonts, + std::shared_ptr chosen_interop_font, boost::filesystem::path output_dcp, bool ensure_subtitles, std::set ensure_closed_captions @@ -114,7 +112,8 @@ private: void create_reel_text ( std::shared_ptr reel, std::list const & refs, - std::vector const& fonts, + FontIdMap const& fonts, + std::shared_ptr chosen_interop_font, int64_t duration, boost::filesystem::path output_dcp, bool ensure_subtitles, diff --git a/src/lib/render_text.cc b/src/lib/render_text.cc index f1fce62b0..284e4fa88 100644 --- a/src/lib/render_text.cc +++ b/src/lib/render_text.cc @@ -171,14 +171,12 @@ create_surface (shared_ptr image) static string -setup_font (StringText const& subtitle, list> 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 subtitles, list> fonts, dcp::Size target, DCPTime time, int frame_rate) +render_line (list 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 subtitles, list> 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 subtitles, list> fonts, dcp::Size * @param frame_rate DCP frame rate. */ list -render_text (list subtitles, list> fonts, dcp::Size target, DCPTime time, int frame_rate) +render_text (list subtitles, dcp::Size target, DCPTime time, int frame_rate) { list pending; list 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; diff --git a/src/lib/render_text.h b/src/lib/render_text.h index 07a97bb40..6ff91dce1 100644 --- a/src/lib/render_text.h +++ b/src/lib/render_text.h @@ -28,6 +28,4 @@ namespace dcpomatic { } std::string marked_up (std::list subtitles, int target_height, float fade_factor, std::string font_name); -std::list render_text ( - std::list, std::list > fonts, dcp::Size, dcpomatic::DCPTime, int - ); +std::list render_text (std::list, dcp::Size, dcpomatic::DCPTime, int); diff --git a/src/lib/string_text.h b/src/lib/string_text.h index 887224d53..8c505f36a 100644 --- a/src/lib/string_text.h +++ b/src/lib/string_text.h @@ -23,22 +23,29 @@ #define DCPOMATIC_STRING_TEXT_H +#include "font.h" #include -/** 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 font_) : dcp::SubtitleString (dcp_) , outline_width (outline_width_) + , font (font_) {} int outline_width; + std::shared_ptr font; }; diff --git a/src/lib/string_text_file.h b/src/lib/string_text_file.h index c6fdac1e8..261c2ca6e 100644 --- a/src/lib/string_text_file.h +++ b/src/lib/string_text_file.h @@ -44,6 +44,9 @@ public: boost::optional first () const; dcpomatic::ContentTime length () const; + std::vector const& subtitles() const { + return _subtitles; + } protected: std::vector _subtitles; diff --git a/src/lib/string_text_file_content.cc b/src/lib/string_text_file_content.cc index 2ce343f2e..3750b0a50 100644 --- a/src/lib/string_text_file_content.cc +++ b/src/lib/string_text_file_content.cc @@ -61,14 +61,29 @@ void StringTextFileContent::examine (shared_ptr film, shared_ptr 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 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(name)); + } + boost::mutex::scoped_lock lm (_mutex); - _length = s.length (); - only_text()->add_font (make_shared(TEXT_FONT_ID)); + _length = file.length(); } diff --git a/src/lib/string_text_file_decoder.cc b/src/lib/string_text_file_decoder.cc index d366bedc0..d5f320a27 100644 --- a/src/lib/string_text_file_decoder.cc +++ b/src/lib/string_text_file_decoder.cc @@ -91,17 +91,3 @@ StringTextFileDecoder::content_time_period (sub::Subtitle s) const ContentTime::from_seconds (s.to.all_as_seconds()) ); } - - -vector -StringTextFileDecoder::fonts () const -{ - vector data; - for (auto i: text) { - for (auto j: i->content()->fonts()) { - data.push_back (FontData(j)); - } - } - return data; -} - diff --git a/src/lib/string_text_file_decoder.h b/src/lib/string_text_file_decoder.h index 5887f34c6..8fbf09608 100644 --- a/src/lib/string_text_file_decoder.h +++ b/src/lib/string_text_file_decoder.h @@ -34,8 +34,6 @@ public: void seek (dcpomatic::ContentTime time, bool accurate) override; bool pass () override; - std::vector fonts () const override; - private: dcpomatic::ContentTimePeriod content_time_period (sub::Subtitle s) const; diff --git a/src/lib/subtitle_encoder.cc b/src/lib/subtitle_encoder.cc index ba47e1231..2331eb52a 100644 --- a/src/lib/subtitle_encoder.cc +++ b/src/lib/subtitle_encoder.cc @@ -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)); } } diff --git a/src/lib/subtitle_encoder.h b/src/lib/subtitle_encoder.h index b43cc2683..a10e4ba47 100644 --- a/src/lib/subtitle_encoder.h +++ b/src/lib/subtitle_encoder.h @@ -54,7 +54,7 @@ public: private: void text (PlayerText subs, TextType type, boost::optional track, dcpomatic::DCPTimePeriod period); - std::vector, boost::filesystem::path> > _assets; + std::vector, boost::filesystem::path>> _assets; std::vector _reels; bool _split_reels; bool _include_font; diff --git a/src/lib/text_content.cc b/src/lib/text_content.cc index 1e9c609c9..9c925cbcf 100644 --- a/src/lib/text_content.cc +++ b/src/lib/text_content.cc @@ -445,6 +445,7 @@ TextContent::identifier () const void TextContent::add_font (shared_ptr font) { + DCPOMATIC_ASSERT(!get_font(font->id())); _fonts.push_back (font); connect_to_fonts (); } @@ -647,3 +648,19 @@ TextContent::take_settings_from (shared_ptr c) set_language (c->_language); set_language_is_additional (c->_language_is_additional); } + + +shared_ptr +TextContent::get_font(string id) const +{ + auto iter = std::find_if(_fonts.begin(), _fonts.end(), [&id](shared_ptr font) { + return font->id() == id; + }); + + if (iter == _fonts.end()) { + return {}; + } + + return *iter; +} + diff --git a/src/lib/text_content.h b/src/lib/text_content.h index 66bedecf5..f46b6c6d0 100644 --- a/src/lib/text_content.h +++ b/src/lib/text_content.h @@ -77,6 +77,7 @@ public: void take_settings_from (std::shared_ptr c); void add_font (std::shared_ptr font); + std::shared_ptr get_font(std::string id) const; void set_use (bool); void set_burn (bool); diff --git a/src/lib/text_decoder.cc b/src/lib/text_decoder.cc index c655fb5b5..e3b7d9958 100644 --- a/src/lib/text_decoder.cc +++ b/src/lib/text_decoder.cc @@ -83,7 +83,7 @@ escape_text (string text) static void -set_forced_appearance(shared_ptr content, dcp::SubtitleString& subtitle) +set_forced_appearance(shared_ptr content, StringText& subtitle) { if (content->colour()) { subtitle.set_colour(*content->colour()); @@ -106,12 +106,16 @@ set_forced_appearance(shared_ptr content, dcp::SubtitleString void TextDecoder::emit_plain_start (ContentTime from, vector subtitles) { + vector 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()); + 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_subtitles; + vector 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(), + 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; } diff --git a/src/lib/util.cc b/src/lib/util.cc index 79730d347..c964d8533 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -410,8 +410,8 @@ LIBDCP_ENABLE_WARNINGS optional(), 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>(), 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 (); diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 6e3821aa7..bc0c5bf87 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -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 #include #include +#include #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 track, DCP period = back_off(period); } - (*reel)->write (text, type, track, period); + (*reel)->write(text, type, track, period, _fonts); } void -Writer::write (vector fonts) +Writer::write (vector> 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 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 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(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 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); } diff --git a/src/lib/writer.h b/src/lib/writer.h index 67172b0d4..1ae761dea 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -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, dcpomatic::DCPTime time); void write (PlayerText text, TextType type, boost::optional, dcpomatic::DCPTimePeriod period); - void write (std::vector fonts); + void write (std::vector> fonts); void write (ReferencedReelAsset asset); void write (std::shared_ptr 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 _reel_assets; - std::vector _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 _chosen_interop_font; /** true if any reel has any subtitles */ bool _have_subtitles = false; diff --git a/src/lib/wscript b/src/lib/wscript index 285ebfab5..6bab60d54 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -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 diff --git a/src/wx/fonts_dialog.cc b/src/wx/fonts_dialog.cc index e81f4a853..4ca338709 100644 --- a/src/wx/fonts_dialog.cc +++ b/src/wx/fonts_dialog.cc @@ -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; - 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; } diff --git a/test/dcp_subtitle_test.cc b/test/dcp_subtitle_test.cc index fb8888d0c..640f6510e 100644 --- a/test/dcp_subtitle_test.cc +++ b/test/dcp_subtitle_test.cc @@ -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("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"); diff --git a/test/render_subtitles_test.cc b/test/render_subtitles_test.cc index 5774c8e9a..d850062c2 100644 --- a/test/render_subtitles_test.cc +++ b/test/render_subtitles_test.cc @@ -18,15 +18,19 @@ */ + /** @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 #include + static void add (std::list& s, std::string text, bool italic, bool bold, bool underline) { @@ -54,7 +58,8 @@ add (std::list& s, std::string text, bool italic, bool bold, bool un dcp::Time (), 0 ), - 2 + 2, + std::shared_ptr() ) ); } diff --git a/test/srt_subtitle_test.cc b/test/srt_subtitle_test.cc index cd4ce3a05..11b6b28b9 100644 --- a/test/srt_subtitle_test.cc +++ b/test/srt_subtitle_test.cc @@ -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("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("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 ())); diff --git a/test/writer_test.cc b/test/writer_test.cc index 1dfc75bfc..e0df0610e 100644 --- a/test/writer_test.cc +++ b/test/writer_test.cc @@ -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> { + make_shared("a"), + make_shared("b"), + make_shared("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> { + make_shared("a"), + make_shared("a"), + make_shared("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> { + make_shared("a_2"), + make_shared("a_1"), + make_shared("a_1"), + make_shared("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"); +} + -- 2.30.2