Vertical align.
[libdcp.git] / src / subtitle_asset.cc
1 /*
2     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "subtitle_asset.h"
21
22 using namespace std;
23 using namespace boost;
24 using namespace libdcp;
25
26 SubtitleAsset::SubtitleAsset (string directory, string xml)
27         : Asset (directory, xml)
28         , XMLFile (path().string(), "DCSubtitle")
29 {
30         _subtitle_id = string_node ("SubtitleID");
31         _movie_title = string_node ("MovieTitle");
32         _reel_number = int64_node ("ReelNumber");
33         _language = string_node ("Language");
34
35         ignore_node ("LoadFont");
36
37         list<shared_ptr<FontNode> > font_nodes = sub_nodes<FontNode> ("Font");
38         _load_font_nodes = sub_nodes<LoadFontNode> ("LoadFont");
39
40         /* Now make Subtitle objects to represent the raw XML nodes
41            in a sane way.
42         */
43
44         list<shared_ptr<FontNode> > current_font_nodes;
45         for (list<shared_ptr<FontNode> >::iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
46                 examine_font_node (*i, current_font_nodes);
47         }
48 }
49
50 void
51 SubtitleAsset::examine_font_node (shared_ptr<FontNode> font_node, list<shared_ptr<FontNode> >& current_font_nodes)
52 {
53         current_font_nodes.push_back (font_node);
54
55         for (list<shared_ptr<SubtitleNode> >::iterator j = font_node->subtitle_nodes.begin(); j != font_node->subtitle_nodes.end(); ++j) {
56                 for (list<shared_ptr<TextNode> >::iterator k = (*j)->text_nodes.begin(); k != (*j)->text_nodes.end(); ++k) {
57                         FontNode effective (current_font_nodes);
58                         _subtitles.push_back (
59                                 shared_ptr<Subtitle> (
60                                         new Subtitle (
61                                                 font_id_to_name (effective.id),
62                                                 effective.italic.get(),
63                                                 effective.color.get(),
64                                                 effective.size,
65                                                 (*j)->in,
66                                                 (*j)->out,
67                                                 (*k)->v_position,
68                                                 (*k)->v_align,
69                                                 (*k)->text,
70                                                 effective.effect.get(),
71                                                 effective.effect_color.get()
72                                                 )
73                                         )
74                                 );
75                 }
76         }
77
78         for (list<shared_ptr<FontNode> >::iterator j = font_node->font_nodes.begin(); j != font_node->font_nodes.end(); ++j) {
79                 examine_font_node (*j, current_font_nodes);
80         }
81
82         current_font_nodes.pop_back ();
83 }
84
85 FontNode::FontNode (xmlpp::Node const * node)
86         : XMLNode (node)
87 {
88         id = optional_string_attribute ("Id");
89         size = optional_int64_attribute ("Size");
90         italic = optional_bool_attribute ("Italic");
91         color = optional_color_attribute ("Color");
92         string const e = optional_string_attribute ("Effect");
93         if (e == "none") {
94                 effect = NONE;
95         } else if (e == "border") {
96                 effect = BORDER;
97         } else if (e == "shadow") {
98                 effect = SHADOW;
99         } else if (!e.empty ()) {
100                 throw DCPReadError ("unknown subtitle effect type");
101         }
102         effect_color = optional_color_attribute ("EffectColor");
103         subtitle_nodes = sub_nodes<SubtitleNode> ("Subtitle");
104         font_nodes = sub_nodes<FontNode> ("Font");
105 }
106
107 FontNode::FontNode (list<shared_ptr<FontNode> > const & font_nodes)
108         : size (0)
109         , italic (false)
110         , color ("FFFFFFFF")
111         , effect_color ("FFFFFFFF")
112 {
113         for (list<shared_ptr<FontNode> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
114                 if (!(*i)->id.empty ()) {
115                         id = (*i)->id;
116                 }
117                 if ((*i)->size != 0) {
118                         size = (*i)->size;
119                 }
120                 if ((*i)->italic) {
121                         italic = (*i)->italic.get ();
122                 }
123                 if ((*i)->color) {
124                         color = (*i)->color.get ();
125                 }
126                 if ((*i)->effect) {
127                         effect = (*i)->effect.get ();
128                 }
129                 if ((*i)->effect_color) {
130                         effect_color = (*i)->effect_color.get ();
131                 }
132         }
133 }
134
135 LoadFontNode::LoadFontNode (xmlpp::Node const * node)
136         : XMLNode (node)
137 {
138         id = string_attribute ("Id");
139         uri = string_attribute ("URI");
140 }
141         
142
143 SubtitleNode::SubtitleNode (xmlpp::Node const * node)
144         : XMLNode (node)
145 {
146         in = time_attribute ("TimeIn");
147         out = time_attribute ("TimeOut");
148         text_nodes = sub_nodes<TextNode> ("Text");
149 }
150
151 TextNode::TextNode (xmlpp::Node const * node)
152         : XMLNode (node)
153         , v_align (CENTER)
154 {
155         text = content ();
156         v_position = float_attribute ("VPosition");
157         string const v = optional_string_attribute ("VAlign");
158         if (v == "top") {
159                 v_align = TOP;
160         } else if (v == "center") {
161                 v_align = CENTER;
162         } else if (v == "bottom") {
163                 v_align = BOTTOM;
164         }
165 }
166
167 list<shared_ptr<Subtitle> >
168 SubtitleAsset::subtitles_at (Time t) const
169 {
170         list<shared_ptr<Subtitle> > s;
171         for (list<shared_ptr<Subtitle> >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
172                 if ((*i)->in() <= t && t <= (*i)->out ()) {
173                         s.push_back (*i);
174                 }
175         }
176
177         return s;
178 }
179
180 std::string
181 SubtitleAsset::font_id_to_name (string id) const
182 {
183         list<shared_ptr<LoadFontNode> >::const_iterator i = _load_font_nodes.begin();
184         while (i != _load_font_nodes.end() && (*i)->id != id) {
185                 ++i;
186         }
187
188         if (i == _load_font_nodes.end ()) {
189                 return "";
190         }
191
192         if ((*i)->uri == "arial.ttf") {
193                 return "Arial";
194         }
195
196         return "";
197 }
198
199 Subtitle::Subtitle (
200         string font,
201         bool italic,
202         Color color,
203         int size,
204         Time in,
205         Time out,
206         float v_position,
207         VAlign v_align,
208         string text,
209         Effect effect,
210         Color effect_color
211         )
212         : _font (font)
213         , _italic (italic)
214         , _color (color)
215         , _size (size)
216         , _in (in)
217         , _out (out)
218         , _v_position (v_position)
219         , _v_align (v_align)
220         , _text (text)
221         , _effect (effect)
222         , _effect_color (effect_color)
223 {
224
225 }
226
227 int
228 Subtitle::size_in_pixels (int screen_height) const
229 {
230         /* Size in the subtitle file is given in points as if the screen
231            height is 11 inches, so a 72pt font would be 1/11th of the screen
232            height.
233         */
234         
235         return _size * screen_height / (11 * 72);
236 }