Fix/hide some warnings.
[libdcp.git] / src / interop_subtitle_asset.cc
1 /*
2     Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34
35 /** @file  src/interop_subtitle_asset.cc
36  *  @brief InteropSubtitleAsset class
37  */
38
39
40 #include "compose.hpp"
41 #include "dcp_assert.h"
42 #include "font_asset.h"
43 #include "interop_load_font_node.h"
44 #include "interop_subtitle_asset.h"
45 #include "raw_convert.h"
46 #include "subtitle_asset_internal.h"
47 #include "subtitle_image.h"
48 #include "util.h"
49 #include "warnings.h"
50 #include "xml.h"
51 LIBDCP_DISABLE_WARNINGS
52 #include <libxml++/libxml++.h>
53 LIBDCP_ENABLE_WARNINGS
54 #include <boost/weak_ptr.hpp>
55 #include <cmath>
56 #include <cstdio>
57
58
59 using std::list;
60 using std::string;
61 using std::cout;
62 using std::cerr;
63 using std::map;
64 using std::shared_ptr;
65 using std::dynamic_pointer_cast;
66 using std::vector;
67 using std::make_shared;
68 using boost::shared_array;
69 using boost::optional;
70 using namespace dcp;
71
72
73 InteropSubtitleAsset::InteropSubtitleAsset (boost::filesystem::path file)
74         : SubtitleAsset (file)
75 {
76         _raw_xml = dcp::file_to_string (file);
77
78         auto xml = make_shared<cxml::Document>("DCSubtitle");
79         xml->read_file (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");
85
86         /* Now we need to drop down to xmlpp */
87
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_subtitles (e, ps, optional<int>(), Standard::INTEROP);
93                 }
94         }
95
96         for (auto i: _subtitles) {
97                 auto si = dynamic_pointer_cast<SubtitleImage>(i);
98                 if (si) {
99                         si->read_png_file (file.parent_path() / String::compose("%1.png", si->id()));
100                 }
101         }
102 }
103
104
105 InteropSubtitleAsset::InteropSubtitleAsset ()
106 {
107
108 }
109
110
111 string
112 InteropSubtitleAsset::xml_as_string () const
113 {
114         xmlpp::Document doc;
115         auto root = doc.create_root_node ("DCSubtitle");
116         root->set_attribute ("Version", "1.0");
117
118         root->add_child("SubtitleID")->add_child_text (_id);
119         root->add_child("MovieTitle")->add_child_text (_movie_title);
120         root->add_child("ReelNumber")->add_child_text (raw_convert<string> (_reel_number));
121         root->add_child("Language")->add_child_text (_language);
122
123         for (auto i: _load_font_nodes) {
124                 xmlpp::Element* load_font = root->add_child("LoadFont");
125                 load_font->set_attribute ("Id", i->id);
126                 load_font->set_attribute ("URI", i->uri);
127         }
128
129         subtitles_as_xml (root, 250, Standard::INTEROP);
130
131         return doc.write_to_string ("UTF-8");
132 }
133
134
135 void
136 InteropSubtitleAsset::add_font (string load_id, dcp::ArrayData data)
137 {
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 (shared_ptr<InteropLoadFontNode>(new InteropLoadFontNode(load_id, uri)));
141 }
142
143
144 bool
145 InteropSubtitleAsset::equals (shared_ptr<const Asset> other_asset, EqualityOptions options, NoteHandler note) const
146 {
147         if (!SubtitleAsset::equals (other_asset, options, note)) {
148                 return false;
149         }
150
151         auto other = dynamic_pointer_cast<const InteropSubtitleAsset> (other_asset);
152         if (!other) {
153                 return false;
154         }
155
156         if (!options.load_font_nodes_can_differ) {
157                 auto i = _load_font_nodes.begin();
158                 auto j = other->_load_font_nodes.begin();
159
160                 while (i != _load_font_nodes.end ()) {
161                         if (j == other->_load_font_nodes.end ()) {
162                                 note (NoteType::ERROR, "<LoadFont> nodes differ");
163                                 return false;
164                         }
165
166                         if (**i != **j) {
167                                 note (NoteType::ERROR, "<LoadFont> nodes differ");
168                                 return false;
169                         }
170
171                         ++i;
172                         ++j;
173                 }
174         }
175
176         if (_movie_title != other->_movie_title) {
177                 note (NoteType::ERROR, "Subtitle movie titles differ");
178                 return false;
179         }
180
181         return true;
182 }
183
184
185 vector<shared_ptr<LoadFontNode>>
186 InteropSubtitleAsset::load_font_nodes () const
187 {
188         vector<shared_ptr<LoadFontNode>> lf;
189         copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter (lf));
190         return lf;
191 }
192
193
194 void
195 InteropSubtitleAsset::write (boost::filesystem::path p) const
196 {
197         auto f = fopen_boost (p, "w");
198         if (!f) {
199                 throw FileError ("Could not open file for writing", p, -1);
200         }
201
202         auto const s = xml_as_string ();
203         /* length() here gives bytes not characters */
204         fwrite (s.c_str(), 1, s.length(), f);
205         fclose (f);
206
207         _file = p;
208
209         /* Image subtitles */
210         for (auto i: _subtitles) {
211                 auto im = dynamic_pointer_cast<dcp::SubtitleImage> (i);
212                 if (im) {
213                         im->write_png_file(p.parent_path() / String::compose("%1.png", im->id()));
214                 }
215         }
216
217         /* Fonts */
218         for (auto i: _load_font_nodes) {
219                 auto file = p.parent_path() / i->uri;
220                 auto j = _fonts.begin();
221                 while (j != _fonts.end() && j->load_id != i->id) {
222                         ++j;
223                 }
224                 if (j != _fonts.end ()) {
225                         j->data.write (file);
226                         j->file = file;
227                 }
228         }
229 }
230
231
232 /** Look at a supplied list of assets and find the fonts.  Then match these
233  *  fonts up with anything requested by a <LoadFont> so that _fonts contains
234  *  a list of font ID, load ID and data.
235  */
236 void
237 InteropSubtitleAsset::resolve_fonts (vector<shared_ptr<Asset>> assets)
238 {
239         for (auto i: assets) {
240                 auto font = dynamic_pointer_cast<FontAsset> (i);
241                 if (!font) {
242                         continue;
243                 }
244
245                 for (auto j: _load_font_nodes) {
246                         bool got = false;
247                         for (auto const& k: _fonts) {
248                                 if (k.load_id == j->id) {
249                                         got = true;
250                                         break;
251                                 }
252                         }
253
254                         if (!got && font->file() && j->uri == font->file()->leaf().string()) {
255                                 _fonts.push_back (Font (j->id, i->id(), font->file().get()));
256                         }
257                 }
258         }
259 }
260
261
262 void
263 InteropSubtitleAsset::add_font_assets (vector<shared_ptr<Asset>>& assets)
264 {
265         for (auto const& i: _fonts) {
266                 DCP_ASSERT (i.file);
267                 assets.push_back (make_shared<FontAsset>(i.uuid, i.file.get()));
268         }
269 }
270
271
272 void
273 InteropSubtitleAsset::write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const
274 {
275         Asset::write_to_assetmap (node, root);
276
277         for (auto i: _subtitles) {
278                 auto im = dynamic_pointer_cast<dcp::SubtitleImage> (i);
279                 if (im) {
280                         DCP_ASSERT (im->file());
281                         write_file_to_assetmap (node, root, im->file().get(), im->id());
282                 }
283         }
284 }
285
286
287 void
288 InteropSubtitleAsset::add_to_pkl (shared_ptr<PKL> pkl, boost::filesystem::path root) const
289 {
290         Asset::add_to_pkl (pkl, root);
291
292         for (auto i: _subtitles) {
293                 auto im = dynamic_pointer_cast<dcp::SubtitleImage> (i);
294                 if (im) {
295                         auto png_image = im->png_image ();
296                         pkl->add_asset (im->id(), optional<string>(), make_digest(png_image), png_image.size(), "image/png");
297                 }
298         }
299 }
300
301
302 void
303 InteropSubtitleAsset::set_font_file (string load_id, boost::filesystem::path file)
304 {
305         for (auto& i: _fonts) {
306                 if (i.load_id == load_id) {
307                         i.file = file;
308                 }
309         }
310
311         for (auto i: _load_font_nodes) {
312                 if (i->id == load_id) {
313                         i->uri = file.filename().string();
314                 }
315         }
316 }
317