diff options
| author | Carl Hetherington <cth@carlh.net> | 2023-12-04 23:14:40 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2023-12-05 00:49:33 +0100 |
| commit | e3033879f7693d40f652f013b00c76deed6994da (patch) | |
| tree | 16c6a62810d61306e15b0e81e8f4ef92015d0473 /src/interop_text_asset.cc | |
| parent | 6973568117ed23c300f5ffa538f7eae87a9a9927 (diff) | |
Rename everything.
Diffstat (limited to 'src/interop_text_asset.cc')
| -rw-r--r-- | src/interop_text_asset.cc | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/src/interop_text_asset.cc b/src/interop_text_asset.cc new file mode 100644 index 00000000..d9515573 --- /dev/null +++ b/src/interop_text_asset.cc @@ -0,0 +1,334 @@ +/* + Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +/** @file src/interop_subtitle_asset.cc + * @brief InteropTextAsset class + */ + + +#include "compose.hpp" +#include "dcp_assert.h" +#include "equality_options.h" +#include "filesystem.h" +#include "font_asset.h" +#include "file.h" +#include "interop_load_font_node.h" +#include "interop_text_asset.h" +#include "raw_convert.h" +#include "subtitle_image.h" +#include "text_asset_internal.h" +#include "util.h" +#include "warnings.h" +#include "xml.h" +LIBDCP_DISABLE_WARNINGS +#include <libxml++/libxml++.h> +LIBDCP_ENABLE_WARNINGS +#include <boost/weak_ptr.hpp> +#include <cmath> +#include <cstdio> + + +using std::cerr; +using std::cout; +using std::dynamic_pointer_cast; +using std::make_shared; +using std::shared_ptr; +using std::string; +using std::vector; +using boost::optional; +using namespace dcp; + + +InteropTextAsset::InteropTextAsset(boost::filesystem::path file) + : TextAsset(file) +{ + _raw_xml = dcp::file_to_string (file); + + auto xml = make_shared<cxml::Document>("DCSubtitle"); + xml->read_file(dcp::filesystem::fix_long_path(file)); + _id = xml->string_child ("SubtitleID"); + _reel_number = xml->string_child ("ReelNumber"); + _language = xml->string_child ("Language"); + _movie_title = xml->string_child ("MovieTitle"); + _load_font_nodes = type_children<InteropLoadFontNode> (xml, "LoadFont"); + + /* Now we need to drop down to xmlpp */ + + vector<ParseState> ps; + for (auto i: xml->node()->get_children()) { + auto e = dynamic_cast<xmlpp::Element const *>(i); + if (e && (e->get_name() == "Font" || e->get_name() == "Subtitle")) { + parse_texts(e, ps, optional<int>(), Standard::INTEROP); + } + } + + for (auto i: _texts) { + auto si = dynamic_pointer_cast<SubtitleImage>(i); + if (si) { + si->read_png_file (file.parent_path() / String::compose("%1.png", si->id())); + } + } +} + + +string +InteropTextAsset::xml_as_string() const +{ + xmlpp::Document doc; + auto root = doc.create_root_node ("DCSubtitle"); + root->set_attribute ("Version", "1.0"); + + root->add_child("SubtitleID")->add_child_text (_id); + root->add_child("MovieTitle")->add_child_text (_movie_title); + root->add_child("ReelNumber")->add_child_text (raw_convert<string> (_reel_number)); + root->add_child("Language")->add_child_text (_language); + + for (auto i: _load_font_nodes) { + auto load_font = root->add_child("LoadFont"); + load_font->set_attribute ("Id", i->id); + load_font->set_attribute ("URI", i->uri); + } + + texts_as_xml(root, 250, Standard::INTEROP); + + return format_xml(doc, {}); +} + + +void +InteropTextAsset::add_font(string load_id, dcp::ArrayData data) +{ + _fonts.push_back (Font(load_id, make_uuid(), data)); + auto const uri = String::compose("font_%1.ttf", _load_font_nodes.size()); + _load_font_nodes.push_back (make_shared<InteropLoadFontNode>(load_id, uri)); +} + + +bool +InteropTextAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const& options, NoteHandler note) const +{ + if (!TextAsset::equals(other_asset, options, note)) { + return false; + } + + auto other = dynamic_pointer_cast<const InteropTextAsset>(other_asset); + if (!other) { + return false; + } + + if (!options.load_font_nodes_can_differ) { + auto i = _load_font_nodes.begin(); + auto j = other->_load_font_nodes.begin(); + + while (i != _load_font_nodes.end ()) { + if (j == other->_load_font_nodes.end ()) { + note (NoteType::ERROR, "<LoadFont> nodes differ"); + return false; + } + + if (**i != **j) { + note (NoteType::ERROR, "<LoadFont> nodes differ"); + return false; + } + + ++i; + ++j; + } + } + + if (_movie_title != other->_movie_title) { + note (NoteType::ERROR, "Subtitle or closed caption movie titles differ"); + return false; + } + + return true; +} + + +vector<shared_ptr<LoadFontNode>> +InteropTextAsset::load_font_nodes() const +{ + vector<shared_ptr<LoadFontNode>> lf; + copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter (lf)); + return lf; +} + + +void +InteropTextAsset::write(boost::filesystem::path p) const +{ + File f(p, "wb"); + if (!f) { + throw FileError ("Could not open file for writing", p, -1); + } + + _raw_xml = xml_as_string (); + /* length() here gives bytes not characters */ + f.write(_raw_xml->c_str(), 1, _raw_xml->length()); + + _file = p; + + /* Image subtitles */ + for (auto i: _texts) { + auto im = dynamic_pointer_cast<dcp::SubtitleImage> (i); + if (im) { + im->write_png_file(p.parent_path() / String::compose("%1.png", im->id())); + } + } + + /* Fonts */ + for (auto i: _load_font_nodes) { + auto file = p.parent_path() / i->uri; + auto font_with_id = std::find_if(_fonts.begin(), _fonts.end(), [i](Font const& font) { return font.load_id == i->id; }); + if (font_with_id != _fonts.end()) { + font_with_id->data.write(file); + font_with_id->file = file; + } + } +} + + +/** Look at a supplied list of assets and find the fonts. Then match these + * fonts up with anything requested by a <LoadFont> so that _fonts contains + * a list of font ID, load ID and data. + */ +void +InteropTextAsset::resolve_fonts(vector<shared_ptr<Asset>> assets) +{ + for (auto asset: assets) { + auto font = dynamic_pointer_cast<FontAsset>(asset); + if (!font) { + continue; + } + + DCP_ASSERT(_file); + + for (auto load_font_node: _load_font_nodes) { + auto const path_in_load_font_node = _file->parent_path() / load_font_node->uri; + if (font->file() && path_in_load_font_node == *font->file()) { + auto existing = std::find_if(_fonts.begin(), _fonts.end(), [load_font_node](Font const& font) { return font.load_id == load_font_node->id; }); + if (existing != _fonts.end()) { + *existing = Font(load_font_node->id, asset->id(), font->file().get()); + } else { + _fonts.push_back(Font(load_font_node->id, asset->id(), font->file().get())); + } + } + } + } +} + + +vector<shared_ptr<Asset>> +InteropTextAsset::font_assets() +{ + vector<shared_ptr<Asset>> assets; + for (auto const& i: _fonts) { + DCP_ASSERT (i.file); + assets.push_back(make_shared<FontAsset>(i.uuid, i.file.get())); + } + return assets; +} + + +vector<shared_ptr<const Asset>> +InteropTextAsset::font_assets() const +{ + vector<shared_ptr<const Asset>> assets; + for (auto const& i: _fonts) { + DCP_ASSERT (i.file); + assets.push_back(make_shared<const FontAsset>(i.uuid, i.file.get())); + } + return assets; +} + + +void +InteropTextAsset::add_to_assetmap(AssetMap& asset_map, boost::filesystem::path root) const +{ + Asset::add_to_assetmap(asset_map, root); + + for (auto i: _texts) { + auto im = dynamic_pointer_cast<dcp::SubtitleImage>(i); + if (im) { + DCP_ASSERT(im->file()); + add_file_to_assetmap(asset_map, root, im->file().get(), im->id()); + } + } +} + + +void +InteropTextAsset::add_to_pkl(shared_ptr<PKL> pkl, boost::filesystem::path root) const +{ + Asset::add_to_pkl (pkl, root); + + for (auto i: _texts) { + auto im = dynamic_pointer_cast<dcp::SubtitleImage> (i); + if (im) { + auto png_image = im->png_image (); + pkl->add_asset(im->id(), optional<string>(), make_digest(png_image), png_image.size(), "image/png", root.filename().string()); + } + } +} + + +void +InteropTextAsset::set_font_file(string load_id, boost::filesystem::path file) +{ + for (auto& i: _fonts) { + if (i.load_id == load_id) { + i.file = file; + } + } + + for (auto i: _load_font_nodes) { + if (i->id == load_id) { + i->uri = file.filename().string(); + } + } +} + + +vector<string> +InteropTextAsset::unresolved_fonts() const +{ + vector<string> unresolved; + for (auto load_font_node: _load_font_nodes) { + if (std::find_if(_fonts.begin(), _fonts.end(), [load_font_node](Font const& font) { return font.load_id == load_font_node->id; }) == _fonts.end()) { + unresolved.push_back(load_font_node->id); + } + } + return unresolved; +} + |
