#include "font_node.h"
#include "exceptions.h"
#include "xml.h"
+#include "raw_convert.h"
+#include "dcp_assert.h"
+#include "util.h"
#include "AS_DCP.h"
#include "KM_util.h"
-#include "raw_convert.h"
#include <libxml++/libxml++.h>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
using std::stringstream;
using std::cout;
using std::vector;
+using std::map;
using boost::shared_ptr;
using boost::split;
using boost::is_any_of;
+using boost::shared_array;
using namespace dcp;
SMPTESubtitleAsset::SMPTESubtitleAsset ()
- : _time_code_rate (0)
+ : _edit_rate (24, 1)
+ , _time_code_rate (24)
{
}
SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file)
: SubtitleAsset (file)
{
- shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
-
ASDCP::TimedText::MXFReader reader;
Kumu::Result_t r = reader.OpenRead (file.string().c_str ());
if (ASDCP_FAILURE (r)) {
boost::throw_exception (MXFFileError ("could not open MXF file for reading", file, r));
}
+
+ /* Read the subtitle XML */
string s;
reader.ReadTimedTextResource (s, 0, 0);
stringstream t;
t << s;
+ shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
xml->read_stream (t);
ASDCP::WriterInfo info;
reader.FillWriterInfo (info);
_id = read_writer_info (info);
-
+
_load_font_nodes = type_children<dcp::SMPTELoadFontNode> (xml, "LoadFont");
_content_title_text = xml->string_child ("ContentTitleText");
}
parse_subtitles (xml, font_nodes);
+
+ /* Read fonts */
+
+ ASDCP::TimedText::TimedTextDescriptor text_descriptor;
+ reader.FillTimedTextDescriptor (text_descriptor);
+ for (
+ ASDCP::TimedText::ResourceList_t::const_iterator i = text_descriptor.ResourceList.begin();
+ i != text_descriptor.ResourceList.end();
+ ++i) {
+
+ if (i->Type == ASDCP::TimedText::MT_OPENTYPE) {
+ ASDCP::TimedText::FrameBuffer buffer;
+ buffer.Capacity (10 * 1024 * 1024);
+ reader.ReadAncillaryResource (i->ResourceID, buffer);
+
+ char id[64];
+ Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof (id));
+
+ shared_array<uint8_t> data (new uint8_t[buffer.Size()]);
+ memcpy (data.get(), buffer.RoData(), buffer.Size());
+
+ /* The IDs in the MXF have a 9 character prefix of unknown origin and meaning... */
+ string check_id = string (id).substr (9);
+
+ list<shared_ptr<SMPTELoadFontNode> >::const_iterator j = _load_font_nodes.begin ();
+ while (j != _load_font_nodes.end() && (*j)->urn != check_id) {
+ ++j;
+ }
+
+ if (j != _load_font_nodes.end ()) {
+ _fonts[(*j)->id] = FontData (data, buffer.Size ());
+ }
+ }
+ }
+
+
}
list<shared_ptr<LoadFontNode> >
ASDCP::TimedText::TimedTextDescriptor descriptor;
descriptor.EditRate = ASDCP::Rational (_edit_rate.numerator, _edit_rate.denominator);
descriptor.EncodingName = "UTF-8";
- descriptor.ResourceList.clear ();
+
+ BOOST_FOREACH (shared_ptr<dcp::SMPTELoadFontNode> i, _load_font_nodes) {
+ map<string, FontData>::const_iterator j = _fonts.find (i->id);
+ if (j != _fonts.end ()) {
+ ASDCP::TimedText::TimedTextResourceDescriptor res;
+ unsigned int c;
+ Kumu::hex2bin (i->urn.c_str(), res.ResourceID, Kumu::UUID_Length, &c);
+ DCP_ASSERT (c == Kumu::UUID_Length);
+ res.Type = ASDCP::TimedText::MT_OPENTYPE;
+ descriptor.ResourceList.push_back (res);
+ }
+ }
+
descriptor.NamespaceName = "dcst";
memcpy (descriptor.AssetID, writer_info.AssetUUID, ASDCP::UUIDlen);
descriptor.ContainerDuration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator);
- /* XXX: should write fonts into the file somehow */
-
ASDCP::TimedText::MXFWriter writer;
ASDCP::Result_t r = writer.OpenWrite (p.string().c_str(), writer_info, descriptor);
if (ASDCP_FAILURE (r)) {
boost::throw_exception (MXFFileError ("could not write XML to timed text resource", p.string(), r));
}
+ BOOST_FOREACH (shared_ptr<dcp::SMPTELoadFontNode> i, _load_font_nodes) {
+ map<string, FontData>::const_iterator j = _fonts.find (i->id);
+ if (j != _fonts.end ()) {
+ ASDCP::TimedText::FrameBuffer buffer;
+ buffer.SetData (j->second.data.get(), j->second.size);
+ buffer.Size (j->second.size);
+ r = writer.WriteAncillaryResource (buffer);
+ if (ASDCP_FAILURE (r)) {
+ boost::throw_exception (MXFFileError ("could not write font to timed text resource", p.string(), r));
+ }
+ }
+ }
+
writer.Finalize ();
_file = p;
void
SMPTESubtitleAsset::add_font (string id, boost::filesystem::path file)
{
- /* XXX */
+ add_font_data (id, file);
+ _load_font_nodes.push_back (shared_ptr<SMPTELoadFontNode> (new SMPTELoadFontNode (id, make_uuid ())));
}
}
struct interop_dcp_font_test;
+struct smpte_dcp_font_test;
namespace dcp
{
protected:
friend struct ::interop_dcp_font_test;
+ friend struct ::smpte_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;
: 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;
+ /** .ttf file that this data was last written to */
+ mutable boost::optional<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.
+ * For Interop, the string is the font ID from the subtitle file.
+ * For SMPTE, the string is the font's URN from the subtitle file.
*/
std::map<std::string, FontData> _fonts;
*/
#include "interop_subtitle_asset.h"
+#include "smpte_subtitle_asset.h"
#include "dcp.h"
#include "cpl.h"
#include "test.h"
BOOST_CHECK_EQUAL (memcmp (subs2->_fonts["theFontId"].data.get(), ref.get(), size), 0);
}
+
+/** Create a DCP with SMPTE subtitles and check that the font is written and read back correctly */
+BOOST_AUTO_TEST_CASE (smpte_dcp_font_test)
+{
+ boost::filesystem::path directory = "build/test/smpte_dcp_font_test";
+ dcp::DCP dcp (directory);
+
+ shared_ptr<dcp::SMPTESubtitleAsset> subs (new dcp::SMPTESubtitleAsset ());
+ subs->add_font ("theFontId", "test/data/dummy.ttf");
+ subs->write (directory / "frobozz.mxf");
+
+ 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::SMPTE);
+
+ 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_REQUIRE (subs2->_fonts["theFontId"].data);
+ BOOST_CHECK_EQUAL (memcmp (subs2->_fonts["theFontId"].data.get(), ref.get(), size), 0);
+}