Support PNG subs in DCPSubtitleDecoder (#1479).
[dcpomatic.git] / src / lib / util.cc
index 595c7e76eb38355bf014c5a05647fea714b8a9a7..b5265519413976328254749d63cb5236caa200cd 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 "rect.h"
 #include "digester.h"
 #include "audio_processor.h"
+#include "crypto.h"
 #include "compose.hpp"
 #include "audio_buffers.h"
+#include "string_text.h"
+#include "font.h"
+#include "render_text.h"
+#include "ffmpeg_image_proxy.h"
+#include "image.h"
+#include "text_decoder.h"
 #include <dcp/locale_convert.h>
 #include <dcp/util.h>
 #include <dcp/raw_convert.h>
@@ -93,6 +100,7 @@ using boost::thread;
 using boost::optional;
 using boost::lexical_cast;
 using boost::bad_lexical_cast;
+using boost::scoped_array;
 using dcp::Size;
 using dcp::raw_convert;
 using dcp::locale_convert;
@@ -356,9 +364,30 @@ dcpomatic_setup ()
 
        set_terminate (terminate);
 
+#ifdef DCPOMATIC_WINDOWS
+       putenv ("PANGOCAIRO_BACKEND=fontconfig");
+       putenv (String::compose("FONTCONFIG_PATH=%1", shared_path().string()).c_str());
+#endif
+
+#ifdef DCPOMATIC_OSX
+       setenv ("PANGOCAIRO_BACKEND", "fontconfig", 1);
+       setenv ("FONTCONFIG_PATH", shared_path().string().c_str(), 1);
+#endif
+
        Pango::init ();
        dcp::init ();
 
+#ifdef DCPOMATIC_WINDOWS
+       /* Render something to fontconfig to create its cache */
+       list<StringText> subs;
+       dcp::SubtitleString ss(
+               optional<string>(), false, false, false, dcp::Colour(), 42, 1, dcp::Time(), dcp::Time(), 0, dcp::HALIGN_CENTER, 0, dcp::VALIGN_CENTER, dcp::DIRECTION_LTR,
+               "Hello dolly", dcp::NONE, dcp::Colour(), dcp::Time(), dcp::Time()
+               );
+       subs.push_back (StringText(ss, 0));
+       render_text (subs, list<shared_ptr<Font> >(), dcp::Size(640, 480), DCPTime(), 24);
+#endif
+
        Ratio::setup_ratios ();
        PresetColourConversion::setup_colour_conversion_presets ();
        VideoContentScale::setup_scales ();
@@ -700,7 +729,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);
        }
@@ -812,3 +841,189 @@ checked_fread (void* ptr, size_t size, FILE* stream, boost::filesystem::path pat
                }
        }
 }
+
+size_t
+utf8_strlen (string s)
+{
+       size_t const len = s.length ();
+       int N = 0;
+       for (size_t i = 0; i < len; ++i) {
+               unsigned char c = s[i];
+               if ((c & 0xe0) == 0xc0) {
+                       ++i;
+               } else if ((c & 0xf0) == 0xe0) {
+                       i += 2;
+               } else if ((c & 0xf8) == 0xf0) {
+                       i += 3;
+               }
+               ++N;
+       }
+       return N;
+}
+
+string
+day_of_week_to_string (boost::gregorian::greg_weekday d)
+{
+       switch (d.as_enum()) {
+       case boost::date_time::Sunday:
+               return _("Sunday");
+       case boost::date_time::Monday:
+               return _("Monday");
+       case boost::date_time::Tuesday:
+               return _("Tuesday");
+       case boost::date_time::Wednesday:
+               return _("Wednesday");
+       case boost::date_time::Thursday:
+               return _("Thursday");
+       case boost::date_time::Friday:
+               return _("Friday");
+       case boost::date_time::Saturday:
+               return _("Saturday");
+       }
+
+       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);
+}
+
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+
+/* Make up a key from the machine UUID */
+dcp::Data
+key_from_uuid ()
+{
+       dcp::Data key (dcpomatic::crypto_key_length());
+       memset (key.data().get(), 0, key.size());
+       string const magic = command_and_read ("dcpomatic2_uuid");
+       strncpy ((char *) key.data().get(), magic.c_str(), dcpomatic::crypto_key_length());
+       return key;
+}
+
+/* swaroop chain file format:
+ *
+ *  0 [int16_t] IV length
+ *  2 [int16_t] cert #1 length, or 0 for none
+ *  4 [int16_t] cert #2 length, or 0 for none
+ *  6 [int16_t] cert #3 length, or 0 for none
+ *  8 [int16_t] cert #4 length, or 0 for none
+ * 10 [int16_t] cert #5 length, or 0 for none
+ * 12 [int16_t] cert #6 length, or 0 for none
+ * 14 [int16_t] cert #7 length, or 0 for none
+ * 16 [int16_t] cert #8 length, or 0 for none
+ * 16 [int16_t] private key length
+ * 20 IV
+ *    cert #1
+ *    cert #2
+ *    cert #3
+ *    cert #4
+ *    cert #5
+ *    cert #6
+ *    cert #7
+ *    cert #8
+ *    private key
+ */
+
+struct __attribute__ ((packed)) Header_ {
+       int16_t iv_length;
+       int16_t cert_length[8];
+       int16_t private_key_length;
+};
+
+typedef struct Header_ Header;
+
+shared_ptr<dcp::CertificateChain>
+read_swaroop_chain (boost::filesystem::path path)
+{
+       dcp::Data data (path);
+       Header* header = (Header *) data.data().get();
+       uint8_t* p = data.data().get() + sizeof(Header);
+
+       dcp::Data iv (p, header->iv_length);
+       p += iv.size();
+
+       shared_ptr<dcp::CertificateChain> cc (new dcp::CertificateChain());
+       for (int i = 0; i < 8; ++i) {
+               if (header->cert_length[i] == 0) {
+                       break;
+               }
+               dcp::Data c(p, header->cert_length[i]);
+               p += c.size();
+               cc->add (dcp::Certificate(dcpomatic::decrypt(c, key_from_uuid(), iv)));
+       }
+
+       dcp::Data k (p, header->private_key_length);
+       cc->set_key (dcpomatic::decrypt(k, key_from_uuid(), iv));
+       return cc;
+}
+
+void
+write_swaroop_chain (shared_ptr<const dcp::CertificateChain> chain, boost::filesystem::path output)
+{
+       scoped_array<uint8_t> buffer (new uint8_t[65536]);
+       Header* header = (Header *) buffer.get();
+       memset (header, 0, sizeof(Header));
+       uint8_t* p = buffer.get() + sizeof(Header);
+
+       dcp::Data iv = dcpomatic::random_iv ();
+       header->iv_length = iv.size ();
+       memcpy (p, iv.data().get(), iv.size());
+       p += iv.size();
+
+       int N = 0;
+       BOOST_FOREACH (dcp::Certificate i, chain->root_to_leaf()) {
+               dcp::Data e = dcpomatic::encrypt (i.certificate(true), key_from_uuid(), iv);
+               memcpy (p, e.data().get(), e.size());
+               p += e.size();
+               DCPOMATIC_ASSERT (N < 8);
+               header->cert_length[N] = e.size ();
+               ++N;
+       }
+
+       dcp::Data k = dcpomatic::encrypt (chain->key().get(), key_from_uuid(), iv);
+       memcpy (p, k.data().get(), k.size());
+       p += k.size();
+       header->private_key_length = k.size ();
+
+       FILE* f = fopen_boost (output, "wb");
+       checked_fwrite (buffer.get(), p - buffer.get(), f, output);
+       fclose (f);
+}
+
+#endif