Improve OpenFileError so that it doesn't say "opening for read"
[dcpomatic.git] / src / lib / util.cc
index 544045bcb555e528156d01954ee1b7cad7b19b1f..2dedc32c152efc60161722d411c7ef0511b018df 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #include "string_text.h"
 #include "font.h"
 #include "render_text.h"
+#include "ffmpeg_image_proxy.h"
+#include "image.h"
+#include "text_decoder.h"
+#include "job_manager.h"
 #include <dcp/locale_convert.h>
 #include <dcp/util.h>
 #include <dcp/raw_convert.h>
@@ -59,6 +63,7 @@ extern "C" {
 #include <boost/range/algorithm/replace_if.hpp>
 #include <boost/thread.hpp>
 #include <boost/filesystem.hpp>
+#include <boost/locale.hpp>
 #ifdef DCPOMATIC_WINDOWS
 #include <boost/locale.hpp>
 #include <dbghelp.h>
@@ -77,6 +82,7 @@ extern "C" {
 #include "i18n.h"
 
 using std::string;
+using std::wstring;
 using std::setfill;
 using std::ostream;
 using std::endl;
@@ -101,6 +107,7 @@ using boost::scoped_array;
 using dcp::Size;
 using dcp::raw_convert;
 using dcp::locale_convert;
+using namespace dcpomatic;
 
 /** Path to our executable, required by the stacktrace stuff and filled
  *  in during App::onInit().
@@ -374,7 +381,7 @@ dcpomatic_setup ()
        Pango::init ();
        dcp::init ();
 
-#ifdef DCPOMATIC_WINDOWS
+#if defined(DCPOMATIC_WINDOWS) || defined(DCPOMATIC_OSX)
        /* Render something to fontconfig to create its cache */
        list<StringText> subs;
        dcp::SubtitleString ss(
@@ -466,7 +473,7 @@ digest_head_tail (vector<boost::filesystem::path> files, boost::uintmax_t size)
        while (i < int64_t (files.size()) && to_do > 0) {
                FILE* f = fopen_boost (files[i], "rb");
                if (!f) {
-                       throw OpenFileError (files[i].string(), errno, true);
+                       throw OpenFileError (files[i].string(), errno, OpenFileError::READ);
                }
 
                boost::uintmax_t this_time = min (to_do, boost::filesystem::file_size (files[i]));
@@ -486,7 +493,7 @@ digest_head_tail (vector<boost::filesystem::path> files, boost::uintmax_t size)
        while (i >= 0 && to_do > 0) {
                FILE* f = fopen_boost (files[i], "rb");
                if (!f) {
-                       throw OpenFileError (files[i].string(), errno, true);
+                       throw OpenFileError (files[i].string(), errno, OpenFileError::READ);
                }
 
                boost::uintmax_t this_time = min (to_do, boost::filesystem::file_size (files[i]));
@@ -726,7 +733,7 @@ relaxed_string_to_float (string s)
        try {
                boost::algorithm::replace_all (s, ",", ".");
                return lexical_cast<float> (s);
-       } catch (bad_lexical_cast) {
+       } catch (bad_lexical_cast &) {
                boost::algorithm::replace_all (s, ".", ",");
                return lexical_cast<float> (s);
        }
@@ -740,15 +747,34 @@ careful_string_filter (string s)
           Safety first and all that.
        */
 
+       wstring ws = boost::locale::conv::utf_to_utf<wchar_t>(s);
+
        string out;
        string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_%.+";
-       for (size_t i = 0; i < s.size(); ++i) {
-               if (allowed.find (s[i]) != string::npos) {
-                       out += s[i];
+       for (size_t i = 0; i < ws.size(); ++i) {
+
+               wchar_t c = ws[i];
+
+               /* Remove some accents */
+               if (wstring(L"áàâ").find(c) != string::npos) {
+                       c = 'a';
+               }
+               if (wstring(L"éèêë").find(c) != string::npos) {
+                       c = 'e';
+               }
+               if (wstring(L"ö").find(c) != string::npos) {
+                       c = 'o';
+               }
+               if (wstring(L"ü").find(c) != string::npos) {
+                       c = 'u';
+               }
+
+               if (allowed.find(c) != string::npos) {
+                       out += c;
                }
        }
 
-       return out;
+       return boost::locale::conv::utf_to_utf<char>(out);
 }
 
 /** @param mapped List of mapped audio channels from a Film.
@@ -881,6 +907,100 @@ day_of_week_to_string (boost::gregorian::greg_weekday d)
        return d.as_long_string ();
 }
 
+/** @param size Size of picture that the subtitle will be overlaid onto */
+void
+emit_subtitle_image (ContentTimePeriod period, dcp::SubtitleImage sub, dcp::Size size, shared_ptr<TextDecoder> decoder)
+{
+       /* XXX: this is rather inefficient; decoding the image just to get its size */
+       FFmpegImageProxy proxy (sub.png_image());
+       shared_ptr<Image> image = proxy.image().first;
+       /* set up rect with height and width */
+       dcpomatic::Rect<double> rect(0, 0, image->size().width / double(size.width), image->size().height / double(size.height));
+
+       /* add in position */
+
+       switch (sub.h_align()) {
+       case dcp::HALIGN_LEFT:
+               rect.x += sub.h_position();
+               break;
+       case dcp::HALIGN_CENTER:
+               rect.x += 0.5 + sub.h_position() - rect.width / 2;
+               break;
+       case dcp::HALIGN_RIGHT:
+               rect.x += 1 - sub.h_position() - rect.width;
+               break;
+       }
+
+       switch (sub.v_align()) {
+       case dcp::VALIGN_TOP:
+               rect.y += sub.v_position();
+               break;
+       case dcp::VALIGN_CENTER:
+               rect.y += 0.5 + sub.v_position() - rect.height / 2;
+               break;
+       case dcp::VALIGN_BOTTOM:
+               rect.y += 1 - sub.v_position() - rect.height;
+               break;
+       }
+
+       decoder->emit_bitmap (period, image, rect);
+}
+
+bool
+show_jobs_on_console (bool progress)
+{
+       bool first = true;
+       bool error = false;
+       while (true) {
+
+               dcpomatic_sleep (5);
+
+               list<shared_ptr<Job> > jobs = JobManager::instance()->get();
+
+               if (!first && progress) {
+                       for (size_t i = 0; i < jobs.size(); ++i) {
+                               cout << "\033[1A\033[2K";
+                       }
+                       cout.flush ();
+               }
+
+               first = false;
+
+               BOOST_FOREACH (shared_ptr<Job> i, jobs) {
+                       if (progress) {
+                               cout << i->name();
+                               if (!i->sub_name().empty()) {
+                                       cout << "; " << i->sub_name();
+                               }
+                               cout << ": ";
+
+                               if (i->progress ()) {
+                                       cout << i->status() << "                            \n";
+                               } else {
+                                       cout << ": Running           \n";
+                               }
+                       }
+
+                       if (!progress && i->finished_in_error()) {
+                               /* We won't see this error if we haven't been showing progress,
+                                  so show it now.
+                               */
+                               cout << i->status() << "\n";
+                       }
+
+                       if (i->finished_in_error()) {
+                               error = true;
+                       }
+               }
+
+               if (!JobManager::instance()->work_to_do()) {
+                       break;
+               }
+       }
+
+       return error;
+}
+
 #ifdef DCPOMATIC_VARIANT_SWAROOP
 
 /* Make up a key from the machine UUID */