40 #include "compose.hpp"
43 #include "filesystem.h"
54 LIBDCP_DISABLE_WARNINGS
55 #include <libxml++/libxml++.h>
56 LIBDCP_ENABLE_WARNINGS
57 #include <boost/weak_ptr.hpp>
64 using std::dynamic_pointer_cast;
65 using std::make_shared;
66 using std::shared_ptr;
69 using boost::optional;
73 InteropTextAsset::InteropTextAsset(boost::filesystem::path file)
76 _raw_xml = dcp::file_to_string(file, 10 * 1024 * 1024);
78 auto xml = make_shared<cxml::Document>(
"DCSubtitle");
79 xml->read_file(dcp::filesystem::fix_long_path(file));
80 _id = xml->string_child (
"SubtitleID");
81 _reel_number = xml->string_child (
"ReelNumber");
82 _language = xml->string_child (
"Language");
83 _movie_title = xml->string_child (
"MovieTitle");
84 _load_font_nodes = type_children<InteropLoadFontNode> (xml,
"LoadFont");
88 vector<ParseState> ps;
89 for (
auto i: xml->node()->get_children()) {
90 auto e =
dynamic_cast<xmlpp::Element
const *
>(i);
91 if (e && (e->get_name() ==
"Font" || e->get_name() ==
"Subtitle")) {
92 parse_texts (e, ps, optional<int>(), Standard::INTEROP);
96 for (
auto i: _texts) {
97 auto si = dynamic_pointer_cast<TextImage>(i);
99 si->read_png_file (file.parent_path() / String::compose(
"%1.png", si->id()));
105 InteropTextAsset::InteropTextAsset()
112 InteropTextAsset::xml_as_string()
const
115 auto root = doc.create_root_node (
"DCSubtitle");
116 root->set_attribute (
"Version",
"1.0");
118 cxml::add_text_child(root,
"SubtitleID", _id);
119 cxml::add_text_child(root,
"MovieTitle", _movie_title);
120 cxml::add_text_child(root,
"ReelNumber", raw_convert<string> (_reel_number));
121 cxml::add_text_child(root,
"Language", _language);
123 for (
auto i: _load_font_nodes) {
124 auto load_font = cxml::add_child(root,
"LoadFont");
125 load_font->set_attribute (
"Id", i->id);
126 load_font->set_attribute (
"URI", i->uri);
138 _fonts.push_back (Font(load_id, make_uuid(), data));
139 auto const uri = String::compose(
"font_%1.ttf", _load_font_nodes.size());
140 _load_font_nodes.push_back (make_shared<InteropLoadFontNode>(load_id, uri));
145 InteropTextAsset::equals(shared_ptr<const Asset> other_asset,
EqualityOptions const& options, NoteHandler note)
const
147 if (!TextAsset::equals (other_asset, options, note)) {
151 auto other = dynamic_pointer_cast<const InteropTextAsset> (other_asset);
156 if (!options.load_font_nodes_can_differ) {
157 auto i = _load_font_nodes.begin();
158 auto j = other->_load_font_nodes.begin();
160 while (i != _load_font_nodes.end ()) {
161 if (j == other->_load_font_nodes.end ()) {
162 note (NoteType::ERROR,
"<LoadFont> nodes differ");
167 note (NoteType::ERROR,
"<LoadFont> nodes differ");
176 if (_movie_title != other->_movie_title) {
177 note (NoteType::ERROR,
"Subtitle or caption movie titles differ");
185 vector<shared_ptr<LoadFontNode>>
186 InteropTextAsset::load_font_nodes()
const
188 vector<shared_ptr<LoadFontNode>> lf;
189 copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter (lf));
199 throw FileError (
"Could not open file for writing", p, -1);
210 if (
auto im = dynamic_pointer_cast<dcp::TextImage>(i)) {
211 im->write_png_file(p.parent_path() / String::compose(
"%1.png", im->id()));
216 for (
auto i: _load_font_nodes) {
217 auto file = p.parent_path() / i->uri;
218 auto font_with_id = std::find_if(
_fonts.begin(),
_fonts.end(), [i](
Font const& font) { return font.load_id == i->id; });
219 if (font_with_id !=
_fonts.end()) {
220 font_with_id->data.write(
file);
221 font_with_id->file =
file;
234 for (
auto asset: assets) {
235 auto font = dynamic_pointer_cast<FontAsset>(asset);
242 for (
auto load_font_node: _load_font_nodes) {
243 auto const path_in_load_font_node =
_file->parent_path() / load_font_node->uri;
244 if (font->file() && path_in_load_font_node == *font->file()) {
245 auto existing = std::find_if(
_fonts.begin(),
_fonts.end(), [load_font_node](
Font const& font) { return font.load_id == load_font_node->id; });
246 if (existing !=
_fonts.end()) {
247 *existing =
Font(load_font_node->id, asset->id(), font->
file().get());
249 _fonts.push_back(
Font(load_font_node->id, asset->id(), font->
file().get()));
257 vector<shared_ptr<Asset>>
258 InteropTextAsset::font_assets()
260 vector<shared_ptr<Asset>> assets;
261 for (
auto const& i:
_fonts) {
263 assets.push_back(make_shared<FontAsset>(i.uuid, i.file.get()));
269 vector<shared_ptr<const Asset>>
270 InteropTextAsset::font_assets()
const
272 vector<shared_ptr<const Asset>> assets;
273 for (
auto const& i:
_fonts) {
275 assets.push_back(make_shared<const FontAsset>(i.uuid, i.file.get()));
282 InteropTextAsset::add_to_assetmap(
AssetMap& asset_map, boost::filesystem::path root)
const
284 Asset::add_to_assetmap(asset_map, root);
287 auto im = dynamic_pointer_cast<dcp::TextImage>(i);
289 DCP_ASSERT(im->file());
290 add_file_to_assetmap(asset_map, root, im->file().get(), im->id());
297 InteropTextAsset::add_to_pkl(shared_ptr<PKL> pkl, boost::filesystem::path root)
const
299 Asset::add_to_pkl (pkl, root);
302 auto im = dynamic_pointer_cast<dcp::TextImage>(i);
304 auto png_image = im->png_image ();
305 pkl->add_asset(im->id(), optional<string>(),
make_digest(png_image), png_image.size(),
"image/png", root.filename().string());
312 InteropTextAsset::set_font_file(
string load_id, boost::filesystem::path file)
315 if (i.load_id == load_id) {
320 for (
auto i: _load_font_nodes) {
321 if (i->id == load_id) {
322 i->uri =
file.filename().string();
331 vector<string> unresolved;
332 for (
auto load_font_node: _load_font_nodes) {
333 if (std::find_if(
_fonts.begin(),
_fonts.end(), [load_font_node](
Font const& font) { return font.load_id == load_font_node->id; }) ==
_fonts.end()) {
334 unresolved.push_back(load_font_node->id);
Class to hold an arbitrary block of data.
boost::optional< boost::filesystem::path > file() const
boost::optional< boost::filesystem::path > _file
A class to describe what "equality" means for a particular test.
An exception related to a file.
size_t write(const void *ptr, size_t size, size_t nmemb)
void write(boost::filesystem::path path) const override
void resolve_fonts(std::vector< std::shared_ptr< Asset >> assets)
std::vector< std::string > unresolved_fonts() const
boost::optional< boost::filesystem::path > file
A parent for classes representing a file containing subtitles or captions.
boost::optional< std::string > _raw_xml
void texts_as_xml(xmlpp::Element *root, int time_code_rate, Standard standard) const
std::vector< std::shared_ptr< Text > > _texts
std::vector< Font > _fonts
static std::string format_xml(xmlpp::Document const &document, boost::optional< std::pair< std::string, std::string >> xml_namespace)
Class to describe what equality means when calling Asset::equals().
InteropLoadFontNode class.
Namespace for everything in libdcp.
std::string make_digest(boost::filesystem::path filename, boost::function< void(int64_t, int64_t)>)
Methods for conversion to/from string.
Internal TextAsset helpers.
Utility methods and classes.
Helpers for XML reading with libcxml.