diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-01-29 21:47:38 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-03-24 00:29:31 +0100 |
| commit | 0322c4604bf79c11a99dcd929a6f74685a0a4e31 (patch) | |
| tree | c0adfa69e3f931a47c9096646f73ddb796fa42e3 | |
| parent | 178b13ae534bdb75bd6cacf90dc5ca9f72b6a26b (diff) | |
Add LoadVariableZ.
| -rw-r--r-- | src/exceptions.cc | 10 | ||||
| -rw-r--r-- | src/exceptions.h | 7 | ||||
| -rw-r--r-- | src/load_variable_z.cc | 172 | ||||
| -rw-r--r-- | src/load_variable_z.h | 90 | ||||
| -rw-r--r-- | src/wscript | 2 | ||||
| -rw-r--r-- | test/load_variable_z_test.cc | 91 | ||||
| -rw-r--r-- | test/wscript | 1 |
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 |
