/*
- Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2022 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
-#include "text_content.h"
-#include "util.h"
+
+#include "content.h"
#include "exceptions.h"
#include "font.h"
-#include "content.h"
+#include "text_content.h"
+#include "util.h"
#include <dcp/raw_convert.h>
#include <libcxml/cxml.h>
#include <libxml++/libxml++.h>
#include "i18n.h"
+
using std::string;
using std::vector;
using std::cout;
using dcp::raw_convert;
using namespace dcpomatic;
+
int const TextContentProperty::X_OFFSET = 500;
int const TextContentProperty::Y_OFFSET = 501;
int const TextContentProperty::X_SCALE = 502;
int const TextContentProperty::EFFECT = 508;
int const TextContentProperty::EFFECT_COLOUR = 509;
int const TextContentProperty::LINE_SPACING = 510;
-int const TextContentProperty::FADE_IN = 511;
-int const TextContentProperty::FADE_OUT = 512;
-int const TextContentProperty::OUTLINE_WIDTH = 513;
-int const TextContentProperty::TYPE = 514;
-int const TextContentProperty::DCP_TRACK = 515;
+int const TextContentProperty::Z_POSITION = 511;
+int const TextContentProperty::FADE_IN = 512;
+int const TextContentProperty::FADE_OUT = 513;
+int const TextContentProperty::OUTLINE_WIDTH = 514;
+int const TextContentProperty::TYPE = 515;
+int const TextContentProperty::DCP_TRACK = 516;
+int const TextContentProperty::LANGUAGE = 517;
+int const TextContentProperty::LANGUAGE_IS_ADDITIONAL = 518;
+
TextContent::TextContent (Content* parent, TextType type, TextType original_type)
: ContentPart (parent)
* The list could be empty if no TextContents are found.
*/
list<shared_ptr<TextContent>>
-TextContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version)
+TextContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version, list<string>& notes)
{
if (version < 34) {
/* With old metadata FFmpeg content has the subtitle-related tags even with no
if (!node->optional_number_child<double>("SubtitleXOffset") && !node->optional_number_child<double>("SubtitleOffset")) {
return {};
}
- list<shared_ptr<TextContent>> c;
- c.push_back (make_shared<TextContent>(parent, node, version));
- return c;
- }
-
- if (!node->optional_node_child("Text")) {
- return {};
+ return { make_shared<TextContent>(parent, node, version, notes) };
}
list<shared_ptr<TextContent>> c;
for (auto i: node->node_children("Text")) {
- c.push_back (make_shared<TextContent>(parent, i, version));
+ c.push_back (make_shared<TextContent>(parent, i, version, notes));
}
return c;
}
-TextContent::TextContent (Content* parent, cxml::ConstNodePtr node, int version)
+TextContent::TextContent (Content* parent, cxml::ConstNodePtr node, int version, list<string>& notes)
: ContentPart (parent)
, _use (false)
, _burn (false)
, _x_scale (1)
, _y_scale (1)
, _line_spacing (node->optional_number_child<double>("LineSpacing").get_value_or(1))
+ , _z_position (node->optional_number_child<int>("ZPosition").get_value_or(0))
, _outline_width (node->optional_number_child<int>("OutlineWidth").get_value_or(4))
, _type (TextType::OPEN_SUBTITLE)
, _original_type (TextType::OPEN_SUBTITLE)
if (dt) {
_dcp_track = DCPTextTrack (dt);
}
+
+ auto lang = node->optional_node_child("Language");
+ if (lang) {
+ try {
+ _language = dcp::LanguageTag(lang->content());
+ auto add = lang->optional_bool_attribute("Additional");
+ _language_is_additional = add && *add;
+ } catch (dcp::LanguageTagError&) {
+ /* The language tag can be empty or invalid if it was loaded from a
+ * 2.14.x metadata file; we'll just ignore it in that case.
+ */
+ if (version <= 37) {
+ if (!lang->content().empty()) {
+ notes.push_back (String::compose(
+ _("A subtitle or closed caption file in this project is marked with the language '%1', "
+ "which DCP-o-matic does not recognise. The file's language has been cleared."), lang->content()));
+ }
+ } else {
+ throw;
+ }
+ }
+ }
}
TextContent::TextContent (Content* parent, vector<shared_ptr<Content>> c)
throw JoinError (_("Content to be joined must have the same subtitle line spacing."));
}
+ if (c[i]->only_text()->z_position() != ref->z_position()) {
+ throw JoinError (_("Content to be joined must have the same subtitle Z position."));
+ }
+
if ((c[i]->only_text()->fade_in() != ref->fade_in()) || (c[i]->only_text()->fade_out() != ref->fade_out())) {
throw JoinError (_("Content to be joined must have the same subtitle fades."));
}
throw JoinError (_("Content to be joined must use the same DCP track."));
}
+ if (c[i]->only_text()->language() != ref->language()) {
+ throw JoinError (_("Content to be joined must use the same text language."));
+ }
+
+ if (c[i]->only_text()->language_is_additional() != ref->language_is_additional()) {
+ throw JoinError (_("Content to be joined must both be main subtitle languages or both additional."));
+ }
+
auto j = ref_fonts.begin ();
auto k = fonts.begin ();
_y_scale = ref->y_scale ();
_fonts = ref_fonts;
_line_spacing = ref->line_spacing ();
+ _z_position = ref->z_position ();
_fade_in = ref->fade_in ();
_fade_out = ref->fade_out ();
_outline_width = ref->outline_width ();
_type = ref->type ();
_original_type = ref->original_type ();
_dcp_track = ref->dcp_track ();
+ _language = ref->language ();
+ _language_is_additional = ref->language_is_additional ();
connect_to_fonts ();
}
text->add_child("EffectBlue")->add_child_text (raw_convert<string> (_effect_colour->b));
}
text->add_child("LineSpacing")->add_child_text (raw_convert<string> (_line_spacing));
+ text->add_child("ZPosition")->add_child_text(raw_convert<string>(_z_position));
if (_fade_in) {
text->add_child("FadeIn")->add_child_text (raw_convert<string> (_fade_in->get()));
}
if (_dcp_track) {
_dcp_track->as_xml(text->add_child("DCPTrack"));
}
+ if (_language) {
+ auto lang = text->add_child("Language");
+ lang->add_child_text (_language->to_string());
+ lang->set_attribute ("Additional", _language_is_additional ? "1" : "0");
+ }
}
string
+ "_" + raw_convert<string> (x_offset())
+ "_" + raw_convert<string> (y_offset())
+ "_" + raw_convert<string> (line_spacing())
+ + "_" + raw_convert<string> (z_position())
+ "_" + raw_convert<string> (fade_in().get_value_or(ContentTime()).get())
+ "_" + raw_convert<string> (fade_out().get_value_or(ContentTime()).get())
+ "_" + raw_convert<string> (outline_width())
s += "_" + f->file().get_value_or("Default").string();
}
- /* The DCP track is for metadata only, and doesn't affect how this content looks */
+ /* The DCP track and language are for metadata only, and don't affect how this content looks */
return s;
}
void
TextContent::add_font (shared_ptr<Font> font)
{
+ boost::mutex::scoped_lock lm(_mutex);
+
+ DCPOMATIC_ASSERT(!get_font_unlocked(font->id()));
_fonts.push_back (font);
connect_to_fonts ();
}
TextContent::font_changed ()
{
/* XXX: too late */
- ChangeSignaller<Content> cc (_parent, TextContentProperty::FONTS);
+ ContentChangeSignaller cc (_parent, TextContentProperty::FONTS);
}
void
maybe_set (_line_spacing, s, TextContentProperty::LINE_SPACING);
}
+void
+TextContent::set_z_position (int z)
+{
+ maybe_set (_z_position, z, TextContentProperty::Z_POSITION);
+}
+
+
void
TextContent::set_fade_in (ContentTime t)
{
maybe_set (_dcp_track, optional<DCPTextTrack>(), TextContentProperty::DCP_TRACK);
}
+void
+TextContent::set_language (optional<dcp::LanguageTag> language)
+{
+ maybe_set (_language, language, TextContentProperty::LANGUAGE);
+}
+
+void
+TextContent::set_language_is_additional (bool additional)
+{
+ maybe_set (_language_is_additional, additional, TextContentProperty::LANGUAGE_IS_ADDITIONAL);
+}
+
void
TextContent::take_settings_from (shared_ptr<const TextContent> c)
{
unset_effect_colour ();
}
set_line_spacing (c->_line_spacing);
+ set_z_position (c->_z_position);
if (c->_fade_in) {
set_fade_in (*c->_fade_in);
}
} else {
unset_dcp_track ();
}
+ set_language (c->_language);
+ set_language_is_additional (c->_language_is_additional);
+}
+
+
+shared_ptr<dcpomatic::Font>
+TextContent::get_font(string id) const
+{
+ boost::mutex::scoped_lock lm(_mutex);
+ return get_font_unlocked(id);
+}
+
+
+shared_ptr<dcpomatic::Font>
+TextContent::get_font_unlocked(string id) const
+{
+ auto iter = std::find_if(_fonts.begin(), _fonts.end(), [&id](shared_ptr<dcpomatic::Font> font) {
+ return font->id() == id;
+ });
+
+ if (iter == _fonts.end()) {
+ return {};
+ }
+
+ return *iter;
}
+
+
+void
+TextContent::clear_fonts()
+{
+ boost::mutex::scoped_lock lm(_mutex);
+
+ _fonts.clear();
+}
+