libdcp
text_asset_internal.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3 
4  This file is part of libdcp.
5 
6  libdcp is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  libdcp is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with libdcp. If not, see <http://www.gnu.org/licenses/>.
18 
19  In addition, as a special exception, the copyright holders give
20  permission to link the code of portions of this program with the
21  OpenSSL library under certain conditions as described in each
22  individual source file, and distribute linked combinations
23  including the two.
24 
25  You must obey the GNU General Public License in all respects
26  for all of the code used other than OpenSSL. If you modify
27  file(s) with this exception, you may extend this exception to your
28  version of the file(s), but you are not obligated to do so. If you
29  do not wish to do so, delete this exception statement from your
30  version. If you delete this exception statement from all source
31  files in the program, then also delete it here.
32 */
33 
34 
40 #include "text_asset_internal.h"
41 #include "text_string.h"
42 #include "compose.hpp"
43 #include <cmath>
44 
45 
46 using std::string;
47 using std::map;
48 using std::shared_ptr;
49 using namespace dcp;
50 
51 
52 order::Font::Font(shared_ptr<TextString> s, Standard standard)
53 {
54  if (s->font()) {
55  if (standard == Standard::SMPTE) {
56  _values["ID"] = s->font().get ();
57  } else {
58  _values["Id"] = s->font().get ();
59  }
60  }
61  _values["Italic"] = s->italic() ? "yes" : "no";
62  _values["Color"] = s->colour().to_argb_string();
63  _values["Size"] = raw_convert<string> (s->size());
64  _values["AspectAdjust"] = raw_convert<string>(s->aspect_adjust(), 1, true);
65  _values["Effect"] = effect_to_string (s->effect());
66  _values["EffectColor"] = s->effect_colour().to_argb_string();
67  _values["Script"] = "normal";
68  if (standard == Standard::SMPTE) {
69  _values["Underline"] = s->underline() ? "yes" : "no";
70  } else {
71  _values["Underlined"] = s->underline() ? "yes" : "no";
72  }
73  _values["Weight"] = s->bold() ? "bold" : "normal";
74 }
75 
76 
77 xmlpp::Element*
78 order::Font::as_xml (xmlpp::Element* parent, Context&) const
79 {
80  auto e = cxml::add_child(parent, "Font");
81  for (const auto& i: _values) {
82  e->set_attribute (i.first, i.second);
83  }
84  return e;
85 }
86 
87 
91 void
93 {
94  map<string, string> inter;
95 
96  for (auto const& i: other._values) {
97  auto t = _values.find (i.first);
98  if (t != _values.end() && t->second == i.second) {
99  inter.insert (i);
100  }
101  }
102 
103  _values = inter;
104 }
105 
106 
108 void
110 {
111  map<string, string> diff;
112  for (auto const& i: _values) {
113  if (other._values.find (i.first) == other._values.end()) {
114  diff.insert (i);
115  }
116  }
117 
118  _values = diff;
119 }
120 
121 
122 bool
123 order::Font::empty () const
124 {
125  return _values.empty ();
126 }
127 
128 
129 xmlpp::Element*
130 order::Part::as_xml (xmlpp::Element* parent, Context &) const
131 {
132  return parent;
133 }
134 
135 
136 xmlpp::Element*
137 order::String::as_xml (xmlpp::Element* parent, Context& context) const
138 {
139  if (fabs(_space_before) > SPACE_BEFORE_EPSILON) {
140  auto space = cxml::add_child(parent, "Space");
141  auto size = raw_convert<string>(_space_before, 2);
142  if (context.standard == Standard::INTEROP) {
143  size += "em";
144  }
145  space->set_attribute("Size", size);
146  }
147  parent->add_child_text (_text);
148  return 0;
149 }
150 
151 
152 void
153 order::Part::write_xml (xmlpp::Element* parent, order::Context& context) const
154 {
155  if (!font.empty ()) {
156  parent = font.as_xml (parent, context);
157  }
158 
159  parent = as_xml (parent, context);
160 
161  for (auto i: children) {
162  i->write_xml (parent, context);
163  }
164 }
165 
166 
167 static void
168 position_align (xmlpp::Element* e, order::Context& context, HAlign h_align, float h_position, VAlign v_align, float v_position, float z_position)
169 {
170  if (h_align != HAlign::CENTER) {
171  if (context.standard == Standard::SMPTE) {
172  e->set_attribute ("Halign", halign_to_string (h_align));
173  } else {
174  e->set_attribute ("HAlign", halign_to_string (h_align));
175  }
176  }
177 
178  if (fabs(h_position) > ALIGN_EPSILON) {
179  if (context.standard == Standard::SMPTE) {
180  e->set_attribute ("Hposition", raw_convert<string> (h_position * 100, 6));
181  } else {
182  e->set_attribute ("HPosition", raw_convert<string> (h_position * 100, 6));
183  }
184  }
185 
186  if (context.standard == Standard::SMPTE) {
187  e->set_attribute ("Valign", valign_to_string (v_align));
188  } else {
189  e->set_attribute ("VAlign", valign_to_string (v_align));
190  }
191 
192  if (fabs(v_position) > ALIGN_EPSILON) {
193  if (context.standard == Standard::SMPTE) {
194  e->set_attribute ("Vposition", raw_convert<string> (v_position * 100, 6));
195  } else {
196  e->set_attribute ("VPosition", raw_convert<string> (v_position * 100, 6));
197  }
198  } else {
199  if (context.standard == Standard::SMPTE) {
200  e->set_attribute ("Vposition", "0");
201  } else {
202  e->set_attribute ("VPosition", "0");
203  }
204  }
205 
206  if (fabs(z_position) > ALIGN_EPSILON && context.standard == Standard::SMPTE) {
207  e->set_attribute("Zposition", raw_convert<string>(z_position * 100, 6));
208  }
209 }
210 
211 
212 xmlpp::Element*
213 order::Text::as_xml (xmlpp::Element* parent, Context& context) const
214 {
215  auto e = cxml::add_child(parent, "Text");
216 
217  position_align(e, context, _h_align, _h_position, _v_align, _v_position, _z_position);
218 
219  /* Interop only supports "horizontal" or "vertical" for direction, so only write this
220  for SMPTE.
221  */
222  if (_direction != Direction::LTR && context.standard == Standard::SMPTE) {
223  e->set_attribute ("Direction", direction_to_string (_direction));
224  }
225 
226  for (auto const& ruby: _rubies) {
227  auto xml = cxml::add_child(e, "Ruby");
228  cxml::add_child(xml, "Rb")->add_child_text(ruby.base);
229  auto rt = cxml::add_child(xml, "Rt");
230  rt->add_child_text(ruby.annotation);
231  rt->set_attribute("Size", dcp::raw_convert<string>(ruby.size, 6));
232  rt->set_attribute("Position", ruby.position == RubyPosition::BEFORE ? "before" : "after");
233  rt->set_attribute("Offset", dcp::raw_convert<string>(ruby.offset, 6));
234  rt->set_attribute("Spacing", dcp::raw_convert<string>(ruby.spacing, 6));
235  rt->set_attribute("AspectAdjust", dcp::raw_convert<string>(ruby.aspect_adjust, 6));
236  }
237 
238  return e;
239 }
240 
241 
242 xmlpp::Element*
243 order::Subtitle::as_xml (xmlpp::Element* parent, Context& context) const
244 {
245  auto e = cxml::add_child(parent, "Subtitle");
246  e->set_attribute ("SpotNumber", raw_convert<string> (context.spot_number++));
247  e->set_attribute ("TimeIn", _in.rebase(context.time_code_rate).as_string(context.standard));
248  e->set_attribute ("TimeOut", _out.rebase(context.time_code_rate).as_string(context.standard));
249  if (context.standard == Standard::SMPTE) {
250  e->set_attribute ("FadeUpTime", _fade_up.rebase(context.time_code_rate).as_string(context.standard));
251  e->set_attribute ("FadeDownTime", _fade_down.rebase(context.time_code_rate).as_string(context.standard));
252  } else {
253  e->set_attribute ("FadeUpTime", raw_convert<string> (_fade_up.as_editable_units_ceil(context.time_code_rate)));
254  e->set_attribute ("FadeDownTime", raw_convert<string> (_fade_down.as_editable_units_ceil(context.time_code_rate)));
255  }
256  return e;
257 }
258 
259 
260 bool
261 order::Font::operator== (Font const & other) const
262 {
263  return _values == other._values;
264 }
265 
266 
267 void
268 order::Font::clear ()
269 {
270  _values.clear ();
271 }
272 
273 
274 xmlpp::Element *
275 order::Image::as_xml (xmlpp::Element* parent, Context& context) const
276 {
277  auto e = cxml::add_child(parent, "Image");
278 
279  position_align(e, context, _h_align, _h_position, _v_align, _v_position, _z_position);
280  if (context.standard == Standard::SMPTE) {
281  e->add_child_text ("urn:uuid:" + _id);
282  } else {
283  e->add_child_text (_id + ".png");
284  }
285 
286  return e;
287 }
void take_difference(Font other)
void take_intersection(Font other)
Namespace for everything in libdcp.
Definition: array_data.h:50
@ LTR
left-to-right
HAlign
Definition: h_align.h:46
@ CENTER
horizontal position is distance from centre of screen to centre of subtitle
constexpr float SPACE_BEFORE_EPSILON
Definition: types.h:278
VAlign
Definition: v_align.h:46
constexpr float ALIGN_EPSILON
Definition: types.h:272
Internal TextAsset helpers.
TextString class.