diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/interop_subtitle_asset.cc | 2 | ||||
| -rw-r--r-- | src/smpte_subtitle_asset.cc | 4 | ||||
| -rw-r--r-- | src/subtitle_asset.cc | 124 | ||||
| -rw-r--r-- | src/subtitle_asset.h | 6 |
4 files changed, 127 insertions, 9 deletions
diff --git a/src/interop_subtitle_asset.cc b/src/interop_subtitle_asset.cc index d5147284..bb0cad70 100644 --- a/src/interop_subtitle_asset.cc +++ b/src/interop_subtitle_asset.cc @@ -125,7 +125,7 @@ InteropSubtitleAsset::xml_as_string () const subtitles_as_xml (root, 250, Standard::INTEROP); - return doc.write_to_string ("UTF-8"); + return format_xml(doc, {}); } diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc index f09d99f3..0693323c 100644 --- a/src/smpte_subtitle_asset.cc +++ b/src/smpte_subtitle_asset.cc @@ -344,8 +344,6 @@ SMPTESubtitleAsset::xml_as_string () const { xmlpp::Document doc; auto root = doc.create_root_node ("SubtitleReel"); - root->set_namespace_declaration (subtitle_smpte_ns); - root->set_namespace_declaration ("http://www.w3.org/2001/XMLSchema", "xs"); DCP_ASSERT (_xml_id); root->add_child("Id")->add_child_text("urn:uuid:" + *_xml_id); @@ -374,7 +372,7 @@ SMPTESubtitleAsset::xml_as_string () const subtitles_as_xml (root->add_child("SubtitleList"), _time_code_rate, Standard::SMPTE); - return doc.write_to_string ("UTF-8"); + return format_xml(doc, { {"", subtitle_smpte_ns}, {"xs", "http://www.w3.org/2001/XMLSchema"} }); } diff --git a/src/subtitle_asset.cc b/src/subtitle_asset.cc index 22781196..bd8bb993 100644 --- a/src/subtitle_asset.cc +++ b/src/subtitle_asset.cc @@ -54,18 +54,20 @@ #include <boost/algorithm/string.hpp> #include <boost/lexical_cast.hpp> #include <boost/shared_array.hpp> +#include <algorithm> -using std::dynamic_pointer_cast; -using std::string; -using std::cout; using std::cerr; +using std::cout; +using std::dynamic_pointer_cast; +using std::make_shared; using std::map; +using std::pair; using std::shared_ptr; +using std::string; using std::vector; -using std::make_shared; -using boost::optional; using boost::lexical_cast; +using boost::optional; using namespace dcp; @@ -799,3 +801,115 @@ SubtitleAsset::fix_empty_font_ids () } } } + + +namespace { + +struct State +{ + int indent; + string xml; + int disable_formatting; +}; + +} + + +static +void +format_xml_node (xmlpp::Node const* node, State& state) +{ + if (auto text_node = dynamic_cast<const xmlpp::TextNode*>(node)) { + string content = text_node->get_content(); + boost::replace_all(content, "&", "&"); + boost::replace_all(content, "<", "<"); + boost::replace_all(content, ">", ">"); + state.xml += content; + } else if (auto element = dynamic_cast<const xmlpp::Element*>(node)) { + ++state.indent; + + auto children = element->get_children(); + auto const should_disable_formatting = + std::any_of( + children.begin(), children.end(), + [](xmlpp::Node const* node) { return static_cast<bool>(dynamic_cast<const xmlpp::ContentNode*>(node)); } + ) || element->get_name() == "Text"; + + if (!state.disable_formatting) { + state.xml += "\n" + string(state.indent * 2, ' '); + } + + state.xml += "<" + element->get_name(); + + for (auto attribute: element->get_attributes()) { + state.xml += String::compose(" %1=\"%2\"", attribute->get_name().raw(), attribute->get_value().raw()); + } + + if (children.empty()) { + state.xml += "/>"; + } else { + state.xml += ">"; + + if (should_disable_formatting) { + ++state.disable_formatting; + } + + for (auto child: children) { + format_xml_node(child, state); + } + + if (!state.disable_formatting) { + state.xml += "\n" + string(state.indent * 2, ' '); + } + + state.xml += String::compose("</%1>", element->get_name().raw()); + + if (should_disable_formatting) { + --state.disable_formatting; + } + } + + --state.indent; + } +} + + +/** Format XML much as write_to_string_formatted() would do, except without adding any white space + * to <Text> nodes. This is an attempt to avoid changing what is actually displayed as subtitles + * while also formatting the XML in such a way as to avoid DoM bug 2205. + * + * namespace is a list of namespaces for the root node; it would be nicer to set these up with + * set_namespace_declaration in the caller and then to extract them here but I couldn't find a way + * to get all namespaces with the libxml++ API. + */ +string +SubtitleAsset::format_xml (xmlpp::Document const& document, vector<pair<string, string>> const& namespaces) +{ + auto root = document.get_root_node(); + + State state = {}; + state.xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<" + root->get_name(); + + for (auto const& ns: namespaces) { + if (ns.first.empty()) { + state.xml += String::compose(" xmlns=\"%1\"", ns.second); + } else { + state.xml += String::compose(" xmlns:%1=\"%2\"", ns.first, ns.second); + } + } + + for (auto attribute: root->get_attributes()) { + state.xml += String::compose(" %1=\"%2\"", attribute->get_name().raw(), attribute->get_value().raw()); + } + + state.xml += ">"; + + for (auto child: document.get_root_node()->get_children()) { + format_xml_node(child, state); + } + + state.xml += String::compose("\n</%1>\n", root->get_name().raw()); + + return state.xml; +} + diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h index f51906e2..88b5378c 100644 --- a/src/subtitle_asset.h +++ b/src/subtitle_asset.h @@ -48,9 +48,13 @@ #include <libcxml/cxml.h> #include <boost/shared_array.hpp> #include <map> +#include <string> +#include <utility> +#include <vector> namespace xmlpp { + class Document; class Element; } @@ -128,6 +132,8 @@ public: return _raw_xml; } + static std::string format_xml (xmlpp::Document const& document, std::vector<std::pair<std::string, std::string>> const& namespaces); + protected: friend struct ::interop_dcp_font_test; friend struct ::smpte_dcp_font_test; |
