summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2015-06-09 11:13:02 +0100
committerCarl Hetherington <cth@carlh.net>2015-06-09 11:13:02 +0100
commit5c1b079a28941781c9cfa46d57e0c20b55681b32 (patch)
tree35c0d2aac409ca1b52699e974f299a64aa5ffc1f
parent6425742f95a4c2e539061b29016a4a5bd10ac9ae (diff)
Basically-working interop subtitle font handling.
-rw-r--r--src/asset.cc8
-rw-r--r--src/dcp.cc18
-rw-r--r--src/font.h2
-rw-r--r--src/interop_subtitle_asset.cc50
-rw-r--r--src/interop_subtitle_asset.h4
-rw-r--r--src/reel.cc9
-rw-r--r--src/smpte_subtitle_asset.cc5
-rw-r--r--src/smpte_subtitle_asset.h1
-rw-r--r--src/subtitle_asset.cc23
-rw-r--r--src/subtitle_asset.h33
-rw-r--r--test/data/dummy.ttf1
-rw-r--r--test/dcp_font_test.cc72
-rw-r--r--test/test.cc42
-rw-r--r--test/test.h1
-rw-r--r--test/wscript1
15 files changed, 258 insertions, 12 deletions
diff --git a/src/asset.cc b/src/asset.cc
index 24fc5919..5760c905 100644
--- a/src/asset.cc
+++ b/src/asset.cc
@@ -71,10 +71,16 @@ Asset::write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const
asset->add_child("Id")->add_child_text ("urn:uuid:" + _id);
xmlpp::Node* chunk_list = asset->add_child ("ChunkList");
xmlpp::Node* chunk = chunk_list->add_child ("Chunk");
- optional<boost::filesystem::path> path = relative_to_root (root, _file);
+
+ optional<boost::filesystem::path> path = relative_to_root (
+ boost::filesystem::canonical (root),
+ boost::filesystem::canonical (_file)
+ );
+
if (!path) {
throw MiscError (String::compose ("Asset %1 is not within the directory %2", _file, root));
}
+
chunk->add_child("Path")->add_child_text (path.get().string ());
chunk->add_child("VolumeIndex")->add_child_text ("1");
chunk->add_child("Offset")->add_child_text ("0");
diff --git a/src/dcp.cc b/src/dcp.cc
index da12e4f9..12aaee47 100644
--- a/src/dcp.cc
+++ b/src/dcp.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-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
@@ -29,6 +29,7 @@
#include "smpte_subtitle_asset.h"
#include "mono_picture_asset.h"
#include "stereo_picture_asset.h"
+#include "reel_subtitle_asset.h"
#include "util.h"
#include "metadata.h"
#include "exceptions.h"
@@ -40,6 +41,7 @@
#include "decrypted_kdm_key.h"
#include "dcp_assert.h"
#include "reel_asset.h"
+#include "font.h"
#include <xmlsec/xmldsig.h>
#include <xmlsec/app.h>
#include <libxml++/libxml++.h>
@@ -55,8 +57,10 @@ using std::ostream;
using std::make_pair;
using std::map;
using std::cout;
+using std::cerr;
using std::exception;
using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
using boost::algorithm::starts_with;
using namespace dcp;
@@ -117,7 +121,7 @@ DCP::read (bool keep_going, ReadErrors* errors)
*/
/* Make a list of non-CPL assets so that we can resolve the references
- from the CPL.
+ from the CPLs.
*/
list<shared_ptr<Asset> > other_assets;
@@ -171,6 +175,8 @@ DCP::read (bool keep_going, ReadErrors* errors)
default:
throw DCPReadError ("Unknown MXF essence type");
}
+ } else if (boost::filesystem::extension (path) == ".ttf") {
+ other_assets.push_back (shared_ptr<Font> (new Font (path)));
}
}
@@ -423,7 +429,13 @@ DCP::assets () const
BOOST_FOREACH (shared_ptr<CPL> i, cpls ()) {
assets.push_back (i);
BOOST_FOREACH (shared_ptr<const ReelAsset> j, i->reel_assets ()) {
- assets.push_back (j->asset_ref().object ());
+ shared_ptr<Asset> o = j->asset_ref().object ();
+ assets.push_back (o);
+ /* More Interop special-casing */
+ shared_ptr<InteropSubtitleAsset> sub = dynamic_pointer_cast<InteropSubtitleAsset> (o);
+ if (sub) {
+ sub->add_font_assets (assets);
+ }
}
}
diff --git a/src/font.h b/src/font.h
index 8e0b4e18..e87419c0 100644
--- a/src/font.h
+++ b/src/font.h
@@ -26,7 +26,7 @@
namespace dcp {
/** @class Font
- * @brief A (truetype) font asset for subtitles in a DCP.
+ * @brief A (truetype) font asset for subtitles in an Interop DCP.
*/
class Font : public Asset
{
diff --git a/src/interop_subtitle_asset.cc b/src/interop_subtitle_asset.cc
index 231dda16..9565b12d 100644
--- a/src/interop_subtitle_asset.cc
+++ b/src/interop_subtitle_asset.cc
@@ -23,6 +23,8 @@
#include "raw_convert.h"
#include "font_node.h"
#include "util.h"
+#include "font.h"
+#include "dcp_assert.h"
#include <libxml++/libxml++.h>
#include <boost/foreach.hpp>
#include <cmath>
@@ -31,6 +33,8 @@
using std::list;
using std::string;
using std::cout;
+using std::cerr;
+using std::map;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
@@ -85,9 +89,10 @@ InteropSubtitleAsset::xml_as_string () const
}
void
-InteropSubtitleAsset::add_font (string id, string uri)
+InteropSubtitleAsset::add_font (string id, boost::filesystem::path file)
{
- _load_font_nodes.push_back (shared_ptr<InteropLoadFontNode> (new InteropLoadFontNode (id, uri)));
+ add_font_data (id, file);
+ _load_font_nodes.push_back (shared_ptr<InteropLoadFontNode> (new InteropLoadFontNode (id, file.leaf().string ())));
}
bool
@@ -136,7 +141,7 @@ InteropSubtitleAsset::load_font_nodes () const
return lf;
}
-/** Write this content to an XML file */
+/** Write this content to an XML file with its fonts alongside */
void
InteropSubtitleAsset::write (boost::filesystem::path p) const
{
@@ -150,4 +155,43 @@ InteropSubtitleAsset::write (boost::filesystem::path p) const
fclose (f);
_file = p;
+
+ BOOST_FOREACH (shared_ptr<InteropLoadFontNode> i, _load_font_nodes) {
+ boost::filesystem::path file = p.parent_path() / i->uri;
+ FILE* f = fopen_boost (file, "w");
+ if (!f) {
+ throw FileError ("could not open font file for writing", file, errno);
+ }
+ map<string, FontData>::const_iterator j = _fonts.find (i->id);
+ if (j != _fonts.end ()) {
+ fwrite (j->second.data.get(), 1, j->second.size, f);
+ j->second.file = file;
+ }
+ fclose (f);
+ }
+}
+
+void
+InteropSubtitleAsset::resolve_fonts (list<shared_ptr<Object> > objects)
+{
+ BOOST_FOREACH (shared_ptr<Object> i, objects) {
+ shared_ptr<Font> font = dynamic_pointer_cast<Font> (i);
+ if (!font) {
+ continue;
+ }
+
+ BOOST_FOREACH (shared_ptr<InteropLoadFontNode> j, _load_font_nodes) {
+ if (j->uri == font->file().leaf().string ()) {
+ add_font_data (j->id, font->file ());
+ }
+ }
+ }
+}
+
+void
+InteropSubtitleAsset::add_font_assets (list<shared_ptr<Asset> >& assets)
+{
+ for (map<string, FontData>::const_iterator i = _fonts.begin(); i != _fonts.end(); ++i) {
+ assets.push_back (shared_ptr<Font> (new Font (i->second.file)));
+ }
}
diff --git a/src/interop_subtitle_asset.h b/src/interop_subtitle_asset.h
index 378bacc7..4d2c2893 100644
--- a/src/interop_subtitle_asset.h
+++ b/src/interop_subtitle_asset.h
@@ -47,10 +47,12 @@ public:
std::list<boost::shared_ptr<LoadFontNode> > load_font_nodes () const;
- void add_font (std::string id, std::string uri);
+ void add_font (std::string id, boost::filesystem::path file);
Glib::ustring xml_as_string () const;
void write (boost::filesystem::path path) const;
+ void resolve_fonts (std::list<boost::shared_ptr<Object> > objects);
+ void add_font_assets (std::list<boost::shared_ptr<Asset> >& assets);
/** Set the reel number or sub-element identifier
* of these subtitles.
diff --git a/src/reel.cc b/src/reel.cc
index 106fbf40..4d92d345 100644
--- a/src/reel.cc
+++ b/src/reel.cc
@@ -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
@@ -30,6 +30,7 @@
#include "reel_subtitle_asset.h"
#include "decrypted_kdm_key.h"
#include "decrypted_kdm.h"
+#include "interop_subtitle_asset.h"
#include <libxml++/nodes/element.h>
using std::string;
@@ -176,5 +177,11 @@ Reel::resolve_refs (list<shared_ptr<Object> > objects)
if (_main_subtitle) {
_main_subtitle->asset_ref().resolve (objects);
+
+ /* Interop subtitle handling is all special cases */
+ shared_ptr<InteropSubtitleAsset> iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().object ());
+ if (iop) {
+ iop->resolve_fonts (objects);
+ }
}
}
diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc
index 69e2712f..28d8b261 100644
--- a/src/smpte_subtitle_asset.cc
+++ b/src/smpte_subtitle_asset.cc
@@ -209,3 +209,8 @@ SMPTESubtitleAsset::equals (shared_ptr<const Asset> other_asset, EqualityOptions
return false;
}
+void
+SMPTESubtitleAsset::add_font (string id, boost::filesystem::path file)
+{
+ /* XXX */
+}
diff --git a/src/smpte_subtitle_asset.h b/src/smpte_subtitle_asset.h
index 3fb2571a..37f58c6f 100644
--- a/src/smpte_subtitle_asset.h
+++ b/src/smpte_subtitle_asset.h
@@ -53,6 +53,7 @@ public:
Glib::ustring xml_as_string () const;
void write (boost::filesystem::path path) const;
+ void add_font (std::string id, boost::filesystem::path file);
void set_content_title_text (std::string t) {
_content_title_text = t;
diff --git a/src/subtitle_asset.cc b/src/subtitle_asset.cc
index 45177b95..70d7a955 100644
--- a/src/subtitle_asset.cc
+++ b/src/subtitle_asset.cc
@@ -29,6 +29,7 @@
#include "KM_util.h"
#include <libxml++/nodes/element.h>
#include <boost/algorithm/string.hpp>
+#include <boost/shared_array.hpp>
#include <fstream>
using std::string;
@@ -37,7 +38,9 @@ using std::ostream;
using std::ofstream;
using std::stringstream;
using std::cout;
+using std::cerr;
using boost::shared_ptr;
+using boost::shared_array;
using boost::optional;
using boost::dynamic_pointer_cast;
using namespace dcp;
@@ -304,4 +307,22 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* root, int time_code_rate, strin
}
}
-
+void
+SubtitleAsset::add_font_data (string id, boost::filesystem::path file)
+{
+ boost::uintmax_t size = boost::filesystem::file_size (file);
+ FILE* f = fopen_boost (file, "r");
+ if (!f) {
+ throw FileError ("could not open font file for reading", file, errno);
+ }
+
+ shared_array<uint8_t> data (new uint8_t[size]);
+ size_t const read = fread (data.get(), 1, size, f);
+ fclose (f);
+
+ if (read != size) {
+ throw FileError ("could not read font file", file, -1);
+ }
+
+ _fonts[id] = FontData (data, size, file);
+}
diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h
index 5fdb5f7a..481556aa 100644
--- a/src/subtitle_asset.h
+++ b/src/subtitle_asset.h
@@ -24,11 +24,14 @@
#include "dcp_time.h"
#include "subtitle_string.h"
#include <libcxml/cxml.h>
+#include <boost/shared_array.hpp>
namespace xmlpp {
class Element;
}
+struct interop_dcp_font_test;
+
namespace dcp
{
@@ -64,6 +67,7 @@ public:
}
void add (SubtitleString);
+ virtual void add_font (std::string id, boost::filesystem::path file) = 0;
virtual void write (boost::filesystem::path) const = 0;
virtual Glib::ustring xml_as_string () const = 0;
@@ -73,13 +77,40 @@ public:
virtual std::list<boost::shared_ptr<LoadFontNode> > load_font_nodes () const = 0;
protected:
+ friend struct ::interop_dcp_font_test;
+
void parse_subtitles (boost::shared_ptr<cxml::Document> xml, std::list<boost::shared_ptr<FontNode> > font_nodes);
-
void subtitles_as_xml (xmlpp::Element* root, int time_code_rate, std::string xmlns) const;
+ void add_font_data (std::string id, boost::filesystem::path file);
/** All our subtitles, in no particular order */
std::list<SubtitleString> _subtitles;
+ class FontData {
+ public:
+ FontData () {}
+
+ FontData (boost::shared_array<uint8_t> data_, boost::uintmax_t size_)
+ : data (data_)
+ , size (size_)
+ {}
+
+ FontData (boost::shared_array<uint8_t> data_, boost::uintmax_t size_, boost::filesystem::path file_)
+ : data (data_)
+ , size (size_)
+ , file (file_)
+ {}
+
+ boost::shared_array<uint8_t> data;
+ boost::uintmax_t size;
+ mutable boost::filesystem::path file;
+ };
+
+ /** Font data, keyed by a subclass-dependent identifier.
+ * For Interop fonts, the string is the font ID from the subtitle file.
+ */
+ std::map<std::string, FontData> _fonts;
+
private:
/** @struct ParseState
* @brief A struct to hold state when parsing a subtitle XML file.
diff --git a/test/data/dummy.ttf b/test/data/dummy.ttf
new file mode 100644
index 00000000..1353512a
--- /dev/null
+++ b/test/data/dummy.ttf
@@ -0,0 +1 @@
+Uap[~"ײB&ccOFJXPx{(TI{8tO'ـr"@7pNͷ-4'4BVlhy<yg \ No newline at end of file
diff --git a/test/dcp_font_test.cc b/test/dcp_font_test.cc
new file mode 100644
index 00000000..e50219a0
--- /dev/null
+++ b/test/dcp_font_test.cc
@@ -0,0 +1,72 @@
+/*
+ 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 "interop_subtitle_asset.h"
+#include "dcp.h"
+#include "cpl.h"
+#include "test.h"
+#include "reel.h"
+#include "util.h"
+#include "reel_subtitle_asset.h"
+#include <boost/test/unit_test.hpp>
+#include <cstdio>
+
+using std::list;
+using std::string;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using boost::shared_array;
+
+/** Create a DCP with interop subtitles and check that the font is written and read back correctly */
+BOOST_AUTO_TEST_CASE (interop_dcp_font_test)
+{
+ boost::filesystem::path directory = "build/test/interop_dcp_font_test";
+ dcp::DCP dcp (directory);
+
+ shared_ptr<dcp::InteropSubtitleAsset> subs (new dcp::InteropSubtitleAsset ());
+ subs->add_font ("theFontId", "test/data/dummy.ttf");
+ subs->write (directory / "frobozz.xml");
+ check_file ("test/data/dummy.ttf", "build/test/interop_dcp_font_test/dummy.ttf");
+
+ shared_ptr<dcp::Reel> reel (new dcp::Reel ());
+ reel->add (shared_ptr<dcp::ReelAsset> (new dcp::ReelSubtitleAsset (subs, dcp::Fraction (24, 1), 24, 0)));
+
+ shared_ptr<dcp::CPL> cpl (new dcp::CPL ("", dcp::TRAILER));
+ cpl->add (reel);
+
+ dcp.add (cpl);
+ dcp.write_xml (dcp::INTEROP);
+
+ dcp::DCP dcp2 (directory);
+ dcp2.read ();
+ shared_ptr<dcp::SubtitleAsset> subs2 = dynamic_pointer_cast<dcp::SubtitleAsset> (
+ dcp2.cpls().front()->reels().front()->main_subtitle()->asset_ref().object()
+ );
+ BOOST_REQUIRE (subs2);
+ BOOST_REQUIRE_EQUAL (subs2->_fonts.size(), 1);
+
+ boost::uintmax_t const size = boost::filesystem::file_size ("test/data/dummy.ttf");
+ FILE* f = dcp::fopen_boost ("test/data/dummy.ttf", "r");
+ BOOST_REQUIRE (f);
+ shared_array<uint8_t> ref (new uint8_t[size]);
+ fread (ref.get(), 1, size, f);
+ fclose (f);
+
+ BOOST_CHECK_EQUAL (memcmp (subs2->_fonts["theFontId"].data.get(), ref.get(), size), 0);
+}
diff --git a/test/test.cc b/test/test.cc
index 0c3d2c5d..c83c1048 100644
--- a/test/test.cc
+++ b/test/test.cc
@@ -23,8 +23,11 @@
#include "test.h"
#include <libxml++/libxml++.h>
#include <boost/test/unit_test.hpp>
+#include <cstdio>
using std::string;
+using std::min;
+using std::stringstream;
using std::list;
boost::filesystem::path private_test;
@@ -106,4 +109,43 @@ check_xml (string ref, string test, list<string> ignore)
check_xml (ref_root, test_root, ignore);
}
+void
+check_file (boost::filesystem::path ref, boost::filesystem::path check)
+{
+ uintmax_t N = boost::filesystem::file_size (ref);
+ BOOST_CHECK_EQUAL (N, boost::filesystem::file_size (check));
+ FILE* ref_file = dcp::fopen_boost (ref, "rb");
+ BOOST_CHECK (ref_file);
+ FILE* check_file = dcp::fopen_boost (check, "rb");
+ BOOST_CHECK (check_file);
+
+ int const buffer_size = 65536;
+ uint8_t* ref_buffer = new uint8_t[buffer_size];
+ uint8_t* check_buffer = new uint8_t[buffer_size];
+
+ stringstream error;
+ error << "File " << check.string() << " differs from reference " << ref.string();
+
+ while (N) {
+ uintmax_t this_time = min (uintmax_t (buffer_size), N);
+ size_t r = fread (ref_buffer, 1, this_time, ref_file);
+ BOOST_CHECK_EQUAL (r, this_time);
+ r = fread (check_buffer, 1, this_time, check_file);
+ BOOST_CHECK_EQUAL (r, this_time);
+
+ BOOST_CHECK_MESSAGE (memcmp (ref_buffer, check_buffer, this_time) == 0, error.str ());
+ if (memcmp (ref_buffer, check_buffer, this_time)) {
+ break;
+ }
+
+ N -= this_time;
+ }
+
+ delete[] ref_buffer;
+ delete[] check_buffer;
+
+ fclose (ref_file);
+ fclose (check_file);
+}
+
BOOST_GLOBAL_FIXTURE (TestConfig);
diff --git a/test/test.h b/test/test.h
index 4c941023..62a4c942 100644
--- a/test/test.h
+++ b/test/test.h
@@ -24,3 +24,4 @@ namespace xmlpp {
extern boost::filesystem::path private_test;
extern void check_xml (xmlpp::Element* ref, xmlpp::Element* test, std::list<std::string> ignore);
extern void check_xml (std::string ref, std::string test, std::list<std::string> ignore);
+extern void check_file (boost::filesystem::path ref, boost::filesystem::path check);
diff --git a/test/wscript b/test/wscript
index f640f6fc..6cf0d958 100644
--- a/test/wscript
+++ b/test/wscript
@@ -29,6 +29,7 @@ def build(bld):
colour_test.cc
colour_conversion_test.cc
cpl_sar_test.cc
+ dcp_font_test.cc
dcp_test.cc
dcp_time_test.cc
decryption_test.cc