More automated renaming.
authorCarl Hetherington <cth@carlh.net>
Mon, 23 Jul 2018 10:20:12 +0000 (11:20 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 23 Jul 2018 10:20:12 +0000 (11:20 +0100)
ActiveCaptions -> ActiveText
BitmapCaption -> BitmapText
ContentCaption -> ContentText
ContentTextCaption -> ContentStringText
TextCaptionFileContent -> StringTextFileContent
TextCaptionFileDecoder -> StringTextFileDecoder
TextCaptionFile -> StringTextFile
TextCaption -> StringText
PlayerCaption -> PlayerText
CaptionContent -> TextContent
CaptionDecoder -> TextDecoder
CaptionPanel -> TextPanel
CaptionView -> TextView
CaptionAppearanceDialog -> SubtitleAppearanceDialog
CaptionType -> TextType

109 files changed:
hacks/rename [new file with mode: 0644]
src/lib/active_captions.cc [deleted file]
src/lib/active_captions.h [deleted file]
src/lib/active_text.cc [new file with mode: 0644]
src/lib/active_text.h [new file with mode: 0644]
src/lib/bitmap_caption.h [deleted file]
src/lib/bitmap_text.h [new file with mode: 0644]
src/lib/caption_content.cc [deleted file]
src/lib/caption_content.h [deleted file]
src/lib/caption_decoder.cc [deleted file]
src/lib/caption_decoder.h [deleted file]
src/lib/content.cc
src/lib/content.h
src/lib/content_caption.h [deleted file]
src/lib/content_factory.cc
src/lib/content_text.h [new file with mode: 0644]
src/lib/dcp_content.cc
src/lib/dcp_content.h
src/lib/dcp_decoder.cc
src/lib/dcp_decoder.h
src/lib/dcp_encoder.cc
src/lib/dcp_encoder.h
src/lib/dcp_examiner.h
src/lib/dcp_subtitle_content.cc
src/lib/dcp_subtitle_decoder.cc
src/lib/dcp_subtitle_decoder.h
src/lib/decoder.cc
src/lib/decoder.h
src/lib/decoder_factory.cc
src/lib/encoder.h
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_encoder.cc
src/lib/ffmpeg_encoder.h
src/lib/film.cc
src/lib/hints.cc
src/lib/player.cc
src/lib/player.h
src/lib/player_caption.cc [deleted file]
src/lib/player_caption.h [deleted file]
src/lib/player_text.cc [new file with mode: 0644]
src/lib/player_text.h [new file with mode: 0644]
src/lib/playlist.cc
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 [new file with mode: 0644]
src/lib/string_text_file.cc [new file with mode: 0644]
src/lib/string_text_file.h [new file with mode: 0644]
src/lib/string_text_file_content.cc [new file with mode: 0644]
src/lib/string_text_file_content.h [new file with mode: 0644]
src/lib/string_text_file_decoder.cc [new file with mode: 0644]
src/lib/string_text_file_decoder.h [new file with mode: 0644]
src/lib/text_caption.h [deleted file]
src/lib/text_caption_file.cc [deleted file]
src/lib/text_caption_file.h [deleted file]
src/lib/text_caption_file_content.cc [deleted file]
src/lib/text_caption_file_content.h [deleted file]
src/lib/text_caption_file_decoder.cc [deleted file]
src/lib/text_caption_file_decoder.h [deleted file]
src/lib/text_content.cc [new file with mode: 0644]
src/lib/text_content.h [new file with mode: 0644]
src/lib/text_decoder.cc [new file with mode: 0644]
src/lib/text_decoder.h [new file with mode: 0644]
src/lib/types.cc
src/lib/types.h
src/lib/writer.cc
src/lib/writer.h
src/lib/wscript
src/tools/dcpomatic.cc
src/tools/dcpomatic_player.cc
src/wx/caption_appearance_dialog.cc [deleted file]
src/wx/caption_appearance_dialog.h [deleted file]
src/wx/caption_panel.cc [deleted file]
src/wx/caption_panel.h [deleted file]
src/wx/caption_view.cc [deleted file]
src/wx/caption_view.h [deleted file]
src/wx/closed_captions_dialog.cc
src/wx/content_panel.cc
src/wx/content_panel.h
src/wx/dcp_panel.cc
src/wx/film_viewer.h
src/wx/fonts_dialog.cc
src/wx/fonts_dialog.h
src/wx/subtitle_appearance_dialog.cc [new file with mode: 0644]
src/wx/subtitle_appearance_dialog.h [new file with mode: 0644]
src/wx/text_panel.cc [new file with mode: 0644]
src/wx/text_panel.h [new file with mode: 0644]
src/wx/text_view.cc [new file with mode: 0644]
src/wx/text_view.h [new file with mode: 0644]
src/wx/timeline.cc
src/wx/timeline_text_content_view.cc
src/wx/timeline_text_content_view.h
src/wx/timing_panel.cc
src/wx/wscript
test/burnt_subtitle_test.cc
test/closed_caption_test.cc
test/dcp_subtitle_test.cc
test/ffmpeg_encoder_test.cc
test/player_test.cc
test/reels_test.cc
test/remake_id_test.cc
test/remake_with_subtitle_test.cc
test/render_subtitles_test.cc
test/srt_subtitle_test.cc
test/ssa_subtitle_test.cc
test/subtitle_charset_test.cc
test/subtitle_reel_number_test.cc

diff --git a/hacks/rename b/hacks/rename
new file mode 100644 (file)
index 0000000..c68ae1b
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+import re
+import os
+
+files = "src/lib/*.cc src/lib/*.h src/wx/*.cc src/wx/*.h src/tools/*.cc test/*.cc"
+
+def convert(name):
+    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
+    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
+
+def try_to_rename(f, t):
+    try:
+        print '%s -> %s' % (f, t),
+        os.rename(f, t)
+        print 'OK'
+    except:
+        print 'Failed'
+
+def rename_class(f, t):
+    os.system('sed -i "s/%s/%s/g" %s' % (f, t, files))
+    os.system('sed -i "s/%s/%s/g" src/lib/wscript src/wx/wscript' % (convert(f), convert(t)))
+    try_to_rename('src/lib/%s.cc' % convert(f), 'src/lib/%s.cc' % convert(t))
+    try_to_rename('src/lib/%s.h' % convert(f), 'src/lib/%s.h' % convert(t))
+    try_to_rename('src/wx/%s.cc' % convert(f), 'src/wx/%s.cc' % convert(t))
+    try_to_rename('src/wx/%s.h' % convert(f), 'src/wx/%s.h' % convert(t))
+    os.system('sed -i "s/include \\"%s.h\\"/include \\"%s.h\\"/g" %s' % (convert(f), convert(t), files))
+    os.system('sed -i "s/include \\"lib\/%s.h\\"/include \\"lib\/%s.h\\"/g" %s' % (convert(f), convert(t), files))
+
+rename_class("ActiveCaptions", "ActiveText")
+rename_class("BitmapCaption", "BitmapText")
+rename_class("ContentCaption", "ContentText")
+rename_class("ContentTextCaption", "ContentStringText")
+rename_class("TextCaptionFileContent", "StringTextFileContent")
+rename_class("TextCaptionFileDecoder", "StringTextFileDecoder")
+rename_class("TextCaptionFile", "StringTextFile")
+rename_class("TextCaption", "StringText")
+rename_class("PlayerCaption", "PlayerText")
+rename_class("CaptionContent", "TextContent")
+rename_class("CaptionDecoder", "TextDecoder")
+rename_class("CaptionPanel", "TextPanel")
+rename_class("CaptionView", "TextView")
+rename_class("CaptionAppearanceDialog", "SubtitleAppearanceDialog")
+rename_class("CaptionType", "TextType")
diff --git a/src/lib/active_captions.cc b/src/lib/active_captions.cc
deleted file mode 100644 (file)
index 1d3a536..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
-    Copyright (C) 2017-2018 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 "active_captions.h"
-#include "caption_content.h"
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-using std::list;
-using std::pair;
-using std::make_pair;
-using boost::weak_ptr;
-using boost::shared_ptr;
-using boost::optional;
-
-void
-ActiveCaptions::add (DCPTimePeriod period, list<PlayerCaption>& pc, list<Period> p) const
-{
-       BOOST_FOREACH (Period i, p) {
-               DCPTimePeriod test (i.from, i.to.get_value_or(DCPTime::max()));
-               optional<DCPTimePeriod> overlap = period.overlap (test);
-               if (overlap && overlap->duration() > DCPTime(period.duration().get() / 2)) {
-                       pc.push_back (i.subs);
-               }
-       }
-}
-
-list<PlayerCaption>
-ActiveCaptions::get (DCPTimePeriod period) const
-{
-       list<PlayerCaption> ps;
-
-       for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) {
-
-               shared_ptr<const CaptionContent> caption = i->first.lock ();
-               if (!caption || !caption->use()) {
-                       continue;
-               }
-
-               add (period, ps, i->second);
-       }
-
-       return ps;
-}
-
-/** Get the open captions that should be burnt into a given period.
- *  @param period Period of interest.
- *  @param always_burn_captions Always burn captions even if their content is not set to burn.
- */
-list<PlayerCaption>
-ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_captions) const
-{
-       list<PlayerCaption> ps;
-
-       for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) {
-
-               shared_ptr<const CaptionContent> caption = i->first.lock ();
-               if (!caption) {
-                       continue;
-               }
-
-               if (!caption->use() || (!always_burn_captions && !caption->burn())) {
-                       /* Not burning this content */
-                       continue;
-               }
-
-               add (period, ps, i->second);
-       }
-
-       return ps;
-}
-
-/** Remove subtitles that finish before a given time from our list.
- *  @param time Time to remove before.
- */
-void
-ActiveCaptions::clear_before (DCPTime time)
-{
-       Map updated;
-       for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) {
-               list<Period> as;
-               BOOST_FOREACH (Period j, i->second) {
-                       if (!j.to || j.to.get() >= time) {
-                               as.push_back (j);
-                       }
-               }
-               if (!as.empty ()) {
-                       updated[i->first] = as;
-               }
-       }
-       _data = updated;
-}
-
-/** Add a new subtitle with a from time.
- *  @param content Content that the subtitle is from.
- *  @param ps Subtitles.
- *  @param from From time for these subtitles.
- */
-void
-ActiveCaptions::add_from (weak_ptr<const CaptionContent> content, PlayerCaption ps, DCPTime from)
-{
-       if (_data.find(content) == _data.end()) {
-               _data[content] = list<Period>();
-       }
-       _data[content].push_back (Period (ps, from));
-}
-
-/** Add the to time for the last subtitle added from a piece of content.
- *  @param content Content that the subtitle is from.
- *  @param to To time for the last subtitle submitted to add_from for this content.
- *  @return Return the corresponding subtitles and their from time.
- */
-pair<PlayerCaption, DCPTime>
-ActiveCaptions::add_to (weak_ptr<const CaptionContent> content, DCPTime to)
-{
-       DCPOMATIC_ASSERT (_data.find(content) != _data.end());
-
-       _data[content].back().to = to;
-
-       BOOST_FOREACH (TextCaption& i, _data[content].back().subs.text) {
-               i.set_out (dcp::Time(to.seconds(), 1000));
-       }
-
-       return make_pair (_data[content].back().subs, _data[content].back().from);
-}
-
-/** @param content Some content.
- *  @return true if we have any active subtitles from this content.
- */
-bool
-ActiveCaptions::have (weak_ptr<const CaptionContent> content) const
-{
-       Map::const_iterator i = _data.find(content);
-       if (i == _data.end()) {
-               return false;
-       }
-
-       return !i->second.empty();
-}
-
-void
-ActiveCaptions::clear ()
-{
-       _data.clear ();
-}
diff --git a/src/lib/active_captions.h b/src/lib/active_captions.h
deleted file mode 100644 (file)
index 8e38564..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-    Copyright (C) 2017-2018 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/>.
-
-*/
-
-/** @file  src/lib/active_captions.h
- *  @brief ActiveCaptions class.
- */
-
-#include "dcpomatic_time.h"
-#include "player_caption.h"
-#include <boost/noncopyable.hpp>
-#include <list>
-#include <map>
-
-class CaptionContent;
-
-/** @class ActiveCaptions
- *  @brief A class to maintain information on active subtitles for Player.
- */
-class ActiveCaptions : public boost::noncopyable
-{
-public:
-       std::list<PlayerCaption> get (DCPTimePeriod period) const;
-       std::list<PlayerCaption> get_burnt (DCPTimePeriod period, bool always_burn_captions) const;
-       void clear_before (DCPTime time);
-       void clear ();
-       void add_from (boost::weak_ptr<const CaptionContent> content, PlayerCaption ps, DCPTime from);
-       std::pair<PlayerCaption, DCPTime> add_to (boost::weak_ptr<const CaptionContent> content, DCPTime to);
-       bool have (boost::weak_ptr<const CaptionContent> content) const;
-
-private:
-       class Period
-       {
-       public:
-               Period () {}
-
-               Period (PlayerCaption s, DCPTime f)
-                       : subs (s)
-                       , from (f)
-               {}
-
-               PlayerCaption subs;
-               DCPTime from;
-               boost::optional<DCPTime> to;
-       };
-
-       typedef std::map<boost::weak_ptr<const CaptionContent>, std::list<Period> > Map;
-
-       void add (DCPTimePeriod period, std::list<PlayerCaption>& pc, std::list<Period> p) const;
-
-       Map _data;
-};
diff --git a/src/lib/active_text.cc b/src/lib/active_text.cc
new file mode 100644 (file)
index 0000000..de3dc81
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+    Copyright (C) 2017-2018 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 "active_text.h"
+#include "text_content.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+using std::list;
+using std::pair;
+using std::make_pair;
+using boost::weak_ptr;
+using boost::shared_ptr;
+using boost::optional;
+
+void
+ActiveText::add (DCPTimePeriod period, list<PlayerText>& pc, list<Period> p) const
+{
+       BOOST_FOREACH (Period i, p) {
+               DCPTimePeriod test (i.from, i.to.get_value_or(DCPTime::max()));
+               optional<DCPTimePeriod> overlap = period.overlap (test);
+               if (overlap && overlap->duration() > DCPTime(period.duration().get() / 2)) {
+                       pc.push_back (i.subs);
+               }
+       }
+}
+
+list<PlayerText>
+ActiveText::get (DCPTimePeriod period) const
+{
+       list<PlayerText> ps;
+
+       for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) {
+
+               shared_ptr<const TextContent> caption = i->first.lock ();
+               if (!caption || !caption->use()) {
+                       continue;
+               }
+
+               add (period, ps, i->second);
+       }
+
+       return ps;
+}
+
+/** Get the open captions that should be burnt into a given period.
+ *  @param period Period of interest.
+ *  @param always_burn_captions Always burn captions even if their content is not set to burn.
+ */
+list<PlayerText>
+ActiveText::get_burnt (DCPTimePeriod period, bool always_burn_captions) const
+{
+       list<PlayerText> ps;
+
+       for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) {
+
+               shared_ptr<const TextContent> caption = i->first.lock ();
+               if (!caption) {
+                       continue;
+               }
+
+               if (!caption->use() || (!always_burn_captions && !caption->burn())) {
+                       /* Not burning this content */
+                       continue;
+               }
+
+               add (period, ps, i->second);
+       }
+
+       return ps;
+}
+
+/** Remove subtitles that finish before a given time from our list.
+ *  @param time Time to remove before.
+ */
+void
+ActiveText::clear_before (DCPTime time)
+{
+       Map updated;
+       for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) {
+               list<Period> as;
+               BOOST_FOREACH (Period j, i->second) {
+                       if (!j.to || j.to.get() >= time) {
+                               as.push_back (j);
+                       }
+               }
+               if (!as.empty ()) {
+                       updated[i->first] = as;
+               }
+       }
+       _data = updated;
+}
+
+/** Add a new subtitle with a from time.
+ *  @param content Content that the subtitle is from.
+ *  @param ps Subtitles.
+ *  @param from From time for these subtitles.
+ */
+void
+ActiveText::add_from (weak_ptr<const TextContent> content, PlayerText ps, DCPTime from)
+{
+       if (_data.find(content) == _data.end()) {
+               _data[content] = list<Period>();
+       }
+       _data[content].push_back (Period (ps, from));
+}
+
+/** Add the to time for the last subtitle added from a piece of content.
+ *  @param content Content that the subtitle is from.
+ *  @param to To time for the last subtitle submitted to add_from for this content.
+ *  @return Return the corresponding subtitles and their from time.
+ */
+pair<PlayerText, DCPTime>
+ActiveText::add_to (weak_ptr<const TextContent> content, DCPTime to)
+{
+       DCPOMATIC_ASSERT (_data.find(content) != _data.end());
+
+       _data[content].back().to = to;
+
+       BOOST_FOREACH (StringText& i, _data[content].back().subs.text) {
+               i.set_out (dcp::Time(to.seconds(), 1000));
+       }
+
+       return make_pair (_data[content].back().subs, _data[content].back().from);
+}
+
+/** @param content Some content.
+ *  @return true if we have any active subtitles from this content.
+ */
+bool
+ActiveText::have (weak_ptr<const TextContent> content) const
+{
+       Map::const_iterator i = _data.find(content);
+       if (i == _data.end()) {
+               return false;
+       }
+
+       return !i->second.empty();
+}
+
+void
+ActiveText::clear ()
+{
+       _data.clear ();
+}
diff --git a/src/lib/active_text.h b/src/lib/active_text.h
new file mode 100644 (file)
index 0000000..10ce30f
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+    Copyright (C) 2017-2018 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/>.
+
+*/
+
+/** @file  src/lib/active_captions.h
+ *  @brief ActiveText class.
+ */
+
+#include "dcpomatic_time.h"
+#include "player_text.h"
+#include <boost/noncopyable.hpp>
+#include <list>
+#include <map>
+
+class TextContent;
+
+/** @class ActiveText
+ *  @brief A class to maintain information on active subtitles for Player.
+ */
+class ActiveText : public boost::noncopyable
+{
+public:
+       std::list<PlayerText> get (DCPTimePeriod period) const;
+       std::list<PlayerText> get_burnt (DCPTimePeriod period, bool always_burn_captions) const;
+       void clear_before (DCPTime time);
+       void clear ();
+       void add_from (boost::weak_ptr<const TextContent> content, PlayerText ps, DCPTime from);
+       std::pair<PlayerText, DCPTime> add_to (boost::weak_ptr<const TextContent> content, DCPTime to);
+       bool have (boost::weak_ptr<const TextContent> content) const;
+
+private:
+       class Period
+       {
+       public:
+               Period () {}
+
+               Period (PlayerText s, DCPTime f)
+                       : subs (s)
+                       , from (f)
+               {}
+
+               PlayerText subs;
+               DCPTime from;
+               boost::optional<DCPTime> to;
+       };
+
+       typedef std::map<boost::weak_ptr<const TextContent>, std::list<Period> > Map;
+
+       void add (DCPTimePeriod period, std::list<PlayerText>& pc, std::list<Period> p) const;
+
+       Map _data;
+};
diff --git a/src/lib/bitmap_caption.h b/src/lib/bitmap_caption.h
deleted file mode 100644 (file)
index 8883272..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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_BITMAP_CAPTION_H
-#define DCPOMATIC_BITMAP_CAPTION_H
-
-#include "rect.h"
-#include <boost/shared_ptr.hpp>
-
-class Image;
-
-class BitmapCaption
-{
-public:
-       BitmapCaption (boost::shared_ptr<Image> i, dcpomatic::Rect<double> r)
-               : image (i)
-               , rectangle (r)
-       {}
-
-       boost::shared_ptr<Image> image;
-       /** Area that the subtitle covers on its corresponding video, expressed in
-        *  proportions of the image size; e.g. rectangle.x = 0.5 would mean that
-        *  the rectangle starts half-way across the video.
-        *
-        *  This rectangle may or may not have had a TextContent's offsets and
-        *  scale applied to it, depending on context.
-        */
-       dcpomatic::Rect<double> rectangle;
-};
-
-#endif
diff --git a/src/lib/bitmap_text.h b/src/lib/bitmap_text.h
new file mode 100644 (file)
index 0000000..2314c2d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+    Copyright (C) 2014-2018 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_BITMAP_CAPTION_H
+#define DCPOMATIC_BITMAP_CAPTION_H
+
+#include "rect.h"
+#include <boost/shared_ptr.hpp>
+
+class Image;
+
+class BitmapText
+{
+public:
+       BitmapText (boost::shared_ptr<Image> i, dcpomatic::Rect<double> r)
+               : image (i)
+               , rectangle (r)
+       {}
+
+       boost::shared_ptr<Image> image;
+       /** Area that the subtitle covers on its corresponding video, expressed in
+        *  proportions of the image size; e.g. rectangle.x = 0.5 would mean that
+        *  the rectangle starts half-way across the video.
+        *
+        *  This rectangle may or may not have had a TextContent's offsets and
+        *  scale applied to it, depending on context.
+        */
+       dcpomatic::Rect<double> rectangle;
+};
+
+#endif
diff --git a/src/lib/caption_content.cc b/src/lib/caption_content.cc
deleted file mode 100644 (file)
index 4d07951..0000000
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
-    Copyright (C) 2013-2018 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 "caption_content.h"
-#include "util.h"
-#include "exceptions.h"
-#include "font.h"
-#include "content.h"
-#include <dcp/raw_convert.h>
-#include <libcxml/cxml.h>
-#include <libxml++/libxml++.h>
-#include <boost/foreach.hpp>
-#include <iostream>
-
-#include "i18n.h"
-
-using std::string;
-using std::vector;
-using std::cout;
-using std::list;
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-using boost::optional;
-using dcp::raw_convert;
-
-int const CaptionContentProperty::X_OFFSET = 500;
-int const CaptionContentProperty::Y_OFFSET = 501;
-int const CaptionContentProperty::X_SCALE = 502;
-int const CaptionContentProperty::Y_SCALE = 503;
-int const CaptionContentProperty::USE = 504;
-int const CaptionContentProperty::BURN = 505;
-int const CaptionContentProperty::LANGUAGE = 506;
-int const CaptionContentProperty::FONTS = 507;
-int const CaptionContentProperty::COLOUR = 508;
-int const CaptionContentProperty::EFFECT = 509;
-int const CaptionContentProperty::EFFECT_COLOUR = 510;
-int const CaptionContentProperty::LINE_SPACING = 511;
-int const CaptionContentProperty::FADE_IN = 512;
-int const CaptionContentProperty::FADE_OUT = 513;
-int const CaptionContentProperty::OUTLINE_WIDTH = 514;
-int const CaptionContentProperty::TYPE = 515;
-
-CaptionContent::CaptionContent (Content* parent, CaptionType original_type)
-       : ContentPart (parent)
-       , _use (false)
-       , _burn (false)
-       , _x_offset (0)
-       , _y_offset (0)
-       , _x_scale (1)
-       , _y_scale (1)
-       , _line_spacing (1)
-       , _outline_width (2)
-       , _type (original_type)
-       , _original_type (original_type)
-{
-
-}
-
-/** @return CaptionContents from node or <Caption> nodes under node (according to version).
- *  The list could be empty if no CaptionContents are found.
- */
-list<shared_ptr<CaptionContent> >
-CaptionContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version)
-{
-       if (version < 34) {
-               /* With old metadata FFmpeg content has the subtitle-related tags even with no
-                  subtitle streams, so check for that.
-               */
-               if (node->string_child("Type") == "FFmpeg" && node->node_children("SubtitleStream").empty()) {
-                       return list<shared_ptr<CaptionContent> >();
-               }
-
-               /* Otherwise we can drop through to the newer logic */
-       }
-
-       if (version < 37) {
-               if (!node->optional_number_child<double>("SubtitleXOffset") && !node->optional_number_child<double>("SubtitleOffset")) {
-                       return list<shared_ptr<CaptionContent> >();
-               }
-               list<shared_ptr<CaptionContent> > c;
-               c.push_back (shared_ptr<CaptionContent> (new CaptionContent (parent, node, version)));
-               return c;
-       }
-
-       if (!node->optional_node_child("Caption")) {
-               return list<shared_ptr<CaptionContent> >();
-       }
-
-       list<shared_ptr<CaptionContent> > c;
-       BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children("Caption")) {
-               c.push_back (shared_ptr<CaptionContent> (new CaptionContent (parent, i, version)));
-       }
-       return c;
-}
-
-CaptionContent::CaptionContent (Content* parent, cxml::ConstNodePtr node, int version)
-       : ContentPart (parent)
-       , _use (false)
-       , _burn (false)
-       , _x_offset (0)
-       , _y_offset (0)
-       , _x_scale (1)
-       , _y_scale (1)
-       , _line_spacing (node->optional_number_child<double>("LineSpacing").get_value_or (1))
-       , _outline_width (node->optional_number_child<int>("OutlineWidth").get_value_or (2))
-       , _type (CAPTION_OPEN)
-       , _original_type (CAPTION_OPEN)
-{
-       if (version >= 37) {
-               _use = node->bool_child ("Use");
-               _burn = node->bool_child ("Burn");
-       } else if (version >= 32) {
-               _use = node->bool_child ("UseSubtitles");
-               _burn = node->bool_child ("BurnSubtitles");
-       }
-
-       if (version >= 37) {
-               _x_offset = node->number_child<double> ("XOffset");
-               _y_offset = node->number_child<double> ("YOffset");
-       } else if (version >= 7) {
-               _x_offset = node->number_child<double> ("SubtitleXOffset");
-               _y_offset = node->number_child<double> ("SubtitleYOffset");
-       } else {
-               _y_offset = node->number_child<double> ("SubtitleOffset");
-       }
-
-       if (node->optional_bool_child("Outline").get_value_or(false)) {
-               _effect = dcp::BORDER;
-       } else if (node->optional_bool_child("Shadow").get_value_or(false)) {
-               _effect = dcp::SHADOW;
-       } else {
-               _effect = dcp::NONE;
-       }
-
-       optional<string> effect = node->optional_string_child("Effect");
-       if (effect) {
-               if (*effect == "none") {
-                       _effect = dcp::NONE;
-               } else if (*effect == "outline") {
-                       _effect = dcp::BORDER;
-               } else if (*effect == "shadow") {
-                       _effect = dcp::SHADOW;
-               }
-       }
-
-       if (version >= 37) {
-               _x_scale = node->number_child<double> ("XScale");
-               _y_scale = node->number_child<double> ("YScale");
-       } else if (version >= 10) {
-               _x_scale = node->number_child<double> ("SubtitleXScale");
-               _y_scale = node->number_child<double> ("SubtitleYScale");
-       } else {
-               _x_scale = _y_scale = node->number_child<double> ("SubtitleScale");
-       }
-
-       optional<int> r = node->optional_number_child<int>("Red");
-       optional<int> g = node->optional_number_child<int>("Green");
-       optional<int> b = node->optional_number_child<int>("Blue");
-       if (r && g && b) {
-               _colour = dcp::Colour (*r, *g, *b);
-       }
-
-       if (version >= 36) {
-               optional<int> er = node->optional_number_child<int>("EffectRed");
-               optional<int> eg = node->optional_number_child<int>("EffectGreen");
-               optional<int> eb = node->optional_number_child<int>("EffectBlue");
-               if (er && eg && eb) {
-                       _effect_colour = dcp::Colour (*er, *eg, *eb);
-               }
-       } else {
-               _effect_colour = dcp::Colour (
-                       node->optional_number_child<int>("OutlineRed").get_value_or(255),
-                       node->optional_number_child<int>("OutlineGreen").get_value_or(255),
-                       node->optional_number_child<int>("OutlineBlue").get_value_or(255)
-                       );
-       }
-
-       optional<Frame> fi;
-       if (version >= 37) {
-               fi = node->optional_number_child<Frame>("FadeIn");
-       } else {
-               fi = node->optional_number_child<Frame>("SubtitleFadeIn");
-       }
-       if (fi) {
-               _fade_in = ContentTime (*fi);
-       }
-
-       optional<Frame> fo;
-       if (version >= 37) {
-               fo = node->optional_number_child<Frame>("FadeOut");
-       } else {
-               fo = node->optional_number_child<Frame>("SubtitleFadeOut");
-       }
-       if (fo) {
-               _fade_out = ContentTime (*fo);
-       }
-
-       if (version >= 37) {
-               _language = node->optional_string_child ("Language").get_value_or ("");
-       } else {
-               _language = node->optional_string_child ("SubtitleLanguage").get_value_or ("");
-       }
-
-       list<cxml::NodePtr> fonts = node->node_children ("Font");
-       for (list<cxml::NodePtr>::const_iterator i = fonts.begin(); i != fonts.end(); ++i) {
-               _fonts.push_back (shared_ptr<Font> (new Font (*i)));
-       }
-
-       connect_to_fonts ();
-
-       _type = string_to_caption_type (node->optional_string_child("Type").get_value_or("open"));
-       _original_type = string_to_caption_type (node->optional_string_child("OriginalType").get_value_or("open"));
-}
-
-CaptionContent::CaptionContent (Content* parent, vector<shared_ptr<Content> > c)
-       : ContentPart (parent)
-{
-       /* This constructor is for join which is only supported for content types
-          that have a single caption, so we can use only_caption() here.
-       */
-       shared_ptr<CaptionContent> ref = c[0]->only_caption();
-       DCPOMATIC_ASSERT (ref);
-       list<shared_ptr<Font> > ref_fonts = ref->fonts ();
-
-       for (size_t i = 1; i < c.size(); ++i) {
-
-               if (c[i]->only_caption()->use() != ref->use()) {
-                       throw JoinError (_("Content to be joined must have the same 'use subtitles' setting."));
-               }
-
-               if (c[i]->only_caption()->burn() != ref->burn()) {
-                       throw JoinError (_("Content to be joined must have the same 'burn subtitles' setting."));
-               }
-
-               if (c[i]->only_caption()->x_offset() != ref->x_offset()) {
-                       throw JoinError (_("Content to be joined must have the same subtitle X offset."));
-               }
-
-               if (c[i]->only_caption()->y_offset() != ref->y_offset()) {
-                       throw JoinError (_("Content to be joined must have the same subtitle Y offset."));
-               }
-
-               if (c[i]->only_caption()->x_scale() != ref->x_scale()) {
-                       throw JoinError (_("Content to be joined must have the same subtitle X scale."));
-               }
-
-               if (c[i]->only_caption()->y_scale() != ref->y_scale()) {
-                       throw JoinError (_("Content to be joined must have the same subtitle Y scale."));
-               }
-
-               if (c[i]->only_caption()->line_spacing() != ref->line_spacing()) {
-                       throw JoinError (_("Content to be joined must have the same subtitle line spacing."));
-               }
-
-               if ((c[i]->only_caption()->fade_in() != ref->fade_in()) || (c[i]->only_caption()->fade_out() != ref->fade_out())) {
-                       throw JoinError (_("Content to be joined must have the same subtitle fades."));
-               }
-
-               if ((c[i]->only_caption()->outline_width() != ref->outline_width())) {
-                       throw JoinError (_("Content to be joined must have the same outline width."));
-               }
-
-               list<shared_ptr<Font> > fonts = c[i]->only_caption()->fonts ();
-               if (fonts.size() != ref_fonts.size()) {
-                       throw JoinError (_("Content to be joined must use the same fonts."));
-               }
-
-               list<shared_ptr<Font> >::const_iterator j = ref_fonts.begin ();
-               list<shared_ptr<Font> >::const_iterator k = fonts.begin ();
-
-               while (j != ref_fonts.end ()) {
-                       if (**j != **k) {
-                               throw JoinError (_("Content to be joined must use the same fonts."));
-                       }
-                       ++j;
-                       ++k;
-               }
-       }
-
-       _use = ref->use ();
-       _burn = ref->burn ();
-       _x_offset = ref->x_offset ();
-       _y_offset = ref->y_offset ();
-       _x_scale = ref->x_scale ();
-       _y_scale = ref->y_scale ();
-       _language = ref->language ();
-       _fonts = ref_fonts;
-       _line_spacing = ref->line_spacing ();
-       _fade_in = ref->fade_in ();
-       _fade_out = ref->fade_out ();
-       _outline_width = ref->outline_width ();
-       _type = ref->type ();
-       _original_type = ref->original_type ();
-
-       connect_to_fonts ();
-}
-
-/** _mutex must not be held on entry */
-void
-CaptionContent::as_xml (xmlpp::Node* root) const
-{
-       boost::mutex::scoped_lock lm (_mutex);
-
-       xmlpp::Element* caption = root->add_child ("Caption");
-
-       caption->add_child("Use")->add_child_text (_use ? "1" : "0");
-       caption->add_child("Burn")->add_child_text (_burn ? "1" : "0");
-       caption->add_child("XOffset")->add_child_text (raw_convert<string> (_x_offset));
-       caption->add_child("YOffset")->add_child_text (raw_convert<string> (_y_offset));
-       caption->add_child("XScale")->add_child_text (raw_convert<string> (_x_scale));
-       caption->add_child("YScale")->add_child_text (raw_convert<string> (_y_scale));
-       caption->add_child("Language")->add_child_text (_language);
-       if (_colour) {
-               caption->add_child("Red")->add_child_text (raw_convert<string> (_colour->r));
-               caption->add_child("Green")->add_child_text (raw_convert<string> (_colour->g));
-               caption->add_child("Blue")->add_child_text (raw_convert<string> (_colour->b));
-       }
-       if (_effect) {
-               switch (*_effect) {
-               case dcp::NONE:
-                       caption->add_child("Effect")->add_child_text("none");
-                       break;
-               case dcp::BORDER:
-                       caption->add_child("Effect")->add_child_text("outline");
-                       break;
-               case dcp::SHADOW:
-                       caption->add_child("Effect")->add_child_text("shadow");
-                       break;
-               }
-       }
-       if (_effect_colour) {
-               caption->add_child("EffectRed")->add_child_text (raw_convert<string> (_effect_colour->r));
-               caption->add_child("EffectGreen")->add_child_text (raw_convert<string> (_effect_colour->g));
-               caption->add_child("EffectBlue")->add_child_text (raw_convert<string> (_effect_colour->b));
-       }
-       caption->add_child("LineSpacing")->add_child_text (raw_convert<string> (_line_spacing));
-       if (_fade_in) {
-               caption->add_child("FadeIn")->add_child_text (raw_convert<string> (_fade_in->get()));
-       }
-       if (_fade_out) {
-               caption->add_child("FadeOut")->add_child_text (raw_convert<string> (_fade_out->get()));
-       }
-       caption->add_child("OutlineWidth")->add_child_text (raw_convert<string> (_outline_width));
-
-       for (list<shared_ptr<Font> >::const_iterator i = _fonts.begin(); i != _fonts.end(); ++i) {
-               (*i)->as_xml (caption->add_child("Font"));
-       }
-
-       caption->add_child("Type")->add_child_text (caption_type_to_string(_type));
-       caption->add_child("OriginalType")->add_child_text (caption_type_to_string(_original_type));
-}
-
-string
-CaptionContent::identifier () const
-{
-       string s = raw_convert<string> (x_scale())
-               + "_" + raw_convert<string> (y_scale())
-               + "_" + raw_convert<string> (x_offset())
-               + "_" + raw_convert<string> (y_offset())
-               + "_" + raw_convert<string> (line_spacing())
-               + "_" + raw_convert<string> (fade_in().get_value_or(ContentTime()).get())
-               + "_" + raw_convert<string> (fade_out().get_value_or(ContentTime()).get())
-               + "_" + raw_convert<string> (outline_width())
-               + "_" + raw_convert<string> (colour().get_value_or(dcp::Colour(255, 255, 255)).to_argb_string())
-               + "_" + raw_convert<string> (dcp::effect_to_string(effect().get_value_or(dcp::NONE)))
-               + "_" + raw_convert<string> (effect_colour().get_value_or(dcp::Colour(0, 0, 0)).to_argb_string());
-
-       /* XXX: I suppose really _fonts shouldn't be in here, since not all
-          types of subtitle content involve fonts.
-       */
-       BOOST_FOREACH (shared_ptr<Font> f, _fonts) {
-               for (int i = 0; i < FontFiles::VARIANTS; ++i) {
-                       s += "_" + f->file(static_cast<FontFiles::Variant>(i)).get_value_or("Default").string();
-               }
-       }
-
-       /* The language is for metadata only, and doesn't affect
-          how this content looks.
-       */
-
-       return s;
-}
-
-void
-CaptionContent::add_font (shared_ptr<Font> font)
-{
-       _fonts.push_back (font);
-       connect_to_fonts ();
-}
-
-void
-CaptionContent::connect_to_fonts ()
-{
-       BOOST_FOREACH (boost::signals2::connection& i, _font_connections) {
-               i.disconnect ();
-       }
-
-       _font_connections.clear ();
-
-       BOOST_FOREACH (shared_ptr<Font> i, _fonts) {
-               _font_connections.push_back (i->Changed.connect (boost::bind (&CaptionContent::font_changed, this)));
-       }
-}
-
-void
-CaptionContent::font_changed ()
-{
-       _parent->signal_changed (CaptionContentProperty::FONTS);
-}
-
-void
-CaptionContent::set_colour (dcp::Colour colour)
-{
-       maybe_set (_colour, colour, CaptionContentProperty::COLOUR);
-}
-
-void
-CaptionContent::unset_colour ()
-{
-       maybe_set (_colour, optional<dcp::Colour>(), CaptionContentProperty::COLOUR);
-}
-
-void
-CaptionContent::set_effect (dcp::Effect e)
-{
-       maybe_set (_effect, e, CaptionContentProperty::EFFECT);
-}
-
-void
-CaptionContent::unset_effect ()
-{
-       maybe_set (_effect, optional<dcp::Effect>(), CaptionContentProperty::EFFECT);
-}
-
-void
-CaptionContent::set_effect_colour (dcp::Colour colour)
-{
-       maybe_set (_effect_colour, colour, CaptionContentProperty::EFFECT_COLOUR);
-}
-
-void
-CaptionContent::unset_effect_colour ()
-{
-       maybe_set (_effect_colour, optional<dcp::Colour>(), CaptionContentProperty::EFFECT_COLOUR);
-}
-
-void
-CaptionContent::set_use (bool u)
-{
-       maybe_set (_use, u, CaptionContentProperty::USE);
-}
-
-void
-CaptionContent::set_burn (bool b)
-{
-       maybe_set (_burn, b, CaptionContentProperty::BURN);
-}
-
-void
-CaptionContent::set_x_offset (double o)
-{
-       maybe_set (_x_offset, o, CaptionContentProperty::X_OFFSET);
-}
-
-void
-CaptionContent::set_y_offset (double o)
-{
-       maybe_set (_y_offset, o, CaptionContentProperty::Y_OFFSET);
-}
-
-void
-CaptionContent::set_x_scale (double s)
-{
-       maybe_set (_x_scale, s, CaptionContentProperty::X_SCALE);
-}
-
-void
-CaptionContent::set_y_scale (double s)
-{
-       maybe_set (_y_scale, s, CaptionContentProperty::Y_SCALE);
-}
-
-void
-CaptionContent::set_language (string language)
-{
-       maybe_set (_language, language, CaptionContentProperty::LANGUAGE);
-}
-
-void
-CaptionContent::set_line_spacing (double s)
-{
-       maybe_set (_line_spacing, s, CaptionContentProperty::LINE_SPACING);
-}
-
-void
-CaptionContent::set_fade_in (ContentTime t)
-{
-       maybe_set (_fade_in, t, CaptionContentProperty::FADE_IN);
-}
-
-void
-CaptionContent::unset_fade_in ()
-{
-       maybe_set (_fade_in, optional<ContentTime>(), CaptionContentProperty::FADE_IN);
-}
-
-void
-CaptionContent::set_fade_out (ContentTime t)
-{
-       maybe_set (_fade_out, t, CaptionContentProperty::FADE_OUT);
-}
-
-void
-CaptionContent::unset_fade_out ()
-{
-       maybe_set (_fade_out, optional<ContentTime>(), CaptionContentProperty::FADE_OUT);
-}
-
-void
-CaptionContent::set_type (CaptionType type)
-{
-       maybe_set (_type, type, CaptionContentProperty::TYPE);
-}
-
-void
-CaptionContent::set_outline_width (int w)
-{
-       maybe_set (_outline_width, w, CaptionContentProperty::OUTLINE_WIDTH);
-}
-
-void
-CaptionContent::take_settings_from (shared_ptr<const CaptionContent> c)
-{
-       set_use (c->_use);
-       set_burn (c->_burn);
-       set_x_offset (c->_x_offset);
-       set_y_offset (c->_y_offset);
-       set_x_scale (c->_x_scale);
-       set_y_scale (c->_y_scale);
-       maybe_set (_fonts, c->_fonts, CaptionContentProperty::FONTS);
-       if (c->_colour) {
-               set_colour (*c->_colour);
-       } else {
-               unset_colour ();
-       }
-       if (c->_effect) {
-               set_effect (*c->_effect);
-       }
-       if (c->_effect_colour) {
-               set_effect_colour (*c->_effect_colour);
-       } else {
-               unset_effect_colour ();
-       }
-       set_line_spacing (c->_line_spacing);
-       if (c->_fade_in) {
-               set_fade_in (*c->_fade_in);
-       }
-       if (c->_fade_out) {
-               set_fade_out (*c->_fade_out);
-       }
-       set_outline_width (c->_outline_width);
-}
diff --git a/src/lib/caption_content.h b/src/lib/caption_content.h
deleted file mode 100644 (file)
index 4f5780d..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
-    Copyright (C) 2013-2018 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_CAPTION_CONTENT_H
-#define DCPOMATIC_CAPTION_CONTENT_H
-
-#include "content_part.h"
-#include <libcxml/cxml.h>
-#include <dcp/types.h>
-#include <boost/signals2.hpp>
-
-class Font;
-
-class CaptionContentProperty
-{
-public:
-       static int const X_OFFSET;
-       static int const Y_OFFSET;
-       static int const X_SCALE;
-       static int const Y_SCALE;
-       static int const USE;
-       static int const BURN;
-       static int const LANGUAGE;
-       static int const FONTS;
-       static int const COLOUR;
-       static int const EFFECT;
-       static int const EFFECT_COLOUR;
-       static int const LINE_SPACING;
-       static int const FADE_IN;
-       static int const FADE_OUT;
-       static int const OUTLINE_WIDTH;
-       static int const TYPE;
-};
-
-/** @class CaptionContent
- *  @brief Description of how some text content should be presented.
- *
- *  There are `bitmap' subtitles and `plain' subtitles (plain text),
- *  and not all of the settings in this class correspond to both types.
- */
-class CaptionContent : public ContentPart
-{
-public:
-       CaptionContent (Content* parent, CaptionType original_type);
-       CaptionContent (Content* parent, std::vector<boost::shared_ptr<Content> >);
-
-       void as_xml (xmlpp::Node *) const;
-       std::string identifier () const;
-       void take_settings_from (boost::shared_ptr<const CaptionContent> c);
-
-       void add_font (boost::shared_ptr<Font> font);
-
-       void set_use (bool);
-       void set_burn (bool);
-       void set_x_offset (double);
-       void set_y_offset (double);
-       void set_x_scale (double);
-       void set_y_scale (double);
-       void set_language (std::string language);
-       void set_colour (dcp::Colour);
-       void unset_colour ();
-       void set_effect (dcp::Effect);
-       void unset_effect ();
-       void set_effect_colour (dcp::Colour);
-       void unset_effect_colour ();
-       void set_line_spacing (double s);
-       void set_fade_in (ContentTime);
-       void unset_fade_in ();
-       void set_fade_out (ContentTime);
-       void set_outline_width (int);
-       void unset_fade_out ();
-       void set_type (CaptionType type);
-
-       bool use () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _use;
-       }
-
-       bool burn () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _burn;
-       }
-
-       double x_offset () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _x_offset;
-       }
-
-       double y_offset () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _y_offset;
-       }
-
-       double x_scale () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _x_scale;
-       }
-
-       double y_scale () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _y_scale;
-       }
-
-       std::list<boost::shared_ptr<Font> > fonts () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _fonts;
-       }
-
-       std::string language () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _language;
-       }
-
-       boost::optional<dcp::Colour> colour () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _colour;
-       }
-
-       boost::optional<dcp::Effect> effect () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _effect;
-       }
-
-       boost::optional<dcp::Colour> effect_colour () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _effect_colour;
-       }
-
-       double line_spacing () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _line_spacing;
-       }
-
-       boost::optional<ContentTime> fade_in () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _fade_in;
-       }
-
-       boost::optional<ContentTime> fade_out () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _fade_out;
-       }
-
-       int outline_width () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _outline_width;
-       }
-
-       CaptionType type () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _type;
-       }
-
-       CaptionType original_type () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _original_type;
-       }
-
-       static std::list<boost::shared_ptr<CaptionContent> > from_xml (Content* parent, cxml::ConstNodePtr, int version);
-
-protected:
-       /** subtitle language (e.g. "German") or empty if it is not known */
-       std::string _language;
-
-private:
-       friend struct ffmpeg_pts_offset_test;
-
-       CaptionContent (Content* parent, cxml::ConstNodePtr, int version);
-       void font_changed ();
-       void connect_to_fonts ();
-
-       std::list<boost::signals2::connection> _font_connections;
-
-       bool _use;
-       bool _burn;
-       /** x offset for placing subtitles, as a proportion of the container width;
-        * +ve is further right, -ve is further left.
-        */
-       double _x_offset;
-       /** y offset for placing subtitles, as a proportion of the container height;
-        *  +ve is further down the frame, -ve is further up.
-        */
-       double _y_offset;
-       /** x scale factor to apply to subtitles */
-       double _x_scale;
-       /** y scale factor to apply to subtitles */
-       double _y_scale;
-       std::list<boost::shared_ptr<Font> > _fonts;
-       boost::optional<dcp::Colour> _colour;
-       boost::optional<dcp::Effect> _effect;
-       boost::optional<dcp::Colour> _effect_colour;
-       /** scaling factor for line spacing; 1 is "standard", < 1 is closer together, > 1 is further apart */
-       double _line_spacing;
-       boost::optional<ContentTime> _fade_in;
-       boost::optional<ContentTime> _fade_out;
-       int _outline_width;
-       /** what these captions will be used for in the output DCP (not necessarily what
-        *  they were originally).
-        */
-       CaptionType _type;
-       /** the original type of these captions in their content */
-       CaptionType _original_type;
-};
-
-#endif
diff --git a/src/lib/caption_decoder.cc b/src/lib/caption_decoder.cc
deleted file mode 100644 (file)
index 1a22210..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
-    Copyright (C) 2013-2017 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 "caption_decoder.h"
-#include "caption_content.h"
-#include "util.h"
-#include "log.h"
-#include "compose.hpp"
-#include <sub/subtitle.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/foreach.hpp>
-#include <boost/algorithm/string.hpp>
-#include <iostream>
-
-using std::list;
-using std::cout;
-using std::string;
-using std::min;
-using boost::shared_ptr;
-using boost::optional;
-using boost::function;
-
-CaptionDecoder::CaptionDecoder (
-       Decoder* parent,
-       shared_ptr<const CaptionContent> c,
-       shared_ptr<Log> log,
-       ContentTime first
-       )
-       : DecoderPart (parent, log)
-       , _content (c)
-       , _position (first)
-{
-
-}
-
-/** Called by subclasses when an image subtitle is starting.
- *  @param from From time of the subtitle.
- *  @param image Subtitle image.
- *  @param rect Area expressed as a fraction of the video frame that this subtitle
- *  is for (e.g. a width of 0.5 means the width of the subtitle is half the width
- *  of the video frame)
- */
-void
-CaptionDecoder::emit_bitmap_start (ContentTime from, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
-{
-       BitmapStart (ContentBitmapCaption (from, _content->type(), image, rect));
-       _position = from;
-}
-
-void
-CaptionDecoder::emit_plain_start (ContentTime from, list<dcp::SubtitleString> s)
-{
-       BOOST_FOREACH (dcp::SubtitleString& i, s) {
-               /* We must escape < and > in strings, otherwise they might confuse our subtitle
-                  renderer (which uses some HTML-esque markup to do bold/italic etc.)
-               */
-               string t = i.text ();
-               boost::algorithm::replace_all (t, "<", "&lt;");
-               boost::algorithm::replace_all (t, ">", "&gt;");
-               i.set_text (t);
-
-               /* Set any forced appearance */
-               if (content()->colour()) {
-                       i.set_colour (*content()->colour());
-               }
-               if (content()->effect_colour()) {
-                       i.set_effect_colour (*content()->effect_colour());
-               }
-               if (content()->effect()) {
-                       i.set_effect (*content()->effect());
-               }
-               if (content()->fade_in()) {
-                       i.set_fade_up_time (dcp::Time(content()->fade_in()->seconds(), 1000));
-               }
-               if (content()->fade_out()) {
-                       i.set_fade_down_time (dcp::Time(content()->fade_out()->seconds(), 1000));
-               }
-       }
-
-       PlainStart (ContentTextCaption (from, _content->type(), s));
-       _position = from;
-}
-
-void
-CaptionDecoder::emit_plain_start (ContentTime from, sub::Subtitle const & subtitle)
-{
-       /* See if our next subtitle needs to be vertically placed on screen by us */
-       bool needs_placement = false;
-       optional<int> bottom_line;
-       BOOST_FOREACH (sub::Line i, subtitle.lines) {
-               if (!i.vertical_position.reference || i.vertical_position.reference.get() == sub::TOP_OF_SUBTITLE) {
-                       needs_placement = true;
-                       DCPOMATIC_ASSERT (i.vertical_position.line);
-                       if (!bottom_line || bottom_line.get() < i.vertical_position.line.get()) {
-                               bottom_line = i.vertical_position.line.get();
-                       }
-               }
-       }
-
-       /* Find the lowest proportional position */
-       optional<float> lowest_proportional;
-       BOOST_FOREACH (sub::Line i, subtitle.lines) {
-               if (i.vertical_position.proportional) {
-                       if (!lowest_proportional) {
-                               lowest_proportional = i.vertical_position.proportional;
-                       } else {
-                               lowest_proportional = min (lowest_proportional.get(), i.vertical_position.proportional.get());
-                       }
-               }
-       }
-
-       list<dcp::SubtitleString> out;
-       BOOST_FOREACH (sub::Line i, subtitle.lines) {
-               BOOST_FOREACH (sub::Block j, i.blocks) {
-
-                       if (!j.font_size.specified()) {
-                               /* Fallback default font size if no other has been specified */
-                               j.font_size.set_points (48);
-                       }
-
-                       float v_position;
-                       dcp::VAlign v_align;
-                       if (needs_placement) {
-                               DCPOMATIC_ASSERT (i.vertical_position.line);
-                               /* This 1.015 is an arbitrary value to lift the bottom sub off the bottom
-                                  of the screen a bit to a pleasing degree.
-                               */
-                               v_position = 1.015 -
-                                       (1 + bottom_line.get() - i.vertical_position.line.get())
-                                       * 1.2 * content()->line_spacing() * content()->y_scale() * j.font_size.proportional (72 * 11);
-
-                               v_align = dcp::VALIGN_TOP;
-                       } else {
-                               DCPOMATIC_ASSERT (i.vertical_position.proportional);
-                               DCPOMATIC_ASSERT (i.vertical_position.reference);
-                               v_position = i.vertical_position.proportional.get();
-
-                               if (lowest_proportional) {
-                                       /* Adjust line spacing */
-                                       v_position = ((v_position - lowest_proportional.get()) * content()->line_spacing()) + lowest_proportional.get();
-                               }
-
-                               switch (i.vertical_position.reference.get()) {
-                               case sub::TOP_OF_SCREEN:
-                                       v_align = dcp::VALIGN_TOP;
-                                       break;
-                               case sub::VERTICAL_CENTRE_OF_SCREEN:
-                                       v_align = dcp::VALIGN_CENTER;
-                                       break;
-                               case sub::BOTTOM_OF_SCREEN:
-                                       v_align = dcp::VALIGN_BOTTOM;
-                                       break;
-                               default:
-                                       v_align = dcp::VALIGN_TOP;
-                                       break;
-                               }
-                       }
-
-                       dcp::HAlign h_align;
-                       switch (i.horizontal_position.reference) {
-                       case sub::LEFT_OF_SCREEN:
-                               h_align = dcp::HALIGN_LEFT;
-                               break;
-                       case sub::HORIZONTAL_CENTRE_OF_SCREEN:
-                               h_align = dcp::HALIGN_CENTER;
-                               break;
-                       case sub::RIGHT_OF_SCREEN:
-                               h_align = dcp::HALIGN_RIGHT;
-                               break;
-                       default:
-                               h_align = dcp::HALIGN_CENTER;
-                               break;
-                       }
-
-                       /* The idea here (rightly or wrongly) is that we set the appearance based on the
-                          values in the libsub objects, and these are overridden with values from the
-                          content by the other emit_plain_start() above.
-                       */
-
-                       out.push_back (
-                               dcp::SubtitleString (
-                                       string(TEXT_FONT_ID),
-                                       j.italic,
-                                       j.bold,
-                                       j.underline,
-                                       j.colour.dcp(),
-                                       j.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 (),
-                                       i.horizontal_position.proportional,
-                                       h_align,
-                                       v_position,
-                                       v_align,
-                                       dcp::DIRECTION_LTR,
-                                       j.text,
-                                       dcp::NONE,
-                                       j.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 ()
-                                       )
-                               );
-               }
-       }
-
-       emit_plain_start (from, out);
-}
-
-void
-CaptionDecoder::emit_stop (ContentTime to)
-{
-       Stop (to, _content->type());
-}
-
-void
-CaptionDecoder::emit_plain (ContentTimePeriod period, list<dcp::SubtitleString> s)
-{
-       emit_plain_start (period.from, s);
-       emit_stop (period.to);
-}
-
-void
-CaptionDecoder::emit_plain (ContentTimePeriod period, sub::Subtitle const & s)
-{
-       emit_plain_start (period.from, s);
-       emit_stop (period.to);
-}
-
-void
-CaptionDecoder::seek ()
-{
-       _position = ContentTime ();
-}
diff --git a/src/lib/caption_decoder.h b/src/lib/caption_decoder.h
deleted file mode 100644 (file)
index d555446..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-    Copyright (C) 2013-2018 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_CAPTION_DECODER_H
-#define DCPOMATIC_CAPTION_DECODER_H
-
-#include "decoder.h"
-#include "rect.h"
-#include "types.h"
-#include "content_caption.h"
-#include "decoder_part.h"
-#include <dcp/subtitle_string.h>
-#include <boost/signals2.hpp>
-
-namespace sub {
-       class Subtitle;
-}
-
-class Image;
-
-class CaptionDecoder : public DecoderPart
-{
-public:
-       CaptionDecoder (
-               Decoder* parent,
-               boost::shared_ptr<const CaptionContent>,
-               boost::shared_ptr<Log> log,
-               ContentTime first
-               );
-
-       ContentTime position () const {
-               return _position;
-       }
-
-       void emit_bitmap_start (ContentTime from, boost::shared_ptr<Image> image, dcpomatic::Rect<double> rect);
-       void emit_plain_start (ContentTime from, std::list<dcp::SubtitleString> s);
-       void emit_plain_start (ContentTime from, sub::Subtitle const & subtitle);
-       void emit_plain (ContentTimePeriod period, std::list<dcp::SubtitleString> s);
-       void emit_plain (ContentTimePeriod period, sub::Subtitle const & subtitle);
-       void emit_stop (ContentTime to);
-
-       void seek ();
-
-       boost::shared_ptr<const CaptionContent> content () const {
-               return _content;
-       }
-
-       boost::signals2::signal<void (ContentBitmapCaption)> BitmapStart;
-       boost::signals2::signal<void (ContentTextCaption)> PlainStart;
-       boost::signals2::signal<void (ContentTime, CaptionType)> Stop;
-
-private:
-       boost::shared_ptr<const CaptionContent> _content;
-       ContentTime _position;
-};
-
-#endif
index 629672b..7a80882 100644 (file)
@@ -27,7 +27,7 @@
 #include "content_factory.h"
 #include "video_content.h"
 #include "audio_content.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include "exceptions.h"
 #include "film.h"
 #include "job.h"
@@ -437,8 +437,8 @@ Content::take_settings_from (shared_ptr<const Content> c)
                audio->take_settings_from (c->audio);
        }
 
-       list<shared_ptr<CaptionContent> >::iterator i = caption.begin ();
-       list<shared_ptr<CaptionContent> >::const_iterator j = c->caption.begin ();
+       list<shared_ptr<TextContent> >::iterator i = caption.begin ();
+       list<shared_ptr<TextContent> >::const_iterator j = c->caption.begin ();
        while (i != caption.end() && j != c->caption.end()) {
                (*i)->take_settings_from (*j);
                ++i;
@@ -446,24 +446,24 @@ Content::take_settings_from (shared_ptr<const Content> c)
        }
 }
 
-shared_ptr<CaptionContent>
+shared_ptr<TextContent>
 Content::only_caption () const
 {
        DCPOMATIC_ASSERT (caption.size() < 2);
        if (caption.empty ()) {
-               return shared_ptr<CaptionContent> ();
+               return shared_ptr<TextContent> ();
        }
        return caption.front ();
 }
 
-shared_ptr<CaptionContent>
-Content::caption_of_original_type (CaptionType type) const
+shared_ptr<TextContent>
+Content::caption_of_original_type (TextType type) const
 {
-       BOOST_FOREACH (shared_ptr<CaptionContent> i, caption) {
+       BOOST_FOREACH (shared_ptr<TextContent> i, caption) {
                if (i->original_type() == type) {
                        return i;
                }
        }
 
-       return shared_ptr<CaptionContent> ();
+       return shared_ptr<TextContent> ();
 }
index d84f636..850cb08 100644 (file)
@@ -182,10 +182,10 @@ public:
 
        boost::shared_ptr<VideoContent> video;
        boost::shared_ptr<AudioContent> audio;
-       std::list<boost::shared_ptr<CaptionContent> > caption;
+       std::list<boost::shared_ptr<TextContent> > caption;
 
-       boost::shared_ptr<CaptionContent> only_caption () const;
-       boost::shared_ptr<CaptionContent> caption_of_original_type (CaptionType type) const;
+       boost::shared_ptr<TextContent> only_caption () const;
+       boost::shared_ptr<TextContent> caption_of_original_type (TextType type) const;
 
        void signal_changed (int);
 
diff --git a/src/lib/content_caption.h b/src/lib/content_caption.h
deleted file mode 100644 (file)
index ad83fcb..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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_CONTENT_TEXT_H
-#define DCPOMATIC_CONTENT_TEXT_H
-
-#include "dcpomatic_time.h"
-#include "rect.h"
-#include "types.h"
-#include "bitmap_caption.h"
-#include <dcp/subtitle_string.h>
-#include <list>
-
-class Image;
-
-class ContentCaption
-{
-public:
-       explicit ContentCaption (ContentTime f, CaptionType t)
-               : _from (f)
-               , _type (t)
-       {}
-
-       ContentTime from () const {
-               return _from;
-       }
-
-       CaptionType type () const {
-               return _type;
-       }
-
-private:
-       ContentTime _from;
-       CaptionType _type;
-};
-
-class ContentBitmapCaption : public ContentCaption
-{
-public:
-       ContentBitmapCaption (ContentTime f, CaptionType type, boost::shared_ptr<Image> im, dcpomatic::Rect<double> r)
-               : ContentCaption (f, type)
-               , sub (im, r)
-       {}
-
-       /* Our text, with its rectangle unmodified by any offsets or scales that the content specifies */
-       BitmapCaption sub;
-};
-
-/** A text caption.  We store the time period separately (as well as in the dcp::SubtitleStrings)
- *  as the dcp::SubtitleString timings are sometimes quite heavily quantised and this causes problems
- *  when we want to compare the quantised periods to the unquantised ones.
- */
-class ContentTextCaption : public ContentCaption
-{
-public:
-       ContentTextCaption (ContentTime f, CaptionType type, std::list<dcp::SubtitleString> s)
-               : ContentCaption (f, type)
-               , subs (s)
-       {}
-
-       std::list<dcp::SubtitleString> subs;
-};
-
-#endif
index 8c473b7..077ef52 100644 (file)
@@ -26,7 +26,7 @@
 #include "audio_content.h"
 #include "image_content.h"
 #include "atmos_mxf_content.h"
-#include "text_caption_file_content.h"
+#include "string_text_file_content.h"
 #include "dcp_content.h"
 #include "dcp_subtitle_content.h"
 #include "util.h"
@@ -88,7 +88,7 @@ content_factory (shared_ptr<const Film> film, cxml::NodePtr node, int version, l
                        );
 
        } else if (type == "SubRip" || type == "TextSubtitle") {
-               content.reset (new TextCaptionFileContent (film, node, version));
+               content.reset (new StringTextFileContent (film, node, version));
        } else if (type == "DCP") {
                content.reset (new DCPContent (film, node, version));
        } else if (type == "DCPSubtitle") {
@@ -210,7 +210,7 @@ content_factory (shared_ptr<const Film> film, boost::filesystem::path path)
                if (valid_image_file (path)) {
                        single.reset (new ImageContent (film, path));
                } else if (ext == ".srt" || ext == ".ssa" || ext == ".ass") {
-                       single.reset (new TextCaptionFileContent (film, path));
+                       single.reset (new StringTextFileContent (film, path));
                } else if (ext == ".xml") {
                        cxml::Document doc;
                        doc.read_file (path);
diff --git a/src/lib/content_text.h b/src/lib/content_text.h
new file mode 100644 (file)
index 0000000..3ef011f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+    Copyright (C) 2014-2018 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_CONTENT_TEXT_H
+#define DCPOMATIC_CONTENT_TEXT_H
+
+#include "dcpomatic_time.h"
+#include "rect.h"
+#include "types.h"
+#include "bitmap_text.h"
+#include <dcp/subtitle_string.h>
+#include <list>
+
+class Image;
+
+class ContentText
+{
+public:
+       explicit ContentText (ContentTime f, TextType t)
+               : _from (f)
+               , _type (t)
+       {}
+
+       ContentTime from () const {
+               return _from;
+       }
+
+       TextType type () const {
+               return _type;
+       }
+
+private:
+       ContentTime _from;
+       TextType _type;
+};
+
+class ContentBitmapText : public ContentText
+{
+public:
+       ContentBitmapText (ContentTime f, TextType type, boost::shared_ptr<Image> im, dcpomatic::Rect<double> r)
+               : ContentText (f, type)
+               , sub (im, r)
+       {}
+
+       /* Our text, with its rectangle unmodified by any offsets or scales that the content specifies */
+       BitmapText sub;
+};
+
+/** A text caption.  We store the time period separately (as well as in the dcp::SubtitleStrings)
+ *  as the dcp::SubtitleString timings are sometimes quite heavily quantised and this causes problems
+ *  when we want to compare the quantised periods to the unquantised ones.
+ */
+class ContentStringText : public ContentText
+{
+public:
+       ContentStringText (ContentTime f, TextType type, std::list<dcp::SubtitleString> s)
+               : ContentText (f, type)
+               , subs (s)
+       {}
+
+       std::list<dcp::SubtitleString> subs;
+};
+
+#endif
index 414a222..aaf8064 100644 (file)
@@ -28,7 +28,7 @@
 #include "overlaps.h"
 #include "compose.hpp"
 #include "dcp_decoder.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include <dcp/dcp.h>
 #include <dcp/raw_convert.h>
 #include <dcp/exceptions.h>
@@ -84,7 +84,7 @@ DCPContent::DCPContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, in
 {
        video = VideoContent::from_xml (this, node, version);
        audio = AudioContent::from_xml (this, node, version);
-       caption = CaptionContent::from_xml (this, node, version);
+       caption = TextContent::from_xml (this, node, version);
 
        for (int i = 0; i < CAPTION_COUNT; ++i) {
                _reference_caption[i] = false;
@@ -192,8 +192,8 @@ DCPContent::examine (shared_ptr<Job> job)
                boost::mutex::scoped_lock lm (_mutex);
                _name = examiner->name ();
                for (int i = 0; i < CAPTION_COUNT; ++i) {
-                       if (examiner->has_caption(static_cast<CaptionType>(i))) {
-                               caption.push_back (shared_ptr<CaptionContent>(new CaptionContent(this, static_cast<CaptionType>(i))));
+                       if (examiner->has_caption(static_cast<TextType>(i))) {
+                               caption.push_back (shared_ptr<TextContent>(new TextContent(this, static_cast<TextType>(i))));
                        }
                }
                captions = caption.size ();
@@ -267,7 +267,7 @@ DCPContent::as_xml (xmlpp::Node* node, bool with_paths) const
                audio->stream()->mapping().as_xml (node->add_child("AudioMapping"));
        }
 
-       BOOST_FOREACH (shared_ptr<CaptionContent> i, caption) {
+       BOOST_FOREACH (shared_ptr<TextContent> i, caption) {
                i->as_xml (node);
        }
 
@@ -323,7 +323,7 @@ DCPContent::identifier () const
                s += video->identifier() + "_";
        }
 
-       BOOST_FOREACH (shared_ptr<CaptionContent> i, caption) {
+       BOOST_FOREACH (shared_ptr<TextContent> i, caption) {
                s += i->identifier () + " ";
        }
 
@@ -416,7 +416,7 @@ DCPContent::set_reference_audio (bool r)
 }
 
 void
-DCPContent::set_reference_caption (CaptionType type, bool r)
+DCPContent::set_reference_caption (TextType type, bool r)
 {
        {
                boost::mutex::scoped_lock lm (_mutex);
@@ -596,7 +596,7 @@ bool check_caption (shared_ptr<const Content> c)
        return !c->caption.empty();
 }
 bool
-DCPContent::can_reference_caption (CaptionType type, string& why_not) const
+DCPContent::can_reference_caption (TextType type, string& why_not) const
 {
        shared_ptr<DCPDecoder> decoder;
        try {
index f01790b..fedc43d 100644 (file)
@@ -108,17 +108,17 @@ public:
 
        bool can_reference_audio (std::string &) const;
 
-       void set_reference_caption (CaptionType type, bool r);
+       void set_reference_caption (TextType type, bool r);
 
        /** @param type Original type of captions in the DCP.
         *  @return true if these captions are to be referenced.
         */
-       bool reference_caption (CaptionType type) const {
+       bool reference_caption (TextType type) const {
                boost::mutex::scoped_lock lm (_mutex);
                return _reference_caption[type];
        }
 
-       bool can_reference_caption (CaptionType type, std::string &) const;
+       bool can_reference_caption (TextType type, std::string &) const;
 
        void set_cpl (std::string id);
 
index ab655eb..86152c8 100644 (file)
@@ -24,7 +24,7 @@
 #include "video_decoder.h"
 #include "audio_decoder.h"
 #include "j2k_image_proxy.h"
-#include "caption_decoder.h"
+#include "text_decoder.h"
 #include "image.h"
 #include "config.h"
 #include <dcp/dcp.h>
@@ -63,9 +63,9 @@ DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log, boo
        if (c->audio) {
                audio.reset (new AudioDecoder (this, c->audio, log, fast));
        }
-       BOOST_FOREACH (shared_ptr<CaptionContent> i, c->caption) {
+       BOOST_FOREACH (shared_ptr<TextContent> i, c->caption) {
                /* XXX: this time here should be the time of the first subtitle, not 0 */
-               caption.push_back (shared_ptr<CaptionDecoder> (new CaptionDecoder (this, i, log, ContentTime())));
+               caption.push_back (shared_ptr<TextDecoder> (new TextDecoder (this, i, log, ContentTime())));
        }
 
        list<shared_ptr<dcp::CPL> > cpl_list = cpls ();
@@ -193,7 +193,7 @@ DCPDecoder::pass ()
 void
 DCPDecoder::pass_captions (ContentTime next)
 {
-       list<shared_ptr<CaptionDecoder> >::const_iterator decoder = caption.begin ();
+       list<shared_ptr<TextDecoder> >::const_iterator decoder = caption.begin ();
        if ((*_reel)->main_subtitle()) {
                pass_captions (
                        next, (*_reel)->main_subtitle()->asset(), _dcp_content->reference_caption(CAPTION_OPEN), (*_reel)->main_subtitle()->entry_point(), *decoder
@@ -209,7 +209,7 @@ DCPDecoder::pass_captions (ContentTime next)
 }
 
 void
-DCPDecoder::pass_captions (ContentTime next, shared_ptr<dcp::SubtitleAsset> asset, bool reference, int64_t entry_point, shared_ptr<CaptionDecoder> decoder)
+DCPDecoder::pass_captions (ContentTime next, shared_ptr<dcp::SubtitleAsset> asset, bool reference, int64_t entry_point, shared_ptr<TextDecoder> decoder)
 {
        double const vfr = _dcp_content->active_video_frame_rate ();
        /* Frame within the (played part of the) reel that is coming up next */
index 898b84e..2fbdb88 100644 (file)
@@ -58,7 +58,7 @@ private:
        void next_reel ();
        void get_readers ();
        void pass_captions (ContentTime next);
-       void pass_captions (ContentTime next, boost::shared_ptr<dcp::SubtitleAsset> asset, bool reference, int64_t entry_point, boost::shared_ptr<CaptionDecoder> decoder);
+       void pass_captions (ContentTime next, boost::shared_ptr<dcp::SubtitleAsset> asset, bool reference, int64_t entry_point, boost::shared_ptr<TextDecoder> decoder);
 
        /** Time of next thing to return from pass relative to the start of _reel */
        ContentTime _next;
index f518aef..4298488 100644 (file)
@@ -35,7 +35,7 @@
 #include "writer.h"
 #include "compose.hpp"
 #include "referenced_reel_asset.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include "player_video.h"
 #include <boost/signals2.hpp>
 #include <boost/foreach.hpp>
@@ -64,7 +64,7 @@ DCPEncoder::DCPEncoder (shared_ptr<const Film> film, weak_ptr<Job> job)
        _player_caption_connection = _player->Caption.connect (bind (&DCPEncoder::caption, this, _1, _2, _3));
 
        BOOST_FOREACH (shared_ptr<const Content> c, film->content ()) {
-               BOOST_FOREACH (shared_ptr<CaptionContent> i, c->caption) {
+               BOOST_FOREACH (shared_ptr<TextContent> i, c->caption) {
                        if (i->use() && !i->burn()) {
                                _non_burnt_subtitles = true;
                        }
@@ -143,7 +143,7 @@ DCPEncoder::audio (shared_ptr<AudioBuffers> data, DCPTime time)
 }
 
 void
-DCPEncoder::caption (PlayerCaption data, CaptionType type, DCPTimePeriod period)
+DCPEncoder::caption (PlayerText data, TextType type, DCPTimePeriod period)
 {
        if (type == CAPTION_CLOSED || _non_burnt_subtitles) {
                _writer->write (data, type, period);
index 9808987..23b05d0 100644 (file)
@@ -19,7 +19,7 @@
 */
 
 #include "types.h"
-#include "player_caption.h"
+#include "player_text.h"
 #include "encoder.h"
 #include <boost/weak_ptr.hpp>
 
@@ -52,7 +52,7 @@ private:
 
        void video (boost::shared_ptr<PlayerVideo>, DCPTime);
        void audio (boost::shared_ptr<AudioBuffers>, DCPTime);
-       void caption (PlayerCaption, CaptionType, DCPTimePeriod);
+       void caption (PlayerText, TextType, DCPTimePeriod);
 
        boost::shared_ptr<Writer> _writer;
        boost::shared_ptr<J2KEncoder> _j2k_encoder;
index 16385e0..29fcc48 100644 (file)
@@ -83,7 +83,7 @@ public:
                return _audio_frame_rate.get_value_or (48000);
        }
 
-       bool has_caption (CaptionType type) const {
+       bool has_caption (TextType type) const {
                return _has_caption[type];
        }
 
index 779361f..6a7e381 100644 (file)
@@ -21,7 +21,7 @@
 #include "font.h"
 #include "dcp_subtitle_content.h"
 #include "film.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include <dcp/raw_convert.h>
 #include <dcp/interop_subtitle_asset.h>
 #include <dcp/smpte_subtitle_asset.h>
@@ -40,14 +40,14 @@ using dcp::raw_convert;
 DCPSubtitleContent::DCPSubtitleContent (shared_ptr<const Film> film, boost::filesystem::path path)
        : Content (film, path)
 {
-       caption.push_back (shared_ptr<CaptionContent> (new CaptionContent (this, CAPTION_OPEN)));
+       caption.push_back (shared_ptr<TextContent> (new TextContent (this, CAPTION_OPEN)));
 }
 
 DCPSubtitleContent::DCPSubtitleContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version)
        : Content (film, node)
        , _length (node->number_child<ContentTime::Type> ("Length"))
 {
-       caption = CaptionContent::from_xml (this, node, version);
+       caption = TextContent::from_xml (this, node, version);
 }
 
 void
index 3ed4a68..05d8152 100644 (file)
@@ -39,7 +39,7 @@ DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr<const DCPSubtitleContent> con
        if (_next != _subtitles.end()) {
                first = content_time_period(*_next).from;
        }
-       caption.push_back (shared_ptr<CaptionDecoder> (new CaptionDecoder (this, content->only_caption(), log, first)));
+       caption.push_back (shared_ptr<TextDecoder> (new TextDecoder (this, content->only_caption(), log, first)));
 }
 
 void
@@ -64,7 +64,7 @@ DCPSubtitleDecoder::pass ()
        /* Gather all subtitles with the same time period that are next
           on the list.  We must emit all subtitles for the same time
           period with the same plain_text() call otherwise the
-          CaptionDecoder will assume there is nothing else at the
+          TextDecoder will assume there is nothing else at the
           time of emit the first.
        */
 
index ef4dad3..984d988 100644 (file)
@@ -18,7 +18,7 @@
 
 */
 
-#include "caption_decoder.h"
+#include "text_decoder.h"
 #include "dcp_subtitle.h"
 
 class DCPSubtitleContent;
index 2fddddc..52949a0 100644 (file)
@@ -21,7 +21,7 @@
 #include "decoder.h"
 #include "video_decoder.h"
 #include "audio_decoder.h"
-#include "caption_decoder.h"
+#include "text_decoder.h"
 #include <boost/optional.hpp>
 #include <iostream>
 
@@ -43,7 +43,7 @@ Decoder::position () const
                pos = audio->position();
        }
 
-       BOOST_FOREACH (shared_ptr<CaptionDecoder> i, caption) {
+       BOOST_FOREACH (shared_ptr<TextDecoder> i, caption) {
                if (!i->ignore() && (!pos || i->position() < *pos)) {
                        pos = i->position();
                }
@@ -61,17 +61,17 @@ Decoder::seek (ContentTime, bool)
        if (audio) {
                audio->seek ();
        }
-       BOOST_FOREACH (shared_ptr<CaptionDecoder> i, caption) {
+       BOOST_FOREACH (shared_ptr<TextDecoder> i, caption) {
                i->seek ();
        }
 }
 
-shared_ptr<CaptionDecoder>
+shared_ptr<TextDecoder>
 Decoder::only_caption () const
 {
        DCPOMATIC_ASSERT (caption.size() < 2);
        if (caption.empty ()) {
-               return shared_ptr<CaptionDecoder> ();
+               return shared_ptr<TextDecoder> ();
        }
        return caption.front ();
 }
index d48df75..1b33031 100644 (file)
@@ -32,7 +32,7 @@
 class Decoded;
 class VideoDecoder;
 class AudioDecoder;
-class CaptionDecoder;
+class TextDecoder;
 class DecoderPart;
 
 /** @class Decoder.
@@ -45,9 +45,9 @@ public:
 
        boost::shared_ptr<VideoDecoder> video;
        boost::shared_ptr<AudioDecoder> audio;
-       std::list<boost::shared_ptr<CaptionDecoder> > caption;
+       std::list<boost::shared_ptr<TextDecoder> > caption;
 
-       boost::shared_ptr<CaptionDecoder> only_caption () const;
+       boost::shared_ptr<TextDecoder> only_caption () const;
 
        /** Do some decoding and perhaps emit video, audio or subtitle data.
         *  @return true if this decoder will emit no more data unless a seek() happens.
index bb3bdb1..02d7064 100644 (file)
@@ -24,8 +24,8 @@
 #include "dcp_decoder.h"
 #include "image_content.h"
 #include "image_decoder.h"
-#include "text_caption_file_content.h"
-#include "text_caption_file_decoder.h"
+#include "string_text_file_content.h"
+#include "string_text_file_decoder.h"
 #include "dcp_subtitle_content.h"
 #include "dcp_subtitle_decoder.h"
 #include "video_mxf_content.h"
@@ -54,9 +54,9 @@ decoder_factory (shared_ptr<const Content> content, shared_ptr<Log> log, bool fa
                return shared_ptr<Decoder> (new ImageDecoder (ic, log));
        }
 
-       shared_ptr<const TextCaptionFileContent> rc = dynamic_pointer_cast<const TextCaptionFileContent> (content);
+       shared_ptr<const StringTextFileContent> rc = dynamic_pointer_cast<const StringTextFileContent> (content);
        if (rc) {
-               return shared_ptr<Decoder> (new TextCaptionFileDecoder (rc, log));
+               return shared_ptr<Decoder> (new StringTextFileDecoder (rc, log));
        }
 
        shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (content);
index 27fa074..f4116c5 100644 (file)
@@ -22,7 +22,7 @@
 #define DCPOMATIC_ENCODER_H
 
 #include "types.h"
-#include "player_caption.h"
+#include "player_text.h"
 #include <boost/weak_ptr.hpp>
 #include <boost/signals2.hpp>
 
index 3f0a692..86965ff 100644 (file)
@@ -32,7 +32,7 @@
 #include "log.h"
 #include "exceptions.h"
 #include "frame_rate_change.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include <dcp/raw_convert.h>
 #include <libcxml/cxml.h>
 extern "C" {
@@ -85,7 +85,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> film, cxml::ConstNodePtr no
 {
        video = VideoContent::from_xml (this, node, version);
        audio = AudioContent::from_xml (this, node, version);
-       caption = CaptionContent::from_xml (this, node, version);
+       caption = TextContent::from_xml (this, node, version);
 
        list<cxml::NodePtr> c = node->node_children ("SubtitleStream");
        for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
@@ -163,7 +163,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> film, vector<shared_ptr<Con
                audio.reset (new AudioContent (this, c));
        }
        if (need_caption) {
-               caption.push_back (shared_ptr<CaptionContent> (new CaptionContent (this, c)));
+               caption.push_back (shared_ptr<TextContent> (new TextContent (this, c)));
        }
 
        shared_ptr<FFmpegContent> ref = dynamic_pointer_cast<FFmpegContent> (c[0]);
@@ -304,7 +304,7 @@ FFmpegContent::examine (shared_ptr<Job> job)
                _subtitle_streams = examiner->subtitle_streams ();
                if (!_subtitle_streams.empty ()) {
                        caption.clear ();
-                       caption.push_back (shared_ptr<CaptionContent> (new CaptionContent (this, CAPTION_OPEN)));
+                       caption.push_back (shared_ptr<TextContent> (new TextContent (this, CAPTION_OPEN)));
                        _subtitle_stream = _subtitle_streams.front ();
                }
 
index 1f2fcfe..665ee3e 100644 (file)
@@ -28,7 +28,7 @@
 #include "util.h"
 #include "log.h"
 #include "ffmpeg_decoder.h"
-#include "caption_decoder.h"
+#include "text_decoder.h"
 #include "ffmpeg_audio_stream.h"
 #include "ffmpeg_subtitle_stream.h"
 #include "video_filter_graph.h"
@@ -39,7 +39,7 @@
 #include "film.h"
 #include "audio_decoder.h"
 #include "compose.hpp"
-#include "caption_content.h"
+#include "text_content.h"
 #include "audio_content.h"
 #include <dcp/subtitle_string.h>
 #include <sub/ssa_reader.h>
@@ -99,7 +99,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log>
 
        if (c->only_caption()) {
                /* XXX: this time here should be the time of the first subtitle, not 0 */
-               caption.push_back (shared_ptr<CaptionDecoder> (new CaptionDecoder (this, c->only_caption(), log, ContentTime())));
+               caption.push_back (shared_ptr<TextDecoder> (new TextDecoder (this, c->only_caption(), log, ContentTime())));
        }
 
        _next_time.resize (_format_context->nb_streams);
index 29334e8..f82290c 100644 (file)
@@ -411,7 +411,7 @@ FFmpegEncoder::audio_frame (int size)
 }
 
 void
-FFmpegEncoder::subtitle (PlayerCaption, DCPTimePeriod)
+FFmpegEncoder::subtitle (PlayerText, DCPTimePeriod)
 {
 
 }
index 35fd850..b1d07eb 100644 (file)
@@ -54,7 +54,7 @@ public:
 private:
        void video (boost::shared_ptr<PlayerVideo>, DCPTime);
        void audio (boost::shared_ptr<AudioBuffers>);
-       void subtitle (PlayerCaption, DCPTimePeriod);
+       void subtitle (PlayerText, DCPTimePeriod);
 
        void setup_video ();
        void setup_audio ();
index b56f85d..475d28b 100644 (file)
@@ -46,7 +46,7 @@
 #include "screen.h"
 #include "audio_content.h"
 #include "video_content.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include "ffmpeg_content.h"
 #include "dcp_content.h"
 #include "screen_kdm.h"
@@ -125,7 +125,7 @@ string const Film::metadata_file = "metadata.xml";
  * 35 -> 36
  * EffectColour rather than OutlineColour in Subtitle.
  * 36 -> 37
- * CaptionContent can be in a Caption tag, and some of the tag names
+ * TextContent can be in a Caption tag, and some of the tag names
  * have had Subtitle prefixes or suffixes removed.
  */
 int const Film::current_state_version = 37;
@@ -704,7 +704,7 @@ Film::isdcf_name (bool if_created_now) const
                        bool burnt_in = true;
                        bool ccap = false;
                        BOOST_FOREACH (shared_ptr<Content> i, content()) {
-                               BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+                               BOOST_FOREACH (shared_ptr<TextContent> j, i->caption) {
                                        if (j->type() == CAPTION_OPEN && j->use() && !j->burn()) {
                                                burnt_in = false;
                                        } else if (j->type() == CAPTION_CLOSED) {
@@ -784,7 +784,7 @@ Film::isdcf_name (bool if_created_now) const
 
                bool any_caption = false;
                for (int i = 0; i < CAPTION_COUNT; ++i) {
-                       if (dc->reference_caption(static_cast<CaptionType>(i))) {
+                       if (dc->reference_caption(static_cast<TextType>(i))) {
                                any_caption = true;
                        }
                }
@@ -1391,7 +1391,7 @@ Film::subtitle_language () const
        set<string> languages;
 
        BOOST_FOREACH (shared_ptr<Content> i, content()) {
-               BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+               BOOST_FOREACH (shared_ptr<TextContent> j, i->caption) {
                        languages.insert (j->language ());
                }
        }
index 33c2fab..8cc0cfb 100644 (file)
@@ -23,7 +23,7 @@
 #include "film.h"
 #include "content.h"
 #include "video_content.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include "audio_processor.h"
 #include "font.h"
 #include "ratio.h"
@@ -56,7 +56,7 @@ get_hints (shared_ptr<const Film> film)
        bool big_font_files = false;
        if (film->interop ()) {
                BOOST_FOREACH (shared_ptr<Content> i, content) {
-                       BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+                       BOOST_FOREACH (shared_ptr<TextContent> j, i->caption) {
                                BOOST_FOREACH (shared_ptr<Font> k, j->fonts()) {
                                        for (int l = 0; l < FontFiles::VARIANTS; ++l) {
                                                optional<boost::filesystem::path> const p = k->file (static_cast<FontFiles::Variant>(l));
index 4635233..22526ed 100644 (file)
@@ -40,8 +40,8 @@
 #include "decoder.h"
 #include "video_decoder.h"
 #include "audio_decoder.h"
-#include "caption_content.h"
-#include "caption_decoder.h"
+#include "text_content.h"
+#include "text_decoder.h"
 #include "ffmpeg_content.h"
 #include "audio_content.h"
 #include "dcp_decoder.h"
@@ -138,7 +138,7 @@ Player::setup_pieces ()
                }
 
                if (_ignore_caption) {
-                       BOOST_FOREACH (shared_ptr<CaptionDecoder> i, decoder->caption) {
+                       BOOST_FOREACH (shared_ptr<TextDecoder> i, decoder->caption) {
                                i->set_ignore (true);
                        }
                }
@@ -167,17 +167,17 @@ Player::setup_pieces ()
                        decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr<Piece> (piece), _1, _2));
                }
 
-               list<shared_ptr<CaptionDecoder> >::const_iterator j = decoder->caption.begin();
+               list<shared_ptr<TextDecoder> >::const_iterator j = decoder->caption.begin();
 
                while (j != decoder->caption.end()) {
                        (*j)->BitmapStart.connect (
-                               bind(&Player::bitmap_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const CaptionContent>((*j)->content()), _1)
+                               bind(&Player::bitmap_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>((*j)->content()), _1)
                                );
                        (*j)->PlainStart.connect (
-                               bind(&Player::plain_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const CaptionContent>((*j)->content()), _1)
+                               bind(&Player::plain_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>((*j)->content()), _1)
                                );
                        (*j)->Stop.connect (
-                               bind(&Player::subtitle_stop, this, weak_ptr<Piece>(piece), weak_ptr<const CaptionContent>((*j)->content()), _1, _2)
+                               bind(&Player::subtitle_stop, this, weak_ptr<Piece>(piece), weak_ptr<const TextContent>((*j)->content()), _1, _2)
                                );
 
                        ++j;
@@ -221,9 +221,9 @@ Player::playlist_content_changed (weak_ptr<Content> w, int property, bool freque
                property == AudioContentProperty::STREAMS ||
                property == DCPContentProperty::NEEDS_ASSETS ||
                property == DCPContentProperty::NEEDS_KDM ||
-               property == CaptionContentProperty::COLOUR ||
-               property == CaptionContentProperty::EFFECT ||
-               property == CaptionContentProperty::EFFECT_COLOUR ||
+               property == TextContentProperty::COLOUR ||
+               property == TextContentProperty::EFFECT ||
+               property == TextContentProperty::EFFECT_COLOUR ||
                property == FFmpegContentProperty::SUBTITLE_STREAM ||
                property == FFmpegContentProperty::FILTERS
                ) {
@@ -232,18 +232,18 @@ Player::playlist_content_changed (weak_ptr<Content> w, int property, bool freque
                Changed (property, frequent);
 
        } else if (
-               property == CaptionContentProperty::LINE_SPACING ||
-               property == CaptionContentProperty::OUTLINE_WIDTH ||
-               property == CaptionContentProperty::Y_SCALE ||
-               property == CaptionContentProperty::FADE_IN ||
-               property == CaptionContentProperty::FADE_OUT ||
+               property == TextContentProperty::LINE_SPACING ||
+               property == TextContentProperty::OUTLINE_WIDTH ||
+               property == TextContentProperty::Y_SCALE ||
+               property == TextContentProperty::FADE_IN ||
+               property == TextContentProperty::FADE_OUT ||
                property == ContentProperty::VIDEO_FRAME_RATE ||
-               property == CaptionContentProperty::USE ||
-               property == CaptionContentProperty::X_OFFSET ||
-               property == CaptionContentProperty::Y_OFFSET ||
-               property == CaptionContentProperty::X_SCALE ||
-               property == CaptionContentProperty::FONTS ||
-               property == CaptionContentProperty::TYPE ||
+               property == TextContentProperty::USE ||
+               property == TextContentProperty::X_OFFSET ||
+               property == TextContentProperty::Y_OFFSET ||
+               property == TextContentProperty::X_SCALE ||
+               property == TextContentProperty::FONTS ||
+               property == TextContentProperty::TYPE ||
                property == VideoContentProperty::CROP ||
                property == VideoContentProperty::SCALE ||
                property == VideoContentProperty::FADE_IN ||
@@ -302,11 +302,11 @@ Player::film_changed (Film::Property p)
 }
 
 list<PositionImage>
-Player::transform_bitmap_captions (list<BitmapCaption> subs) const
+Player::transform_bitmap_captions (list<BitmapText> subs) const
 {
        list<PositionImage> all;
 
-       for (list<BitmapCaption>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
+       for (list<BitmapText>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
                if (!i->image) {
                        continue;
                }
@@ -419,7 +419,7 @@ Player::get_subtitle_fonts ()
 
        list<shared_ptr<Font> > fonts;
        BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
-               BOOST_FOREACH (shared_ptr<CaptionContent> j, i->content->caption) {
+               BOOST_FOREACH (shared_ptr<TextContent> j, i->content->caption) {
                        /* XXX: things may go wrong if there are duplicate font IDs
                           with different font files.
                        */
@@ -676,7 +676,7 @@ Player::pass ()
        return done;
 }
 
-list<PlayerCaption>
+list<PlayerText>
 Player::closed_captions_for_frame (DCPTime time) const
 {
        return _active_captions[CAPTION_CLOSED].get (
@@ -692,7 +692,7 @@ Player::open_captions_for_frame (DCPTime time) const
        int const vfr = _film->video_frame_rate();
 
        BOOST_FOREACH (
-               PlayerCaption j,
+               PlayerText j,
                _active_captions[CAPTION_OPEN].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_open_captions)
                ) {
 
@@ -870,10 +870,10 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
 }
 
 void
-Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc, ContentBitmapCaption subtitle)
+Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, ContentBitmapText subtitle)
 {
        shared_ptr<Piece> piece = wp.lock ();
-       shared_ptr<const CaptionContent> caption = wc.lock ();
+       shared_ptr<const TextContent> caption = wc.lock ();
        if (!piece || !caption) {
                return;
        }
@@ -890,7 +890,7 @@ Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc
        subtitle.sub.rectangle.width *= caption->x_scale ();
        subtitle.sub.rectangle.height *= caption->y_scale ();
 
-       PlayerCaption ps;
+       PlayerText ps;
        ps.image.push_back (subtitle.sub);
        DCPTime from (content_time_to_dcp (piece, subtitle.from()));
 
@@ -898,15 +898,15 @@ Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc
 }
 
 void
-Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc, ContentTextCaption subtitle)
+Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, ContentStringText subtitle)
 {
        shared_ptr<Piece> piece = wp.lock ();
-       shared_ptr<const CaptionContent> caption = wc.lock ();
+       shared_ptr<const TextContent> caption = wc.lock ();
        if (!piece || !caption) {
                return;
        }
 
-       PlayerCaption ps;
+       PlayerText ps;
        DCPTime const from (content_time_to_dcp (piece, subtitle.from()));
 
        if (from > piece->content->end()) {
@@ -934,7 +934,7 @@ Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc,
                }
 
                s.set_in (dcp::Time(from.seconds(), 1000));
-               ps.text.push_back (TextCaption (s, caption->outline_width()));
+               ps.text.push_back (StringText (s, caption->outline_width()));
                ps.add_fonts (caption->fonts ());
        }
 
@@ -942,14 +942,14 @@ Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc,
 }
 
 void
-Player::subtitle_stop (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc, ContentTime to, CaptionType type)
+Player::subtitle_stop (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, ContentTime to, TextType type)
 {
        if (!_active_captions[type].have (wc)) {
                return;
        }
 
        shared_ptr<Piece> piece = wp.lock ();
-       shared_ptr<const CaptionContent> caption = wc.lock ();
+       shared_ptr<const TextContent> caption = wc.lock ();
        if (!piece || !caption) {
                return;
        }
@@ -960,7 +960,7 @@ Player::subtitle_stop (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc, Co
                return;
        }
 
-       pair<PlayerCaption, DCPTime> from = _active_captions[type].add_to (wc, dcp_to);
+       pair<PlayerText, DCPTime> from = _active_captions[type].add_to (wc, dcp_to);
 
        bool const always = type == CAPTION_OPEN && _always_burn_open_captions;
        if (caption->use() && !always && !caption->burn()) {
index c086b96..2803737 100644 (file)
@@ -21,9 +21,9 @@
 #ifndef DCPOMATIC_PLAYER_H
 #define DCPOMATIC_PLAYER_H
 
-#include "player_caption.h"
-#include "active_captions.h"
-#include "content_caption.h"
+#include "player_text.h"
+#include "active_text.h"
+#include "content_text.h"
 #include "film.h"
 #include "content.h"
 #include "position_image.h"
@@ -86,7 +86,7 @@ public:
 
        DCPTime content_time_to_dcp (boost::shared_ptr<Content> content, ContentTime t);
 
-       std::list<PlayerCaption> closed_captions_for_frame (DCPTime time) const;
+       std::list<PlayerText> closed_captions_for_frame (DCPTime time) const;
 
        /** Emitted when something has changed such that if we went back and emitted
         *  the last frame again it would look different.  This is not emitted after
@@ -103,7 +103,7 @@ public:
        /** Emitted when a caption is ready.  This signal may be emitted considerably
         *  after the corresponding Video.
         */
-       boost::signals2::signal<void (PlayerCaption, CaptionType, DCPTimePeriod)> Caption;
+       boost::signals2::signal<void (PlayerText, TextType, DCPTimePeriod)> Caption;
 
 private:
        friend class PlayerWrapper;
@@ -118,7 +118,7 @@ private:
        void film_changed (Film::Property);
        void playlist_changed ();
        void playlist_content_changed (boost::weak_ptr<Content>, int, bool);
-       std::list<PositionImage> transform_bitmap_captions (std::list<BitmapCaption>) const;
+       std::list<PositionImage> transform_bitmap_captions (std::list<BitmapText>) const;
        Frame dcp_to_content_video (boost::shared_ptr<const Piece> piece, DCPTime t) const;
        DCPTime content_video_to_dcp (boost::shared_ptr<const Piece> piece, Frame f) const;
        Frame dcp_to_resampled_audio (boost::shared_ptr<const Piece> piece, DCPTime t) const;
@@ -128,9 +128,9 @@ private:
        boost::shared_ptr<PlayerVideo> black_player_video_frame (Eyes eyes) const;
        void video (boost::weak_ptr<Piece>, ContentVideo);
        void audio (boost::weak_ptr<Piece>, AudioStreamPtr, ContentAudio);
-       void bitmap_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<const CaptionContent>, ContentBitmapCaption);
-       void plain_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<const CaptionContent>, ContentTextCaption);
-       void subtitle_stop (boost::weak_ptr<Piece>, boost::weak_ptr<const CaptionContent>, ContentTime, CaptionType);
+       void bitmap_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<const TextContent>, ContentBitmapText);
+       void plain_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<const TextContent>, ContentStringText);
+       void subtitle_stop (boost::weak_ptr<Piece>, boost::weak_ptr<const TextContent>, ContentTime, TextType);
        DCPTime one_video_frame () const;
        void fill_audio (DCPTimePeriod period);
        std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> discard_audio (
@@ -195,7 +195,7 @@ private:
        Empty _black;
        Empty _silent;
 
-       ActiveCaptions _active_captions[CAPTION_COUNT];
+       ActiveText _active_captions[CAPTION_COUNT];
        boost::shared_ptr<AudioProcessor> _audio_processor;
 
        boost::signals2::scoped_connection _film_changed_connection;
diff --git a/src/lib/player_caption.cc b/src/lib/player_caption.cc
deleted file mode 100644 (file)
index 74c847a..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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 "player_caption.h"
-#include "font.h"
-#include <boost/foreach.hpp>
-
-using std::list;
-using boost::shared_ptr;
-
-void
-PlayerCaption::add_fonts (list<shared_ptr<Font> > fonts_)
-{
-       BOOST_FOREACH (shared_ptr<Font> i, fonts_) {
-               bool got = false;
-               BOOST_FOREACH (shared_ptr<Font> j, fonts) {
-                       if (*i == *j) {
-                               got = true;
-                       }
-               }
-               if (!got) {
-                       fonts.push_back (i);
-               }
-       }
-}
diff --git a/src/lib/player_caption.h b/src/lib/player_caption.h
deleted file mode 100644 (file)
index 2f7e909..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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_PLAYER_CAPTION_H
-#define DCPOMATIC_PLAYER_CAPTION_H
-
-#include "bitmap_caption.h"
-#include "dcpomatic_time.h"
-#include "text_caption.h"
-
-class Font;
-
-/** A set of text (subtitle/CCAP) which span the same time period */
-class PlayerCaption
-{
-public:
-       void add_fonts (std::list<boost::shared_ptr<Font> > fonts_);
-       std::list<boost::shared_ptr<Font> > fonts;
-
-       /** BitmapCaptions, with their rectangles transformed as specified by their content */
-       std::list<BitmapCaption> image;
-       std::list<TextCaption> text;
-};
-
-#endif
diff --git a/src/lib/player_text.cc b/src/lib/player_text.cc
new file mode 100644 (file)
index 0000000..16e89b0
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+    Copyright (C) 2014-2018 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 "player_text.h"
+#include "font.h"
+#include <boost/foreach.hpp>
+
+using std::list;
+using boost::shared_ptr;
+
+void
+PlayerText::add_fonts (list<shared_ptr<Font> > fonts_)
+{
+       BOOST_FOREACH (shared_ptr<Font> i, fonts_) {
+               bool got = false;
+               BOOST_FOREACH (shared_ptr<Font> j, fonts) {
+                       if (*i == *j) {
+                               got = true;
+                       }
+               }
+               if (!got) {
+                       fonts.push_back (i);
+               }
+       }
+}
diff --git a/src/lib/player_text.h b/src/lib/player_text.h
new file mode 100644 (file)
index 0000000..5a87991
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+    Copyright (C) 2014-2018 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_PLAYER_CAPTION_H
+#define DCPOMATIC_PLAYER_CAPTION_H
+
+#include "bitmap_text.h"
+#include "dcpomatic_time.h"
+#include "string_text.h"
+
+class Font;
+
+/** A set of text (subtitle/CCAP) which span the same time period */
+class PlayerText
+{
+public:
+       void add_fonts (std::list<boost::shared_ptr<Font> > fonts_);
+       std::list<boost::shared_ptr<Font> > fonts;
+
+       /** BitmapTexts, with their rectangles transformed as specified by their content */
+       std::list<BitmapText> image;
+       std::list<StringText> text;
+};
+
+#endif
index 6c7fd7f..a558932 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "playlist.h"
 #include "video_content.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include "ffmpeg_decoder.h"
 #include "ffmpeg_content.h"
 #include "image_decoder.h"
@@ -156,7 +156,7 @@ Playlist::video_identifier () const
 
        BOOST_FOREACH (shared_ptr<const Content> i, _content) {
                bool burn = false;
-               BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+               BOOST_FOREACH (shared_ptr<TextContent> j, i->caption) {
                        if (j->burn()) {
                                burn = true;
                        }
index 1836b65..7f5572c 100644 (file)
@@ -545,7 +545,7 @@ ReelWriter::write (shared_ptr<const AudioBuffers> audio)
 }
 
 void
-ReelWriter::write (PlayerCaption subs, CaptionType type, DCPTimePeriod period)
+ReelWriter::write (PlayerText subs, TextType type, DCPTimePeriod period)
 {
        if (!_caption_asset[type]) {
                string lang = _film->subtitle_language ();
@@ -573,14 +573,14 @@ ReelWriter::write (PlayerCaption subs, CaptionType type, DCPTimePeriod period)
                }
        }
 
-       BOOST_FOREACH (TextCaption i, subs.text) {
+       BOOST_FOREACH (StringText i, subs.text) {
                /* XXX: couldn't / shouldn't we use period here rather than getting time from the subtitle? */
                i.set_in  (i.in()  - dcp::Time (_period.from.seconds(), i.in().tcr));
                i.set_out (i.out() - dcp::Time (_period.from.seconds(), i.out().tcr));
                _caption_asset[type]->add (shared_ptr<dcp::Subtitle>(new dcp::SubtitleString(i)));
        }
 
-       BOOST_FOREACH (BitmapCaption i, subs.image) {
+       BOOST_FOREACH (BitmapText i, subs.image) {
                _caption_asset[type]->add (
                        shared_ptr<dcp::Subtitle>(
                                new dcp::SubtitleImage(
index 24a5e9a..8007b7f 100644 (file)
@@ -21,7 +21,7 @@
 #include "types.h"
 #include "dcpomatic_time.h"
 #include "referenced_reel_asset.h"
-#include "player_caption.h"
+#include "player_text.h"
 #include <dcp/picture_asset_writer.h>
 #include <boost/shared_ptr.hpp>
 
@@ -60,7 +60,7 @@ public:
        void fake_write (Frame frame, Eyes eyes, int size);
        void repeat_write (Frame frame, Eyes eyes);
        void write (boost::shared_ptr<const AudioBuffers> audio);
-       void write (PlayerCaption text, CaptionType type, DCPTimePeriod period);
+       void write (PlayerText text, TextType type, DCPTimePeriod period);
 
        void finish ();
        boost::shared_ptr<dcp::Reel> create_reel (std::list<ReferencedReelAsset> const & refs, std::list<boost::shared_ptr<Font> > const & fonts);
index 63ff2b7..cbc19ef 100644 (file)
@@ -51,11 +51,11 @@ static FcConfig* fc_config = 0;
 static list<pair<FontFiles, string> > fc_config_fonts;
 
 string
-marked_up (list<TextCaption> subtitles, int target_height, float fade_factor)
+marked_up (list<StringText> subtitles, int target_height, float fade_factor)
 {
        string out;
 
-       BOOST_FOREACH (TextCaption const & i, subtitles) {
+       BOOST_FOREACH (StringText const & i, subtitles) {
                out += "<span ";
                if (i.italic()) {
                        out += "style=\"italic\" ";
@@ -91,7 +91,7 @@ set_source_rgba (Cairo::RefPtr<Cairo::Context> context, dcp::Colour colour, floa
  *  at the same time and with the same fade in/out.
  */
 static PositionImage
-render_line (list<TextCaption> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time, int frame_rate)
+render_line (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time, int frame_rate)
 {
        /* XXX: this method can only handle italic / bold changes mid-line,
           nothing else yet.
@@ -365,12 +365,12 @@ render_line (list<TextCaption> subtitles, list<shared_ptr<Font> > fonts, dcp::Si
  *  @param frame_rate DCP frame rate.
  */
 list<PositionImage>
-render_text (list<TextCaption> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time, int frame_rate)
+render_text (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time, int frame_rate)
 {
-       list<TextCaption> pending;
+       list<StringText> pending;
        list<PositionImage> images;
 
-       BOOST_FOREACH (TextCaption const & i, subtitles) {
+       BOOST_FOREACH (StringText const & i, subtitles) {
                if (!pending.empty() && fabs (i.v_position() - pending.back().v_position()) > 1e-4) {
                        images.push_back (render_line (pending, fonts, target, time, frame_rate));
                        pending.clear ();
index 0a8e97b..99b4391 100644 (file)
 
 #include "position_image.h"
 #include "dcpomatic_time.h"
-#include "text_caption.h"
+#include "string_text.h"
 #include <dcp/util.h>
 
 class Font;
 
-std::string marked_up (std::list<TextCaption> subtitles, int target_height, float fade_factor);
+std::string marked_up (std::list<StringText> subtitles, int target_height, float fade_factor);
 std::list<PositionImage> render_text (
-       std::list<TextCaption>, std::list<boost::shared_ptr<Font> > fonts, dcp::Size, DCPTime, int
+       std::list<StringText>, std::list<boost::shared_ptr<Font> > fonts, dcp::Size, DCPTime, int
        );
diff --git a/src/lib/string_text.h b/src/lib/string_text.h
new file mode 100644 (file)
index 0000000..feef90d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    Copyright (C) 2016-2018 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_PLAIN_TEXT_H
+#define DCPOMATIC_PLAIN_TEXT_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.
+ */
+class StringText : public dcp::SubtitleString
+{
+public:
+       explicit StringText (dcp::SubtitleString dcp_)
+               : dcp::SubtitleString (dcp_)
+               , outline_width (2)
+       {}
+
+       StringText (dcp::SubtitleString dcp_, int outline_width_)
+               : dcp::SubtitleString (dcp_)
+               , outline_width (outline_width_)
+       {}
+
+       int outline_width;
+};
+
+#endif
diff --git a/src/lib/string_text_file.cc b/src/lib/string_text_file.cc
new file mode 100644 (file)
index 0000000..c7eadbb
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+    Copyright (C) 2014-2018 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 "string_text_file.h"
+#include "cross.h"
+#include "exceptions.h"
+#include "string_text_file_content.h"
+#include <sub/subrip_reader.h>
+#include <sub/ssa_reader.h>
+#include <sub/collect.h>
+#include <unicode/ucsdet.h>
+#include <unicode/ucnv.h>
+#include <iostream>
+
+#include "i18n.h"
+
+using std::vector;
+using std::cout;
+using std::string;
+using boost::shared_ptr;
+using boost::scoped_array;
+using boost::optional;
+using dcp::Data;
+
+StringTextFile::StringTextFile (shared_ptr<const StringTextFileContent> content)
+{
+       Data in (content->path (0));
+
+       UErrorCode status = U_ZERO_ERROR;
+       UCharsetDetector* detector = ucsdet_open (&status);
+       ucsdet_setText (detector, reinterpret_cast<const char *> (in.data().get()), in.size(), &status);
+
+       UCharsetMatch const * match = ucsdet_detect (detector, &status);
+       char const * in_charset = ucsdet_getName (match, &status);
+
+       UConverter* to_utf16 = ucnv_open (in_charset, &status);
+       /* This is a guess; I think we should be able to encode any input in 4 times its input size */
+       scoped_array<uint16_t> utf16 (new uint16_t[in.size() * 2]);
+       int const utf16_len = ucnv_toUChars (
+               to_utf16, reinterpret_cast<UChar*>(utf16.get()), in.size() * 2,
+               reinterpret_cast<const char *> (in.data().get()), in.size(),
+               &status
+               );
+
+       UConverter* to_utf8 = ucnv_open ("UTF-8", &status);
+       /* Another guess */
+       scoped_array<char> utf8 (new char[utf16_len * 2]);
+       ucnv_fromUChars (to_utf8, utf8.get(), utf16_len * 2, reinterpret_cast<UChar*>(utf16.get()), utf16_len, &status);
+
+       /* Fix OS X line endings */
+       size_t utf8_len = strlen (utf8.get ());
+       for (size_t i = 0; i < utf8_len; ++i) {
+               if (utf8[i] == '\r' && ((i == utf8_len - 1) || utf8[i + 1] != '\n')) {
+                       utf8[i] = '\n';
+               }
+       }
+
+       ucsdet_close (detector);
+       ucnv_close (to_utf16);
+       ucnv_close (to_utf8);
+
+       sub::Reader* reader = 0;
+
+       string ext = content->path(0).extension().string();
+       transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
+
+       if (ext == ".srt") {
+               reader = new sub::SubripReader (utf8.get());
+       } else if (ext == ".ssa" || ext == ".ass") {
+               reader = new sub::SSAReader (utf8.get());
+       }
+
+       if (reader) {
+               _subtitles = sub::collect<vector<sub::Subtitle> > (reader->subtitles ());
+       }
+
+       delete reader;
+}
+
+/** @return time of first subtitle, if there is one */
+optional<ContentTime>
+StringTextFile::first () const
+{
+       if (_subtitles.empty()) {
+               return optional<ContentTime>();
+       }
+
+       return ContentTime::from_seconds(_subtitles[0].from.all_as_seconds());
+}
+
+ContentTime
+StringTextFile::length () const
+{
+       if (_subtitles.empty ()) {
+               return ContentTime ();
+       }
+
+       return ContentTime::from_seconds (_subtitles.back().to.all_as_seconds ());
+}
diff --git a/src/lib/string_text_file.h b/src/lib/string_text_file.h
new file mode 100644 (file)
index 0000000..0091973
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+    Copyright (C) 2014-2018 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_TEXT_CAPTION_FILE_H
+#define DCPOMATIC_TEXT_CAPTION_FILE_H
+
+#include "dcpomatic_time.h"
+#include <sub/subtitle.h>
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+class StringTextFileContent;
+class plain_text_time_test;
+class plain_text_coordinate_test;
+class plain_text_content_test;
+class plain_text_parse_test;
+
+/** @class StringTextFile
+ *  @brief Base for StringTextFile decoder and examiner.
+ *
+ *  In fact this is sufficient for the examiner, so it's used as-is rather than deriving
+ *  a pointless StringTextFileExaminer.
+ */
+class StringTextFile
+{
+public:
+       explicit StringTextFile (boost::shared_ptr<const StringTextFileContent>);
+
+       boost::optional<ContentTime> first () const;
+       ContentTime length () const;
+
+protected:
+       std::vector<sub::Subtitle> _subtitles;
+};
+
+#endif
diff --git a/src/lib/string_text_file_content.cc b/src/lib/string_text_file_content.cc
new file mode 100644 (file)
index 0000000..6c181bb
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+    Copyright (C) 2014-2018 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 "string_text_file_content.h"
+#include "util.h"
+#include "string_text_file.h"
+#include "film.h"
+#include "font.h"
+#include "text_content.h"
+#include <dcp/raw_convert.h>
+#include <libxml++/libxml++.h>
+#include <iostream>
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+using dcp::raw_convert;
+
+StringTextFileContent::StringTextFileContent (shared_ptr<const Film> film, boost::filesystem::path path)
+       : Content (film, path)
+{
+       caption.push_back (shared_ptr<TextContent> (new TextContent (this, CAPTION_OPEN)));
+}
+
+StringTextFileContent::StringTextFileContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version)
+       : Content (film, node)
+       , _length (node->number_child<ContentTime::Type> ("Length"))
+{
+       caption = TextContent::from_xml (this, node, version);
+}
+
+void
+StringTextFileContent::examine (boost::shared_ptr<Job> job)
+{
+       Content::examine (job);
+       StringTextFile s (shared_from_this ());
+
+       /* Default to turning these subtitles on */
+       only_caption()->set_use (true);
+
+       boost::mutex::scoped_lock lm (_mutex);
+       _length = s.length ();
+       only_caption()->add_font (shared_ptr<Font> (new Font (TEXT_FONT_ID)));
+}
+
+string
+StringTextFileContent::summary () const
+{
+       return path_summary() + " " + _("[subtitles]");
+}
+
+string
+StringTextFileContent::technical_summary () const
+{
+       return Content::technical_summary() + " - " + _("Text subtitles");
+}
+
+void
+StringTextFileContent::as_xml (xmlpp::Node* node, bool with_paths) const
+{
+       node->add_child("Type")->add_child_text ("TextSubtitle");
+       Content::as_xml (node, with_paths);
+
+       if (only_caption()) {
+               only_caption()->as_xml (node);
+       }
+
+       node->add_child("Length")->add_child_text (raw_convert<string> (_length.get ()));
+}
+
+DCPTime
+StringTextFileContent::full_length () const
+{
+       FrameRateChange const frc (active_video_frame_rate(), film()->video_frame_rate ());
+       return DCPTime (_length, frc);
+}
diff --git a/src/lib/string_text_file_content.h b/src/lib/string_text_file_content.h
new file mode 100644 (file)
index 0000000..c2de86c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    Copyright (C) 2014-2018 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 "content.h"
+
+class Job;
+
+/** @class StringTextFileContent
+ *  @brief A SubRip, SSA or ASS file.
+ */
+class StringTextFileContent : public Content
+{
+public:
+       StringTextFileContent (boost::shared_ptr<const Film>, boost::filesystem::path);
+       StringTextFileContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
+
+       boost::shared_ptr<StringTextFileContent> shared_from_this () {
+               return boost::dynamic_pointer_cast<StringTextFileContent> (Content::shared_from_this ());
+       }
+
+       void examine (boost::shared_ptr<Job>);
+       std::string summary () const;
+       std::string technical_summary () const;
+       void as_xml (xmlpp::Node *, bool with_paths) const;
+       DCPTime full_length () const;
+
+private:
+       ContentTime _length;
+};
diff --git a/src/lib/string_text_file_decoder.cc b/src/lib/string_text_file_decoder.cc
new file mode 100644 (file)
index 0000000..a81f259
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+    Copyright (C) 2014-2018 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 "string_text_file_decoder.h"
+#include "string_text_file_content.h"
+#include "text_content.h"
+#include "text_decoder.h"
+#include <dcp/subtitle_string.h>
+#include <boost/foreach.hpp>
+#include <iostream>
+
+using std::list;
+using std::vector;
+using std::string;
+using std::cout;
+using std::max;
+using boost::shared_ptr;
+using boost::optional;
+using boost::dynamic_pointer_cast;
+
+StringTextFileDecoder::StringTextFileDecoder (shared_ptr<const StringTextFileContent> content, shared_ptr<Log> log)
+       : StringTextFile (content)
+       , _next (0)
+{
+       ContentTime first;
+       if (!_subtitles.empty()) {
+               first = content_time_period(_subtitles[0]).from;
+       }
+       caption.push_back (shared_ptr<TextDecoder> (new TextDecoder (this, content->only_caption(), log, first)));
+}
+
+void
+StringTextFileDecoder::seek (ContentTime time, bool accurate)
+{
+       /* It's worth back-tracking a little here as decoding is cheap and it's nice if we don't miss
+          too many subtitles when seeking.
+       */
+       time -= ContentTime::from_seconds (5);
+       if (time < ContentTime()) {
+               time = ContentTime();
+       }
+
+       Decoder::seek (time, accurate);
+
+       _next = 0;
+       while (_next < _subtitles.size() && ContentTime::from_seconds (_subtitles[_next].from.all_as_seconds ()) < time) {
+               ++_next;
+       }
+}
+
+bool
+StringTextFileDecoder::pass ()
+{
+       if (_next >= _subtitles.size ()) {
+               return true;
+       }
+
+       ContentTimePeriod const p = content_time_period (_subtitles[_next]);
+       only_caption()->emit_plain (p, _subtitles[_next]);
+
+       ++_next;
+       return false;
+}
+
+ContentTimePeriod
+StringTextFileDecoder::content_time_period (sub::Subtitle s) const
+{
+       return ContentTimePeriod (
+               ContentTime::from_seconds (s.from.all_as_seconds()),
+               ContentTime::from_seconds (s.to.all_as_seconds())
+               );
+}
diff --git a/src/lib/string_text_file_decoder.h b/src/lib/string_text_file_decoder.h
new file mode 100644 (file)
index 0000000..6a9552b
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    Copyright (C) 2014-2018 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_TEXT_CAPTION_FILE_DECODER_H
+#define DCPOMATIC_TEXT_CAPTION_FILE_DECODER_H
+
+#include "string_text_file.h"
+#include "decoder.h"
+
+class StringTextFileContent;
+class Log;
+
+class StringTextFileDecoder : public Decoder, public StringTextFile
+{
+public:
+       StringTextFileDecoder (boost::shared_ptr<const StringTextFileContent>, boost::shared_ptr<Log> log);
+
+       void seek (ContentTime time, bool accurate);
+       bool pass ();
+
+private:
+       ContentTimePeriod content_time_period (sub::Subtitle s) const;
+
+       size_t _next;
+};
+
+#endif
diff --git a/src/lib/text_caption.h b/src/lib/text_caption.h
deleted file mode 100644 (file)
index b4400f5..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-    Copyright (C) 2016-2018 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_PLAIN_TEXT_H
-#define DCPOMATIC_PLAIN_TEXT_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.
- */
-class TextCaption : public dcp::SubtitleString
-{
-public:
-       explicit TextCaption (dcp::SubtitleString dcp_)
-               : dcp::SubtitleString (dcp_)
-               , outline_width (2)
-       {}
-
-       TextCaption (dcp::SubtitleString dcp_, int outline_width_)
-               : dcp::SubtitleString (dcp_)
-               , outline_width (outline_width_)
-       {}
-
-       int outline_width;
-};
-
-#endif
diff --git a/src/lib/text_caption_file.cc b/src/lib/text_caption_file.cc
deleted file mode 100644 (file)
index 11d4517..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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 "text_caption_file.h"
-#include "cross.h"
-#include "exceptions.h"
-#include "text_caption_file_content.h"
-#include <sub/subrip_reader.h>
-#include <sub/ssa_reader.h>
-#include <sub/collect.h>
-#include <unicode/ucsdet.h>
-#include <unicode/ucnv.h>
-#include <iostream>
-
-#include "i18n.h"
-
-using std::vector;
-using std::cout;
-using std::string;
-using boost::shared_ptr;
-using boost::scoped_array;
-using boost::optional;
-using dcp::Data;
-
-TextCaptionFile::TextCaptionFile (shared_ptr<const TextCaptionFileContent> content)
-{
-       Data in (content->path (0));
-
-       UErrorCode status = U_ZERO_ERROR;
-       UCharsetDetector* detector = ucsdet_open (&status);
-       ucsdet_setText (detector, reinterpret_cast<const char *> (in.data().get()), in.size(), &status);
-
-       UCharsetMatch const * match = ucsdet_detect (detector, &status);
-       char const * in_charset = ucsdet_getName (match, &status);
-
-       UConverter* to_utf16 = ucnv_open (in_charset, &status);
-       /* This is a guess; I think we should be able to encode any input in 4 times its input size */
-       scoped_array<uint16_t> utf16 (new uint16_t[in.size() * 2]);
-       int const utf16_len = ucnv_toUChars (
-               to_utf16, reinterpret_cast<UChar*>(utf16.get()), in.size() * 2,
-               reinterpret_cast<const char *> (in.data().get()), in.size(),
-               &status
-               );
-
-       UConverter* to_utf8 = ucnv_open ("UTF-8", &status);
-       /* Another guess */
-       scoped_array<char> utf8 (new char[utf16_len * 2]);
-       ucnv_fromUChars (to_utf8, utf8.get(), utf16_len * 2, reinterpret_cast<UChar*>(utf16.get()), utf16_len, &status);
-
-       /* Fix OS X line endings */
-       size_t utf8_len = strlen (utf8.get ());
-       for (size_t i = 0; i < utf8_len; ++i) {
-               if (utf8[i] == '\r' && ((i == utf8_len - 1) || utf8[i + 1] != '\n')) {
-                       utf8[i] = '\n';
-               }
-       }
-
-       ucsdet_close (detector);
-       ucnv_close (to_utf16);
-       ucnv_close (to_utf8);
-
-       sub::Reader* reader = 0;
-
-       string ext = content->path(0).extension().string();
-       transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
-
-       if (ext == ".srt") {
-               reader = new sub::SubripReader (utf8.get());
-       } else if (ext == ".ssa" || ext == ".ass") {
-               reader = new sub::SSAReader (utf8.get());
-       }
-
-       if (reader) {
-               _subtitles = sub::collect<vector<sub::Subtitle> > (reader->subtitles ());
-       }
-
-       delete reader;
-}
-
-/** @return time of first subtitle, if there is one */
-optional<ContentTime>
-TextCaptionFile::first () const
-{
-       if (_subtitles.empty()) {
-               return optional<ContentTime>();
-       }
-
-       return ContentTime::from_seconds(_subtitles[0].from.all_as_seconds());
-}
-
-ContentTime
-TextCaptionFile::length () const
-{
-       if (_subtitles.empty ()) {
-               return ContentTime ();
-       }
-
-       return ContentTime::from_seconds (_subtitles.back().to.all_as_seconds ());
-}
diff --git a/src/lib/text_caption_file.h b/src/lib/text_caption_file.h
deleted file mode 100644 (file)
index 4a5657a..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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_TEXT_CAPTION_FILE_H
-#define DCPOMATIC_TEXT_CAPTION_FILE_H
-
-#include "dcpomatic_time.h"
-#include <sub/subtitle.h>
-#include <boost/shared_ptr.hpp>
-#include <vector>
-
-class TextCaptionFileContent;
-class plain_text_time_test;
-class plain_text_coordinate_test;
-class plain_text_content_test;
-class plain_text_parse_test;
-
-/** @class TextCaptionFile
- *  @brief Base for TextCaptionFile decoder and examiner.
- *
- *  In fact this is sufficient for the examiner, so it's used as-is rather than deriving
- *  a pointless TextCaptionFileExaminer.
- */
-class TextCaptionFile
-{
-public:
-       explicit TextCaptionFile (boost::shared_ptr<const TextCaptionFileContent>);
-
-       boost::optional<ContentTime> first () const;
-       ContentTime length () const;
-
-protected:
-       std::vector<sub::Subtitle> _subtitles;
-};
-
-#endif
diff --git a/src/lib/text_caption_file_content.cc b/src/lib/text_caption_file_content.cc
deleted file mode 100644 (file)
index aa64ca5..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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 "text_caption_file_content.h"
-#include "util.h"
-#include "text_caption_file.h"
-#include "film.h"
-#include "font.h"
-#include "caption_content.h"
-#include <dcp/raw_convert.h>
-#include <libxml++/libxml++.h>
-#include <iostream>
-
-#include "i18n.h"
-
-using std::string;
-using std::cout;
-using boost::shared_ptr;
-using dcp::raw_convert;
-
-TextCaptionFileContent::TextCaptionFileContent (shared_ptr<const Film> film, boost::filesystem::path path)
-       : Content (film, path)
-{
-       caption.push_back (shared_ptr<CaptionContent> (new CaptionContent (this, CAPTION_OPEN)));
-}
-
-TextCaptionFileContent::TextCaptionFileContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version)
-       : Content (film, node)
-       , _length (node->number_child<ContentTime::Type> ("Length"))
-{
-       caption = CaptionContent::from_xml (this, node, version);
-}
-
-void
-TextCaptionFileContent::examine (boost::shared_ptr<Job> job)
-{
-       Content::examine (job);
-       TextCaptionFile s (shared_from_this ());
-
-       /* Default to turning these subtitles on */
-       only_caption()->set_use (true);
-
-       boost::mutex::scoped_lock lm (_mutex);
-       _length = s.length ();
-       only_caption()->add_font (shared_ptr<Font> (new Font (TEXT_FONT_ID)));
-}
-
-string
-TextCaptionFileContent::summary () const
-{
-       return path_summary() + " " + _("[subtitles]");
-}
-
-string
-TextCaptionFileContent::technical_summary () const
-{
-       return Content::technical_summary() + " - " + _("Text subtitles");
-}
-
-void
-TextCaptionFileContent::as_xml (xmlpp::Node* node, bool with_paths) const
-{
-       node->add_child("Type")->add_child_text ("TextSubtitle");
-       Content::as_xml (node, with_paths);
-
-       if (only_caption()) {
-               only_caption()->as_xml (node);
-       }
-
-       node->add_child("Length")->add_child_text (raw_convert<string> (_length.get ()));
-}
-
-DCPTime
-TextCaptionFileContent::full_length () const
-{
-       FrameRateChange const frc (active_video_frame_rate(), film()->video_frame_rate ());
-       return DCPTime (_length, frc);
-}
diff --git a/src/lib/text_caption_file_content.h b/src/lib/text_caption_file_content.h
deleted file mode 100644 (file)
index f8f4a98..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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 "content.h"
-
-class Job;
-
-/** @class TextCaptionFileContent
- *  @brief A SubRip, SSA or ASS file.
- */
-class TextCaptionFileContent : public Content
-{
-public:
-       TextCaptionFileContent (boost::shared_ptr<const Film>, boost::filesystem::path);
-       TextCaptionFileContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
-
-       boost::shared_ptr<TextCaptionFileContent> shared_from_this () {
-               return boost::dynamic_pointer_cast<TextCaptionFileContent> (Content::shared_from_this ());
-       }
-
-       void examine (boost::shared_ptr<Job>);
-       std::string summary () const;
-       std::string technical_summary () const;
-       void as_xml (xmlpp::Node *, bool with_paths) const;
-       DCPTime full_length () const;
-
-private:
-       ContentTime _length;
-};
diff --git a/src/lib/text_caption_file_decoder.cc b/src/lib/text_caption_file_decoder.cc
deleted file mode 100644 (file)
index 65de6a5..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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 "text_caption_file_decoder.h"
-#include "text_caption_file_content.h"
-#include "caption_content.h"
-#include "caption_decoder.h"
-#include <dcp/subtitle_string.h>
-#include <boost/foreach.hpp>
-#include <iostream>
-
-using std::list;
-using std::vector;
-using std::string;
-using std::cout;
-using std::max;
-using boost::shared_ptr;
-using boost::optional;
-using boost::dynamic_pointer_cast;
-
-TextCaptionFileDecoder::TextCaptionFileDecoder (shared_ptr<const TextCaptionFileContent> content, shared_ptr<Log> log)
-       : TextCaptionFile (content)
-       , _next (0)
-{
-       ContentTime first;
-       if (!_subtitles.empty()) {
-               first = content_time_period(_subtitles[0]).from;
-       }
-       caption.push_back (shared_ptr<CaptionDecoder> (new CaptionDecoder (this, content->only_caption(), log, first)));
-}
-
-void
-TextCaptionFileDecoder::seek (ContentTime time, bool accurate)
-{
-       /* It's worth back-tracking a little here as decoding is cheap and it's nice if we don't miss
-          too many subtitles when seeking.
-       */
-       time -= ContentTime::from_seconds (5);
-       if (time < ContentTime()) {
-               time = ContentTime();
-       }
-
-       Decoder::seek (time, accurate);
-
-       _next = 0;
-       while (_next < _subtitles.size() && ContentTime::from_seconds (_subtitles[_next].from.all_as_seconds ()) < time) {
-               ++_next;
-       }
-}
-
-bool
-TextCaptionFileDecoder::pass ()
-{
-       if (_next >= _subtitles.size ()) {
-               return true;
-       }
-
-       ContentTimePeriod const p = content_time_period (_subtitles[_next]);
-       only_caption()->emit_plain (p, _subtitles[_next]);
-
-       ++_next;
-       return false;
-}
-
-ContentTimePeriod
-TextCaptionFileDecoder::content_time_period (sub::Subtitle s) const
-{
-       return ContentTimePeriod (
-               ContentTime::from_seconds (s.from.all_as_seconds()),
-               ContentTime::from_seconds (s.to.all_as_seconds())
-               );
-}
diff --git a/src/lib/text_caption_file_decoder.h b/src/lib/text_caption_file_decoder.h
deleted file mode 100644 (file)
index 7697775..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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_TEXT_CAPTION_FILE_DECODER_H
-#define DCPOMATIC_TEXT_CAPTION_FILE_DECODER_H
-
-#include "text_caption_file.h"
-#include "decoder.h"
-
-class TextCaptionFileContent;
-class Log;
-
-class TextCaptionFileDecoder : public Decoder, public TextCaptionFile
-{
-public:
-       TextCaptionFileDecoder (boost::shared_ptr<const TextCaptionFileContent>, boost::shared_ptr<Log> log);
-
-       void seek (ContentTime time, bool accurate);
-       bool pass ();
-
-private:
-       ContentTimePeriod content_time_period (sub::Subtitle s) const;
-
-       size_t _next;
-};
-
-#endif
diff --git a/src/lib/text_content.cc b/src/lib/text_content.cc
new file mode 100644 (file)
index 0000000..fbc7d82
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+    Copyright (C) 2013-2018 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 "text_content.h"
+#include "util.h"
+#include "exceptions.h"
+#include "font.h"
+#include "content.h"
+#include <dcp/raw_convert.h>
+#include <libcxml/cxml.h>
+#include <libxml++/libxml++.h>
+#include <boost/foreach.hpp>
+#include <iostream>
+
+#include "i18n.h"
+
+using std::string;
+using std::vector;
+using std::cout;
+using std::list;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using boost::optional;
+using dcp::raw_convert;
+
+int const TextContentProperty::X_OFFSET = 500;
+int const TextContentProperty::Y_OFFSET = 501;
+int const TextContentProperty::X_SCALE = 502;
+int const TextContentProperty::Y_SCALE = 503;
+int const TextContentProperty::USE = 504;
+int const TextContentProperty::BURN = 505;
+int const TextContentProperty::LANGUAGE = 506;
+int const TextContentProperty::FONTS = 507;
+int const TextContentProperty::COLOUR = 508;
+int const TextContentProperty::EFFECT = 509;
+int const TextContentProperty::EFFECT_COLOUR = 510;
+int const TextContentProperty::LINE_SPACING = 511;
+int const TextContentProperty::FADE_IN = 512;
+int const TextContentProperty::FADE_OUT = 513;
+int const TextContentProperty::OUTLINE_WIDTH = 514;
+int const TextContentProperty::TYPE = 515;
+
+TextContent::TextContent (Content* parent, TextType original_type)
+       : ContentPart (parent)
+       , _use (false)
+       , _burn (false)
+       , _x_offset (0)
+       , _y_offset (0)
+       , _x_scale (1)
+       , _y_scale (1)
+       , _line_spacing (1)
+       , _outline_width (2)
+       , _type (original_type)
+       , _original_type (original_type)
+{
+
+}
+
+/** @return TextContents from node or <Caption> nodes under node (according to version).
+ *  The list could be empty if no TextContents are found.
+ */
+list<shared_ptr<TextContent> >
+TextContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version)
+{
+       if (version < 34) {
+               /* With old metadata FFmpeg content has the subtitle-related tags even with no
+                  subtitle streams, so check for that.
+               */
+               if (node->string_child("Type") == "FFmpeg" && node->node_children("SubtitleStream").empty()) {
+                       return list<shared_ptr<TextContent> >();
+               }
+
+               /* Otherwise we can drop through to the newer logic */
+       }
+
+       if (version < 37) {
+               if (!node->optional_number_child<double>("SubtitleXOffset") && !node->optional_number_child<double>("SubtitleOffset")) {
+                       return list<shared_ptr<TextContent> >();
+               }
+               list<shared_ptr<TextContent> > c;
+               c.push_back (shared_ptr<TextContent> (new TextContent (parent, node, version)));
+               return c;
+       }
+
+       if (!node->optional_node_child("Caption")) {
+               return list<shared_ptr<TextContent> >();
+       }
+
+       list<shared_ptr<TextContent> > c;
+       BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children("Caption")) {
+               c.push_back (shared_ptr<TextContent> (new TextContent (parent, i, version)));
+       }
+       return c;
+}
+
+TextContent::TextContent (Content* parent, cxml::ConstNodePtr node, int version)
+       : ContentPart (parent)
+       , _use (false)
+       , _burn (false)
+       , _x_offset (0)
+       , _y_offset (0)
+       , _x_scale (1)
+       , _y_scale (1)
+       , _line_spacing (node->optional_number_child<double>("LineSpacing").get_value_or (1))
+       , _outline_width (node->optional_number_child<int>("OutlineWidth").get_value_or (2))
+       , _type (CAPTION_OPEN)
+       , _original_type (CAPTION_OPEN)
+{
+       if (version >= 37) {
+               _use = node->bool_child ("Use");
+               _burn = node->bool_child ("Burn");
+       } else if (version >= 32) {
+               _use = node->bool_child ("UseSubtitles");
+               _burn = node->bool_child ("BurnSubtitles");
+       }
+
+       if (version >= 37) {
+               _x_offset = node->number_child<double> ("XOffset");
+               _y_offset = node->number_child<double> ("YOffset");
+       } else if (version >= 7) {
+               _x_offset = node->number_child<double> ("SubtitleXOffset");
+               _y_offset = node->number_child<double> ("SubtitleYOffset");
+       } else {
+               _y_offset = node->number_child<double> ("SubtitleOffset");
+       }
+
+       if (node->optional_bool_child("Outline").get_value_or(false)) {
+               _effect = dcp::BORDER;
+       } else if (node->optional_bool_child("Shadow").get_value_or(false)) {
+               _effect = dcp::SHADOW;
+       } else {
+               _effect = dcp::NONE;
+       }
+
+       optional<string> effect = node->optional_string_child("Effect");
+       if (effect) {
+               if (*effect == "none") {
+                       _effect = dcp::NONE;
+               } else if (*effect == "outline") {
+                       _effect = dcp::BORDER;
+               } else if (*effect == "shadow") {
+                       _effect = dcp::SHADOW;
+               }
+       }
+
+       if (version >= 37) {
+               _x_scale = node->number_child<double> ("XScale");
+               _y_scale = node->number_child<double> ("YScale");
+       } else if (version >= 10) {
+               _x_scale = node->number_child<double> ("SubtitleXScale");
+               _y_scale = node->number_child<double> ("SubtitleYScale");
+       } else {
+               _x_scale = _y_scale = node->number_child<double> ("SubtitleScale");
+       }
+
+       optional<int> r = node->optional_number_child<int>("Red");
+       optional<int> g = node->optional_number_child<int>("Green");
+       optional<int> b = node->optional_number_child<int>("Blue");
+       if (r && g && b) {
+               _colour = dcp::Colour (*r, *g, *b);
+       }
+
+       if (version >= 36) {
+               optional<int> er = node->optional_number_child<int>("EffectRed");
+               optional<int> eg = node->optional_number_child<int>("EffectGreen");
+               optional<int> eb = node->optional_number_child<int>("EffectBlue");
+               if (er && eg && eb) {
+                       _effect_colour = dcp::Colour (*er, *eg, *eb);
+               }
+       } else {
+               _effect_colour = dcp::Colour (
+                       node->optional_number_child<int>("OutlineRed").get_value_or(255),
+                       node->optional_number_child<int>("OutlineGreen").get_value_or(255),
+                       node->optional_number_child<int>("OutlineBlue").get_value_or(255)
+                       );
+       }
+
+       optional<Frame> fi;
+       if (version >= 37) {
+               fi = node->optional_number_child<Frame>("FadeIn");
+       } else {
+               fi = node->optional_number_child<Frame>("SubtitleFadeIn");
+       }
+       if (fi) {
+               _fade_in = ContentTime (*fi);
+       }
+
+       optional<Frame> fo;
+       if (version >= 37) {
+               fo = node->optional_number_child<Frame>("FadeOut");
+       } else {
+               fo = node->optional_number_child<Frame>("SubtitleFadeOut");
+       }
+       if (fo) {
+               _fade_out = ContentTime (*fo);
+       }
+
+       if (version >= 37) {
+               _language = node->optional_string_child ("Language").get_value_or ("");
+       } else {
+               _language = node->optional_string_child ("SubtitleLanguage").get_value_or ("");
+       }
+
+       list<cxml::NodePtr> fonts = node->node_children ("Font");
+       for (list<cxml::NodePtr>::const_iterator i = fonts.begin(); i != fonts.end(); ++i) {
+               _fonts.push_back (shared_ptr<Font> (new Font (*i)));
+       }
+
+       connect_to_fonts ();
+
+       _type = string_to_caption_type (node->optional_string_child("Type").get_value_or("open"));
+       _original_type = string_to_caption_type (node->optional_string_child("OriginalType").get_value_or("open"));
+}
+
+TextContent::TextContent (Content* parent, vector<shared_ptr<Content> > c)
+       : ContentPart (parent)
+{
+       /* This constructor is for join which is only supported for content types
+          that have a single caption, so we can use only_caption() here.
+       */
+       shared_ptr<TextContent> ref = c[0]->only_caption();
+       DCPOMATIC_ASSERT (ref);
+       list<shared_ptr<Font> > ref_fonts = ref->fonts ();
+
+       for (size_t i = 1; i < c.size(); ++i) {
+
+               if (c[i]->only_caption()->use() != ref->use()) {
+                       throw JoinError (_("Content to be joined must have the same 'use subtitles' setting."));
+               }
+
+               if (c[i]->only_caption()->burn() != ref->burn()) {
+                       throw JoinError (_("Content to be joined must have the same 'burn subtitles' setting."));
+               }
+
+               if (c[i]->only_caption()->x_offset() != ref->x_offset()) {
+                       throw JoinError (_("Content to be joined must have the same subtitle X offset."));
+               }
+
+               if (c[i]->only_caption()->y_offset() != ref->y_offset()) {
+                       throw JoinError (_("Content to be joined must have the same subtitle Y offset."));
+               }
+
+               if (c[i]->only_caption()->x_scale() != ref->x_scale()) {
+                       throw JoinError (_("Content to be joined must have the same subtitle X scale."));
+               }
+
+               if (c[i]->only_caption()->y_scale() != ref->y_scale()) {
+                       throw JoinError (_("Content to be joined must have the same subtitle Y scale."));
+               }
+
+               if (c[i]->only_caption()->line_spacing() != ref->line_spacing()) {
+                       throw JoinError (_("Content to be joined must have the same subtitle line spacing."));
+               }
+
+               if ((c[i]->only_caption()->fade_in() != ref->fade_in()) || (c[i]->only_caption()->fade_out() != ref->fade_out())) {
+                       throw JoinError (_("Content to be joined must have the same subtitle fades."));
+               }
+
+               if ((c[i]->only_caption()->outline_width() != ref->outline_width())) {
+                       throw JoinError (_("Content to be joined must have the same outline width."));
+               }
+
+               list<shared_ptr<Font> > fonts = c[i]->only_caption()->fonts ();
+               if (fonts.size() != ref_fonts.size()) {
+                       throw JoinError (_("Content to be joined must use the same fonts."));
+               }
+
+               list<shared_ptr<Font> >::const_iterator j = ref_fonts.begin ();
+               list<shared_ptr<Font> >::const_iterator k = fonts.begin ();
+
+               while (j != ref_fonts.end ()) {
+                       if (**j != **k) {
+                               throw JoinError (_("Content to be joined must use the same fonts."));
+                       }
+                       ++j;
+                       ++k;
+               }
+       }
+
+       _use = ref->use ();
+       _burn = ref->burn ();
+       _x_offset = ref->x_offset ();
+       _y_offset = ref->y_offset ();
+       _x_scale = ref->x_scale ();
+       _y_scale = ref->y_scale ();
+       _language = ref->language ();
+       _fonts = ref_fonts;
+       _line_spacing = ref->line_spacing ();
+       _fade_in = ref->fade_in ();
+       _fade_out = ref->fade_out ();
+       _outline_width = ref->outline_width ();
+       _type = ref->type ();
+       _original_type = ref->original_type ();
+
+       connect_to_fonts ();
+}
+
+/** _mutex must not be held on entry */
+void
+TextContent::as_xml (xmlpp::Node* root) const
+{
+       boost::mutex::scoped_lock lm (_mutex);
+
+       xmlpp::Element* caption = root->add_child ("Caption");
+
+       caption->add_child("Use")->add_child_text (_use ? "1" : "0");
+       caption->add_child("Burn")->add_child_text (_burn ? "1" : "0");
+       caption->add_child("XOffset")->add_child_text (raw_convert<string> (_x_offset));
+       caption->add_child("YOffset")->add_child_text (raw_convert<string> (_y_offset));
+       caption->add_child("XScale")->add_child_text (raw_convert<string> (_x_scale));
+       caption->add_child("YScale")->add_child_text (raw_convert<string> (_y_scale));
+       caption->add_child("Language")->add_child_text (_language);
+       if (_colour) {
+               caption->add_child("Red")->add_child_text (raw_convert<string> (_colour->r));
+               caption->add_child("Green")->add_child_text (raw_convert<string> (_colour->g));
+               caption->add_child("Blue")->add_child_text (raw_convert<string> (_colour->b));
+       }
+       if (_effect) {
+               switch (*_effect) {
+               case dcp::NONE:
+                       caption->add_child("Effect")->add_child_text("none");
+                       break;
+               case dcp::BORDER:
+                       caption->add_child("Effect")->add_child_text("outline");
+                       break;
+               case dcp::SHADOW:
+                       caption->add_child("Effect")->add_child_text("shadow");
+                       break;
+               }
+       }
+       if (_effect_colour) {
+               caption->add_child("EffectRed")->add_child_text (raw_convert<string> (_effect_colour->r));
+               caption->add_child("EffectGreen")->add_child_text (raw_convert<string> (_effect_colour->g));
+               caption->add_child("EffectBlue")->add_child_text (raw_convert<string> (_effect_colour->b));
+       }
+       caption->add_child("LineSpacing")->add_child_text (raw_convert<string> (_line_spacing));
+       if (_fade_in) {
+               caption->add_child("FadeIn")->add_child_text (raw_convert<string> (_fade_in->get()));
+       }
+       if (_fade_out) {
+               caption->add_child("FadeOut")->add_child_text (raw_convert<string> (_fade_out->get()));
+       }
+       caption->add_child("OutlineWidth")->add_child_text (raw_convert<string> (_outline_width));
+
+       for (list<shared_ptr<Font> >::const_iterator i = _fonts.begin(); i != _fonts.end(); ++i) {
+               (*i)->as_xml (caption->add_child("Font"));
+       }
+
+       caption->add_child("Type")->add_child_text (caption_type_to_string(_type));
+       caption->add_child("OriginalType")->add_child_text (caption_type_to_string(_original_type));
+}
+
+string
+TextContent::identifier () const
+{
+       string s = raw_convert<string> (x_scale())
+               + "_" + raw_convert<string> (y_scale())
+               + "_" + raw_convert<string> (x_offset())
+               + "_" + raw_convert<string> (y_offset())
+               + "_" + raw_convert<string> (line_spacing())
+               + "_" + raw_convert<string> (fade_in().get_value_or(ContentTime()).get())
+               + "_" + raw_convert<string> (fade_out().get_value_or(ContentTime()).get())
+               + "_" + raw_convert<string> (outline_width())
+               + "_" + raw_convert<string> (colour().get_value_or(dcp::Colour(255, 255, 255)).to_argb_string())
+               + "_" + raw_convert<string> (dcp::effect_to_string(effect().get_value_or(dcp::NONE)))
+               + "_" + raw_convert<string> (effect_colour().get_value_or(dcp::Colour(0, 0, 0)).to_argb_string());
+
+       /* XXX: I suppose really _fonts shouldn't be in here, since not all
+          types of subtitle content involve fonts.
+       */
+       BOOST_FOREACH (shared_ptr<Font> f, _fonts) {
+               for (int i = 0; i < FontFiles::VARIANTS; ++i) {
+                       s += "_" + f->file(static_cast<FontFiles::Variant>(i)).get_value_or("Default").string();
+               }
+       }
+
+       /* The language is for metadata only, and doesn't affect
+          how this content looks.
+       */
+
+       return s;
+}
+
+void
+TextContent::add_font (shared_ptr<Font> font)
+{
+       _fonts.push_back (font);
+       connect_to_fonts ();
+}
+
+void
+TextContent::connect_to_fonts ()
+{
+       BOOST_FOREACH (boost::signals2::connection& i, _font_connections) {
+               i.disconnect ();
+       }
+
+       _font_connections.clear ();
+
+       BOOST_FOREACH (shared_ptr<Font> i, _fonts) {
+               _font_connections.push_back (i->Changed.connect (boost::bind (&TextContent::font_changed, this)));
+       }
+}
+
+void
+TextContent::font_changed ()
+{
+       _parent->signal_changed (TextContentProperty::FONTS);
+}
+
+void
+TextContent::set_colour (dcp::Colour colour)
+{
+       maybe_set (_colour, colour, TextContentProperty::COLOUR);
+}
+
+void
+TextContent::unset_colour ()
+{
+       maybe_set (_colour, optional<dcp::Colour>(), TextContentProperty::COLOUR);
+}
+
+void
+TextContent::set_effect (dcp::Effect e)
+{
+       maybe_set (_effect, e, TextContentProperty::EFFECT);
+}
+
+void
+TextContent::unset_effect ()
+{
+       maybe_set (_effect, optional<dcp::Effect>(), TextContentProperty::EFFECT);
+}
+
+void
+TextContent::set_effect_colour (dcp::Colour colour)
+{
+       maybe_set (_effect_colour, colour, TextContentProperty::EFFECT_COLOUR);
+}
+
+void
+TextContent::unset_effect_colour ()
+{
+       maybe_set (_effect_colour, optional<dcp::Colour>(), TextContentProperty::EFFECT_COLOUR);
+}
+
+void
+TextContent::set_use (bool u)
+{
+       maybe_set (_use, u, TextContentProperty::USE);
+}
+
+void
+TextContent::set_burn (bool b)
+{
+       maybe_set (_burn, b, TextContentProperty::BURN);
+}
+
+void
+TextContent::set_x_offset (double o)
+{
+       maybe_set (_x_offset, o, TextContentProperty::X_OFFSET);
+}
+
+void
+TextContent::set_y_offset (double o)
+{
+       maybe_set (_y_offset, o, TextContentProperty::Y_OFFSET);
+}
+
+void
+TextContent::set_x_scale (double s)
+{
+       maybe_set (_x_scale, s, TextContentProperty::X_SCALE);
+}
+
+void
+TextContent::set_y_scale (double s)
+{
+       maybe_set (_y_scale, s, TextContentProperty::Y_SCALE);
+}
+
+void
+TextContent::set_language (string language)
+{
+       maybe_set (_language, language, TextContentProperty::LANGUAGE);
+}
+
+void
+TextContent::set_line_spacing (double s)
+{
+       maybe_set (_line_spacing, s, TextContentProperty::LINE_SPACING);
+}
+
+void
+TextContent::set_fade_in (ContentTime t)
+{
+       maybe_set (_fade_in, t, TextContentProperty::FADE_IN);
+}
+
+void
+TextContent::unset_fade_in ()
+{
+       maybe_set (_fade_in, optional<ContentTime>(), TextContentProperty::FADE_IN);
+}
+
+void
+TextContent::set_fade_out (ContentTime t)
+{
+       maybe_set (_fade_out, t, TextContentProperty::FADE_OUT);
+}
+
+void
+TextContent::unset_fade_out ()
+{
+       maybe_set (_fade_out, optional<ContentTime>(), TextContentProperty::FADE_OUT);
+}
+
+void
+TextContent::set_type (TextType type)
+{
+       maybe_set (_type, type, TextContentProperty::TYPE);
+}
+
+void
+TextContent::set_outline_width (int w)
+{
+       maybe_set (_outline_width, w, TextContentProperty::OUTLINE_WIDTH);
+}
+
+void
+TextContent::take_settings_from (shared_ptr<const TextContent> c)
+{
+       set_use (c->_use);
+       set_burn (c->_burn);
+       set_x_offset (c->_x_offset);
+       set_y_offset (c->_y_offset);
+       set_x_scale (c->_x_scale);
+       set_y_scale (c->_y_scale);
+       maybe_set (_fonts, c->_fonts, TextContentProperty::FONTS);
+       if (c->_colour) {
+               set_colour (*c->_colour);
+       } else {
+               unset_colour ();
+       }
+       if (c->_effect) {
+               set_effect (*c->_effect);
+       }
+       if (c->_effect_colour) {
+               set_effect_colour (*c->_effect_colour);
+       } else {
+               unset_effect_colour ();
+       }
+       set_line_spacing (c->_line_spacing);
+       if (c->_fade_in) {
+               set_fade_in (*c->_fade_in);
+       }
+       if (c->_fade_out) {
+               set_fade_out (*c->_fade_out);
+       }
+       set_outline_width (c->_outline_width);
+}
diff --git a/src/lib/text_content.h b/src/lib/text_content.h
new file mode 100644 (file)
index 0000000..5aacc7e
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+    Copyright (C) 2013-2018 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_CAPTION_CONTENT_H
+#define DCPOMATIC_CAPTION_CONTENT_H
+
+#include "content_part.h"
+#include <libcxml/cxml.h>
+#include <dcp/types.h>
+#include <boost/signals2.hpp>
+
+class Font;
+
+class TextContentProperty
+{
+public:
+       static int const X_OFFSET;
+       static int const Y_OFFSET;
+       static int const X_SCALE;
+       static int const Y_SCALE;
+       static int const USE;
+       static int const BURN;
+       static int const LANGUAGE;
+       static int const FONTS;
+       static int const COLOUR;
+       static int const EFFECT;
+       static int const EFFECT_COLOUR;
+       static int const LINE_SPACING;
+       static int const FADE_IN;
+       static int const FADE_OUT;
+       static int const OUTLINE_WIDTH;
+       static int const TYPE;
+};
+
+/** @class TextContent
+ *  @brief Description of how some text content should be presented.
+ *
+ *  There are `bitmap' subtitles and `plain' subtitles (plain text),
+ *  and not all of the settings in this class correspond to both types.
+ */
+class TextContent : public ContentPart
+{
+public:
+       TextContent (Content* parent, TextType original_type);
+       TextContent (Content* parent, std::vector<boost::shared_ptr<Content> >);
+
+       void as_xml (xmlpp::Node *) const;
+       std::string identifier () const;
+       void take_settings_from (boost::shared_ptr<const TextContent> c);
+
+       void add_font (boost::shared_ptr<Font> font);
+
+       void set_use (bool);
+       void set_burn (bool);
+       void set_x_offset (double);
+       void set_y_offset (double);
+       void set_x_scale (double);
+       void set_y_scale (double);
+       void set_language (std::string language);
+       void set_colour (dcp::Colour);
+       void unset_colour ();
+       void set_effect (dcp::Effect);
+       void unset_effect ();
+       void set_effect_colour (dcp::Colour);
+       void unset_effect_colour ();
+       void set_line_spacing (double s);
+       void set_fade_in (ContentTime);
+       void unset_fade_in ();
+       void set_fade_out (ContentTime);
+       void set_outline_width (int);
+       void unset_fade_out ();
+       void set_type (TextType type);
+
+       bool use () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _use;
+       }
+
+       bool burn () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _burn;
+       }
+
+       double x_offset () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _x_offset;
+       }
+
+       double y_offset () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _y_offset;
+       }
+
+       double x_scale () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _x_scale;
+       }
+
+       double y_scale () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _y_scale;
+       }
+
+       std::list<boost::shared_ptr<Font> > fonts () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _fonts;
+       }
+
+       std::string language () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _language;
+       }
+
+       boost::optional<dcp::Colour> colour () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _colour;
+       }
+
+       boost::optional<dcp::Effect> effect () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _effect;
+       }
+
+       boost::optional<dcp::Colour> effect_colour () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _effect_colour;
+       }
+
+       double line_spacing () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _line_spacing;
+       }
+
+       boost::optional<ContentTime> fade_in () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _fade_in;
+       }
+
+       boost::optional<ContentTime> fade_out () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _fade_out;
+       }
+
+       int outline_width () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _outline_width;
+       }
+
+       TextType type () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _type;
+       }
+
+       TextType original_type () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _original_type;
+       }
+
+       static std::list<boost::shared_ptr<TextContent> > from_xml (Content* parent, cxml::ConstNodePtr, int version);
+
+protected:
+       /** subtitle language (e.g. "German") or empty if it is not known */
+       std::string _language;
+
+private:
+       friend struct ffmpeg_pts_offset_test;
+
+       TextContent (Content* parent, cxml::ConstNodePtr, int version);
+       void font_changed ();
+       void connect_to_fonts ();
+
+       std::list<boost::signals2::connection> _font_connections;
+
+       bool _use;
+       bool _burn;
+       /** x offset for placing subtitles, as a proportion of the container width;
+        * +ve is further right, -ve is further left.
+        */
+       double _x_offset;
+       /** y offset for placing subtitles, as a proportion of the container height;
+        *  +ve is further down the frame, -ve is further up.
+        */
+       double _y_offset;
+       /** x scale factor to apply to subtitles */
+       double _x_scale;
+       /** y scale factor to apply to subtitles */
+       double _y_scale;
+       std::list<boost::shared_ptr<Font> > _fonts;
+       boost::optional<dcp::Colour> _colour;
+       boost::optional<dcp::Effect> _effect;
+       boost::optional<dcp::Colour> _effect_colour;
+       /** scaling factor for line spacing; 1 is "standard", < 1 is closer together, > 1 is further apart */
+       double _line_spacing;
+       boost::optional<ContentTime> _fade_in;
+       boost::optional<ContentTime> _fade_out;
+       int _outline_width;
+       /** what these captions will be used for in the output DCP (not necessarily what
+        *  they were originally).
+        */
+       TextType _type;
+       /** the original type of these captions in their content */
+       TextType _original_type;
+};
+
+#endif
diff --git a/src/lib/text_decoder.cc b/src/lib/text_decoder.cc
new file mode 100644 (file)
index 0000000..3b6a06e
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+    Copyright (C) 2013-2017 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 "text_decoder.h"
+#include "text_content.h"
+#include "util.h"
+#include "log.h"
+#include "compose.hpp"
+#include <sub/subtitle.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/foreach.hpp>
+#include <boost/algorithm/string.hpp>
+#include <iostream>
+
+using std::list;
+using std::cout;
+using std::string;
+using std::min;
+using boost::shared_ptr;
+using boost::optional;
+using boost::function;
+
+TextDecoder::TextDecoder (
+       Decoder* parent,
+       shared_ptr<const TextContent> c,
+       shared_ptr<Log> log,
+       ContentTime first
+       )
+       : DecoderPart (parent, log)
+       , _content (c)
+       , _position (first)
+{
+
+}
+
+/** Called by subclasses when an image subtitle is starting.
+ *  @param from From time of the subtitle.
+ *  @param image Subtitle image.
+ *  @param rect Area expressed as a fraction of the video frame that this subtitle
+ *  is for (e.g. a width of 0.5 means the width of the subtitle is half the width
+ *  of the video frame)
+ */
+void
+TextDecoder::emit_bitmap_start (ContentTime from, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
+{
+       BitmapStart (ContentBitmapText (from, _content->type(), image, rect));
+       _position = from;
+}
+
+void
+TextDecoder::emit_plain_start (ContentTime from, list<dcp::SubtitleString> s)
+{
+       BOOST_FOREACH (dcp::SubtitleString& i, s) {
+               /* We must escape < and > in strings, otherwise they might confuse our subtitle
+                  renderer (which uses some HTML-esque markup to do bold/italic etc.)
+               */
+               string t = i.text ();
+               boost::algorithm::replace_all (t, "<", "&lt;");
+               boost::algorithm::replace_all (t, ">", "&gt;");
+               i.set_text (t);
+
+               /* Set any forced appearance */
+               if (content()->colour()) {
+                       i.set_colour (*content()->colour());
+               }
+               if (content()->effect_colour()) {
+                       i.set_effect_colour (*content()->effect_colour());
+               }
+               if (content()->effect()) {
+                       i.set_effect (*content()->effect());
+               }
+               if (content()->fade_in()) {
+                       i.set_fade_up_time (dcp::Time(content()->fade_in()->seconds(), 1000));
+               }
+               if (content()->fade_out()) {
+                       i.set_fade_down_time (dcp::Time(content()->fade_out()->seconds(), 1000));
+               }
+       }
+
+       PlainStart (ContentStringText (from, _content->type(), s));
+       _position = from;
+}
+
+void
+TextDecoder::emit_plain_start (ContentTime from, sub::Subtitle const & subtitle)
+{
+       /* See if our next subtitle needs to be vertically placed on screen by us */
+       bool needs_placement = false;
+       optional<int> bottom_line;
+       BOOST_FOREACH (sub::Line i, subtitle.lines) {
+               if (!i.vertical_position.reference || i.vertical_position.reference.get() == sub::TOP_OF_SUBTITLE) {
+                       needs_placement = true;
+                       DCPOMATIC_ASSERT (i.vertical_position.line);
+                       if (!bottom_line || bottom_line.get() < i.vertical_position.line.get()) {
+                               bottom_line = i.vertical_position.line.get();
+                       }
+               }
+       }
+
+       /* Find the lowest proportional position */
+       optional<float> lowest_proportional;
+       BOOST_FOREACH (sub::Line i, subtitle.lines) {
+               if (i.vertical_position.proportional) {
+                       if (!lowest_proportional) {
+                               lowest_proportional = i.vertical_position.proportional;
+                       } else {
+                               lowest_proportional = min (lowest_proportional.get(), i.vertical_position.proportional.get());
+                       }
+               }
+       }
+
+       list<dcp::SubtitleString> out;
+       BOOST_FOREACH (sub::Line i, subtitle.lines) {
+               BOOST_FOREACH (sub::Block j, i.blocks) {
+
+                       if (!j.font_size.specified()) {
+                               /* Fallback default font size if no other has been specified */
+                               j.font_size.set_points (48);
+                       }
+
+                       float v_position;
+                       dcp::VAlign v_align;
+                       if (needs_placement) {
+                               DCPOMATIC_ASSERT (i.vertical_position.line);
+                               /* This 1.015 is an arbitrary value to lift the bottom sub off the bottom
+                                  of the screen a bit to a pleasing degree.
+                               */
+                               v_position = 1.015 -
+                                       (1 + bottom_line.get() - i.vertical_position.line.get())
+                                       * 1.2 * content()->line_spacing() * content()->y_scale() * j.font_size.proportional (72 * 11);
+
+                               v_align = dcp::VALIGN_TOP;
+                       } else {
+                               DCPOMATIC_ASSERT (i.vertical_position.proportional);
+                               DCPOMATIC_ASSERT (i.vertical_position.reference);
+                               v_position = i.vertical_position.proportional.get();
+
+                               if (lowest_proportional) {
+                                       /* Adjust line spacing */
+                                       v_position = ((v_position - lowest_proportional.get()) * content()->line_spacing()) + lowest_proportional.get();
+                               }
+
+                               switch (i.vertical_position.reference.get()) {
+                               case sub::TOP_OF_SCREEN:
+                                       v_align = dcp::VALIGN_TOP;
+                                       break;
+                               case sub::VERTICAL_CENTRE_OF_SCREEN:
+                                       v_align = dcp::VALIGN_CENTER;
+                                       break;
+                               case sub::BOTTOM_OF_SCREEN:
+                                       v_align = dcp::VALIGN_BOTTOM;
+                                       break;
+                               default:
+                                       v_align = dcp::VALIGN_TOP;
+                                       break;
+                               }
+                       }
+
+                       dcp::HAlign h_align;
+                       switch (i.horizontal_position.reference) {
+                       case sub::LEFT_OF_SCREEN:
+                               h_align = dcp::HALIGN_LEFT;
+                               break;
+                       case sub::HORIZONTAL_CENTRE_OF_SCREEN:
+                               h_align = dcp::HALIGN_CENTER;
+                               break;
+                       case sub::RIGHT_OF_SCREEN:
+                               h_align = dcp::HALIGN_RIGHT;
+                               break;
+                       default:
+                               h_align = dcp::HALIGN_CENTER;
+                               break;
+                       }
+
+                       /* The idea here (rightly or wrongly) is that we set the appearance based on the
+                          values in the libsub objects, and these are overridden with values from the
+                          content by the other emit_plain_start() above.
+                       */
+
+                       out.push_back (
+                               dcp::SubtitleString (
+                                       string(TEXT_FONT_ID),
+                                       j.italic,
+                                       j.bold,
+                                       j.underline,
+                                       j.colour.dcp(),
+                                       j.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 (),
+                                       i.horizontal_position.proportional,
+                                       h_align,
+                                       v_position,
+                                       v_align,
+                                       dcp::DIRECTION_LTR,
+                                       j.text,
+                                       dcp::NONE,
+                                       j.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 ()
+                                       )
+                               );
+               }
+       }
+
+       emit_plain_start (from, out);
+}
+
+void
+TextDecoder::emit_stop (ContentTime to)
+{
+       Stop (to, _content->type());
+}
+
+void
+TextDecoder::emit_plain (ContentTimePeriod period, list<dcp::SubtitleString> s)
+{
+       emit_plain_start (period.from, s);
+       emit_stop (period.to);
+}
+
+void
+TextDecoder::emit_plain (ContentTimePeriod period, sub::Subtitle const & s)
+{
+       emit_plain_start (period.from, s);
+       emit_stop (period.to);
+}
+
+void
+TextDecoder::seek ()
+{
+       _position = ContentTime ();
+}
diff --git a/src/lib/text_decoder.h b/src/lib/text_decoder.h
new file mode 100644 (file)
index 0000000..25125e7
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+    Copyright (C) 2013-2018 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_CAPTION_DECODER_H
+#define DCPOMATIC_CAPTION_DECODER_H
+
+#include "decoder.h"
+#include "rect.h"
+#include "types.h"
+#include "content_text.h"
+#include "decoder_part.h"
+#include <dcp/subtitle_string.h>
+#include <boost/signals2.hpp>
+
+namespace sub {
+       class Subtitle;
+}
+
+class Image;
+
+class TextDecoder : public DecoderPart
+{
+public:
+       TextDecoder (
+               Decoder* parent,
+               boost::shared_ptr<const TextContent>,
+               boost::shared_ptr<Log> log,
+               ContentTime first
+               );
+
+       ContentTime position () const {
+               return _position;
+       }
+
+       void emit_bitmap_start (ContentTime from, boost::shared_ptr<Image> image, dcpomatic::Rect<double> rect);
+       void emit_plain_start (ContentTime from, std::list<dcp::SubtitleString> s);
+       void emit_plain_start (ContentTime from, sub::Subtitle const & subtitle);
+       void emit_plain (ContentTimePeriod period, std::list<dcp::SubtitleString> s);
+       void emit_plain (ContentTimePeriod period, sub::Subtitle const & subtitle);
+       void emit_stop (ContentTime to);
+
+       void seek ();
+
+       boost::shared_ptr<const TextContent> content () const {
+               return _content;
+       }
+
+       boost::signals2::signal<void (ContentBitmapText)> BitmapStart;
+       boost::signals2::signal<void (ContentStringText)> PlainStart;
+       boost::signals2::signal<void (ContentTime, TextType)> Stop;
+
+private:
+       boost::shared_ptr<const TextContent> _content;
+       ContentTime _position;
+};
+
+#endif
index 5474b60..7f0bf43 100644 (file)
@@ -93,7 +93,7 @@ Crop::as_xml (xmlpp::Node* node) const
        node->add_child("BottomCrop")->add_child_text (raw_convert<string> (bottom));
 }
 
-CaptionType
+TextType
 string_to_caption_type (string s)
 {
        if (s == "open") {
@@ -106,7 +106,7 @@ string_to_caption_type (string s)
 }
 
 string
-caption_type_to_string (CaptionType t)
+caption_type_to_string (TextType t)
 {
        switch (t) {
        case CAPTION_OPEN:
@@ -119,7 +119,7 @@ caption_type_to_string (CaptionType t)
 }
 
 string
-caption_type_to_name (CaptionType t)
+caption_type_to_name (TextType t)
 {
        switch (t) {
        case CAPTION_OPEN:
index 6a05f66..6e3732d 100644 (file)
@@ -31,7 +31,7 @@
 class Content;
 class VideoContent;
 class AudioContent;
-class CaptionContent;
+class TextContent;
 class FFmpegContent;
 
 namespace cxml {
@@ -139,16 +139,16 @@ enum ReelType
  *  There is also still use of the word `subtitle' in the code; these are the
  *  same as open captions in DoM.
  */
-enum CaptionType
+enum TextType
 {
        CAPTION_OPEN,
        CAPTION_CLOSED,
        CAPTION_COUNT
 };
 
-extern std::string caption_type_to_string (CaptionType t);
-extern std::string caption_type_to_name (CaptionType t);
-extern CaptionType string_to_caption_type (std::string s);
+extern std::string caption_type_to_string (TextType t);
+extern std::string caption_type_to_name (TextType t);
+extern TextType string_to_caption_type (std::string s);
 
 /** @struct Crop
  *  @brief A description of the crop of an image or video.
index c0a774f..3ab1cc5 100644 (file)
@@ -665,7 +665,7 @@ Writer::can_fake_write (Frame frame) const
 }
 
 void
-Writer::write (PlayerCaption text, CaptionType type, DCPTimePeriod period)
+Writer::write (PlayerText text, TextType type, DCPTimePeriod period)
 {
        while (_caption_reel[type]->period().to <= period.from) {
                ++_caption_reel[type];
index f674190..71d11b7 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 #include "types.h"
-#include "player_caption.h"
+#include "player_text.h"
 #include "exception_store.h"
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
@@ -104,7 +104,7 @@ public:
        bool can_repeat (Frame) const;
        void repeat (Frame, Eyes);
        void write (boost::shared_ptr<const AudioBuffers>, DCPTime time);
-       void write (PlayerCaption text, CaptionType type, DCPTimePeriod period);
+       void write (PlayerText text, TextType type, DCPTimePeriod period);
        void write (std::list<boost::shared_ptr<Font> > fonts);
        void write (ReferencedReelAsset asset);
        void finish ();
index a841e5b..a9ed0c6 100644 (file)
@@ -21,7 +21,7 @@ import os
 import i18n
 
 sources = """
-          active_captions.cc
+          active_text.cc
           analyse_audio_job.cc
           atmos_mxf_content.cc
           audio_analysis.cc
@@ -38,8 +38,8 @@ sources = """
           audio_ring_buffers.cc
           audio_stream.cc
           butler.cc
-          caption_content.cc
-          caption_decoder.cc
+          text_content.cc
+          text_decoder.cc
           case_insensitive_sorter.cc
           cinema.cc
           cinema_kdms.cc
@@ -114,7 +114,7 @@ sources = """
           mid_side_decoder.cc
           overlaps.cc
           player.cc
-          player_caption.cc
+          player_text.cc
           player_video.cc
           playlist.cc
           position_image.cc
@@ -134,9 +134,9 @@ sources = """
           server.cc
           shuffler.cc
           string_log_entry.cc
-          text_caption_file.cc
-          text_caption_file_content.cc
-          text_caption_file_decoder.cc
+          string_text_file.cc
+          string_text_file_content.cc
+          string_text_file_decoder.cc
           timer.cc
           transcode_job.cc
           types.cc
index b6aa218..a2b5569 100644 (file)
@@ -71,7 +71,7 @@
 #include "lib/transcode_job.h"
 #include "lib/dkdm_wrapper.h"
 #include "lib/audio_content.h"
-#include "lib/caption_content.h"
+#include "lib/text_content.h"
 #include <dcp/exceptions.h>
 #include <dcp/raw_convert.h>
 #include <wx/generic/aboutdlgg.h>
@@ -580,8 +580,8 @@ private:
                                }
 
                                if (d->caption()) {
-                                       list<shared_ptr<CaptionContent> >::iterator j = i->caption.begin ();
-                                       list<shared_ptr<CaptionContent> >::const_iterator k = _clipboard->caption.begin ();
+                                       list<shared_ptr<TextContent> >::iterator j = i->caption.begin ();
+                                       list<shared_ptr<TextContent> >::const_iterator k = _clipboard->caption.begin ();
                                        while (j != i->caption.end() && k != _clipboard->caption.end()) {
                                                (*j)->take_settings_from (*k);
                                                ++j;
index e574540..44c6b86 100644 (file)
@@ -27,7 +27,7 @@
 #include "lib/job_manager.h"
 #include "lib/job.h"
 #include "lib/video_content.h"
-#include "lib/caption_content.h"
+#include "lib/text_content.h"
 #include "lib/ratio.h"
 #include "lib/verify_dcp_job.h"
 #include "lib/dcp_examiner.h"
@@ -626,7 +626,7 @@ private:
 
        void setup_from_dcp (shared_ptr<DCPContent> dcp)
        {
-               BOOST_FOREACH (shared_ptr<CaptionContent> i, dcp->caption) {
+               BOOST_FOREACH (shared_ptr<TextContent> i, dcp->caption) {
                        i->set_use (true);
                }
 
diff --git a/src/wx/caption_appearance_dialog.cc b/src/wx/caption_appearance_dialog.cc
deleted file mode 100644 (file)
index af01dcb..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
-    Copyright (C) 2015-2018 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 "caption_appearance_dialog.h"
-#include "rgba_colour_picker.h"
-#include "lib/text_caption_file_content.h"
-#include "lib/caption_content.h"
-#include "lib/ffmpeg_subtitle_stream.h"
-#include "lib/ffmpeg_content.h"
-#include <wx/wx.h>
-#include <wx/clrpicker.h>
-#include <wx/spinctrl.h>
-#include <wx/gbsizer.h>
-
-using std::map;
-using boost::shared_ptr;
-using boost::bind;
-using boost::dynamic_pointer_cast;
-using boost::optional;
-
-int const CaptionAppearanceDialog::NONE = 0;
-int const CaptionAppearanceDialog::OUTLINE = 1;
-int const CaptionAppearanceDialog::SHADOW = 2;
-
-CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptr<Content> content, shared_ptr<CaptionContent> caption)
-       : wxDialog (parent, wxID_ANY, _("Caption appearance"))
-       , _content (content)
-       , _caption (caption)
-{
-       shared_ptr<FFmpegContent> ff = dynamic_pointer_cast<FFmpegContent> (content);
-       if (ff) {
-               _stream = ff->subtitle_stream ();
-       }
-
-       wxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
-       SetSizer (overall_sizer);
-
-       _table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
-
-       overall_sizer->Add (_table, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
-
-       int r = 0;
-
-       add_label_to_sizer (_table, this, _("Colour"), true, wxGBPosition (r, 0));
-       _force_colour = set_to (_colour = new wxColourPickerCtrl (this, wxID_ANY), r);
-
-       add_label_to_sizer (_table, this, _("Effect"), true, wxGBPosition (r, 0));
-       _force_effect = set_to (_effect = new wxChoice (this, wxID_ANY), r);
-
-       add_label_to_sizer (_table, this, _("Effect colour"), true, wxGBPosition (r, 0));
-       _force_effect_colour = set_to (_effect_colour = new wxColourPickerCtrl (this, wxID_ANY), r);
-
-       add_label_to_sizer (_table, this, _("Outline width"), true, wxGBPosition (r, 0));
-       _outline_width = new wxSpinCtrl (this, wxID_ANY);
-       _table->Add (_outline_width, wxGBPosition (r, 1));
-       ++r;
-
-       add_label_to_sizer (_table, this, _("Fade in time"), true, wxGBPosition (r, 0));
-       _force_fade_in = set_to (_fade_in = new Timecode<ContentTime> (this), r);
-
-       add_label_to_sizer (_table, this, _("Fade out time"), true, wxGBPosition (r, 0));
-       _force_fade_out = set_to (_fade_out = new Timecode<ContentTime> (this), r);
-
-       if (_stream) {
-               wxScrolled<wxPanel>* colours_panel = new wxScrolled<wxPanel> (this);
-               colours_panel->EnableScrolling (false, true);
-               colours_panel->ShowScrollbars (wxSHOW_SB_NEVER, wxSHOW_SB_ALWAYS);
-               colours_panel->SetScrollRate (0, 16);
-
-               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
-               table->AddGrowableCol (1, 1);
-
-               map<RGBA, RGBA> colours = _stream->colours ();
-
-               wxStaticText* t = new wxStaticText (colours_panel, wxID_ANY, "");
-               t->SetLabelMarkup (_("<b>Original colour</b>"));
-               table->Add (t, 1, wxEXPAND);
-               t = new wxStaticText (colours_panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
-               t->SetLabelMarkup (_("<b>New colour</b>"));
-               table->Add (t, 1, wxALIGN_CENTER);
-
-               for (map<RGBA, RGBA>::const_iterator i = colours.begin(); i != colours.end(); ++i) {
-                       wxPanel* from = new wxPanel (colours_panel, wxID_ANY);
-                       from->SetBackgroundColour (wxColour (i->first.r, i->first.g, i->first.b, i->first.a));
-                       table->Add (from, 1, wxEXPAND);
-                       RGBAColourPicker* to = new RGBAColourPicker (colours_panel, i->second);
-                       table->Add (to, 1, wxEXPAND);
-                       _pickers[i->first] = to;
-               }
-
-               colours_panel->SetSizer (table);
-
-               overall_sizer->Add (colours_panel, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
-
-               wxButton* restore = new wxButton (this, wxID_ANY, _("Restore to original colours"));
-               restore->Bind (wxEVT_BUTTON, bind (&CaptionAppearanceDialog::restore, this));
-               overall_sizer->Add (restore, 0, wxALL, DCPOMATIC_SIZER_X_GAP);
-       }
-
-       wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
-       if (buttons) {
-               overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
-       }
-
-       overall_sizer->Layout ();
-       overall_sizer->SetSizeHints (this);
-
-       /* Keep these Appends() up to date with NONE/OUTLINE/SHADOW variables */
-       _effect->Append (_("None"));
-       _effect->Append (_("Outline"));
-       _effect->Append (_("Shadow"));;
-
-       optional<dcp::Colour> colour = _caption->colour();
-       _force_colour->SetValue (static_cast<bool>(colour));
-       if (colour) {
-               _colour->SetColour (wxColour (colour->r, colour->g, colour->b));
-       } else {
-               _colour->SetColour (wxColour (255, 255, 255));
-       }
-
-       optional<dcp::Effect> effect = _caption->effect();
-       _force_effect->SetValue (static_cast<bool>(effect));
-       if (effect) {
-               switch (*effect) {
-               case dcp::NONE:
-                       _effect->SetSelection (NONE);
-                       break;
-               case dcp::BORDER:
-                       _effect->SetSelection (OUTLINE);
-                       break;
-               case dcp::SHADOW:
-                       _effect->SetSelection (SHADOW);
-                       break;
-               }
-       } else {
-               _effect->SetSelection (NONE);
-       }
-
-       optional<dcp::Colour> effect_colour = _caption->effect_colour();
-       _force_effect_colour->SetValue (static_cast<bool>(effect_colour));
-       if (effect_colour) {
-               _effect_colour->SetColour (wxColour (effect_colour->r, effect_colour->g, effect_colour->b));
-       } else {
-               _effect_colour->SetColour (wxColour (0, 0, 0));
-       }
-
-       optional<ContentTime> fade_in = _caption->fade_in();
-       _force_fade_in->SetValue (static_cast<bool>(fade_in));
-       if (fade_in) {
-               _fade_in->set (*fade_in, _content->active_video_frame_rate());
-       } else {
-               _fade_in->set (ContentTime(), _content->active_video_frame_rate());
-       }
-
-       optional<ContentTime> fade_out = _caption->fade_out();
-       _force_fade_out->SetValue (static_cast<bool>(fade_out));
-       if (fade_out) {
-               _fade_out->set (*fade_out, _content->active_video_frame_rate ());
-       } else {
-               _fade_out->set (ContentTime(), _content->active_video_frame_rate ());
-       }
-
-       _outline_width->SetValue (_caption->outline_width ());
-
-       _force_colour->Bind (wxEVT_CHECKBOX, bind (&CaptionAppearanceDialog::setup_sensitivity, this));
-       _force_effect_colour->Bind (wxEVT_CHECKBOX, bind (&CaptionAppearanceDialog::setup_sensitivity, this));
-       _force_effect->Bind (wxEVT_CHECKBOX, bind (&CaptionAppearanceDialog::setup_sensitivity, this));
-       _force_fade_in->Bind (wxEVT_CHECKBOX, bind (&CaptionAppearanceDialog::setup_sensitivity, this));
-       _force_fade_out->Bind (wxEVT_CHECKBOX, bind (&CaptionAppearanceDialog::setup_sensitivity, this));
-       _effect->Bind (wxEVT_CHOICE, bind (&CaptionAppearanceDialog::setup_sensitivity, this));
-       _content_connection = _content->Changed.connect (bind (&CaptionAppearanceDialog::setup_sensitivity, this));
-
-       setup_sensitivity ();
-}
-
-wxCheckBox*
-CaptionAppearanceDialog::set_to (wxWindow* w, int& r)
-{
-       wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-       wxCheckBox* set_to = new wxCheckBox (this, wxID_ANY, _("Set to"));
-       s->Add (set_to, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 8);
-       s->Add (w, 0, wxALIGN_CENTER_VERTICAL);
-       _table->Add (s, wxGBPosition (r, 1));
-       ++r;
-       return set_to;
-}
-
-void
-CaptionAppearanceDialog::apply ()
-{
-       if (_force_colour->GetValue ()) {
-               wxColour const c = _colour->GetColour ();
-               _caption->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue()));
-       } else {
-               _caption->unset_colour ();
-       }
-       if (_force_effect->GetValue()) {
-               switch (_effect->GetSelection()) {
-               case NONE:
-                       _caption->set_effect (dcp::NONE);
-                       break;
-               case OUTLINE:
-                       _caption->set_effect (dcp::BORDER);
-                       break;
-               case SHADOW:
-                       _caption->set_effect (dcp::SHADOW);
-                       break;
-               }
-       } else {
-               _caption->unset_effect ();
-       }
-       if (_force_effect_colour->GetValue ()) {
-               wxColour const ec = _effect_colour->GetColour ();
-               _caption->set_effect_colour (dcp::Colour (ec.Red(), ec.Green(), ec.Blue()));
-       } else {
-               _caption->unset_effect_colour ();
-       }
-       if (_force_fade_in->GetValue ()) {
-               _caption->set_fade_in (_fade_in->get (_content->active_video_frame_rate ()));
-       } else {
-               _caption->unset_fade_in ();
-       }
-       if (_force_fade_out->GetValue ()) {
-               _caption->set_fade_out (_fade_out->get (_content->active_video_frame_rate ()));
-       } else {
-               _caption->unset_fade_out ();
-       }
-       _caption->set_outline_width (_outline_width->GetValue ());
-
-       if (_stream) {
-               for (map<RGBA, RGBAColourPicker*>::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) {
-                       _stream->set_colour (i->first, i->second->colour ());
-               }
-       }
-
-       shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (_content);
-       if (fc) {
-               fc->signal_subtitle_stream_changed ();
-       }
-}
-
-void
-CaptionAppearanceDialog::restore ()
-{
-       for (map<RGBA, RGBAColourPicker*>::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) {
-               i->second->set (i->first);
-       }
-}
-
-void
-CaptionAppearanceDialog::setup_sensitivity ()
-{
-       _colour->Enable (_force_colour->GetValue ());
-       _effect_colour->Enable (_force_effect_colour->GetValue ());
-       _effect->Enable (_force_effect->GetValue ());
-       _fade_in->Enable (_force_fade_in->GetValue ());
-       _fade_out->Enable (_force_fade_out->GetValue ());
-
-       bool const can_outline_width = _effect->GetSelection() == OUTLINE && _caption->burn ();
-       _outline_width->Enable (can_outline_width);
-       if (can_outline_width) {
-               _outline_width->UnsetToolTip ();
-       } else {
-               _outline_width->SetToolTip (_("Outline width cannot be set unless you are burning in captions"));
-       }
-}
diff --git a/src/wx/caption_appearance_dialog.h b/src/wx/caption_appearance_dialog.h
deleted file mode 100644 (file)
index 3fb9930..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-    Copyright (C) 2015-2018 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 "timecode.h"
-#include "lib/rgba.h"
-#include <wx/wx.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/signals2.hpp>
-
-class wxRadioButton;
-class wxColourPickerCtrl;
-class wxGridBagSizer;
-class Content;
-class RGBAColourPicker;
-class FFmpegSubtitleStream;
-class wxCheckBox;
-class wxWidget;
-
-class CaptionAppearanceDialog : public wxDialog
-{
-public:
-       CaptionAppearanceDialog (wxWindow* parent, boost::shared_ptr<Content> content, boost::shared_ptr<CaptionContent> caption);
-
-       void apply ();
-
-private:
-       void setup_sensitivity ();
-       void restore ();
-       wxCheckBox* set_to (wxWindow* w, int& r);
-
-       wxCheckBox* _force_colour;
-       wxColourPickerCtrl* _colour;
-       wxCheckBox* _force_effect;
-       wxChoice* _effect;
-       wxCheckBox* _force_effect_colour;
-       wxColourPickerCtrl* _effect_colour;
-       wxCheckBox* _force_fade_in;
-       Timecode<ContentTime>* _fade_in;
-       wxCheckBox* _force_fade_out;
-       Timecode<ContentTime>* _fade_out;
-       wxSpinCtrl* _outline_width;
-       wxGridBagSizer* _table;
-       std::map<RGBA, RGBAColourPicker*> _pickers;
-
-       boost::shared_ptr<Content> _content;
-       boost::shared_ptr<CaptionContent> _caption;
-       boost::shared_ptr<FFmpegSubtitleStream> _stream;
-
-       boost::signals2::scoped_connection _content_connection;
-
-       static int const NONE;
-       static int const OUTLINE;
-       static int const SHADOW;
-};
diff --git a/src/wx/caption_panel.cc b/src/wx/caption_panel.cc
deleted file mode 100644 (file)
index 1d2f592..0000000
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
-    Copyright (C) 2012-2018 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 "caption_panel.h"
-#include "film_editor.h"
-#include "wx_util.h"
-#include "caption_view.h"
-#include "content_panel.h"
-#include "fonts_dialog.h"
-#include "caption_appearance_dialog.h"
-#include "lib/ffmpeg_content.h"
-#include "lib/text_caption_file_content.h"
-#include "lib/ffmpeg_subtitle_stream.h"
-#include "lib/dcp_subtitle_content.h"
-#include "lib/text_caption_file_decoder.h"
-#include "lib/dcp_subtitle_decoder.h"
-#include "lib/dcp_content.h"
-#include "lib/caption_content.h"
-#include "lib/decoder_factory.h"
-#include <wx/spinctrl.h>
-#include <boost/foreach.hpp>
-
-using std::vector;
-using std::string;
-using std::list;
-using std::cout;
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-
-CaptionPanel::CaptionPanel (ContentPanel* p, CaptionType t)
-       : ContentSubPanel (p, std_to_wx(caption_type_to_name(t)))
-       , _caption_view (0)
-       , _fonts_dialog (0)
-       , _original_type (t)
-{
-       wxBoxSizer* reference_sizer = new wxBoxSizer (wxVERTICAL);
-
-       _reference = new wxCheckBox (this, wxID_ANY, _("Use this DCP's subtitle as OV and make VF"));
-       reference_sizer->Add (_reference, 0, wxLEFT | wxRIGHT | wxTOP, DCPOMATIC_SIZER_GAP);
-
-       _reference_note = new wxStaticText (this, wxID_ANY, _(""));
-       _reference_note->Wrap (200);
-       reference_sizer->Add (_reference_note, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
-       wxFont font = _reference_note->GetFont();
-       font.SetStyle(wxFONTSTYLE_ITALIC);
-       font.SetPointSize(font.GetPointSize() - 1);
-       _reference_note->SetFont(font);
-
-       _sizer->Add (reference_sizer);
-
-       wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
-       _sizer->Add (grid, 0, wxALL, 8);
-       int r = 0;
-
-       wxBoxSizer* use = new wxBoxSizer (wxHORIZONTAL);
-       _use = new wxCheckBox (this, wxID_ANY, _("Use as"));
-       use->Add (_use, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
-       _type = new wxChoice (this, wxID_ANY);
-       _type->Append (_("subtitles (open captions)"));
-       _type->Append (_("closed captions"));
-       use->Add (_type, 1, wxEXPAND, 0);
-       grid->Add (use, wxGBPosition (r, 0), wxGBSpan (1, 2));
-       ++r;
-
-       _burn = new wxCheckBox (this, wxID_ANY, _("Burn subtitles into image"));
-       grid->Add (_burn, wxGBPosition (r, 0), wxGBSpan (1, 2));
-       ++r;
-
-       {
-               add_label_to_sizer (grid, this, _("X Offset"), true, wxGBPosition (r, 0));
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-               _x_offset = new wxSpinCtrl (this);
-               s->Add (_x_offset);
-               add_label_to_sizer (s, this, _("%"), false);
-               grid->Add (s, wxGBPosition (r, 1));
-               ++r;
-       }
-
-       {
-               add_label_to_sizer (grid, this, _("Y Offset"), true, wxGBPosition (r, 0));
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-               _y_offset = new wxSpinCtrl (this);
-               s->Add (_y_offset);
-               add_label_to_sizer (s, this, _("%"), false);
-               grid->Add (s, wxGBPosition (r, 1));
-               ++r;
-       }
-
-       {
-               add_label_to_sizer (grid, this, _("X Scale"), true, wxGBPosition (r, 0));
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-               _x_scale = new wxSpinCtrl (this);
-               s->Add (_x_scale);
-               add_label_to_sizer (s, this, _("%"), false);
-               grid->Add (s, wxGBPosition (r, 1));
-               ++r;
-       }
-
-       {
-               add_label_to_sizer (grid, this, _("Y Scale"), true, wxGBPosition (r, 0));
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-               _y_scale = new wxSpinCtrl (this);
-               s->Add (_y_scale);
-               add_label_to_sizer (s, this, _("%"), false);
-               grid->Add (s, wxGBPosition (r, 1));
-               ++r;
-       }
-
-       {
-               add_label_to_sizer (grid, this, _("Line spacing"), true, wxGBPosition (r, 0));
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-               _line_spacing = new wxSpinCtrl (this);
-               s->Add (_line_spacing);
-               add_label_to_sizer (s, this, _("%"), false);
-               grid->Add (s, wxGBPosition (r, 1));
-               ++r;
-       }
-
-       add_label_to_sizer (grid, this, _("Language"), true, wxGBPosition (r, 0));
-       _language = new wxTextCtrl (this, wxID_ANY);
-       grid->Add (_language, wxGBPosition (r, 1));
-       ++r;
-
-       add_label_to_sizer (grid, this, _("Stream"), true, wxGBPosition (r, 0));
-       _stream = new wxChoice (this, wxID_ANY);
-       grid->Add (_stream, wxGBPosition (r, 1));
-       ++r;
-
-       {
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-
-               _caption_view_button = new wxButton (this, wxID_ANY, _("View..."));
-               s->Add (_caption_view_button, 1, wxALL, DCPOMATIC_SIZER_GAP);
-               _fonts_dialog_button = new wxButton (this, wxID_ANY, _("Fonts..."));
-               s->Add (_fonts_dialog_button, 1, wxALL, DCPOMATIC_SIZER_GAP);
-               _appearance_dialog_button = new wxButton (this, wxID_ANY, _("Appearance..."));
-               s->Add (_appearance_dialog_button, 1, wxALL, DCPOMATIC_SIZER_GAP);
-
-               grid->Add (s, wxGBPosition (r, 0), wxGBSpan (1, 2));
-               ++r;
-       }
-
-       _x_offset->SetRange (-100, 100);
-       _y_offset->SetRange (-100, 100);
-       _x_scale->SetRange (10, 1000);
-       _y_scale->SetRange (10, 1000);
-       _line_spacing->SetRange (10, 1000);
-
-       _reference->Bind                (wxEVT_CHECKBOX, boost::bind (&CaptionPanel::reference_clicked, this));
-       _use->Bind                      (wxEVT_CHECKBOX, boost::bind (&CaptionPanel::use_toggled, this));
-       _type->Bind              (wxEVT_CHOICE,   boost::bind (&CaptionPanel::type_changed, this));
-       _burn->Bind                     (wxEVT_CHECKBOX, boost::bind (&CaptionPanel::burn_toggled, this));
-       _x_offset->Bind                 (wxEVT_SPINCTRL, boost::bind (&CaptionPanel::x_offset_changed, this));
-       _y_offset->Bind                 (wxEVT_SPINCTRL, boost::bind (&CaptionPanel::y_offset_changed, this));
-       _x_scale->Bind                  (wxEVT_SPINCTRL, boost::bind (&CaptionPanel::x_scale_changed, this));
-       _y_scale->Bind                  (wxEVT_SPINCTRL, boost::bind (&CaptionPanel::y_scale_changed, this));
-       _line_spacing->Bind             (wxEVT_SPINCTRL, boost::bind (&CaptionPanel::line_spacing_changed, this));
-       _language->Bind                 (wxEVT_TEXT,     boost::bind (&CaptionPanel::language_changed, this));
-       _stream->Bind                   (wxEVT_CHOICE,   boost::bind (&CaptionPanel::stream_changed, this));
-       _caption_view_button->Bind      (wxEVT_BUTTON,   boost::bind (&CaptionPanel::caption_view_clicked, this));
-       _fonts_dialog_button->Bind      (wxEVT_BUTTON,   boost::bind (&CaptionPanel::fonts_dialog_clicked, this));
-       _appearance_dialog_button->Bind (wxEVT_BUTTON,   boost::bind (&CaptionPanel::appearance_dialog_clicked, this));
-}
-
-void
-CaptionPanel::film_changed (Film::Property property)
-{
-       if (property == Film::CONTENT || property == Film::REEL_TYPE) {
-               setup_sensitivity ();
-       }
-}
-
-void
-CaptionPanel::film_content_changed (int property)
-{
-       FFmpegContentList fc = _parent->selected_ffmpeg ();
-       ContentList sc = _parent->selected_caption ();
-
-       shared_ptr<FFmpegContent> fcs;
-       if (fc.size() == 1) {
-               fcs = fc.front ();
-       }
-
-       shared_ptr<Content> scs;
-       if (sc.size() == 1) {
-               scs = sc.front ();
-       }
-
-       shared_ptr<CaptionContent> caption;
-       if (scs) {
-               caption = scs->caption_of_original_type(_original_type);
-       }
-
-       if (property == FFmpegContentProperty::SUBTITLE_STREAMS) {
-               _stream->Clear ();
-               if (fcs) {
-                       vector<shared_ptr<FFmpegSubtitleStream> > s = fcs->subtitle_streams ();
-                       for (vector<shared_ptr<FFmpegSubtitleStream> >::iterator i = s.begin(); i != s.end(); ++i) {
-                               _stream->Append (std_to_wx ((*i)->name), new wxStringClientData (std_to_wx ((*i)->identifier ())));
-                       }
-
-                       if (fcs->subtitle_stream()) {
-                               checked_set (_stream, fcs->subtitle_stream()->identifier ());
-                       } else {
-                               _stream->SetSelection (wxNOT_FOUND);
-                       }
-               }
-               setup_sensitivity ();
-       } else if (property == CaptionContentProperty::USE) {
-               checked_set (_use, caption ? caption->use() : false);
-               setup_sensitivity ();
-       } else if (property == CaptionContentProperty::TYPE) {
-               if (caption) {
-                       switch (caption->type()) {
-                       case CAPTION_OPEN:
-                               _type->SetSelection (0);
-                               break;
-                       case CAPTION_CLOSED:
-                               _type->SetSelection (1);
-                               break;
-                       default:
-                               DCPOMATIC_ASSERT (false);
-                       }
-               } else {
-                       _type->SetSelection (0);
-               }
-               setup_sensitivity ();
-       } else if (property == CaptionContentProperty::BURN) {
-               checked_set (_burn, caption ? caption->burn() : false);
-       } else if (property == CaptionContentProperty::X_OFFSET) {
-               checked_set (_x_offset, caption ? lrint (caption->x_offset() * 100) : 0);
-       } else if (property == CaptionContentProperty::Y_OFFSET) {
-               checked_set (_y_offset, caption ? lrint (caption->y_offset() * 100) : 0);
-       } else if (property == CaptionContentProperty::X_SCALE) {
-               checked_set (_x_scale, caption ? lrint (caption->x_scale() * 100) : 100);
-       } else if (property == CaptionContentProperty::Y_SCALE) {
-               checked_set (_y_scale, caption ? lrint (caption->y_scale() * 100) : 100);
-       } else if (property == CaptionContentProperty::LINE_SPACING) {
-               checked_set (_line_spacing, caption ? lrint (caption->line_spacing() * 100) : 100);
-       } else if (property == CaptionContentProperty::LANGUAGE) {
-               checked_set (_language, caption ? caption->language() : "");
-       } else if (property == DCPContentProperty::REFERENCE_CAPTION) {
-               if (scs) {
-                       shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (scs);
-                       checked_set (_reference, dcp ? dcp->reference_caption(_original_type) : false);
-               } else {
-                       checked_set (_reference, false);
-               }
-
-               setup_sensitivity ();
-       } else if (property == DCPContentProperty::CAPTIONS) {
-               setup_sensitivity ();
-       }
-}
-
-void
-CaptionPanel::use_toggled ()
-{
-       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption()) {
-               i->caption_of_original_type(_original_type)->set_use (_use->GetValue());
-       }
-}
-
-void
-CaptionPanel::type_changed ()
-{
-       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption()) {
-               switch (_type->GetSelection()) {
-               case 0:
-                       i->caption_of_original_type(_original_type)->set_type (CAPTION_OPEN);
-                       break;
-               case 1:
-                       i->caption_of_original_type(_original_type)->set_type (CAPTION_CLOSED);
-                       break;
-               }
-       }
-}
-
-void
-CaptionPanel::burn_toggled ()
-{
-       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption ()) {
-               i->caption_of_original_type(_original_type)->set_burn (_burn->GetValue());
-       }
-}
-
-void
-CaptionPanel::setup_sensitivity ()
-{
-       int any_subs = 0;
-       int ffmpeg_subs = 0;
-       ContentList sel = _parent->selected_caption ();
-       BOOST_FOREACH (shared_ptr<Content> i, sel) {
-               /* These are the content types that could include subtitles */
-               shared_ptr<const FFmpegContent> fc = boost::dynamic_pointer_cast<const FFmpegContent> (i);
-               shared_ptr<const TextCaptionFileContent> sc = boost::dynamic_pointer_cast<const TextCaptionFileContent> (i);
-               shared_ptr<const DCPContent> dc = boost::dynamic_pointer_cast<const DCPContent> (i);
-               shared_ptr<const DCPSubtitleContent> dsc = boost::dynamic_pointer_cast<const DCPSubtitleContent> (i);
-               if (fc) {
-                       if (!fc->caption.empty()) {
-                               ++ffmpeg_subs;
-                               ++any_subs;
-                       }
-               } else if (sc || dc || dsc) {
-                       /* XXX: in the future there could be bitmap subs from DCPs */
-                       ++any_subs;
-               }
-       }
-
-       /* Decide whether we can reference these subs */
-
-       shared_ptr<DCPContent> dcp;
-       if (sel.size() == 1) {
-               dcp = dynamic_pointer_cast<DCPContent> (sel.front ());
-       }
-
-       string why_not;
-       bool const can_reference = dcp && dcp->can_reference_caption (_original_type, why_not);
-       setup_refer_button (_reference, _reference_note, dcp, can_reference, why_not);
-
-       bool const reference = _reference->GetValue ();
-
-       /* Set up sensitivity */
-       _use->Enable (!reference && any_subs > 0);
-       bool const use = _use->GetValue ();
-       _type->Enable (!reference && any_subs > 0 && use);
-       _burn->Enable (!reference && any_subs > 0 && use && _type->GetSelection() == 0);
-       _x_offset->Enable (!reference && any_subs > 0 && use);
-       _y_offset->Enable (!reference && any_subs > 0 && use);
-       _x_scale->Enable (!reference && any_subs > 0 && use);
-       _y_scale->Enable (!reference && any_subs > 0 && use);
-       _line_spacing->Enable (!reference && use);
-       _language->Enable (!reference && any_subs > 0 && use);
-       _stream->Enable (!reference && ffmpeg_subs == 1);
-       _caption_view_button->Enable (!reference);
-       _fonts_dialog_button->Enable (!reference);
-       _appearance_dialog_button->Enable (!reference && any_subs > 0 && use);
-}
-
-void
-CaptionPanel::stream_changed ()
-{
-       FFmpegContentList fc = _parent->selected_ffmpeg ();
-       if (fc.size() != 1) {
-               return;
-       }
-
-       shared_ptr<FFmpegContent> fcs = fc.front ();
-
-       vector<shared_ptr<FFmpegSubtitleStream> > a = fcs->subtitle_streams ();
-       vector<shared_ptr<FFmpegSubtitleStream> >::iterator i = a.begin ();
-       string const s = string_client_data (_stream->GetClientObject (_stream->GetSelection ()));
-       while (i != a.end() && (*i)->identifier () != s) {
-               ++i;
-       }
-
-       if (i != a.end ()) {
-               fcs->set_subtitle_stream (*i);
-       }
-}
-
-void
-CaptionPanel::x_offset_changed ()
-{
-       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption ()) {
-               i->caption_of_original_type(_original_type)->set_x_offset (_x_offset->GetValue() / 100.0);
-       }
-}
-
-void
-CaptionPanel::y_offset_changed ()
-{
-       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption ()) {
-               i->caption_of_original_type(_original_type)->set_y_offset (_y_offset->GetValue() / 100.0);
-       }
-}
-
-void
-CaptionPanel::x_scale_changed ()
-{
-       ContentList c = _parent->selected_caption ();
-       if (c.size() == 1) {
-               c.front()->caption_of_original_type(_original_type)->set_x_scale (_x_scale->GetValue() / 100.0);
-       }
-}
-
-void
-CaptionPanel::y_scale_changed ()
-{
-       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption ()) {
-               i->caption_of_original_type(_original_type)->set_y_scale (_y_scale->GetValue() / 100.0);
-       }
-}
-
-void
-CaptionPanel::line_spacing_changed ()
-{
-       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption ()) {
-               i->caption_of_original_type(_original_type)->set_line_spacing (_line_spacing->GetValue() / 100.0);
-       }
-}
-
-void
-CaptionPanel::language_changed ()
-{
-       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption ()) {
-               i->caption_of_original_type(_original_type)->set_language (wx_to_std (_language->GetValue()));
-       }
-}
-
-void
-CaptionPanel::content_selection_changed ()
-{
-       film_content_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
-       film_content_changed (CaptionContentProperty::USE);
-       film_content_changed (CaptionContentProperty::BURN);
-       film_content_changed (CaptionContentProperty::X_OFFSET);
-       film_content_changed (CaptionContentProperty::Y_OFFSET);
-       film_content_changed (CaptionContentProperty::X_SCALE);
-       film_content_changed (CaptionContentProperty::Y_SCALE);
-       film_content_changed (CaptionContentProperty::LINE_SPACING);
-       film_content_changed (CaptionContentProperty::LANGUAGE);
-       film_content_changed (CaptionContentProperty::FONTS);
-       film_content_changed (CaptionContentProperty::TYPE);
-       film_content_changed (DCPContentProperty::REFERENCE_CAPTION);
-}
-
-void
-CaptionPanel::caption_view_clicked ()
-{
-       if (_caption_view) {
-               _caption_view->Destroy ();
-               _caption_view = 0;
-       }
-
-       ContentList c = _parent->selected_caption ();
-       DCPOMATIC_ASSERT (c.size() == 1);
-
-       shared_ptr<Decoder> decoder = decoder_factory (c.front(), _parent->film()->log(), false);
-
-       if (decoder) {
-               _caption_view = new CaptionView (this, _parent->film(), c.front(), c.front()->caption_of_original_type(_original_type), decoder, _parent->film_viewer());
-               _caption_view->Show ();
-       }
-}
-
-void
-CaptionPanel::fonts_dialog_clicked ()
-{
-       if (_fonts_dialog) {
-               _fonts_dialog->Destroy ();
-               _fonts_dialog = 0;
-       }
-
-       ContentList c = _parent->selected_caption ();
-       DCPOMATIC_ASSERT (c.size() == 1);
-
-       _fonts_dialog = new FontsDialog (this, c.front(), c.front()->caption_of_original_type(_original_type));
-       _fonts_dialog->Show ();
-}
-
-void
-CaptionPanel::reference_clicked ()
-{
-       ContentList c = _parent->selected ();
-       if (c.size() != 1) {
-               return;
-       }
-
-       shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (c.front ());
-       if (!d) {
-               return;
-       }
-
-       d->set_reference_caption (_original_type, _reference->GetValue ());
-}
-
-void
-CaptionPanel::appearance_dialog_clicked ()
-{
-       ContentList c = _parent->selected_caption ();
-       DCPOMATIC_ASSERT (c.size() == 1);
-
-       CaptionAppearanceDialog* d = new CaptionAppearanceDialog (this, c.front(), c.front()->caption_of_original_type(_original_type));
-       if (d->ShowModal () == wxID_OK) {
-               d->apply ();
-       }
-       d->Destroy ();
-}
diff --git a/src/wx/caption_panel.h b/src/wx/caption_panel.h
deleted file mode 100644 (file)
index 20af29d..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
-    Copyright (C) 2012-2018 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 "content_sub_panel.h"
-
-class wxCheckBox;
-class wxSpinCtrl;
-class CaptionView;
-class FontsDialog;
-
-class CaptionPanel : public ContentSubPanel
-{
-public:
-       CaptionPanel (ContentPanel *, CaptionType t);
-
-       void film_changed (Film::Property);
-       void film_content_changed (int);
-       void content_selection_changed ();
-
-private:
-       void use_toggled ();
-       void type_changed ();
-       void burn_toggled ();
-       void x_offset_changed ();
-       void y_offset_changed ();
-       void x_scale_changed ();
-       void y_scale_changed ();
-       void line_spacing_changed ();
-       void language_changed ();
-       void stream_changed ();
-       void caption_view_clicked ();
-       void fonts_dialog_clicked ();
-       void reference_clicked ();
-       void appearance_dialog_clicked ();
-
-       void setup_sensitivity ();
-
-       wxCheckBox* _reference;
-       wxStaticText* _reference_note;
-       wxCheckBox* _use;
-       wxChoice* _type;
-       wxCheckBox* _burn;
-       wxSpinCtrl* _x_offset;
-       wxSpinCtrl* _y_offset;
-       wxSpinCtrl* _x_scale;
-       wxSpinCtrl* _y_scale;
-       wxSpinCtrl* _line_spacing;
-       wxTextCtrl* _language;
-       wxChoice* _stream;
-       wxButton* _caption_view_button;
-       CaptionView* _caption_view;
-       wxButton* _fonts_dialog_button;
-       FontsDialog* _fonts_dialog;
-       wxButton* _appearance_dialog_button;
-       CaptionType _original_type;
-};
diff --git a/src/wx/caption_view.cc b/src/wx/caption_view.cc
deleted file mode 100644 (file)
index e6e63ef..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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 "lib/text_caption_file_decoder.h"
-#include "lib/content_caption.h"
-#include "lib/video_decoder.h"
-#include "lib/audio_decoder.h"
-#include "lib/film.h"
-#include "lib/config.h"
-#include "lib/text_caption_file_content.h"
-#include "lib/caption_decoder.h"
-#include "caption_view.h"
-#include "film_viewer.h"
-#include "wx_util.h"
-
-using std::list;
-using boost::shared_ptr;
-using boost::bind;
-using boost::dynamic_pointer_cast;
-
-CaptionView::CaptionView (wxWindow* parent, shared_ptr<Film> film, shared_ptr<Content> content, shared_ptr<CaptionContent> caption, shared_ptr<Decoder> decoder, FilmViewer* viewer)
-       : wxDialog (parent, wxID_ANY, _("Captions"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
-       , _content (content)
-       , _film_viewer (viewer)
-{
-       _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
-
-       {
-               wxListItem ip;
-               ip.SetId (0);
-               ip.SetText (_("Start"));
-               ip.SetWidth (100);
-               _list->InsertColumn (0, ip);
-       }
-
-       {
-               wxListItem ip;
-               ip.SetId (1);
-               ip.SetText (_("End"));
-               ip.SetWidth (100);
-               _list->InsertColumn (1, ip);
-       }
-
-       {
-               wxListItem ip;
-               ip.SetId (2);
-               ip.SetText (_("Caption"));
-               ip.SetWidth (640);
-               _list->InsertColumn (2, ip);
-       }
-
-       wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
-       sizer->Add (_list, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_X_GAP);
-
-       _list->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind (&CaptionView::subtitle_selected, this, _1));
-
-       wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
-       if (buttons) {
-               sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
-       }
-
-       if (decoder->video) {
-               decoder->video->set_ignore (true);
-       }
-       if (decoder->audio) {
-               decoder->audio->set_ignore (true);
-       }
-
-       _subs = 0;
-       _frc = film->active_frame_rate_change (content->position());
-
-       /* Find the decoder that is being used for our CaptionContent and attach to it */
-       BOOST_FOREACH (shared_ptr<CaptionDecoder> i, decoder->caption) {
-               if (i->content() == caption) {
-                       i->PlainStart.connect (bind (&CaptionView::data_start, this, _1));
-                       i->Stop.connect (bind (&CaptionView::data_stop, this, _1));
-               }
-       }
-       while (!decoder->pass ()) {}
-       SetSizerAndFit (sizer);
-}
-
-void
-CaptionView::data_start (ContentTextCaption cts)
-{
-       BOOST_FOREACH (dcp::SubtitleString const & i, cts.subs) {
-               wxListItem list_item;
-               list_item.SetId (_subs);
-               _list->InsertItem (list_item);
-               _list->SetItem (_subs, 0, std_to_wx (cts.from().timecode (_frc->source)));
-               _list->SetItem (_subs, 2, std_to_wx (i.text ()));
-               _start_times.push_back (cts.from ());
-               ++_subs;
-       }
-
-       _last_count = cts.subs.size ();
-}
-
-void
-CaptionView::data_stop (ContentTime time)
-{
-       if (!_last_count) {
-               return;
-       }
-
-       for (int i = _subs - *_last_count; i < _subs; ++i) {
-               _list->SetItem (i, 1, std_to_wx (time.timecode (_frc->source)));
-       }
-}
-
-void
-CaptionView::subtitle_selected (wxListEvent& ev)
-{
-       if (!Config::instance()->jump_to_selected ()) {
-               return;
-       }
-
-       DCPOMATIC_ASSERT (ev.GetIndex() < int(_start_times.size()));
-       shared_ptr<Content> locked = _content.lock ();
-       DCPOMATIC_ASSERT (locked);
-       _film_viewer->set_position (locked, _start_times[ev.GetIndex()]);
-}
diff --git a/src/wx/caption_view.h b/src/wx/caption_view.h
deleted file mode 100644 (file)
index 71c492a..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-    Copyright (C) 2014-2018 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 "lib/content_caption.h"
-#include <boost/shared_ptr.hpp>
-#include <wx/wx.h>
-#include <wx/listctrl.h>
-
-class Decoder;
-class FilmViewer;
-
-class CaptionView : public wxDialog
-{
-public:
-       CaptionView (
-               wxWindow *, boost::shared_ptr<Film>, boost::shared_ptr<Content> content, boost::shared_ptr<CaptionContent> caption, boost::shared_ptr<Decoder>, FilmViewer* viewer
-               );
-
-private:
-       void data_start (ContentTextCaption cts);
-       void data_stop (ContentTime time);
-       void subtitle_selected (wxListEvent &);
-
-       wxListCtrl* _list;
-       int _subs;
-       boost::optional<FrameRateChange> _frc;
-       boost::optional<int> _last_count;
-       std::vector<ContentTime> _start_times;
-       boost::weak_ptr<Content> _content;
-       FilmViewer* _film_viewer;
-};
index 0b2e630..3e240c6 100644 (file)
@@ -19,7 +19,7 @@
 */
 
 #include "closed_captions_dialog.h"
-#include "lib/text_caption.h"
+#include "lib/string_text.h"
 #include <boost/bind.hpp>
 
 using std::list;
@@ -70,13 +70,13 @@ ClosedCaptionsDialog::paint ()
 class ClosedCaptionSorter
 {
 public:
-       bool operator() (TextCaption const & a, TextCaption const & b)
+       bool operator() (StringText const & a, StringText const & b)
        {
                return from_top(a) < from_top(b);
        }
 
 private:
-       float from_top (TextCaption const & c) const
+       float from_top (StringText const & c) const
        {
                switch (c.v_align()) {
                case dcp::VALIGN_TOP:
@@ -96,9 +96,9 @@ ClosedCaptionsDialog::update (DCPTime time)
 {
        shared_ptr<Player> player = _player.lock ();
        DCPOMATIC_ASSERT (player);
-       list<TextCaption> to_show;
-       BOOST_FOREACH (PlayerCaption i, player->closed_captions_for_frame(time)) {
-               BOOST_FOREACH (TextCaption j, i.text) {
+       list<StringText> to_show;
+       BOOST_FOREACH (PlayerText i, player->closed_captions_for_frame(time)) {
+               BOOST_FOREACH (StringText j, i.text) {
                        to_show.push_back (j);
                }
        }
@@ -109,7 +109,7 @@ ClosedCaptionsDialog::update (DCPTime time)
 
        to_show.sort (ClosedCaptionSorter());
 
-       list<TextCaption>::const_iterator j = to_show.begin();
+       list<StringText>::const_iterator j = to_show.begin();
        int k = 0;
        while (j != to_show.end() && k < _num_lines) {
                _lines[k] = j->text();
index ce9c66a..a6338ec 100644 (file)
 #include "wx_util.h"
 #include "video_panel.h"
 #include "audio_panel.h"
-#include "caption_panel.h"
+#include "text_panel.h"
 #include "timing_panel.h"
 #include "timeline_dialog.h"
 #include "image_sequence_dialog.h"
 #include "film_viewer.h"
 #include "lib/audio_content.h"
-#include "lib/caption_content.h"
+#include "lib/text_content.h"
 #include "lib/video_content.h"
 #include "lib/ffmpeg_content.h"
 #include "lib/content_factory.h"
@@ -39,8 +39,8 @@
 #include "lib/config.h"
 #include "lib/log.h"
 #include "lib/compose.hpp"
-#include "lib/text_caption_file_content.h"
-#include "lib/text_caption_file.h"
+#include "lib/string_text_file_content.h"
+#include "lib/string_text_file.h"
 #include <wx/wx.h>
 #include <wx/notebook.h>
 #include <wx/listctrl.h>
@@ -128,7 +128,7 @@ ContentPanel::ContentPanel (wxNotebook* n, boost::shared_ptr<Film> film, FilmVie
        _audio_panel = new AudioPanel (this);
        _panels.push_back (_audio_panel);
        for (int i = 0; i < CAPTION_COUNT; ++i) {
-               _caption_panel[i] = new CaptionPanel (this, static_cast<CaptionType>(i));
+               _caption_panel[i] = new TextPanel (this, static_cast<TextType>(i));
                _panels.push_back (_caption_panel[i]);
        }
        _timing_panel = new TimingPanel (this, _film_viewer);
@@ -262,11 +262,11 @@ ContentPanel::selection_changed ()
        BOOST_FOREACH (shared_ptr<Content> i, selected()) {
                DCPTime p;
                p = i->position();
-               if (dynamic_pointer_cast<TextCaptionFileContent>(i) && i->paths_valid()) {
+               if (dynamic_pointer_cast<StringTextFileContent>(i) && i->paths_valid()) {
                        /* Rather special case; if we select a text subtitle file jump to its
                           first subtitle.
                        */
-                       TextCaptionFile ts (dynamic_pointer_cast<TextCaptionFileContent>(i));
+                       StringTextFile ts (dynamic_pointer_cast<StringTextFileContent>(i));
                        if (ts.first()) {
                                p += DCPTime(ts.first().get(), _film->active_frame_rate_change(i->position()));
                        }
@@ -306,7 +306,7 @@ ContentPanel::selection_changed ()
                if (i->audio) {
                        have_audio = true;
                }
-               BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+               BOOST_FOREACH (shared_ptr<TextContent> j, i->caption) {
                        have_caption[j->original_type()] = true;
                }
        }
index 3f0fb17..d31375c 100644 (file)
@@ -32,7 +32,7 @@ class wxListEvent;
 class TimelineDialog;
 class FilmEditor;
 class ContentSubPanel;
-class CaptionPanel;
+class TextPanel;
 class AudioPanel;
 class Film;
 class FilmViewer;
@@ -105,7 +105,7 @@ private:
        wxButton* _timeline;
        ContentSubPanel* _video_panel;
        AudioPanel* _audio_panel;
-       CaptionPanel* _caption_panel[CAPTION_COUNT];
+       TextPanel* _caption_panel[CAPTION_COUNT];
        ContentSubPanel* _timing_panel;
        std::list<ContentSubPanel *> _panels;
        ContentMenu* _menu;
index 5188103..f1f8228 100644 (file)
@@ -32,7 +32,7 @@
 #include "lib/ffmpeg_content.h"
 #include "lib/audio_processor.h"
 #include "lib/video_content.h"
-#include "lib/caption_content.h"
+#include "lib/text_content.h"
 #include "lib/dcp_content.h"
 #include "lib/audio_content.h"
 #include <dcp/locale_convert.h>
@@ -433,8 +433,8 @@ void
 DCPPanel::film_content_changed (int property)
 {
        if (property == AudioContentProperty::STREAMS ||
-           property == CaptionContentProperty::USE ||
-           property == CaptionContentProperty::BURN ||
+           property == TextContentProperty::USE ||
+           property == TextContentProperty::BURN ||
            property == VideoContentProperty::SCALE ||
            property == DCPContentProperty::REFERENCE_VIDEO ||
            property == DCPContentProperty::REFERENCE_AUDIO ||
index 266509a..e4449fa 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "lib/film.h"
 #include "lib/config.h"
-#include "lib/player_caption.h"
+#include "lib/player_text.h"
 #include <RtAudio.h>
 #include <wx/wx.h>
 
index 269dad6..3619b7e 100644 (file)
@@ -24,7 +24,7 @@
 #include "font_files_dialog.h"
 #include "lib/font.h"
 #include "lib/content.h"
-#include "lib/caption_content.h"
+#include "lib/text_content.h"
 #include <wx/wx.h>
 #include <boost/foreach.hpp>
 #include <iostream>
@@ -34,7 +34,7 @@ using std::string;
 using std::cout;
 using boost::shared_ptr;
 
-FontsDialog::FontsDialog (wxWindow* parent, shared_ptr<Content> content, shared_ptr<CaptionContent> caption)
+FontsDialog::FontsDialog (wxWindow* parent, shared_ptr<Content> content, shared_ptr<TextContent> caption)
        : wxDialog (parent, wxID_ANY, _("Fonts"))
        , _content (content)
        , _caption (caption)
@@ -100,7 +100,7 @@ void
 FontsDialog::setup ()
 {
        shared_ptr<Content> content = _content.lock ();
-       shared_ptr<CaptionContent> caption = _caption.lock ();
+       shared_ptr<TextContent> caption = _caption.lock ();
        if (!content || !caption) {
                return;
        }
@@ -140,7 +140,7 @@ void
 FontsDialog::edit_clicked ()
 {
        shared_ptr<Content> content = _content.lock ();
-       shared_ptr<CaptionContent> caption = _caption.lock ();
+       shared_ptr<TextContent> caption = _caption.lock ();
        if (!content || !caption) {
                return;
        }
index 6c6873e..ba98e09 100644 (file)
 #include <boost/filesystem.hpp>
 
 class Content;
-class CaptionContent;
+class TextContent;
 
 class FontsDialog : public wxDialog
 {
 public:
-       FontsDialog (wxWindow* parent, boost::shared_ptr<Content>, boost::shared_ptr<CaptionContent> caption);
+       FontsDialog (wxWindow* parent, boost::shared_ptr<Content>, boost::shared_ptr<TextContent> caption);
 
 private:
        void setup ();
@@ -39,7 +39,7 @@ private:
        void edit_clicked ();
 
        boost::weak_ptr<Content> _content;
-       boost::weak_ptr<CaptionContent> _caption;
+       boost::weak_ptr<TextContent> _caption;
        wxListCtrl* _fonts;
        wxButton* _edit;
 };
diff --git a/src/wx/subtitle_appearance_dialog.cc b/src/wx/subtitle_appearance_dialog.cc
new file mode 100644 (file)
index 0000000..cab473c
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+    Copyright (C) 2015-2018 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 "subtitle_appearance_dialog.h"
+#include "rgba_colour_picker.h"
+#include "lib/string_text_file_content.h"
+#include "lib/text_content.h"
+#include "lib/ffmpeg_subtitle_stream.h"
+#include "lib/ffmpeg_content.h"
+#include <wx/wx.h>
+#include <wx/clrpicker.h>
+#include <wx/spinctrl.h>
+#include <wx/gbsizer.h>
+
+using std::map;
+using boost::shared_ptr;
+using boost::bind;
+using boost::dynamic_pointer_cast;
+using boost::optional;
+
+int const SubtitleAppearanceDialog::NONE = 0;
+int const SubtitleAppearanceDialog::OUTLINE = 1;
+int const SubtitleAppearanceDialog::SHADOW = 2;
+
+SubtitleAppearanceDialog::SubtitleAppearanceDialog (wxWindow* parent, shared_ptr<Content> content, shared_ptr<TextContent> caption)
+       : wxDialog (parent, wxID_ANY, _("Caption appearance"))
+       , _content (content)
+       , _caption (caption)
+{
+       shared_ptr<FFmpegContent> ff = dynamic_pointer_cast<FFmpegContent> (content);
+       if (ff) {
+               _stream = ff->subtitle_stream ();
+       }
+
+       wxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+       SetSizer (overall_sizer);
+
+       _table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+
+       overall_sizer->Add (_table, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
+
+       int r = 0;
+
+       add_label_to_sizer (_table, this, _("Colour"), true, wxGBPosition (r, 0));
+       _force_colour = set_to (_colour = new wxColourPickerCtrl (this, wxID_ANY), r);
+
+       add_label_to_sizer (_table, this, _("Effect"), true, wxGBPosition (r, 0));
+       _force_effect = set_to (_effect = new wxChoice (this, wxID_ANY), r);
+
+       add_label_to_sizer (_table, this, _("Effect colour"), true, wxGBPosition (r, 0));
+       _force_effect_colour = set_to (_effect_colour = new wxColourPickerCtrl (this, wxID_ANY), r);
+
+       add_label_to_sizer (_table, this, _("Outline width"), true, wxGBPosition (r, 0));
+       _outline_width = new wxSpinCtrl (this, wxID_ANY);
+       _table->Add (_outline_width, wxGBPosition (r, 1));
+       ++r;
+
+       add_label_to_sizer (_table, this, _("Fade in time"), true, wxGBPosition (r, 0));
+       _force_fade_in = set_to (_fade_in = new Timecode<ContentTime> (this), r);
+
+       add_label_to_sizer (_table, this, _("Fade out time"), true, wxGBPosition (r, 0));
+       _force_fade_out = set_to (_fade_out = new Timecode<ContentTime> (this), r);
+
+       if (_stream) {
+               wxScrolled<wxPanel>* colours_panel = new wxScrolled<wxPanel> (this);
+               colours_panel->EnableScrolling (false, true);
+               colours_panel->ShowScrollbars (wxSHOW_SB_NEVER, wxSHOW_SB_ALWAYS);
+               colours_panel->SetScrollRate (0, 16);
+
+               wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+               table->AddGrowableCol (1, 1);
+
+               map<RGBA, RGBA> colours = _stream->colours ();
+
+               wxStaticText* t = new wxStaticText (colours_panel, wxID_ANY, "");
+               t->SetLabelMarkup (_("<b>Original colour</b>"));
+               table->Add (t, 1, wxEXPAND);
+               t = new wxStaticText (colours_panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
+               t->SetLabelMarkup (_("<b>New colour</b>"));
+               table->Add (t, 1, wxALIGN_CENTER);
+
+               for (map<RGBA, RGBA>::const_iterator i = colours.begin(); i != colours.end(); ++i) {
+                       wxPanel* from = new wxPanel (colours_panel, wxID_ANY);
+                       from->SetBackgroundColour (wxColour (i->first.r, i->first.g, i->first.b, i->first.a));
+                       table->Add (from, 1, wxEXPAND);
+                       RGBAColourPicker* to = new RGBAColourPicker (colours_panel, i->second);
+                       table->Add (to, 1, wxEXPAND);
+                       _pickers[i->first] = to;
+               }
+
+               colours_panel->SetSizer (table);
+
+               overall_sizer->Add (colours_panel, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
+
+               wxButton* restore = new wxButton (this, wxID_ANY, _("Restore to original colours"));
+               restore->Bind (wxEVT_BUTTON, bind (&SubtitleAppearanceDialog::restore, this));
+               overall_sizer->Add (restore, 0, wxALL, DCPOMATIC_SIZER_X_GAP);
+       }
+
+       wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
+       if (buttons) {
+               overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+       }
+
+       overall_sizer->Layout ();
+       overall_sizer->SetSizeHints (this);
+
+       /* Keep these Appends() up to date with NONE/OUTLINE/SHADOW variables */
+       _effect->Append (_("None"));
+       _effect->Append (_("Outline"));
+       _effect->Append (_("Shadow"));;
+
+       optional<dcp::Colour> colour = _caption->colour();
+       _force_colour->SetValue (static_cast<bool>(colour));
+       if (colour) {
+               _colour->SetColour (wxColour (colour->r, colour->g, colour->b));
+       } else {
+               _colour->SetColour (wxColour (255, 255, 255));
+       }
+
+       optional<dcp::Effect> effect = _caption->effect();
+       _force_effect->SetValue (static_cast<bool>(effect));
+       if (effect) {
+               switch (*effect) {
+               case dcp::NONE:
+                       _effect->SetSelection (NONE);
+                       break;
+               case dcp::BORDER:
+                       _effect->SetSelection (OUTLINE);
+                       break;
+               case dcp::SHADOW:
+                       _effect->SetSelection (SHADOW);
+                       break;
+               }
+       } else {
+               _effect->SetSelection (NONE);
+       }
+
+       optional<dcp::Colour> effect_colour = _caption->effect_colour();
+       _force_effect_colour->SetValue (static_cast<bool>(effect_colour));
+       if (effect_colour) {
+               _effect_colour->SetColour (wxColour (effect_colour->r, effect_colour->g, effect_colour->b));
+       } else {
+               _effect_colour->SetColour (wxColour (0, 0, 0));
+       }
+
+       optional<ContentTime> fade_in = _caption->fade_in();
+       _force_fade_in->SetValue (static_cast<bool>(fade_in));
+       if (fade_in) {
+               _fade_in->set (*fade_in, _content->active_video_frame_rate());
+       } else {
+               _fade_in->set (ContentTime(), _content->active_video_frame_rate());
+       }
+
+       optional<ContentTime> fade_out = _caption->fade_out();
+       _force_fade_out->SetValue (static_cast<bool>(fade_out));
+       if (fade_out) {
+               _fade_out->set (*fade_out, _content->active_video_frame_rate ());
+       } else {
+               _fade_out->set (ContentTime(), _content->active_video_frame_rate ());
+       }
+
+       _outline_width->SetValue (_caption->outline_width ());
+
+       _force_colour->Bind (wxEVT_CHECKBOX, bind (&SubtitleAppearanceDialog::setup_sensitivity, this));
+       _force_effect_colour->Bind (wxEVT_CHECKBOX, bind (&SubtitleAppearanceDialog::setup_sensitivity, this));
+       _force_effect->Bind (wxEVT_CHECKBOX, bind (&SubtitleAppearanceDialog::setup_sensitivity, this));
+       _force_fade_in->Bind (wxEVT_CHECKBOX, bind (&SubtitleAppearanceDialog::setup_sensitivity, this));
+       _force_fade_out->Bind (wxEVT_CHECKBOX, bind (&SubtitleAppearanceDialog::setup_sensitivity, this));
+       _effect->Bind (wxEVT_CHOICE, bind (&SubtitleAppearanceDialog::setup_sensitivity, this));
+       _content_connection = _content->Changed.connect (bind (&SubtitleAppearanceDialog::setup_sensitivity, this));
+
+       setup_sensitivity ();
+}
+
+wxCheckBox*
+SubtitleAppearanceDialog::set_to (wxWindow* w, int& r)
+{
+       wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+       wxCheckBox* set_to = new wxCheckBox (this, wxID_ANY, _("Set to"));
+       s->Add (set_to, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 8);
+       s->Add (w, 0, wxALIGN_CENTER_VERTICAL);
+       _table->Add (s, wxGBPosition (r, 1));
+       ++r;
+       return set_to;
+}
+
+void
+SubtitleAppearanceDialog::apply ()
+{
+       if (_force_colour->GetValue ()) {
+               wxColour const c = _colour->GetColour ();
+               _caption->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue()));
+       } else {
+               _caption->unset_colour ();
+       }
+       if (_force_effect->GetValue()) {
+               switch (_effect->GetSelection()) {
+               case NONE:
+                       _caption->set_effect (dcp::NONE);
+                       break;
+               case OUTLINE:
+                       _caption->set_effect (dcp::BORDER);
+                       break;
+               case SHADOW:
+                       _caption->set_effect (dcp::SHADOW);
+                       break;
+               }
+       } else {
+               _caption->unset_effect ();
+       }
+       if (_force_effect_colour->GetValue ()) {
+               wxColour const ec = _effect_colour->GetColour ();
+               _caption->set_effect_colour (dcp::Colour (ec.Red(), ec.Green(), ec.Blue()));
+       } else {
+               _caption->unset_effect_colour ();
+       }
+       if (_force_fade_in->GetValue ()) {
+               _caption->set_fade_in (_fade_in->get (_content->active_video_frame_rate ()));
+       } else {
+               _caption->unset_fade_in ();
+       }
+       if (_force_fade_out->GetValue ()) {
+               _caption->set_fade_out (_fade_out->get (_content->active_video_frame_rate ()));
+       } else {
+               _caption->unset_fade_out ();
+       }
+       _caption->set_outline_width (_outline_width->GetValue ());
+
+       if (_stream) {
+               for (map<RGBA, RGBAColourPicker*>::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) {
+                       _stream->set_colour (i->first, i->second->colour ());
+               }
+       }
+
+       shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (_content);
+       if (fc) {
+               fc->signal_subtitle_stream_changed ();
+       }
+}
+
+void
+SubtitleAppearanceDialog::restore ()
+{
+       for (map<RGBA, RGBAColourPicker*>::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) {
+               i->second->set (i->first);
+       }
+}
+
+void
+SubtitleAppearanceDialog::setup_sensitivity ()
+{
+       _colour->Enable (_force_colour->GetValue ());
+       _effect_colour->Enable (_force_effect_colour->GetValue ());
+       _effect->Enable (_force_effect->GetValue ());
+       _fade_in->Enable (_force_fade_in->GetValue ());
+       _fade_out->Enable (_force_fade_out->GetValue ());
+
+       bool const can_outline_width = _effect->GetSelection() == OUTLINE && _caption->burn ();
+       _outline_width->Enable (can_outline_width);
+       if (can_outline_width) {
+               _outline_width->UnsetToolTip ();
+       } else {
+               _outline_width->SetToolTip (_("Outline width cannot be set unless you are burning in captions"));
+       }
+}
diff --git a/src/wx/subtitle_appearance_dialog.h b/src/wx/subtitle_appearance_dialog.h
new file mode 100644 (file)
index 0000000..6cced71
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+    Copyright (C) 2015-2018 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 "timecode.h"
+#include "lib/rgba.h"
+#include <wx/wx.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/signals2.hpp>
+
+class wxRadioButton;
+class wxColourPickerCtrl;
+class wxGridBagSizer;
+class Content;
+class RGBAColourPicker;
+class FFmpegSubtitleStream;
+class wxCheckBox;
+class wxWidget;
+
+class SubtitleAppearanceDialog : public wxDialog
+{
+public:
+       SubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr<Content> content, boost::shared_ptr<TextContent> caption);
+
+       void apply ();
+
+private:
+       void setup_sensitivity ();
+       void restore ();
+       wxCheckBox* set_to (wxWindow* w, int& r);
+
+       wxCheckBox* _force_colour;
+       wxColourPickerCtrl* _colour;
+       wxCheckBox* _force_effect;
+       wxChoice* _effect;
+       wxCheckBox* _force_effect_colour;
+       wxColourPickerCtrl* _effect_colour;
+       wxCheckBox* _force_fade_in;
+       Timecode<ContentTime>* _fade_in;
+       wxCheckBox* _force_fade_out;
+       Timecode<ContentTime>* _fade_out;
+       wxSpinCtrl* _outline_width;
+       wxGridBagSizer* _table;
+       std::map<RGBA, RGBAColourPicker*> _pickers;
+
+       boost::shared_ptr<Content> _content;
+       boost::shared_ptr<TextContent> _caption;
+       boost::shared_ptr<FFmpegSubtitleStream> _stream;
+
+       boost::signals2::scoped_connection _content_connection;
+
+       static int const NONE;
+       static int const OUTLINE;
+       static int const SHADOW;
+};
diff --git a/src/wx/text_panel.cc b/src/wx/text_panel.cc
new file mode 100644 (file)
index 0000000..bc11834
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+    Copyright (C) 2012-2018 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 "text_panel.h"
+#include "film_editor.h"
+#include "wx_util.h"
+#include "text_view.h"
+#include "content_panel.h"
+#include "fonts_dialog.h"
+#include "subtitle_appearance_dialog.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/string_text_file_content.h"
+#include "lib/ffmpeg_subtitle_stream.h"
+#include "lib/dcp_subtitle_content.h"
+#include "lib/string_text_file_decoder.h"
+#include "lib/dcp_subtitle_decoder.h"
+#include "lib/dcp_content.h"
+#include "lib/text_content.h"
+#include "lib/decoder_factory.h"
+#include <wx/spinctrl.h>
+#include <boost/foreach.hpp>
+
+using std::vector;
+using std::string;
+using std::list;
+using std::cout;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+TextPanel::TextPanel (ContentPanel* p, TextType t)
+       : ContentSubPanel (p, std_to_wx(caption_type_to_name(t)))
+       , _caption_view (0)
+       , _fonts_dialog (0)
+       , _original_type (t)
+{
+       wxBoxSizer* reference_sizer = new wxBoxSizer (wxVERTICAL);
+
+       _reference = new wxCheckBox (this, wxID_ANY, _("Use this DCP's subtitle as OV and make VF"));
+       reference_sizer->Add (_reference, 0, wxLEFT | wxRIGHT | wxTOP, DCPOMATIC_SIZER_GAP);
+
+       _reference_note = new wxStaticText (this, wxID_ANY, _(""));
+       _reference_note->Wrap (200);
+       reference_sizer->Add (_reference_note, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
+       wxFont font = _reference_note->GetFont();
+       font.SetStyle(wxFONTSTYLE_ITALIC);
+       font.SetPointSize(font.GetPointSize() - 1);
+       _reference_note->SetFont(font);
+
+       _sizer->Add (reference_sizer);
+
+       wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+       _sizer->Add (grid, 0, wxALL, 8);
+       int r = 0;
+
+       wxBoxSizer* use = new wxBoxSizer (wxHORIZONTAL);
+       _use = new wxCheckBox (this, wxID_ANY, _("Use as"));
+       use->Add (_use, 0, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
+       _type = new wxChoice (this, wxID_ANY);
+       _type->Append (_("subtitles (open captions)"));
+       _type->Append (_("closed captions"));
+       use->Add (_type, 1, wxEXPAND, 0);
+       grid->Add (use, wxGBPosition (r, 0), wxGBSpan (1, 2));
+       ++r;
+
+       _burn = new wxCheckBox (this, wxID_ANY, _("Burn subtitles into image"));
+       grid->Add (_burn, wxGBPosition (r, 0), wxGBSpan (1, 2));
+       ++r;
+
+       {
+               add_label_to_sizer (grid, this, _("X Offset"), true, wxGBPosition (r, 0));
+               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               _x_offset = new wxSpinCtrl (this);
+               s->Add (_x_offset);
+               add_label_to_sizer (s, this, _("%"), false);
+               grid->Add (s, wxGBPosition (r, 1));
+               ++r;
+       }
+
+       {
+               add_label_to_sizer (grid, this, _("Y Offset"), true, wxGBPosition (r, 0));
+               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               _y_offset = new wxSpinCtrl (this);
+               s->Add (_y_offset);
+               add_label_to_sizer (s, this, _("%"), false);
+               grid->Add (s, wxGBPosition (r, 1));
+               ++r;
+       }
+
+       {
+               add_label_to_sizer (grid, this, _("X Scale"), true, wxGBPosition (r, 0));
+               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               _x_scale = new wxSpinCtrl (this);
+               s->Add (_x_scale);
+               add_label_to_sizer (s, this, _("%"), false);
+               grid->Add (s, wxGBPosition (r, 1));
+               ++r;
+       }
+
+       {
+               add_label_to_sizer (grid, this, _("Y Scale"), true, wxGBPosition (r, 0));
+               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               _y_scale = new wxSpinCtrl (this);
+               s->Add (_y_scale);
+               add_label_to_sizer (s, this, _("%"), false);
+               grid->Add (s, wxGBPosition (r, 1));
+               ++r;
+       }
+
+       {
+               add_label_to_sizer (grid, this, _("Line spacing"), true, wxGBPosition (r, 0));
+               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               _line_spacing = new wxSpinCtrl (this);
+               s->Add (_line_spacing);
+               add_label_to_sizer (s, this, _("%"), false);
+               grid->Add (s, wxGBPosition (r, 1));
+               ++r;
+       }
+
+       add_label_to_sizer (grid, this, _("Language"), true, wxGBPosition (r, 0));
+       _language = new wxTextCtrl (this, wxID_ANY);
+       grid->Add (_language, wxGBPosition (r, 1));
+       ++r;
+
+       add_label_to_sizer (grid, this, _("Stream"), true, wxGBPosition (r, 0));
+       _stream = new wxChoice (this, wxID_ANY);
+       grid->Add (_stream, wxGBPosition (r, 1));
+       ++r;
+
+       {
+               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+
+               _caption_view_button = new wxButton (this, wxID_ANY, _("View..."));
+               s->Add (_caption_view_button, 1, wxALL, DCPOMATIC_SIZER_GAP);
+               _fonts_dialog_button = new wxButton (this, wxID_ANY, _("Fonts..."));
+               s->Add (_fonts_dialog_button, 1, wxALL, DCPOMATIC_SIZER_GAP);
+               _appearance_dialog_button = new wxButton (this, wxID_ANY, _("Appearance..."));
+               s->Add (_appearance_dialog_button, 1, wxALL, DCPOMATIC_SIZER_GAP);
+
+               grid->Add (s, wxGBPosition (r, 0), wxGBSpan (1, 2));
+               ++r;
+       }
+
+       _x_offset->SetRange (-100, 100);
+       _y_offset->SetRange (-100, 100);
+       _x_scale->SetRange (10, 1000);
+       _y_scale->SetRange (10, 1000);
+       _line_spacing->SetRange (10, 1000);
+
+       _reference->Bind                (wxEVT_CHECKBOX, boost::bind (&TextPanel::reference_clicked, this));
+       _use->Bind                      (wxEVT_CHECKBOX, boost::bind (&TextPanel::use_toggled, this));
+       _type->Bind              (wxEVT_CHOICE,   boost::bind (&TextPanel::type_changed, this));
+       _burn->Bind                     (wxEVT_CHECKBOX, boost::bind (&TextPanel::burn_toggled, this));
+       _x_offset->Bind                 (wxEVT_SPINCTRL, boost::bind (&TextPanel::x_offset_changed, this));
+       _y_offset->Bind                 (wxEVT_SPINCTRL, boost::bind (&TextPanel::y_offset_changed, this));
+       _x_scale->Bind                  (wxEVT_SPINCTRL, boost::bind (&TextPanel::x_scale_changed, this));
+       _y_scale->Bind                  (wxEVT_SPINCTRL, boost::bind (&TextPanel::y_scale_changed, this));
+       _line_spacing->Bind             (wxEVT_SPINCTRL, boost::bind (&TextPanel::line_spacing_changed, this));
+       _language->Bind                 (wxEVT_TEXT,     boost::bind (&TextPanel::language_changed, this));
+       _stream->Bind                   (wxEVT_CHOICE,   boost::bind (&TextPanel::stream_changed, this));
+       _caption_view_button->Bind      (wxEVT_BUTTON,   boost::bind (&TextPanel::caption_view_clicked, this));
+       _fonts_dialog_button->Bind      (wxEVT_BUTTON,   boost::bind (&TextPanel::fonts_dialog_clicked, this));
+       _appearance_dialog_button->Bind (wxEVT_BUTTON,   boost::bind (&TextPanel::appearance_dialog_clicked, this));
+}
+
+void
+TextPanel::film_changed (Film::Property property)
+{
+       if (property == Film::CONTENT || property == Film::REEL_TYPE) {
+               setup_sensitivity ();
+       }
+}
+
+void
+TextPanel::film_content_changed (int property)
+{
+       FFmpegContentList fc = _parent->selected_ffmpeg ();
+       ContentList sc = _parent->selected_caption ();
+
+       shared_ptr<FFmpegContent> fcs;
+       if (fc.size() == 1) {
+               fcs = fc.front ();
+       }
+
+       shared_ptr<Content> scs;
+       if (sc.size() == 1) {
+               scs = sc.front ();
+       }
+
+       shared_ptr<TextContent> caption;
+       if (scs) {
+               caption = scs->caption_of_original_type(_original_type);
+       }
+
+       if (property == FFmpegContentProperty::SUBTITLE_STREAMS) {
+               _stream->Clear ();
+               if (fcs) {
+                       vector<shared_ptr<FFmpegSubtitleStream> > s = fcs->subtitle_streams ();
+                       for (vector<shared_ptr<FFmpegSubtitleStream> >::iterator i = s.begin(); i != s.end(); ++i) {
+                               _stream->Append (std_to_wx ((*i)->name), new wxStringClientData (std_to_wx ((*i)->identifier ())));
+                       }
+
+                       if (fcs->subtitle_stream()) {
+                               checked_set (_stream, fcs->subtitle_stream()->identifier ());
+                       } else {
+                               _stream->SetSelection (wxNOT_FOUND);
+                       }
+               }
+               setup_sensitivity ();
+       } else if (property == TextContentProperty::USE) {
+               checked_set (_use, caption ? caption->use() : false);
+               setup_sensitivity ();
+       } else if (property == TextContentProperty::TYPE) {
+               if (caption) {
+                       switch (caption->type()) {
+                       case CAPTION_OPEN:
+                               _type->SetSelection (0);
+                               break;
+                       case CAPTION_CLOSED:
+                               _type->SetSelection (1);
+                               break;
+                       default:
+                               DCPOMATIC_ASSERT (false);
+                       }
+               } else {
+                       _type->SetSelection (0);
+               }
+               setup_sensitivity ();
+       } else if (property == TextContentProperty::BURN) {
+               checked_set (_burn, caption ? caption->burn() : false);
+       } else if (property == TextContentProperty::X_OFFSET) {
+               checked_set (_x_offset, caption ? lrint (caption->x_offset() * 100) : 0);
+       } else if (property == TextContentProperty::Y_OFFSET) {
+               checked_set (_y_offset, caption ? lrint (caption->y_offset() * 100) : 0);
+       } else if (property == TextContentProperty::X_SCALE) {
+               checked_set (_x_scale, caption ? lrint (caption->x_scale() * 100) : 100);
+       } else if (property == TextContentProperty::Y_SCALE) {
+               checked_set (_y_scale, caption ? lrint (caption->y_scale() * 100) : 100);
+       } else if (property == TextContentProperty::LINE_SPACING) {
+               checked_set (_line_spacing, caption ? lrint (caption->line_spacing() * 100) : 100);
+       } else if (property == TextContentProperty::LANGUAGE) {
+               checked_set (_language, caption ? caption->language() : "");
+       } else if (property == DCPContentProperty::REFERENCE_CAPTION) {
+               if (scs) {
+                       shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (scs);
+                       checked_set (_reference, dcp ? dcp->reference_caption(_original_type) : false);
+               } else {
+                       checked_set (_reference, false);
+               }
+
+               setup_sensitivity ();
+       } else if (property == DCPContentProperty::CAPTIONS) {
+               setup_sensitivity ();
+       }
+}
+
+void
+TextPanel::use_toggled ()
+{
+       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption()) {
+               i->caption_of_original_type(_original_type)->set_use (_use->GetValue());
+       }
+}
+
+void
+TextPanel::type_changed ()
+{
+       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_caption()) {
+               switch (_type->GetSelection()) {
+               case 0:
+                       i->caption_of_original_type(_original_type)->set_type (CAPTION_OPEN);
+