*/
-#include "raw_convert.h"
#include "compose.hpp"
+#include "dcp_assert.h"
+#include "load_font_node.h"
+#include "raw_convert.h"
+#include "reel_asset.h"
#include "subtitle_asset.h"
#include "subtitle_asset_internal.h"
+#include "subtitle_image.h"
+#include "subtitle_string.h"
#include "util.h"
#include "xml.h"
-#include "subtitle_string.h"
-#include "subtitle_image.h"
-#include "dcp_assert.h"
-#include "load_font_node.h"
-#include "reel_asset.h"
#include <asdcp/AS_DCP.h>
#include <asdcp/KM_util.h>
#include <libxml++/nodes/element.h>
ps.v_align = string_to_valign (va.get ());
}
+ auto zp = optional_number_attribute<float>(node, "Zposition");
+ if (zp) {
+ ps.z_position = zp.get() / 100;
+ }
}
float space_before = 0;
+ /* Collect <Ruby>s first */
+ auto get_text_content = [](xmlpp::Element const* element) {
+ string all_content;
+ for (auto child: element->get_children()) {
+ auto content = dynamic_cast<xmlpp::ContentNode const*>(child);
+ if (content) {
+ all_content += content->get_content();
+ }
+ }
+ return all_content;
+ };
+
+ vector<Ruby> rubies;
+ for (auto child: node->get_children()) {
+ auto element = dynamic_cast<xmlpp::Element const*>(child);
+ if (element && element->get_name() == "Ruby") {
+ optional<string> base;
+ optional<string> annotation;
+ optional<float> size;
+ optional<RubyPosition> position;
+ optional<float> offset;
+ optional<float> spacing;
+ optional<float> aspect_adjust;
+ for (auto ruby_child: element->get_children()) {
+ if (auto ruby_element = dynamic_cast<xmlpp::Element const*>(ruby_child)) {
+ if (ruby_element->get_name() == "Rb") {
+ base = get_text_content(ruby_element);
+ } else if (ruby_element->get_name() == "Rt") {
+ annotation = get_text_content(ruby_element);
+ size = optional_number_attribute<float>(ruby_element, "Size");
+ if (auto position_string = optional_string_attribute(ruby_element, "Position")) {
+ if (*position_string == "before") {
+ position = RubyPosition::BEFORE;
+ } else if (*position_string == "after") {
+ position = RubyPosition::AFTER;
+ } else {
+ DCP_ASSERT(false);
+ }
+ }
+ offset = optional_number_attribute<float>(ruby_element, "Offset");
+ spacing = optional_number_attribute<float>(ruby_element, "Spacing");
+ aspect_adjust = optional_number_attribute<float>(ruby_element, "AspectAdjust");
+ }
+ }
+ }
+ DCP_ASSERT(base);
+ DCP_ASSERT(annotation);
+ auto ruby = Ruby{*base, *annotation};
+ if (size) {
+ ruby.size = *size;
+ }
+ if (position) {
+ ruby.position = *position;
+ }
+ if (offset) {
+ ruby.offset = *offset;
+ }
+ if (spacing) {
+ ruby.spacing = *spacing;
+ }
+ if (aspect_adjust) {
+ ruby.aspect_adjust = *aspect_adjust;
+ }
+ rubies.push_back(ruby);
+ }
+ }
+
for (auto i: node->get_children()) {
+
+ /* Handle actual content e.g. text */
auto const v = dynamic_cast<xmlpp::ContentNode const *>(i);
if (v) {
- maybe_add_subtitle (v->get_content(), state, space_before, standard);
+ maybe_add_subtitle (v->get_content(), state, space_before, standard, rubies);
space_before = 0;
}
+
+ /* Handle other nodes */
auto const e = dynamic_cast<xmlpp::Element const *>(i);
if (e) {
if (e->get_name() == "Space") {
boost::replace_all(size, "em", "");
}
space_before += raw_convert<float>(size);
- } else {
+ } else if (e->get_name() != "Ruby") {
parse_subtitles (e, state, tcr, standard);
}
}
void
-SubtitleAsset::maybe_add_subtitle (string text, vector<ParseState> const & parse_state, float space_before, Standard standard)
+SubtitleAsset::maybe_add_subtitle(
+ string text,
+ vector<ParseState> const & parse_state,
+ float space_before,
+ Standard standard,
+ vector<Ruby> const& rubies
+ )
{
- if (empty_or_white_space (text)) {
+ auto wanted = [](ParseState const& ps) {
+ return ps.type && (ps.type.get() == ParseState::Type::TEXT || ps.type.get() == ParseState::Type::IMAGE);
+ };
+
+ if (find_if(parse_state.begin(), parse_state.end(), wanted) == parse_state.end()) {
return;
}
if (i.v_align) {
ps.v_align = i.v_align.get();
}
+ if (i.z_position) {
+ ps.z_position = i.z_position.get();
+ }
if (i.direction) {
ps.direction = i.direction.get();
}
ps.h_align.get_value_or(HAlign::CENTER),
ps.v_position.get_value_or(0),
ps.v_align.get_value_or(VAlign::CENTER),
+ ps.z_position.get_value_or(0),
ps.direction.get_value_or (Direction::LTR),
text,
ps.effect.get_value_or (Effect::NONE),
ps.effect_colour.get_value_or (dcp::Colour (0, 0, 0)),
ps.fade_up_time.get_value_or(Time()),
ps.fade_down_time.get_value_or(Time()),
- space_before
+ space_before,
+ rubies
)
);
break;
ps.h_align.get_value_or(HAlign::CENTER),
ps.v_position.get_value_or(0),
ps.v_align.get_value_or(VAlign::CENTER),
+ ps.z_position.get_value_or(0),
ps.fade_up_time.get_value_or(Time()),
ps.fade_down_time.get_value_or(Time())
)
}
-/* XXX: this needs a test */
-vector<shared_ptr<const Subtitle>>
-SubtitleAsset::subtitles_in_reel (shared_ptr<const dcp::ReelAsset> asset) const
-{
- auto frame_rate = asset->edit_rate().as_float();
- auto start = dcp::Time(asset->entry_point().get_value_or(0), frame_rate, time_code_rate());
- auto during = subtitles_during (start, start + dcp::Time(asset->intrinsic_duration(), frame_rate, time_code_rate()), false);
-
- vector<shared_ptr<const dcp::Subtitle>> corrected;
- for (auto i: during) {
- auto c = make_shared<dcp::Subtitle>(*i);
- c->set_in (c->in() - start);
- c->set_out (c->out() - start);
- corrected.push_back (c);
- }
-
- return corrected;
-}
-
-
void
SubtitleAsset::add (shared_ptr<Subtitle> s)
{
bool
-SubtitleAsset::equals (shared_ptr<const Asset> other_asset, EqualityOptions options, NoteHandler note) const
+SubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const& options, NoteHandler note) const
{
if (!Asset::equals (other_asset, options, note)) {
return false;
return false;
}
- if (string_i && *string_i != *string_j) {
- note (NoteType::ERROR, String::compose("subtitles differ in text or metadata: %1 vs %2", string_i->text(), string_j->text()));
+ if (string_i && !string_i->equals(string_j, options, note)) {
return false;
}
float last_h_position;
VAlign last_v_align;
float last_v_position;
+ float last_z_position;
Direction last_direction;
for (auto i: sorted) {
fabs(last_h_position - is->h_position()) > ALIGN_EPSILON ||
last_v_align != is->v_align() ||
fabs(last_v_position - is->v_position()) > ALIGN_EPSILON ||
+ fabs(last_z_position - is->z_position()) > ALIGN_EPSILON ||
last_direction != is->direction()
) {
- text = make_shared<order::Text>(subtitle, is->h_align(), is->h_position(), is->v_align(), is->v_position(), is->direction());
+ text = make_shared<order::Text>(
+ subtitle,
+ is->h_align(),
+ is->h_position(),
+ is->v_align(),
+ is->v_position(),
+ is->z_position(),
+ is->direction(),
+ is->rubies()
+ );
subtitle->children.push_back (text);
last_h_align = is->h_align ();
last_h_position = is->h_position ();
last_v_align = is->v_align ();
last_v_position = is->v_position ();
+ last_z_position = is->z_position();
last_direction = is->direction ();
}
if (ii) {
text.reset ();
subtitle->children.push_back (
- make_shared<order::Image>(subtitle, ii->id(), ii->png_image(), ii->h_align(), ii->h_position(), ii->v_align(), ii->v_position())
+ make_shared<order::Image>(subtitle, ii->id(), ii->png_image(), ii->h_align(), ii->h_position(), ii->v_align(), ii->v_position(), ii->z_position())
);
}
}
* 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
+ * xml_namespace is an optional namespace for the root node; it would be nicer to set this up with
+ * set_namespace_declaration in the caller and then to extract it 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)
+SubtitleAsset::format_xml(xmlpp::Document const& document, optional<pair<string, string>> xml_namespace)
{
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);
+ if (xml_namespace) {
+ if (xml_namespace->first.empty()) {
+ state.xml += String::compose(" xmlns=\"%1\"", xml_namespace->second);
} else {
- state.xml += String::compose(" xmlns:%1=\"%2\"", ns.first, ns.second);
+ state.xml += String::compose(" xmlns:%1=\"%2\"", xml_namespace->first, xml_namespace->second);
}
}
return state.xml;
}
+
+void
+SubtitleAsset::ensure_font(string load_id, dcp::ArrayData data)
+{
+ if (std::find_if(_fonts.begin(), _fonts.end(), [load_id](Font const& font) { return font.load_id == load_id; }) == _fonts.end()) {
+ add_font(load_id, data);
+ }
+}
+