summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-01-29 21:47:38 +0100
committerCarl Hetherington <cth@carlh.net>2025-03-24 00:29:31 +0100
commit0322c4604bf79c11a99dcd929a6f74685a0a4e31 (patch)
treec0adfa69e3f931a47c9096646f73ddb796fa42e3
parent178b13ae534bdb75bd6cacf90dc5ca9f72b6a26b (diff)
Add LoadVariableZ.
-rw-r--r--src/exceptions.cc10
-rw-r--r--src/exceptions.h7
-rw-r--r--src/load_variable_z.cc172
-rw-r--r--src/load_variable_z.h90
-rw-r--r--src/wscript2
-rw-r--r--test/load_variable_z_test.cc91
-rw-r--r--test/wscript1
7 files changed, 371 insertions, 2 deletions
diff --git a/src/exceptions.cc b/src/exceptions.cc
index 4420125c..3f354c57 100644
--- a/src/exceptions.cc
+++ b/src/exceptions.cc
@@ -37,12 +37,13 @@
*/
-#include "exceptions.h"
#include "compose.hpp"
+#include "exceptions.h"
+#include <fmt/format.h>
-using std::string;
using std::runtime_error;
+using std::string;
using boost::optional;
using namespace dcp;
@@ -212,3 +213,8 @@ BadURNUUIDError::BadURNUUIDError(string bad_id)
}
+LoadVariableZError::LoadVariableZError(string variable_z)
+ : runtime_error(fmt::format("Badly-formed LoadVariableZ string {}", variable_z))
+{
+
+}
diff --git a/src/exceptions.h b/src/exceptions.h
index 145df25f..ee4ef142 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -358,6 +358,13 @@ public:
};
+class LoadVariableZError : public std::runtime_error
+{
+public:
+ LoadVariableZError(std::string variable_z);
+};
+
+
}
diff --git a/src/load_variable_z.cc b/src/load_variable_z.cc
new file mode 100644
index 00000000..78882550
--- /dev/null
+++ b/src/load_variable_z.cc
@@ -0,0 +1,172 @@
+/*
+ Copyright (C) 2025 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.
+*/
+
+
+#include "dcp_assert.h"
+#include "exceptions.h"
+#include "load_variable_z.h"
+#include "raw_convert.h"
+#include "warnings.h"
+LIBDCP_DISABLE_WARNINGS
+#include <libxml++/libxml++.h>
+LIBDCP_ENABLE_WARNINGS
+#include <libcxml/cxml.h>
+#include <fmt/format.h>
+#include <boost/algorithm/string.hpp>
+
+
+using std::make_shared;
+using std::string;
+using std::vector;
+using namespace dcp;
+
+
+LoadVariableZ::LoadVariableZ(string id, vector<Text::VariableZPosition> positions)
+ : _id(id)
+ , _positions(positions)
+{
+ _valid = !positions.empty();
+}
+
+
+LoadVariableZ::LoadVariableZ(xmlpp::Element const* xml_node)
+{
+ auto node = make_shared<cxml::Node>(xml_node);
+ _id = node->string_attribute("ID");
+
+ _original_content = node->content();
+ if (_original_content.empty()) {
+ _valid = false;
+ return;
+ }
+
+ vector<string> parts;
+ boost::split(parts, _original_content, boost::is_any_of("\t\n\r "));
+
+ for (auto const& part: parts) {
+ if (part.empty()) {
+ continue;
+ }
+
+ vector<string> halves;
+ boost::split(halves, part, boost::is_any_of(":"));
+
+ auto const allowed = string{"0123456789-."};
+
+ for (auto i = 0U; i < halves[0].size(); ++i) {
+ if (allowed.find(halves[0][i]) == std::string::npos) {
+ _valid = false;
+ return;
+ }
+ }
+
+ auto const position = dcp::raw_convert<float>(halves[0]);
+
+ if (halves.size() == 1) {
+ _positions.push_back({position, 1L});
+ } else if (halves.size() == 2) {
+ auto duration = dcp::raw_convert<int>(halves[1]);
+ if (duration <= 0) {
+ _valid = false;
+ return;
+ }
+ _positions.push_back({position, duration});
+ } else {
+ _valid = false;
+ return;
+ }
+ }
+
+ if (_positions.empty()) {
+ _valid = false;
+ return;
+ }
+}
+
+
+void
+LoadVariableZ::as_xml(xmlpp::Element* element) const
+{
+ element->set_attribute("ID", _id);
+
+ string content;
+
+ if (_valid) {
+ for (auto const& position: _positions) {
+ if (position.duration != 1) {
+ content += fmt::format("{:.1f}:{} ", position.position, position.duration);
+ } else {
+ content += fmt::format("{:.1f} ", position.position);
+ }
+ }
+
+ DCP_ASSERT(!content.empty());
+ content = content.substr(0, content.length() - 1);
+ } else {
+ content = _original_content;
+ }
+
+ element->add_child_text(content);
+}
+
+
+
+void
+LoadVariableZ::set_positions(vector<Text::VariableZPosition> positions)
+{
+ for (auto position: positions) {
+ DCP_ASSERT(position.duration > 0);
+ }
+ DCP_ASSERT(!positions.empty());
+
+ _positions = std::move(positions);
+ _valid = true;
+}
+
+
+vector<Text::VariableZPosition>
+LoadVariableZ::positions() const
+{
+ throw_if_invalid();
+ return _positions;
+}
+
+
+
+void
+LoadVariableZ::throw_if_invalid() const
+{
+ if (!_valid) {
+ throw LoadVariableZError(_original_content);
+ }
+}
diff --git a/src/load_variable_z.h b/src/load_variable_z.h
new file mode 100644
index 00000000..95ba96e0
--- /dev/null
+++ b/src/load_variable_z.h
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) 2025 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.
+*/
+
+
+#ifndef LIBDCP_LOAD_VARIABLE_Z_H
+#define LIBDCP_LOAD_VARIABLE_Z_H
+
+
+#include "text.h"
+#include <libcxml/cxml.h>
+#include <string>
+
+
+namespace xmlpp {
+ class Element;
+}
+
+
+namespace dcp {
+
+
+class LoadVariableZ
+{
+public:
+ explicit LoadVariableZ(std::string id)
+ : _id(id)
+ , _valid(false)
+ {}
+
+ LoadVariableZ(std::string id, std::vector<Text::VariableZPosition> positions);
+
+ explicit LoadVariableZ(xmlpp::Element const* node);
+
+ void as_xml(xmlpp::Element* element) const;
+
+ std::vector<Text::VariableZPosition> positions() const;
+ void set_positions(std::vector<Text::VariableZPosition> positions);
+
+ std::string id() const {
+ return _id;
+ }
+
+ bool valid() const {
+ return _valid;
+ }
+
+private:
+ void throw_if_invalid() const;
+
+ std::string _id;
+ std::string _original_content;
+ std::vector<Text::VariableZPosition> _positions;
+ bool _valid = true;
+};
+
+
+}
+
+
+#endif
diff --git a/src/wscript b/src/wscript
index 8c6ae134..d3d615be 100644
--- a/src/wscript
+++ b/src/wscript
@@ -71,6 +71,7 @@ def build(bld):
j2k_transcode.cc
key.cc
language_tag.cc
+ load_variable_z.cc
local_time.cc
locale_convert.cc
main_sound_configuration.cc
@@ -183,6 +184,7 @@ def build(bld):
key.h
language_tag.h
load_font_node.h
+ load_variable_z.h
local_time.h
locale_convert.h
main_sound_configuration.h
diff --git a/test/load_variable_z_test.cc b/test/load_variable_z_test.cc
new file mode 100644
index 00000000..8448480f
--- /dev/null
+++ b/test/load_variable_z_test.cc
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2025 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.
+*/
+
+
+#include "load_variable_z.h"
+#include "warnings.h"
+#include "compose.hpp"
+LIBDCP_DISABLE_WARNINGS
+#include <libxml++/libxml++.h>
+LIBDCP_ENABLE_WARNINGS
+#include <boost/test/unit_test.hpp>
+
+
+using std::string;
+
+
+static
+dcp::LoadVariableZ
+create(string id, string content)
+{
+ cxml::Document doc("LoadVariableZ");
+ doc.read_string(dcp::String::compose("<LoadVariableZ ID=\"%1\">%2</LoadVariableZ>", id, content));
+ return dcp::LoadVariableZ(dynamic_cast<xmlpp::Element const*>(doc.node()));
+
+}
+
+
+static
+string
+xml(dcp::LoadVariableZ z)
+{
+ xmlpp::Document doc;
+ z.as_xml(doc.create_root_node("LoadVariableZ"));
+ return doc.write_to_string();
+}
+
+
+BOOST_AUTO_TEST_CASE(variable_z_test)
+{
+ for (auto bad: { "", "-4.2 hello", "1:2:3", "-6.4:0", "-6.2:" }) {
+ dcp::LoadVariableZ test = create("foo", bad);
+ BOOST_CHECK_MESSAGE(!test.valid(), bad);
+ BOOST_CHECK_EQUAL(xml(test), dcp::String::compose("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<LoadVariableZ ID=\"foo\">%1</LoadVariableZ>\n", bad));
+ }
+
+ dcp::LoadVariableZ good = create("bar", " -1.4 4.6:2 \t 9.1:9\n");
+ BOOST_CHECK(good.valid());
+ BOOST_CHECK_EQUAL(good.positions().size(), 3U);
+ BOOST_CHECK_CLOSE(good.positions()[0].position, -1.4, 0.1);
+ BOOST_CHECK_EQUAL(good.positions()[0].duration, 1);
+ BOOST_CHECK_CLOSE(good.positions()[1].position, 4.6, 0.1);
+ BOOST_CHECK_EQUAL(good.positions()[1].duration, 2);
+ BOOST_CHECK_CLOSE(good.positions()[2].position, 9.1, 0.1);
+ BOOST_CHECK_EQUAL(good.positions()[2].duration, 9);
+ BOOST_CHECK_EQUAL(xml(good), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<LoadVariableZ ID=\"bar\">-1.4 4.6:2 9.1:9</LoadVariableZ>\n");
+
+ dcp::LoadVariableZ made("baz");
+ BOOST_CHECK(!made.valid());
+ made.set_positions({{-0.6, 2}, {4.2, 9}, {5, 1}});
+ BOOST_CHECK_EQUAL(xml(made), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<LoadVariableZ ID=\"baz\">-0.6:2 4.2:9 5.0</LoadVariableZ>\n");
+}
diff --git a/test/wscript b/test/wscript
index 86ccbca6..5e94b1f7 100644
--- a/test/wscript
+++ b/test/wscript
@@ -91,6 +91,7 @@ def build(bld):
h_align_test.cc
interop_load_font_test.cc
interop_subtitle_test.cc
+ load_variable_z_test.cc
local_time_test.cc
long_filenames_test.cc
make_digest_test.cc