Fix incorrect preview of italic subtitles (#728).
authorCarl Hetherington <cth@carlh.net>
Mon, 2 Nov 2015 22:47:57 +0000 (22:47 +0000)
committerCarl Hetherington <cth@carlh.net>
Mon, 2 Nov 2015 22:47:57 +0000 (22:47 +0000)
This requires fonts to be configured with their italic versions so
that the italic font can be given to FontConfig and hence used to
render text.  Bold font configuration is also added here although
bold subtitles aren't yet supported.

19 files changed:
ChangeLog
LiberationSans-Regular.ttf [deleted file]
src/lib/font.cc
src/lib/font.h
src/lib/font_files.cc [new file with mode: 0644]
src/lib/font_files.h [new file with mode: 0644]
src/lib/player_subtitles.cc
src/lib/reel_writer.cc
src/lib/render_subtitles.cc
src/lib/subtitle_content.cc
src/lib/wscript
src/wx/font_files_dialog.cc [new file with mode: 0644]
src/wx/font_files_dialog.h [new file with mode: 0644]
src/wx/fonts_dialog.cc
src/wx/fonts_dialog.h
src/wx/hints_dialog.cc
src/wx/screen_dialog.h
src/wx/wscript
test/srt_subtitle_test.cc

index c3177562df188ff0a7bb711cffd2c650980300df..46bac2276610c230cfd5eeccfb570fb049e1798e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2015-11-02  Carl Hetherington  <cth@carlh.net>
+
+       * Fix incorrect preview rendering of italic subtitles (#728).
+
 2015-10-30  c.hetherington  <cth@carlh.net>
 
        * Updated ru_RU translation from Igor Voytovich.
diff --git a/LiberationSans-Regular.ttf b/LiberationSans-Regular.ttf
deleted file mode 100644 (file)
index 626dd93..0000000
Binary files a/LiberationSans-Regular.ttf and /dev/null differ
index d729c26965a1490bbef7337701c20dd2eb1d1abd..e9df66cf71e339e49a31bbc5c78e9b5c9cc9664a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 #include "font.h"
+#include "dcpomatic_assert.h"
 #include <libxml++/libxml++.h>
+#include <boost/foreach.hpp>
+
+using std::string;
+
+static char const * names[] = {
+       "Normal",
+       "Italic",
+       "Bold"
+};
 
 Font::Font (cxml::NodePtr node)
        : _id (node->string_child ("Id"))
-       , _file (node->optional_string_child ("File"))
 {
+       DCPOMATIC_ASSERT (FontFiles::VARIANTS == 3);
 
+       BOOST_FOREACH (cxml::NodePtr i, node->node_children ("File")) {
+               string variant = i->optional_string_attribute("Variant").get_value_or ("Normal");
+               for (int j = 0; j < FontFiles::VARIANTS; ++j) {
+                       if (variant == names[j]) {
+                               _files.set (static_cast<FontFiles::Variant>(j), i->content());
+                       }
+               }
+       }
 }
 
 void
 Font::as_xml (xmlpp::Node* node)
 {
+       DCPOMATIC_ASSERT (FontFiles::VARIANTS == 3);
+
        node->add_child("Id")->add_child_text (_id);
-       if (_file) {
-               node->add_child("File")->add_child_text (_file.get().string ());
+       for (int i = 0; i < FontFiles::VARIANTS; ++i) {
+               if (_files.get(static_cast<FontFiles::Variant>(i))) {
+                       xmlpp::Element* e = node->add_child ("File");
+                       e->set_attribute ("Variant", names[i]);
+                       e->add_child_text (_files.get(static_cast<FontFiles::Variant>(i)).get().string ());
+               }
        }
 }
 
+
+bool
+operator== (Font const & a, Font const & b)
+{
+       if (a.id() != b.id()) {
+               return false;
+       }
+
+       for (int i = 0; i < FontFiles::VARIANTS; ++i) {
+               if (a.file(static_cast<FontFiles::Variant>(i)) != b.file(static_cast<FontFiles::Variant>(i))) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 bool
 operator!= (Font const & a, Font const & b)
 {
-       return (a.id() != b.id() || a.file() != b.file());
+       return !(a == b);
 }
index ca7650cc1b68d43406e2c8f27dd27e68762ea760..c165fea9fedcb30b7c5083f5c36725d99e7c5647 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef DCPOMATIC_FONT_H
 #define DCPOMATIC_FONT_H
 
+#include "font_files.h"
 #include <libcxml/cxml.h>
 #include <boost/optional.hpp>
 #include <boost/signals2.hpp>
@@ -40,12 +41,21 @@ public:
                return _id;
        }
 
-       boost::optional<boost::filesystem::path> file () const {
-               return _file;
+       boost::optional<boost::filesystem::path> file (FontFiles::Variant variant) const {
+               return _files.get (variant);
        }
 
-       void set_file (boost::filesystem::path file) {
-               _file = file;
+       void set_file (FontFiles::Variant variant, boost::filesystem::path file) {
+               _files.set (variant, file);
+               Changed ();
+       }
+
+       FontFiles files () const {
+               return _files;
+       }
+
+       void set_files (FontFiles files) {
+               _files = files;
                Changed ();
        }
 
@@ -54,10 +64,10 @@ public:
 private:
        /** Font ID, used to describe it in the subtitle content */
        std::string _id;
-       boost::optional<boost::filesystem::path> _file;
+       FontFiles _files;
 };
 
-bool
-operator!= (Font const & a, Font const & b);
+bool operator!= (Font const & a, Font const & b);
+bool operator== (Font const & a, Font const & b);
 
 #endif
diff --git a/src/lib/font_files.cc b/src/lib/font_files.cc
new file mode 100644 (file)
index 0000000..65b67f7
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "font_files.h"
+
+bool
+operator!= (FontFiles const & a, FontFiles const & b)
+{
+       for (int i = 0; i < FontFiles::VARIANTS; ++i) {
+               if (a.get(static_cast<FontFiles::Variant>(i)) != b.get(static_cast<FontFiles::Variant>(i))) {
+                       return false;
+               }
+       }
+
+       return true;
+}
diff --git a/src/lib/font_files.h b/src/lib/font_files.h
new file mode 100644 (file)
index 0000000..68e7ac0
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_FONT_FILES_H
+#define DCPOMATIC_FONT_FILES_H
+
+#include <boost/filesystem.hpp>
+#include <boost/optional.hpp>
+
+class FontFiles
+{
+public:
+       enum Variant {
+               NORMAL,
+               ITALIC,
+               BOLD,
+               VARIANTS
+       };
+
+       void set (Variant variant, boost::filesystem::path file) {
+               _file[variant] = file;
+       }
+
+       boost::optional<boost::filesystem::path> get (Variant variant) const {
+               return _file[variant];
+       }
+
+private:
+       boost::optional<boost::filesystem::path> _file[VARIANTS];
+};
+
+bool operator!= (FontFiles const & a, FontFiles const & b);
+
+#endif
index da5714349752c3c1ec45cfd5da30f96271d6627b..8838b2f64bd929d4818151094fe61bf323bf9d27 100644 (file)
@@ -30,7 +30,7 @@ PlayerSubtitles::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->file() == j->file()) {
+                       if (*i == *j) {
                                got = true;
                        }
                }
index 743f73dffcb0647467b6bf5c2ea34d30627ad175..803c2a8ed42ae266518aea5ca05edca905f7a39d 100644 (file)
@@ -363,17 +363,19 @@ ReelWriter::create_reel (list<ReferencedReelAsset> const & refs, list<shared_ptr
        }
 
        if (_subtitle_asset) {
-               boost::filesystem::path liberation;
+
+               boost::filesystem::path liberation_normal;
                try {
-                       liberation = shared_path () / "LiberationSans-Regular.ttf";
+                       liberation_normal = shared_path () / "LiberationSans-Regular.ttf";
                } catch (boost::filesystem::filesystem_error& e) {
                        /* Hack: try the debian/ubuntu location if getting the shared path failed */
-                       liberation = "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf";
+                       liberation_normal = "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf";
                }
 
+
                /* Add all the fonts to the subtitle content */
                BOOST_FOREACH (shared_ptr<Font> j, fonts) {
-                       _subtitle_asset->add_font (j->id(), j->file().get_value_or (liberation));
+                       _subtitle_asset->add_font (j->id(), j->file(FontFiles::NORMAL).get_value_or(liberation_normal));
                }
 
                if (dynamic_pointer_cast<dcp::InteropSubtitleAsset> (_subtitle_asset)) {
index f507238fb59033fd078be4399e409b2f5d4676c6..9b5f585e59bf79dcc0ab86d0f06d05b7f7df88e4 100644 (file)
@@ -22,6 +22,7 @@
 #include "image.h"
 #include "cross.h"
 #include "font.h"
+#include "dcpomatic_assert.h"
 #include <cairomm/cairomm.h>
 #include <pangomm.h>
 #include <boost/foreach.hpp>
@@ -39,23 +40,30 @@ using boost::shared_ptr;
 using boost::optional;
 
 static FcConfig* fc_config = 0;
-static list<pair<boost::filesystem::path, string> > fc_config_fonts;
+static list<pair<FontFiles, string> > fc_config_fonts;
 
+/** @param subtitles A list of subtitles that are all on the same line */
 static PositionImage
-render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > fonts, dcp::Size target)
+render_line (list<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target)
 {
+       /* XXX: this method can only handle italic / bold changes mid-line,
+          nothing else yet.
+       */
+
+       DCPOMATIC_ASSERT (!subtitles.empty ());
+
        /* Calculate x and y scale factors.  These are only used to stretch
           the font away from its normal aspect ratio.
        */
        float xscale = 1;
        float yscale = 1;
-       if (fabs (subtitle.aspect_adjust() - 1.0) > dcp::ASPECT_ADJUST_EPSILON) {
-               if (subtitle.aspect_adjust() < 1) {
-                       xscale = max (0.25f, subtitle.aspect_adjust ());
+       if (fabs (subtitles.front().aspect_adjust() - 1.0) > dcp::ASPECT_ADJUST_EPSILON) {
+               if (subtitles.front().aspect_adjust() < 1) {
+                       xscale = max (0.25f, subtitles.front().aspect_adjust ());
                        yscale = 1;
                } else {
                        xscale = 1;
-                       yscale = 1 / min (4.0f, subtitle.aspect_adjust ());
+                       yscale = 1 / min (4.0f, subtitles.front().aspect_adjust ());
                }
        }
 
@@ -64,7 +72,7 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
        */
 
        /* Basic guess on height... */
-       int height = subtitle.size() * target.height / (11 * 72);
+       int height = subtitles.front().size() * target.height / (11 * 72);
        /* ...scaled... */
        height *= yscale;
        /* ...and add a bit more for luck */
@@ -87,22 +95,36 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
                fc_config = FcConfigCreate ();
        }
 
-       boost::filesystem::path font_file;
+       FontFiles font_files;
+
        try {
-               font_file = shared_path () / "LiberationSans-Regular.ttf";
+               font_files.set (FontFiles::NORMAL, shared_path () / "LiberationSans-Regular.ttf");
+               font_files.set (FontFiles::ITALIC, shared_path () / "LiberationSans-Italic.ttf");
+               font_files.set (FontFiles::BOLD, shared_path () / "LiberationSans-Bold.ttf");
        } catch (boost::filesystem::filesystem_error& e) {
-               /* Hack: try the debian/ubuntu location if getting the shared path failed */
-               font_file = "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf";
+
+       }
+
+       /* Hack: try the debian/ubuntu locations if getting the shared path failed */
+
+       if (!font_files.get(FontFiles::NORMAL) || !boost::filesystem::exists(font_files.get(FontFiles::NORMAL).get())) {
+               font_files.set (FontFiles::NORMAL, "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf");
+       }
+       if (!font_files.get(FontFiles::ITALIC) || !boost::filesystem::exists(font_files.get(FontFiles::ITALIC).get())) {
+               font_files.set (FontFiles::ITALIC, "/usr/share/fonts/truetype/liberation/LiberationSans-Italic.ttf");
+       }
+       if (!font_files.get(FontFiles::BOLD) || !boost::filesystem::exists(font_files.get(FontFiles::BOLD).get())) {
+               font_files.set (FontFiles::BOLD, "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf");
        }
 
        BOOST_FOREACH (shared_ptr<Font> i, fonts) {
-               if (i->id() == subtitle.font() && i->file ()) {
-                       font_file = i->file().get ();
+               if (i->id() == subtitles.front().font() && i->file(FontFiles::NORMAL)) {
+                       font_files = i->files ();
                }
        }
 
-       list<pair<boost::filesystem::path, string> >::const_iterator existing = fc_config_fonts.begin ();
-       while (existing != fc_config_fonts.end() && existing->first != font_file) {
+       list<pair<FontFiles, string> >::const_iterator existing = fc_config_fonts.begin ();
+       while (existing != fc_config_fonts.end() && existing->first != font_files) {
                ++existing;
        }
 
@@ -111,9 +133,16 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
                font_name = existing->second;
        } else {
                /* Make this font available to DCP-o-matic */
-               FcConfigAppFontAddFile (fc_config, reinterpret_cast<FcChar8 const *> (font_file.string().c_str ()));
+               for (int i = 0; i < FontFiles::VARIANTS; ++i) {
+                       if (font_files.get(static_cast<FontFiles::Variant>(i))) {
+                               FcConfigAppFontAddFile (
+                                       fc_config,
+                                       reinterpret_cast<FcChar8 const *> (font_files.get(static_cast<FontFiles::Variant>(i)).get().string().c_str())
+                                       );
+                       }
+               }
 
-               FcPattern* pattern = FcPatternBuild (0, FC_FILE, FcTypeString, font_file.string().c_str(), static_cast<char *> (0));
+               FcPattern* pattern = FcPatternBuild (0, FC_FILE, FcTypeString, font_files.get(FontFiles::NORMAL).get().string().c_str(), static_cast<char *> (0));
                FcObjectSet* object_set = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, static_cast<char *> (0));
                FcFontSet* font_set = FcFontList (fc_config, pattern, object_set);
                if (font_set) {
@@ -137,7 +166,7 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
                FcObjectSetDestroy (object_set);
                FcPatternDestroy (pattern);
 
-               fc_config_fonts.push_back (make_pair (font_file, font_name));
+               fc_config_fonts.push_back (make_pair (font_files, font_name));
        }
 
        FcConfigSetCurrent (fc_config);
@@ -151,12 +180,29 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
        /* Render the subtitle at the top left-hand corner of image */
 
        Pango::FontDescription font (font_name);
-       font.set_absolute_size (subtitle.size_in_pixels (target.height) * PANGO_SCALE);
-       if (subtitle.italic ()) {
-               font.set_style (Pango::STYLE_ITALIC);
-       }
+       font.set_absolute_size (subtitles.front().size_in_pixels (target.height) * PANGO_SCALE);
        layout->set_font_description (font);
-       layout->set_text (subtitle.text ());
+
+       string marked_up;
+       bool italic = false;
+       BOOST_FOREACH (dcp::SubtitleString const & i, subtitles) {
+               if (i.italic() != italic) {
+                       if (i.italic()) {
+                               marked_up += "<i>";
+                       } else {
+                               marked_up += "</i>";
+                       }
+                       italic = i.italic ();
+               }
+
+               marked_up += i.text ();
+       }
+
+       if (italic) {
+               marked_up += "</i>";
+       }
+
+       layout->set_markup (marked_up);
 
        /* Compute fade factor */
        /* XXX */
@@ -166,9 +212,9 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
 
        context->scale (xscale, yscale);
 
-       if (subtitle.effect() == dcp::SHADOW) {
+       if (subtitles.front().effect() == dcp::SHADOW) {
                /* Drop-shadow effect */
-               dcp::Colour const ec = subtitle.effect_colour ();
+               dcp::Colour const ec = subtitles.front().effect_colour ();
                context->set_source_rgba (float(ec.r) / 255, float(ec.g) / 255, float(ec.b) / 255, fade_factor);
                context->move_to (4, 4);
                layout->add_to_cairo_context (context);
@@ -177,15 +223,15 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
 
        /* The actual subtitle */
 
-       dcp::Colour const c = subtitle.colour ();
+       dcp::Colour const c = subtitles.front().colour ();
        context->set_source_rgba (float(c.r) / 255, float(c.g) / 255, float(c.b) / 255, fade_factor);
        context->move_to (0, 0);
        layout->add_to_cairo_context (context);
        context->fill ();
 
-       if (subtitle.effect() == dcp::BORDER) {
+       if (subtitles.front().effect() == dcp::BORDER) {
                /* Border effect */
-               dcp::Colour ec = subtitle.effect_colour ();
+               dcp::Colour ec = subtitles.front().effect_colour ();
                context->set_source_rgba (float(ec.r) / 255, float(ec.g) / 255, float(ec.b) / 255, fade_factor);
                context->move_to (0, 0);
                layout->add_to_cairo_context (context);
@@ -197,34 +243,34 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
        layout->get_size (layout_width, layout_height);
 
        int x = 0;
-       switch (subtitle.h_align ()) {
+       switch (subtitles.front().h_align ()) {
        case dcp::HALIGN_LEFT:
                /* h_position is distance between left of frame and left of subtitle */
-               x = subtitle.h_position() * target.width;
+               x = subtitles.front().h_position() * target.width;
                break;
        case dcp::HALIGN_CENTER:
                /* h_position is distance between centre of frame and centre of subtitle */
-               x = (0.5 + subtitle.h_position()) * target.width - layout_width / (PANGO_SCALE * 2);
+               x = (0.5 + subtitles.front().h_position()) * target.width - layout_width / (PANGO_SCALE * 2);
                break;
        case dcp::HALIGN_RIGHT:
                /* h_position is distance between right of frame and right of subtitle */
-               x = (1.0 - subtitle.h_position()) * target.width - layout_width / PANGO_SCALE;
+               x = (1.0 - subtitles.front().h_position()) * target.width - layout_width / PANGO_SCALE;
                break;
        }
 
        int y = 0;
-       switch (subtitle.v_align ()) {
+       switch (subtitles.front().v_align ()) {
        case dcp::VALIGN_TOP:
                /* v_position is distance between top of frame and top of subtitle */
-               y = subtitle.v_position() * target.height;
+               y = subtitles.front().v_position() * target.height;
                break;
        case dcp::VALIGN_CENTER:
                /* v_position is distance between centre of frame and centre of subtitle */
-               y = (0.5 + subtitle.v_position()) * target.height - layout_height / (PANGO_SCALE * 2);
+               y = (0.5 + subtitles.front().v_position()) * target.height - layout_height / (PANGO_SCALE * 2);
                break;
        case dcp::VALIGN_BOTTOM:
                /* v_position is distance between bottom of frame and bottom of subtitle */
-               y = (1.0 - subtitle.v_position()) * target.height - layout_height / PANGO_SCALE;
+               y = (1.0 - subtitles.front().v_position()) * target.height - layout_height / PANGO_SCALE;
                break;
        }
 
@@ -234,9 +280,20 @@ render_subtitle (dcp::SubtitleString const & subtitle, list<shared_ptr<Font> > f
 list<PositionImage>
 render_subtitles (list<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target)
 {
+       list<dcp::SubtitleString> pending;
        list<PositionImage> images;
+
        BOOST_FOREACH (dcp::SubtitleString const & i, subtitles) {
-               images.push_back (render_subtitle (i, fonts, target));
+               if (!pending.empty() && fabs (i.v_position() - pending.back().v_position()) > 1e-4) {
+                       images.push_back (render_line (pending, fonts, target));
+                       pending.clear ();
+               }
+               pending.push_back (i);
        }
+
+       if (!pending.empty ()) {
+               images.push_back (render_line (pending, fonts, target));
+       }
+
        return images;
 }
index 2f4c88975cfa98bb55bddcfcc6e5919c12d38f00..d779a764299f39f99c32afe0759267b56c7d1865 100644 (file)
@@ -275,7 +275,9 @@ SubtitleContent::identifier () const
           types of subtitle content involve fonts.
        */
        BOOST_FOREACH (shared_ptr<Font> f, _fonts) {
-               s << f->file().get_value_or ("Default");
+               for (int i = 0; i < FontFiles::VARIANTS; ++i) {
+                       s << "_" << f->file(static_cast<FontFiles::Variant>(i)).get_value_or ("Default");
+               }
        }
 
        /* The language is for metadata only, and doesn't affect
index c6c73662b6fa54dff121702af951b88367ea7f8e..263d222c2f71813ae72e844b0a6dd0cb942a044d 100644 (file)
@@ -72,6 +72,7 @@ sources = """
           film.cc
           filter.cc
           font.cc
+          font_files.cc
           frame_rate_change.cc
           internet.cc
           image.cc
diff --git a/src/wx/font_files_dialog.cc b/src/wx/font_files_dialog.cc
new file mode 100644 (file)
index 0000000..080a8eb
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "font_files_dialog.h"
+#include "system_font_dialog.h"
+
+using boost::bind;
+
+FontFilesDialog::FontFilesDialog (wxWindow* parent, FontFiles files)
+#ifdef DCPOMATIC_WINDOWS
+       : TableDialog (parent, _("Fonts"), 4, 1, true)
+#else
+       : TableDialog (parent, _("Fonts"), 3, 1, true)
+#endif
+       , _files (files)
+{
+       wxString labels[] = {
+               _("Normal font"),
+               _("Italic font"),
+               _("Bold font")
+       };
+
+       DCPOMATIC_ASSERT (FontFiles::VARIANTS == 3);
+
+       for (int i = 0; i < FontFiles::VARIANTS; ++i) {
+               add (labels[i], true);
+               _name[i] = new wxStaticText (
+                       this, wxID_ANY,
+                       std_to_wx(_files.get(static_cast<FontFiles::Variant>(i)).get_value_or("").string()),
+                       wxDefaultPosition,
+                       wxSize (200, -1)
+                       );
+               _table->Add (_name[i], 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
+               add (_set_file[i] = new wxButton (this, wxID_ANY, _("Set from file...")));
+               _set_file[i]->Bind (wxEVT_COMMAND_BUTTON_CLICKED, bind (&FontFilesDialog::set_from_file_clicked, this, static_cast<FontFiles::Variant>(i)));
+#ifdef DCPOMATIC_WINDOWS
+               add (_set_system[i] = new wxButton (this, wxID_ANY, _("Set from system font...")));
+               _set_system[i]->Bind (wxEVT_COMMAND_BUTTON_CLICKED, bind (&FontFilesDialog::set_from_system_clicked, this, static_cast<FontFiles::Variant>(i)));
+#endif
+       }
+
+       layout ();
+}
+
+void
+FontFilesDialog::set_from_file_clicked (FontFiles::Variant variant)
+{
+       /* The wxFD_CHANGE_DIR here prevents a `could not set working directory' error 123 on Windows when using
+          non-Latin filenames or paths.
+       */
+       wxString default_dir = "";
+#ifdef DCPOMATIC_LINUX
+       if (boost::filesystem::exists ("/usr/share/fonts/truetype")) {
+               default_dir = "/usr/share/fonts/truetype";
+       } else {
+               default_dir = "/usr/share/fonts";
+       }
+#endif
+#ifdef DCPOMATIC_OSX
+       default_dir = "/System/Library/Fonts";
+#endif
+
+       wxFileDialog* d = new wxFileDialog (this, _("Choose a font file"), default_dir, wxT (""), wxT ("*.ttf"), wxFD_CHANGE_DIR);
+       int const r = d->ShowModal ();
+
+       if (r != wxID_OK) {
+               d->Destroy ();
+               return;
+       }
+
+       set (variant, wx_to_std (d->GetPath ()));
+       d->Destroy ();
+}
+
+#ifdef DCPOMATIC_WINDOWS
+void
+FontFilesDialog::set_from_system_clicked (FontFiles::Variant variant)
+{
+       SystemFontDialog* d = new SystemFontDialog (this);
+       int const r = d->ShowModal ();
+
+       if (r != wxID_OK) {
+               d->Destroy ();
+               return;
+       }
+
+       set (variant, d->get_font().get());
+       d->Destroy ();
+}
+#endif
+
+void
+FontFilesDialog::set (FontFiles::Variant variant, boost::filesystem::path path)
+{
+       _files.set (variant, path);
+       _name[variant]->SetLabel (std_to_wx (path.leaf().string()));
+}
diff --git a/src/wx/font_files_dialog.h b/src/wx/font_files_dialog.h
new file mode 100644 (file)
index 0000000..dd53cd5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "table_dialog.h"
+#include "wx_util.h"
+#include "lib/font_files.h"
+
+class FontFilesDialog : public TableDialog
+{
+public:
+       FontFilesDialog (wxWindow* parent, FontFiles files);
+
+       FontFiles get () const {
+               return _files;
+       }
+
+private:
+       void set_from_file_clicked (FontFiles::Variant variant);
+#ifdef DCPOMATIC_WINDOWS
+       void set_from_system_clicked (FontFiles::Variant variant);
+#endif
+       void set (FontFiles::Variant variant, boost::filesystem::path path);
+
+       FontFiles _files;
+
+       wxStaticText* _name[FontFiles::VARIANTS];
+       wxButton* _set_file[FontFiles::VARIANTS];
+
+#ifdef DCPOMATIC_WINDOWS
+       wxButton* _set_system[FontFiles::VARIANTS];
+#endif
+};
index ff5b107c9f8f336653382d45f5c54ef8d97042c8..b79580d7cf9ad5dec487e5ca4bd86f6ced062f81 100644 (file)
@@ -20,6 +20,7 @@
 #include "fonts_dialog.h"
 #include "wx_util.h"
 #include "system_font_dialog.h"
+#include "font_files_dialog.h"
 #include "lib/font.h"
 #include "lib/subtitle_content.h"
 #include <wx/wx.h>
@@ -34,9 +35,8 @@ using boost::shared_ptr;
 FontsDialog::FontsDialog (wxWindow* parent, shared_ptr<SubtitleContent> content)
        : wxDialog (parent, wxID_ANY, _("Fonts"))
        , _content (content)
-       , _set_from_system (0)
 {
-       _fonts = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
+       _fonts = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (550, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
 
        {
                wxListItem ip;
@@ -49,25 +49,33 @@ FontsDialog::FontsDialog (wxWindow* parent, shared_ptr<SubtitleContent> content)
        {
                wxListItem ip;
                ip.SetId (1);
-               ip.SetText (_("Font file"));
-               ip.SetWidth (300);
+               ip.SetText (_("Normal file"));
+               ip.SetWidth (150);
                _fonts->InsertColumn (1, ip);
        }
 
-       wxBoxSizer* sizer = new wxBoxSizer (wxHORIZONTAL);
-       sizer->Add (_fonts, 1, wxEXPAND);
+       {
+               wxListItem ip;
+               ip.SetId (2);
+               ip.SetText (_("Italic file"));
+               ip.SetWidth (150);
+               _fonts->InsertColumn (2, ip);
+       }
 
        {
-               wxSizer* s = new wxBoxSizer (wxVERTICAL);
-               _set_from_file = new wxButton (this, wxID_ANY, _("Set from .ttf file..."));
-               s->Add (_set_from_file, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-#ifdef DCPOMATIC_WINDOWS
-               _set_from_system = new wxButton (this, wxID_ANY, _("Set from system font..."));
-               s->Add (_set_from_system, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
-#endif
-               sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
+               wxListItem ip;
+               ip.SetId (3);
+               ip.SetText (_("Bold file"));
+               ip.SetWidth (150);
+               _fonts->InsertColumn (3, ip);
        }
 
+       wxBoxSizer* sizer = new wxBoxSizer (wxHORIZONTAL);
+       sizer->Add (_fonts, 1, wxEXPAND | wxLEFT | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
+
+       _edit = new wxButton (this, wxID_ANY, _("Edit..."));
+       sizer->Add (_edit, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+
        wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
        overall_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_X_GAP);
 
@@ -78,10 +86,7 @@ FontsDialog::FontsDialog (wxWindow* parent, shared_ptr<SubtitleContent> content)
 
        SetSizerAndFit (overall_sizer);
 
-       _set_from_file->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FontsDialog::set_from_file_clicked, this));
-       if (_set_from_system) {
-               _set_from_system->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FontsDialog::set_from_system_clicked, this));
-       }
+       _edit->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FontsDialog::edit_clicked, this));
        _fonts->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&FontsDialog::selection_changed, this));
        _fonts->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&FontsDialog::selection_changed, this));
 
@@ -97,15 +102,14 @@ FontsDialog::setup ()
        }
 
        _fonts->DeleteAllItems ();
-       list<shared_ptr<Font> > fonts = content->fonts ();
        size_t n = 0;
-       for (list<shared_ptr<Font> >::const_iterator i = fonts.begin(); i != fonts.end(); ++i) {
+       BOOST_FOREACH (shared_ptr<Font> i, content->fonts ()) {
                wxListItem item;
                item.SetId (n);
                _fonts->InsertItem (item);
-               _fonts->SetItem (n, 0, std_to_wx ((*i)->id ()));
-               if ((*i)->file ()) {
-                       _fonts->SetItem (n, 1, (*i)->file().get().leaf().string ());
+               _fonts->SetItem (n, 0, std_to_wx (i->id ()));
+               if (i->file(FontFiles::NORMAL)) {
+                       _fonts->SetItem (n, 1, i->file(FontFiles::NORMAL).get().leaf().string ());
                }
                ++n;
        }
@@ -114,86 +118,44 @@ FontsDialog::setup ()
 }
 
 void
-FontsDialog::set_from_file_clicked ()
+FontsDialog::selection_changed ()
 {
-       /* The wxFD_CHANGE_DIR here prevents a `could not set working directory' error 123 on Windows when using
-          non-Latin filenames or paths.
-       */
-       wxString default_dir = "";
-#ifdef DCPOMATIC_LINUX
-       if (boost::filesystem::exists ("/usr/share/fonts/truetype")) {
-               default_dir = "/usr/share/fonts/truetype";
-       } else {
-               default_dir = "/usr/share/fonts";
-       }
-#endif
-#ifdef DCPOMATIC_OSX
-       default_dir = "/System/Library/Fonts";
-#endif
-
-       wxFileDialog* d = new wxFileDialog (this, _("Choose a font file"), default_dir, wxT (""), wxT ("*.ttf"), wxFD_CHANGE_DIR);
-       int const r = d->ShowModal ();
-
-       if (r != wxID_OK) {
-               d->Destroy ();
-               return;
-       }
-
-       set_selected_font_file (wx_to_std (d->GetPath ()));
-       d->Destroy ();
+       setup_sensitivity ();
 }
 
 void
-FontsDialog::set_from_system_clicked ()
+FontsDialog::setup_sensitivity ()
 {
-       SystemFontDialog* d = new SystemFontDialog (this);
-       int const r = d->ShowModal ();
-
-       if (r != wxID_OK) {
-               d->Destroy ();
-               return;
-       }
-
-       set_selected_font_file (d->get_font().get ());
-       d->Destroy ();
+       int const item = _fonts->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+       _edit->Enable (item != -1);
 }
 
 void
-FontsDialog::set_selected_font_file (boost::filesystem::path file)
+FontsDialog::edit_clicked ()
 {
        shared_ptr<SubtitleContent> content = _content.lock ();
        if (!content) {
                return;
        }
 
-       int item = _fonts->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-       if (item == -1) {
-               return;
-       }
-
-       string id = wx_to_std (_fonts->GetItemText (item, 0));
-
-       BOOST_FOREACH (shared_ptr<Font> i, content->fonts ()) {
+       int const item = _fonts->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+       string const id = wx_to_std (_fonts->GetItemText (item, 0));
+       shared_ptr<Font> font;
+       BOOST_FOREACH (shared_ptr<Font> i, content->fonts()) {
                if (i->id() == id) {
-                       i->set_file (file);
+                       font = i;
                }
        }
 
-       setup ();
-}
-
-void
-FontsDialog::selection_changed ()
-{
-       setup_sensitivity ();
-}
+       if (!font) {
+               return;
+       }
 
-void
-FontsDialog::setup_sensitivity ()
-{
-       int const item = _fonts->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-       _set_from_file->Enable (item != -1);
-       if (_set_from_system) {
-               _set_from_system->Enable (item != -1);
+       FontFilesDialog* d = new FontFilesDialog (this, font->files ());
+       if (d->ShowModal () == wxID_OK) {
+               font->set_files (d->get ());
        }
+       d->Destroy ();
+
+       setup ();
 }
index 26c2a5eaa1848247152674e1abb9708bbbcce69a..e04dc9937035407362eb25ac8c7401a8686dd6f9 100644 (file)
@@ -32,14 +32,11 @@ public:
 
 private:
        void setup ();
-       void set_from_file_clicked ();
-       void set_from_system_clicked ();
        void setup_sensitivity ();
        void selection_changed ();
-       void set_selected_font_file (boost::filesystem::path file);
+       void edit_clicked ();
 
        boost::weak_ptr<SubtitleContent> _content;
        wxListCtrl* _fonts;
-       wxButton* _set_from_file;
-       wxButton* _set_from_system;
+       wxButton* _edit;
 };
index e15263670059eb72ab496d8ead51e0d16dd954ff..a63c6be62f704198aa6c40f1c18eacb21e0eb562 100644 (file)
@@ -28,6 +28,7 @@
 #include <boost/foreach.hpp>
 
 using boost::shared_ptr;
+using boost::optional;
 using boost::dynamic_pointer_cast;
 
 HintsDialog::HintsDialog (wxWindow* parent, boost::weak_ptr<Film> film)
@@ -79,8 +80,11 @@ HintsDialog::film_changed ()
                        shared_ptr<SubtitleContent> s = dynamic_pointer_cast<SubtitleContent> (i);
                        if (s) {
                                BOOST_FOREACH (shared_ptr<Font> j, s->fonts ()) {
-                                       if (j->file() && boost::filesystem::file_size (j->file().get ()) >= (640 * 1024)) {
-                                               big_font_files = true;
+                                       for (int i = 0; i < FontFiles::VARIANTS; ++i) {
+                                               optional<boost::filesystem::path> const p = j->file (static_cast<FontFiles::Variant> (i));
+                                               if (p && boost::filesystem::file_size (p.get()) >= (640 * 1024)) {
+                                                       big_font_files = true;
+                                               }
                                        }
                                }
                        }
index c88b3bb312170660c710e7ca09c0120fbf424309..66f3f422f186ec05a1d6fb6a18c55f172183337e 100644 (file)
@@ -21,6 +21,7 @@
 #include <dcp/certificate.h>
 #include <wx/wx.h>
 #include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
 
 class Progress;
 
index 346d292c667d16029365c2d84160d4eb6a301e64..d2bf33cd35132e194d4253f17a2a6a95f1520cc2 100644 (file)
@@ -51,6 +51,7 @@ sources = """
           filter_dialog.cc
           filter_editor.cc
           fonts_dialog.cc
+          font_files_dialog.cc
           gain_calculator_dialog.cc
           hints_dialog.cc
           job_view.cc
index 4920f0be570d8bf4725704a7964ad45402070c22..759c2720c8f02e5293bd5e264cb3b894ae9f54c3 100644 (file)
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE (srt_subtitle_test2)
        content->set_use_subtitles (true);
        content->set_burn_subtitles (false);
        /* Use test/data/subrip2.srt as if it were a font file  */
-       content->fonts().front()->set_file ("test/data/subrip2.srt");
+       content->fonts().front()->set_file (FontFiles::NORMAL, "test/data/subrip2.srt");
 
        film->make_dcp ();
        wait_for_jobs ();