ContentTime::from_frames(_offset - entry_point, vfr) + ContentTime::from_seconds(b.in().as_seconds()),
ContentTime::from_frames(_offset - entry_point, vfr) + ContentTime::from_seconds(b.out().as_seconds())
),
- strings
+ strings,
+ _dcp_content->standard()
);
strings.clear ();
}
ContentTime::from_frames(_offset - entry_point, vfr) + ContentTime::from_seconds(b.in().as_seconds()),
ContentTime::from_frames(_offset - entry_point, vfr) + ContentTime::from_seconds(b.out().as_seconds())
),
- strings
+ strings,
+ _dcp_content->standard()
);
strings.clear ();
}
_subtitles = asset->subtitles ();
_next = _subtitles.begin ();
+ if (dynamic_pointer_cast<dcp::InteropSubtitleAsset>(asset)) {
+ _standard = dcp::Standard::INTEROP;
+ } else {
+ _standard = dcp::Standard::SMPTE;
+ }
+
text.push_back (make_shared<TextDecoder>(this, content->only_text()));
update_position();
}
}
}
- only_text()->emit_plain (p, s);
+ only_text()->emit_plain(p, s, _standard);
update_position();
std::vector<std::shared_ptr<const dcp::Subtitle>> _subtitles;
std::vector<std::shared_ptr<const dcp::Subtitle>>::const_iterator _next;
+
+ dcp::Standard _standard;
};
static int
-y_position (StringText const& first, int target_height, int layout_height)
+y_position (StringText const& first, int target_height, int baseline_to_bottom, int layout_height)
{
int y = 0;
- switch (first.v_align()) {
- case dcp::VAlign::TOP:
- /* SMPTE says that v_position is the distance between top
- of frame and top of subtitle, but this doesn't always seem to be
- the case in practice; Gunnar Ásgeirsson's Dolby server appears
- to put VAlign::TOP subs with v_position as the distance between top
- of frame and bottom of subtitle.
- */
- y = first.v_position() * target_height - layout_height;
- break;
- case dcp::VAlign::CENTER:
- /* v_position is distance between centre of frame and centre of subtitle */
- y = (0.5 + first.v_position()) * target_height - layout_height / 2;
- break;
- case dcp::VAlign::BOTTOM:
- /* v_position is distance between bottom of frame and bottom of subtitle */
- y = (1.0 - first.v_position()) * target_height - layout_height;
+ switch (first.valign_standard) {
+ case dcp::Standard::INTEROP:
+ switch (first.v_align()) {
+ case dcp::VAlign::TOP:
+ /* v_position is distance from top of frame to subtitle baseline */
+ y = first.v_position() * target_height - (layout_height - baseline_to_bottom);
+ break;
+ case dcp::VAlign::CENTER:
+ /* v_position is distance from centre of frame to subtitle baseline */
+ y = (0.5 + first.v_position()) * target_height - (layout_height - baseline_to_bottom);
+ break;
+ case dcp::VAlign::BOTTOM:
+ /* v_position is distance from bottom of frame to subtitle baseline */
+ y = (1.0 - first.v_position()) * target_height - (layout_height - baseline_to_bottom);
+ break;
+ }
break;
+ case dcp::Standard::SMPTE:
+ switch (first.v_align()) {
+ case dcp::VAlign::TOP:
+ /* v_position is distance from top of frame to top of subtitle */
+ y = first.v_position() * target_height;
+ break;
+ case dcp::VAlign::CENTER:
+ /* v_position is distance from centre of frame to centre of subtitle */
+ y = (0.5 + first.v_position()) * target_height - layout_height / 2;
+ break;
+ case dcp::VAlign::BOTTOM:
+ /* v_position is distance from bottom of frame to bottom of subtitle */
+ y = (1.0 - first.v_position()) * target_height - layout_height;
+ break;
+ }
}
return y;
context->stroke ();
int const x = x_position (first, target.width, size.width);
- int const y = y_position (first, target.height, size.height);
+ int const y = y_position (first, target.height, ink.get_y() / Pango::SCALE, size.height);
return PositionImage (image, Position<int>(max (0, x), max(0, y)));
}
* - include settings that are not applicable to true DCP subtitles.
* For example, we can set outline width for burn-in but this cannot be specified in DCP XML.
*
+ * - include details of how v_align should be interpreted
+ *
* - specify the font by referring to a Font object from the content we came from, rather than
* having to use a DCP ID like in dcp::SubtitleString.
*/
class StringText : public dcp::SubtitleString
{
public:
- StringText (dcp::SubtitleString dcp_, int outline_width_, std::shared_ptr<dcpomatic::Font> font_)
+ StringText(dcp::SubtitleString dcp_, int outline_width_, std::shared_ptr<dcpomatic::Font> font_, dcp::Standard valign_standard_)
: dcp::SubtitleString (dcp_)
, outline_width (outline_width_)
, font (font_)
+ , valign_standard (valign_standard_)
{}
int outline_width;
std::shared_ptr<dcpomatic::Font> font;
+ /** Interop and SMPTE use the same VAlign choices (top, center, bottom) but give them different
+ * meanings. This is the standard which should be used to interpret v_align() in this subtitle;
+ * valign_standard == SMPTE means:
+ * top - top of screen to top of subtitle
+ * center - centre of screen to center of subtitle
+ * bottom - bottom of screen to bottom of subtitle
+ * valign_standard == Interop means:
+ * top - top of screen to baseline of subtitle
+ * center - centre of screen to baseline of subtitle
+ * bottom - bottom of screen to baseline of subtitle
+ */
+ dcp::Standard valign_standard;
};
void
-TextDecoder::emit_plain_start (ContentTime from, vector<dcp::SubtitleString> subtitles)
+TextDecoder::emit_plain_start (ContentTime from, vector<dcp::SubtitleString> subtitles, dcp::Standard valign_standard)
{
vector<StringText> string_texts;
for (auto& subtitle: subtitles) {
- auto string_text = StringText(subtitle, content()->outline_width(), subtitle.font() ? content()->get_font(*subtitle.font()) : shared_ptr<Font>());
+ auto string_text = StringText(
+ subtitle,
+ content()->outline_width(),
+ subtitle.font() ? content()->get_font(*subtitle.font()) : shared_ptr<Font>(),
+ valign_standard
+ );
string_text.set_text(escape_text(string_text.text()));
set_forced_appearance(content(), string_text);
string_texts.push_back(string_text);
0
);
- auto string_text = StringText(dcp_subtitle, content()->outline_width(), content()->get_font(block.font.get_value_or("")));
+ auto string_text = StringText(
+ dcp_subtitle,
+ content()->outline_width(),
+ content()->get_font(block.font.get_value_or("")),
+ dcp::Standard::SMPTE
+ );
set_forced_appearance(content(), string_text);
string_texts.push_back(string_text);
}
void
-TextDecoder::emit_plain (ContentTimePeriod period, vector<dcp::SubtitleString> subtitles)
+TextDecoder::emit_plain (ContentTimePeriod period, vector<dcp::SubtitleString> subtitles, dcp::Standard valign_standard)
{
- emit_plain_start (period.from, subtitles);
+ emit_plain_start (period.from, subtitles, valign_standard);
emit_stop (period.to);
}
void emit_bitmap_start (ContentBitmapText const& bitmap);
void emit_bitmap (dcpomatic::ContentTimePeriod period, std::shared_ptr<const Image> image, dcpomatic::Rect<double> rect);
- void emit_plain_start (dcpomatic::ContentTime from, std::vector<dcp::SubtitleString> s);
+ void emit_plain_start (dcpomatic::ContentTime from, std::vector<dcp::SubtitleString> s, dcp::Standard valign_standard);
void emit_plain_start (dcpomatic::ContentTime from, sub::Subtitle const & subtitle);
- void emit_plain (dcpomatic::ContentTimePeriod period, std::vector<dcp::SubtitleString> s);
+ void emit_plain (dcpomatic::ContentTimePeriod period, std::vector<dcp::SubtitleString> s, dcp::Standard valign_standard);
void emit_plain (dcpomatic::ContentTimePeriod period, sub::Subtitle const & subtitle);
void emit_stop (dcpomatic::ContentTime to);
optional<string>(), false, false, false, dcp::Colour(), 42, 1, dcp::Time(), dcp::Time(), 0, dcp::HAlign::CENTER, 0, dcp::VAlign::CENTER, dcp::Direction::LTR,
"Hello dolly", dcp::Effect::NONE, dcp::Colour(), dcp::Time(), dcp::Time(), 0
);
- subs.push_back (StringText(ss, 0, {}));
+ subs.push_back (StringText(ss, 0, {}, dcp::Standard::SMPTE));
render_text (subs, dcp::Size(640, 480), DCPTime(), 24);
#endif
0
),
2,
- std::shared_ptr<dcpomatic::Font>()
+ std::shared_ptr<dcpomatic::Font>(),
+ dcp::Standard::SMPTE
)
);
}