if (!text.string.empty()) {
/* We can provide dummy values for time and frame rate here as they are only used to calculate fades */
dcp::Size const frame = _film->frame_size();
- for (auto i: render_text(text.string, text.fonts, frame, dcpomatic::DCPTime(), 24)) {
+ for (auto i: render_text(text.string, frame, dcpomatic::DCPTime(), 24)) {
dcpomatic::Rect<double> rect (
double(i.position.x) / frame.width, double(i.position.y) / frame.height,
double(i.image->size().width) / frame.width, double(i.image->size().height) / frame.height
#include "bitmap_text.h"
#include "dcpomatic_time.h"
#include "rect.h"
+#include "string_text.h"
#include "types.h"
#include <dcp/subtitle_string.h>
#include <vector>
class ContentStringText : public ContentText
{
public:
- ContentStringText (dcpomatic::ContentTime f, std::vector<dcp::SubtitleString> s)
+ ContentStringText (dcpomatic::ContentTime f, std::vector<StringText> s)
: ContentText (f)
, subs (s)
{}
- std::vector<dcp::SubtitleString> subs;
+ std::vector<StringText> subs;
};
return ContentTime::from_frames(_offset, _dcp_content->active_video_frame_rate(film())) + _next;
}
-
-vector<FontData>
-DCPDecoder::fonts () const
-{
- vector<FontData> data;
- for (auto i: _reels) {
- if (i->main_subtitle() && i->main_subtitle()->asset()) {
- for (auto const& j: i->main_subtitle()->asset()->font_data()) {
- data.push_back (FontData(j.first, j.second));
- }
- }
- }
- return data;
-}
-
bool pass () override;
void seek (dcpomatic::ContentTime t, bool accurate) override;
- std::vector<dcpomatic::FontData> fonts () const override;
-
std::string lazy_digest () const {
return _lazy_digest;
}
}
if (_non_burnt_subtitles) {
- auto fonts = _player->get_subtitle_fonts ();
-
- if (fonts.size() > 1 && _film->interop()) {
- /* Interop will ignore second and subsequent <LoadFont>s so don't even
- write them as they upset some validators.
- */
- auto first = fonts.front();
- fonts.clear ();
- fonts.push_back (first);
- }
-
- _writer->write (fonts);
+ _writer->write(_player->get_subtitle_fonts());
}
while (!_player->pass ()) {}
first = content_time_period(*_next).from;
}
text.push_back (make_shared<TextDecoder>(this, content->only_text(), first));
-
- /* The fonts that are included in content's file; if it's interop there will be none
- * (as the fonts are held in separate assets).
- */
- auto fonts_in_asset = c->font_data();
-
- /* Fonts specified in the TextContent */
- list<shared_ptr<dcpomatic::Font>> fonts_in_content;
- for (auto i: content->text) {
- auto this_fonts = i->fonts();
- std::copy(this_fonts.begin(), this_fonts.end(), std::back_inserter(fonts_in_content));
- }
- fonts_in_content.sort();
- fonts_in_content.unique();
-
- /* Find a font for each <LoadFont> Node */
- for (auto i: c->load_font_nodes()) {
- bool done = false;
- for (auto j: fonts_in_content) {
- if (j->id() == i->id && j->file()) {
- // One was specified in the content
- _fonts.push_back (FontData(i->id, dcp::ArrayData(*j->file())));
- done = true;
- }
- }
- if (!done) {
- if (fonts_in_asset.find(i->id) != fonts_in_asset.end()) {
- // One was included in the subtitle file
- _fonts.push_back (FontData(i->id, fonts_in_asset[i->id]));
- done = true;
- }
- }
- if (!done) {
- // Give up and add a default
- _fonts.push_back (FontData(i->id, dcp::ArrayData(default_font_file())));
- }
- }
}
}
-vector<dcpomatic::FontData>
-DCPSubtitleDecoder::fonts () const
-{
- return _fonts;
-}
-
-
/** @return time of first subtitle, if there is one */
optional<ContentTime>
DCPSubtitleDecoder::first () const
return ContentTime::from_seconds(_subtitles[0]->in().as_seconds());
}
+
#include "text_decoder.h"
#include "dcp_subtitle.h"
-#include "font_data.h"
class DCPSubtitleContent;
bool pass () override;
void seek (dcpomatic::ContentTime time, bool accurate) override;
- std::vector<dcpomatic::FontData> fonts () const override;
-
boost::optional<dcpomatic::ContentTime> first () const;
private:
std::vector<std::shared_ptr<const dcp::Subtitle>> _subtitles;
std::vector<std::shared_ptr<const dcp::Subtitle>>::const_iterator _next;
-
- std::vector<dcpomatic::FontData> _fonts;
};
#include "dcpomatic_time.h"
#include "film.h"
-#include "font_data.h"
#include "types.h"
#include "weak_film.h"
#include <boost/utility.hpp>
virtual void seek (dcpomatic::ContentTime time, bool accurate);
virtual dcpomatic::ContentTime position () const;
-
- virtual std::vector<dcpomatic::FontData> fonts () const {
- return {};
- }
};
using std::string;
+using boost::optional;
using namespace dcpomatic;
{
return !(a == b);
}
+
+
+optional<dcp::ArrayData>
+Font::data () const
+{
+ if (_data) {
+ return _data;
+ }
+
+ if (_file) {
+ return dcp::ArrayData(*_file);
+ }
+
+ return {};
+}
+
#define DCPOMATIC_FONT_H
+#include <dcp/array_data.h>
#include <libcxml/cxml.h>
#include <boost/optional.hpp>
#include <boost/signals2.hpp>
return _id;
}
+ void set_id (std::string id) {
+ _id = id;
+ }
+
boost::optional<boost::filesystem::path> file () const {
return _file;
}
Changed ();
}
+ boost::optional<dcp::ArrayData> data() const;
+
boost::signals2::signal<void()> Changed;
private:
- /** Font ID, used to describe it in the subtitle content */
+ /** Font ID, used to describe it in the subtitle content; could be either a
+ * font family name or an ID from some DCP font XML.
+ */
std::string _id;
+ boost::optional<dcp::ArrayData> _data;
boost::optional<boost::filesystem::path> _file;
};
+++ /dev/null
-/*
- Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#include "font.h"
-#include "font_data.h"
-#include "dcpomatic_assert.h"
-
-
-using std::shared_ptr;
-
-
-dcpomatic::FontData::FontData (shared_ptr<const Font> font)
- : id (font->id())
-{
- if (font->file()) {
- data = dcp::ArrayData(font->file().get());
- }
-}
-
-
-bool
-dcpomatic::operator== (FontData const& a, FontData const& b)
-{
- if (a.id != b.id) {
- return false;
- }
-
- return a.data == b.data;
-}
-
-
-bool
-dcpomatic::operator!= (FontData const& a, FontData const& b)
-{
- return !(a == b);
-}
-
+++ /dev/null
-/*
- Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#ifndef DCPOMATIC_FONT_DATA_H
-#define DCPOMATIC_FONT_DATA_H
-
-
-#include <dcp/array_data.h>
-#include <boost/optional.hpp>
-#include <string>
-
-
-namespace dcpomatic {
-
-
-class Font;
-
-
-/** A font (TTF) file held as a block of data */
-class FontData
-{
-public:
- FontData (std::shared_ptr<const Font> font);
-
- FontData (std::string id_, dcp::ArrayData data_)
- : id(id_)
- , data(data_)
- {}
-
- /** <LoadFont> ID */
- std::string id;
- boost::optional<dcp::ArrayData> data;
-};
-
-
-extern bool operator== (FontData const& a, FontData const& b);
-extern bool operator!= (FontData const& a, FontData const& b);
-
-
-}
-
-
-#endif
--- /dev/null
+/*
+ Copyright (C) 2022 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "dcpomatic_assert.h"
+#include "font.h"
+#include "font_id_map.h"
+
+
+using std::shared_ptr;
+using std::string;
+
+
+std::string
+FontIdMap::get(shared_ptr<dcpomatic::Font> font) const
+{
+ auto iter = _map.find(font);
+ DCPOMATIC_ASSERT(iter != _map.end());
+ return iter->second;
+}
+
+
+void
+FontIdMap::put(shared_ptr<dcpomatic::Font> font, string id)
+{
+ _map[font] = id;
+}
+
--- /dev/null
+/*
+ Copyright (C) 2022 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef DCPOMATIC_FONT_ID_MAP_H
+#define DCPOMATIC_FONT_ID_MAP_H
+
+
+#include <map>
+#include <string>
+
+
+namespace dcpomatic {
+ class Font;
+}
+
+
+class FontIdMap
+{
+public:
+ std::string get(std::shared_ptr<dcpomatic::Font> font) const;
+ void put(std::shared_ptr<dcpomatic::Font> font, std::string id);
+
+ std::map<std::shared_ptr<dcpomatic::Font>, std::string> const& map() const {
+ return _map;
+ }
+
+private:
+ std::map<std::shared_ptr<dcpomatic::Font>, std::string> _map;
+
+};
+
+
+#endif
+
#include "dcp_content_type.h"
#include "film.h"
#include "font.h"
-#include "font_data.h"
#include "hints.h"
#include "maths_util.h"
#include "player.h"
struct timeval last_pulse;
gettimeofday (&last_pulse, 0);
+ _writer->write (player->get_subtitle_fonts());
+
while (!player->pass()) {
struct timeval now;
}
-vector<FontData>
+vector<shared_ptr<Font>>
Player::get_subtitle_fonts ()
{
boost::mutex::scoped_lock lm (_mutex);
- vector<FontData> fonts;
- for (auto i: _pieces) {
- /* XXX: things may go wrong if there are duplicate font IDs
- with different font files.
- */
- auto f = i->decoder->fonts ();
- copy (f.begin(), f.end(), back_inserter(fonts));
+ vector<shared_ptr<Font>> fonts;
+ for (auto piece: _pieces) {
+ for (auto text: piece->content->text) {
+ auto text_fonts = text->fonts();
+ copy (text_fonts.begin(), text_fonts.end(), back_inserter(fonts));
+ }
}
return fonts;
/* String subtitles (rendered to an image) */
if (!j.string.empty()) {
- auto s = render_text (j.string, j.fonts, _video_container_size, time, vfr);
+ auto s = render_text(j.string, _video_container_size, time, vfr);
copy (s.begin(), s.end(), back_inserter (captions));
}
}
}
s.set_in (dcp::Time(from.seconds(), 1000));
- ps.string.push_back (StringText (s, content->outline_width()));
- ps.add_fonts (content->fonts ());
+ ps.string.push_back (s);
}
_active_texts[static_cast<int>(content->type())].add_from(weak_content, ps, from);
class ReelAsset;
}
-namespace dcpomatic {
- class FontData;
-}
-
class AtmosContent;
class AudioBuffers;
class Content;
bool pass ();
void seek (dcpomatic::DCPTime time, bool accurate);
- std::vector<dcpomatic::FontData> get_subtitle_fonts ();
+ std::vector<std::shared_ptr<dcpomatic::Font>> get_subtitle_fonts ();
std::list<ReferencedReelAsset> get_reel_assets ();
dcp::Size video_container_size () const {
boost::mutex::scoped_lock lm (_mutex);
+++ /dev/null
-/*
- Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#include "font.h"
-#include "player_text.h"
-
-
-using std::list;
-using std::shared_ptr;
-using namespace dcpomatic;
-
-
-void
-PlayerText::add_fonts (list<shared_ptr<Font>> fonts_)
-{
- for (auto i: fonts_) {
- bool got = false;
- for (auto j: fonts) {
- if (*i == *j) {
- got = true;
- }
- }
- if (!got) {
- fonts.push_back (i);
- }
- }
-}
class PlayerText
{
public:
- void add_fonts (std::list<std::shared_ptr<dcpomatic::Font>> fonts_);
- std::list<std::shared_ptr<dcpomatic::Font>> fonts;
-
/** BitmapTexts, with their rectangles transformed as specified by their content */
std::list<BitmapText> bitmap;
std::list<StringText> string;
#include "dcpomatic_log.h"
#include "digester.h"
#include "film.h"
-#include "font_data.h"
#include "image.h"
#include "image_png.h"
#include "job.h"
int reel_count,
optional<string> content_summary,
list<ReferencedReelAsset> const & refs,
- vector<FontData> const & fonts,
+ FontIdMap const& fonts,
+ shared_ptr<dcpomatic::Font> chosen_interop_font,
dcp::ArrayData default_font,
shared_ptr<const Film> film,
DCPTimePeriod period,
shared_ptr<Result> reel_asset;
if (asset) {
- /* Add the font to the subtitle content */
- for (auto const& j: fonts) {
- asset->add_font (j.id, j.data.get_value_or(default_font));
+ if (film->interop()) {
+ if (chosen_interop_font) {
+ /* We only add one font, as Interop will ignore subsequent ones (and some validators will
+ * complain if they are even present)
+ */
+ asset->add_font(fonts.get(chosen_interop_font), chosen_interop_font->data().get_value_or(default_font));
+ }
+ } else {
+ for (auto const& font: fonts.map()) {
+ asset->add_font(font.second, font.first->data().get_value_or(default_font));
+ }
}
if (auto interop = dynamic_pointer_cast<dcp::InteropSubtitleAsset>(asset)) {
ReelWriter::create_reel_text (
shared_ptr<dcp::Reel> reel,
list<ReferencedReelAsset> const & refs,
- vector<FontData> const& fonts,
+ FontIdMap const& fonts,
+ shared_ptr<dcpomatic::Font> chosen_interop_font,
int64_t duration,
boost::filesystem::path output_dcp,
bool ensure_subtitles,
) const
{
auto subtitle = maybe_add_text<dcp::ReelInteropSubtitleAsset, dcp::ReelSMPTESubtitleAsset, dcp::ReelSubtitleAsset> (
- _subtitle_asset, duration, reel, _reel_index, _reel_count, _content_summary, refs, fonts, _default_font, film(), _period, output_dcp, _text_only
+ _subtitle_asset, duration, reel, _reel_index, _reel_count, _content_summary, refs, fonts, chosen_interop_font, _default_font, film(), _period, output_dcp, _text_only
);
if (subtitle) {
_content_summary,
refs,
fonts,
+ chosen_interop_font,
_default_font,
film(),
_period,
for (auto const& i: _closed_caption_assets) {
auto a = maybe_add_text<dcp::ReelInteropClosedCaptionAsset, dcp::ReelSMPTEClosedCaptionAsset, dcp::ReelClosedCaptionAsset> (
- i.second, duration, reel, _reel_index, _reel_count, _content_summary, refs, fonts, _default_font, film(), _period, output_dcp, _text_only
+ i.second, duration, reel, _reel_index, _reel_count, _content_summary, refs, fonts, chosen_interop_font, _default_font, film(), _period, output_dcp, _text_only
);
DCPOMATIC_ASSERT (a);
a->set_annotation_text (i.first.name);
_content_summary,
refs,
fonts,
+ chosen_interop_font,
_default_font,
film(),
_period,
shared_ptr<dcp::Reel>
ReelWriter::create_reel (
list<ReferencedReelAsset> const & refs,
- vector<FontData> const & fonts,
+ FontIdMap const & fonts,
+ shared_ptr<dcpomatic::Font> chosen_interop_font,
boost::filesystem::path output_dcp,
bool ensure_subtitles,
set<DCPTextTrack> ensure_closed_captions
create_reel_markers (reel);
}
- create_reel_text (reel, refs, fonts, duration, output_dcp, ensure_subtitles, ensure_closed_captions);
+ create_reel_text (reel, refs, fonts, chosen_interop_font, duration, output_dcp, ensure_subtitles, ensure_closed_captions);
if (_atmos_asset) {
reel->add (make_shared<dcp::ReelAtmosAsset>(_atmos_asset, 0));
void
-ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
+ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period, FontIdMap const& fonts)
{
shared_ptr<dcp::SubtitleAsset> asset;
for (auto i: subs.string) {
i.set_in (dcp::Time(period.from.seconds() - _period.from.seconds(), tcr));
i.set_out (dcp::Time(period.to.seconds() - _period.from.seconds(), tcr));
- asset->add (make_shared<dcp::SubtitleString>(i));
+ auto sub = make_shared<dcp::SubtitleString>(i);
+ if (type == TextType::OPEN_SUBTITLE) {
+ sub->set_font(fonts.get(i.font));
+ }
+ asset->add(sub);
}
for (auto i: subs.bitmap) {
#include "atmos_metadata.h"
#include "dcp_text_track.h"
#include "dcpomatic_time.h"
+#include "font_id_map.h"
#include "player_text.h"
#include "referenced_reel_asset.h"
#include "types.h"
#include <dcp/picture_asset_writer.h>
-namespace dcpomatic {
- class FontData;
-}
-
class AudioBuffers;
class Film;
class InfoFileHandle;
void fake_write (int size);
void repeat_write (Frame frame, Eyes eyes);
void write (std::shared_ptr<const AudioBuffers> audio);
- void write (PlayerText text, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period);
+ void write (PlayerText text, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period, FontIdMap const& fonts);
void write (std::shared_ptr<const dcp::AtmosFrame> atmos, AtmosMetadata metadata);
void finish (boost::filesystem::path output_dcp);
std::shared_ptr<dcp::Reel> create_reel (
std::list<ReferencedReelAsset> const & refs,
- std::vector<dcpomatic::FontData> const & fonts,
+ FontIdMap const & fonts,
+ std::shared_ptr<dcpomatic::Font> chosen_interop_font,
boost::filesystem::path output_dcp,
bool ensure_subtitles,
std::set<DCPTextTrack> ensure_closed_captions
void create_reel_text (
std::shared_ptr<dcp::Reel> reel,
std::list<ReferencedReelAsset> const & refs,
- std::vector<dcpomatic::FontData> const& fonts,
+ FontIdMap const& fonts,
+ std::shared_ptr<dcpomatic::Font> chosen_interop_font,
int64_t duration,
boost::filesystem::path output_dcp,
bool ensure_subtitles,
static string
-setup_font (StringText const& subtitle, list<shared_ptr<Font>> const& fonts)
+setup_font (StringText const& subtitle)
{
auto font_file = default_font_file ();
- for (auto i: fonts) {
- if (i->id() == subtitle.font() && i->file()) {
- font_file = i->file().get();
- }
+ if (subtitle.font && subtitle.font->file()) {
+ font_file = *subtitle.font->file();
}
return FontConfig::instance()->make_font_available(font_file);
* at the same time and with the same fade in/out.
*/
static PositionImage
-render_line (list<StringText> subtitles, list<shared_ptr<Font>> fonts, dcp::Size target, DCPTime time, int frame_rate)
+render_line (list<StringText> subtitles, dcp::Size target, DCPTime time, int frame_rate)
{
/* XXX: this method can only handle italic / bold changes mid-line,
nothing else yet.
DCPOMATIC_ASSERT (!subtitles.empty ());
auto const& first = subtitles.front ();
- auto const font_name = setup_font (first, fonts);
+ auto const font_name = setup_font (first);
auto const fade_factor = calculate_fade_factor (first, time, frame_rate);
auto const markup = marked_up (subtitles, target.height, fade_factor, font_name);
auto layout = create_layout ();
* @param frame_rate DCP frame rate.
*/
list<PositionImage>
-render_text (list<StringText> subtitles, list<shared_ptr<Font>> fonts, dcp::Size target, DCPTime time, int frame_rate)
+render_text (list<StringText> subtitles, dcp::Size target, DCPTime time, int frame_rate)
{
list<StringText> pending;
list<PositionImage> images;
for (auto const& i: subtitles) {
if (!pending.empty() && (i.v_align() != pending.back().v_align() || fabs(i.v_position() - pending.back().v_position()) > 1e-4)) {
- images.push_back (render_line (pending, fonts, target, time, frame_rate));
+ images.push_back(render_line(pending, target, time, frame_rate));
pending.clear ();
}
pending.push_back (i);
}
if (!pending.empty()) {
- images.push_back (render_line (pending, fonts, target, time, frame_rate));
+ images.push_back(render_line(pending, target, time, frame_rate));
}
return images;
}
std::string marked_up (std::list<StringText> subtitles, int target_height, float fade_factor, std::string font_name);
-std::list<PositionImage> render_text (
- std::list<StringText>, std::list<std::shared_ptr<dcpomatic::Font> > fonts, dcp::Size, dcpomatic::DCPTime, int
- );
+std::list<PositionImage> render_text (std::list<StringText>, dcp::Size, dcpomatic::DCPTime, int);
#define DCPOMATIC_STRING_TEXT_H
+#include "font.h"
#include <dcp/subtitle_string.h>
-/** A wrapper for SubtitleString which allows us to 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.
+/** A wrapper for SubtitleString which allows us to:
+ *
+ * - 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.
+ *
+ * - 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_)
+ StringText (dcp::SubtitleString dcp_, int outline_width_, std::shared_ptr<dcpomatic::Font> font_)
: dcp::SubtitleString (dcp_)
, outline_width (outline_width_)
+ , font (font_)
{}
int outline_width;
+ std::shared_ptr<dcpomatic::Font> font;
};
boost::optional<dcpomatic::ContentTime> first () const;
dcpomatic::ContentTime length () const;
+ std::vector<sub::Subtitle> const& subtitles() const {
+ return _subtitles;
+ }
protected:
std::vector<sub::Subtitle> _subtitles;
StringTextFileContent::examine (shared_ptr<const Film> film, shared_ptr<Job> job)
{
Content::examine (film, job);
- StringTextFile s (shared_from_this());
+ StringTextFile file (shared_from_this());
/* Default to turning these subtitles on */
only_text()->set_use (true);
+ std::set<string> names;
+ for (auto const& subtitle: file.subtitles()) {
+ for (auto const& line: subtitle.lines) {
+ for (auto const& block: line.blocks) {
+ names.insert(block.font.get_value_or(""));
+ }
+ }
+ }
+
+ for (auto name: names) {
+ /* Make a font for each family name that somebody might later
+ * ask about.
+ */
+ only_text()->add_font(make_shared<Font>(name));
+ }
+
boost::mutex::scoped_lock lm (_mutex);
- _length = s.length ();
- only_text()->add_font (make_shared<Font>(TEXT_FONT_ID));
+ _length = file.length();
}
ContentTime::from_seconds (s.to.all_as_seconds())
);
}
-
-
-vector<FontData>
-StringTextFileDecoder::fonts () const
-{
- vector<FontData> data;
- for (auto i: text) {
- for (auto j: i->content()->fonts()) {
- data.push_back (FontData(j));
- }
- }
- return data;
-}
-
void seek (dcpomatic::ContentTime time, bool accurate) override;
bool pass () override;
- std::vector<dcpomatic::FontData> fonts () const override;
-
private:
dcpomatic::ContentTimePeriod content_time_period (sub::Subtitle s) const;
#include "compose.hpp"
-#include "font_data.h"
#include "job.h"
#include "player.h"
#include "subtitle_encoder.h"
if (!_film->interop() || _include_font) {
for (auto j: _player->get_subtitle_fonts()) {
- i.first->add_font (j.id, _default_font);
+ i.first->add_font(j->id(), j->data().get_value_or(_default_font));
}
}
private:
void text (PlayerText subs, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period);
- std::vector<std::pair<std::shared_ptr<dcp::SubtitleAsset>, boost::filesystem::path> > _assets;
+ std::vector<std::pair<std::shared_ptr<dcp::SubtitleAsset>, boost::filesystem::path>> _assets;
std::vector<dcpomatic::DCPTimePeriod> _reels;
bool _split_reels;
bool _include_font;
void
TextContent::add_font (shared_ptr<Font> font)
{
+ DCPOMATIC_ASSERT(!get_font(font->id()));
_fonts.push_back (font);
connect_to_fonts ();
}
set_language (c->_language);
set_language_is_additional (c->_language_is_additional);
}
+
+
+shared_ptr<dcpomatic::Font>
+TextContent::get_font(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 take_settings_from (std::shared_ptr<const TextContent> c);
void add_font (std::shared_ptr<dcpomatic::Font> font);
+ std::shared_ptr<dcpomatic::Font> get_font(std::string id) const;
void set_use (bool);
void set_burn (bool);
static
void
-set_forced_appearance(shared_ptr<const TextContent> content, dcp::SubtitleString& subtitle)
+set_forced_appearance(shared_ptr<const TextContent> content, StringText& subtitle)
{
if (content->colour()) {
subtitle.set_colour(*content->colour());
void
TextDecoder::emit_plain_start (ContentTime from, vector<dcp::SubtitleString> subtitles)
{
+ vector<StringText> string_texts;
+
for (auto& subtitle: subtitles) {
- subtitle.set_text(escape_text(subtitle.text()));
- set_forced_appearance(content(), subtitle);
+ auto string_text = StringText(subtitle, content()->outline_width(), subtitle.font() ? content()->get_font(*subtitle.font()) : shared_ptr<Font>());
+ string_text.set_text(escape_text(string_text.text()));
+ set_forced_appearance(content(), string_text);
+ string_texts.push_back(string_text);
}
- PlainStart(ContentStringText(from, subtitles));
+ PlainStart(ContentStringText(from, string_texts));
_position = from;
}
}
}
- vector<dcp::SubtitleString> dcp_subtitles;
+ vector<StringText> string_texts;
for (auto line: sub_subtitle.lines) {
for (auto block: line.blocks) {
content by the other emit_plain_start() above.
*/
- dcp_subtitles.push_back(
- dcp::SubtitleString (
- string(TEXT_FONT_ID),
- block.italic,
- block.bold,
- block.underline,
- block.colour.dcp(),
- block.font_size.points (72 * 11),
- 1.0,
- dcp::Time (from.seconds(), 1000),
- /* XXX: hmm; this is a bit ugly (we don't know the to time yet) */
- dcp::Time (),
- h_position,
- h_align,
- v_position,
- v_align,
- dcp::Direction::LTR,
- block.text,
- dcp::Effect::NONE,
- block.effect_colour.get_value_or(sub::Colour(0, 0, 0)).dcp(),
- /* Hack: we should use subtitle.fade_up and subtitle.fade_down here
- but the times of these often don't have a frame rate associated
- with them so the sub::Time won't convert them to milliseconds without
- throwing an exception. Since only DCP subs fill those in (and we don't
- use libsub for DCP subs) we can cheat by just putting 0 in here.
- */
- dcp::Time (),
- dcp::Time (),
- 0
- )
+ auto dcp_subtitle = dcp::SubtitleString(
+ optional<string>(),
+ block.italic,
+ block.bold,
+ block.underline,
+ block.colour.dcp(),
+ block.font_size.points (72 * 11),
+ 1.0,
+ dcp::Time (from.seconds(), 1000),
+ /* XXX: hmm; this is a bit ugly (we don't know the to time yet) */
+ dcp::Time (),
+ h_position,
+ h_align,
+ v_position,
+ v_align,
+ dcp::Direction::LTR,
+ escape_text(block.text),
+ dcp::Effect::NONE,
+ block.effect_colour.get_value_or(sub::Colour(0, 0, 0)).dcp(),
+ /* Hack: we should use subtitle.fade_up and subtitle.fade_down here
+ but the times of these often don't have a frame rate associated
+ with them so the sub::Time won't convert them to milliseconds without
+ throwing an exception. Since only DCP subs fill those in (and we don't
+ use libsub for DCP subs) we can cheat by just putting 0 in here.
+ */
+ dcp::Time (),
+ dcp::Time (),
+ 0
);
- }
- }
- for (auto& subtitle: dcp_subtitles) {
- subtitle.set_text(escape_text(subtitle.text()));
- set_forced_appearance(content(), subtitle);
+ auto string_text = StringText(dcp_subtitle, content()->outline_width(), content()->get_font(block.font.get_value_or("")));
+ set_forced_appearance(content(), string_text);
+ string_texts.push_back(string_text);
+ }
}
- PlainStart(ContentStringText(from, dcp_subtitles));
+ PlainStart(ContentStringText(from, string_texts));
_position = from;
}
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));
- render_text (subs, list<shared_ptr<Font>>(), dcp::Size(640, 480), DCPTime(), 24);
+ subs.push_back (StringText(ss, 0, {}));
+ render_text (subs, dcp::Size(640, 480), DCPTime(), 24);
#endif
Ratio::setup_ratios ();
#include "cross.h"
#include "audio_buffers.h"
#include "version.h"
-#include "font_data.h"
#include "util.h"
#include "reel_writer.h"
#include "text_content.h"
#include <cerrno>
#include <iostream>
#include <cfloat>
+#include <set>
#include "i18n.h"
using std::max;
using std::min;
using std::shared_ptr;
+using std::set;
using std::string;
using std::vector;
using std::weak_ptr;
/* Add reels */
for (auto& i: _reels) {
- cpl->add (i.create_reel(_reel_assets, _fonts, output_dcp, _have_subtitles, _have_closed_captions));
+ cpl->add (i.create_reel(_reel_assets, _fonts, _chosen_interop_font, output_dcp, _have_subtitles, _have_closed_captions));
}
/* Add metadata */
period = back_off(period);
}
- (*reel)->write (text, type, track, period);
+ (*reel)->write(text, type, track, period, _fonts);
}
void
-Writer::write (vector<FontData> fonts)
+Writer::write (vector<shared_ptr<Font>> fonts)
{
- /* Just keep a list of unique fonts and we'll deal with them in ::finish */
+ if (fonts.empty()) {
+ return;
+ }
- for (auto const& i: fonts) {
- bool got = false;
- for (auto& j: _fonts) {
- if (i == j) {
- got = true;
- }
+ /* Fonts may come in with empty IDs but we don't want to put those in the DCP */
+ auto fix_id = [](string id) {
+ return id.empty() ? "font" : id;
+ };
+
+ if (film()->interop()) {
+ /* Interop will ignore second and subsequent <LoadFont>s so we don't want to
+ * even write them as they upset some validators. Set up _fonts so that every
+ * font used by any subtitle will be written with the same ID.
+ */
+ for (size_t i = 0; i < fonts.size(); ++i) {
+ _fonts.put(fonts[i], fix_id(fonts[0]->id()));
}
+ _chosen_interop_font = fonts[0];
+ } else {
+ set<string> used_ids;
+
+ /* Return the index of a _N at the end of a string, or string::npos */
+ auto underscore_number_position = [](string s) {
+ auto last_underscore = s.find_last_of("_");
+ if (last_underscore == string::npos) {
+ return string::npos;
+ }
+
+ for (auto i = last_underscore + 1; i < s.size(); ++i) {
+ if (!isdigit(s[i])) {
+ return string::npos;
+ }
+ }
+
+ return last_underscore;
+ };
+
+ /* Write fonts to _fonts, changing any duplicate IDs so that they are unique */
+ for (auto font: fonts) {
+ auto id = fix_id(font->id());
+ if (used_ids.find(id) == used_ids.end()) {
+ /* This ID is unique so we can just use it as-is */
+ _fonts.put(font, id);
+ used_ids.insert(id);
+ } else {
+ auto end = underscore_number_position(id);
+ if (end == string::npos) {
+ /* This string has no _N suffix, so add one */
+ id += "_0";
+ end = underscore_number_position(id);
+ }
+
+ ++end;
- if (!got) {
- _fonts.push_back (i);
+ /* Increment the suffix until we find a unique one */
+ auto number = dcp::raw_convert<int>(id.substr(end));
+ while (used_ids.find(id) != used_ids.end()) {
+ ++number;
+ id = String::compose("%1_%2", id.substr(0, end - 1), number);
+ }
+ used_ids.insert(id);
+ }
+ _fonts.put(font, id);
}
+
+ DCPOMATIC_ASSERT(_fonts.map().size() == used_ids.size());
}
}
vector<HangingText> new_hanging_texts;
for (auto i: _hanging_texts) {
if (i.period.from == reel.period().from) {
- reel.write (i.text, i.type, i.track, i.period);
+ reel.write (i.text, i.type, i.track, i.period, _fonts);
} else {
new_hanging_texts.push_back (i);
}
#include "dcp_text_track.h"
#include "dcpomatic_time.h"
#include "exception_store.h"
+#include "font_id_map.h"
#include "player_text.h"
#include "types.h"
#include "weak_film.h"
class Data;
}
-namespace dcpomatic {
- class FontData;
-}
-
class AudioBuffers;
class Film;
class Job;
class ReelWriter;
class ReferencedReelAsset;
+struct writer_disambiguate_font_ids1;
+struct writer_disambiguate_font_ids2;
+struct writer_disambiguate_font_ids3;
struct QueueItem
void repeat (Frame, Eyes);
void write (std::shared_ptr<const AudioBuffers>, dcpomatic::DCPTime time);
void write (PlayerText text, TextType type, boost::optional<DCPTextTrack>, dcpomatic::DCPTimePeriod period);
- void write (std::vector<dcpomatic::FontData> fonts);
+ void write (std::vector<std::shared_ptr<dcpomatic::Font>> fonts);
void write (ReferencedReelAsset asset);
void write (std::shared_ptr<const dcp::AtmosFrame> atmos, dcpomatic::DCPTime time, AtmosMetadata metadata);
void finish (boost::filesystem::path output_dcp);
void set_encoder_threads (int threads);
private:
+ friend struct ::writer_disambiguate_font_ids1;
+ friend struct ::writer_disambiguate_font_ids2;
+ friend struct ::writer_disambiguate_font_ids3;
+
void thread ();
void terminate_thread (bool);
bool have_sequenced_image_at_queue_head ();
std::list<ReferencedReelAsset> _reel_assets;
- std::vector<dcpomatic::FontData> _fonts;
+ FontIdMap _fonts;
+ /** If we are given many fonts, but we're making an Interop DCP, we'll choose a single
+ * one that we'll use everywher. This is that chosen font.
+ */
+ std::shared_ptr<dcpomatic::Font> _chosen_interop_font;
/** true if any reel has any subtitles */
bool _have_subtitles = false;
film.cc
filter.cc
font.cc
- font_data.cc
font_config.cc
+ font_id_map.cc
frame_interval_checker.cc
frame_rate_change.cc
guess_crop.cc
overlaps.cc
pixel_quanta.cc
player.cc
- player_text.cc
player_video.cc
playlist.cc
position_image.cc
wxListItem item;
item.SetId (n);
_fonts->InsertItem (item);
- _fonts->SetItem (n, 0, std_to_wx (i->id ()));
+ auto const id = i->id().empty() ? _("Unspecified") : std_to_wx(i->id());
+ _fonts->SetItem(n, 0, id);
+ _fonts->SetItemData(n, i->id().empty());
if (i->file()) {
- _fonts->SetItem (n, 1, i->file()->leaf().string ());
+ _fonts->SetItem(n, 1, i->file()->leaf().string());
}
++n;
}
}
int const item = _fonts->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
- auto const id = wx_to_std (_fonts->GetItemText(item, 0));
- shared_ptr<Font> font;
- for (auto i: caption->fonts()) {
- if (i->id() == id) {
- font = i;
- }
- }
-
+ auto const id = _fonts->GetItemData(item) ? "" : wx_to_std(_fonts->GetItemText(item, 0));
+ auto font = caption->get_font(id);
if (!font) {
return;
}
film->set_interop(true);
BOOST_REQUIRE_EQUAL(content->text.size(), 1U);
- content->text.front()->add_font(make_shared<dcpomatic::Font>("theFontId", "test/data/Inconsolata-VF.ttf"));
+ content->text.front()->get_font("theFontId")->set_file("test/data/Inconsolata-VF.ttf");
make_and_verify_dcp (film, { dcp::VerificationNote::Code::INVALID_STANDARD });
check_file (subtitle_file(film).parent_path() / "font_0.ttf", "test/data/Inconsolata-VF.ttf");
*/
+
/** @file test/render_text_test.cc
* @brief Check markup of subtitles for rendering.
* @ingroup feature
*/
+
#include "lib/render_text.h"
+#include "lib/string_text.h"
#include <dcp/subtitle_string.h>
#include <boost/test/unit_test.hpp>
+
static void
add (std::list<StringText>& s, std::string text, bool italic, bool bold, bool underline)
{
dcp::Time (),
0
),
- 2
+ 2,
+ std::shared_ptr<dcpomatic::Font>()
)
);
}
film->set_name ("frobozz");
film->set_interop (true);
film->set_sequence (false);
- auto content = make_shared<StringTextFileContent>("test/data/subrip2.srt");
- content->only_text()->set_use (true);
- content->only_text()->set_burn (false);
- film->examine_and_add_content (content);
- film->examine_and_add_content (content);
- BOOST_REQUIRE (!wait_for_jobs());
- content->set_position (film, DCPTime());
+ for (auto i = 0; i < 2; ++i) {
+ auto content = make_shared<StringTextFileContent>("test/data/subrip2.srt");
+ content->only_text()->set_use (true);
+ content->only_text()->set_burn (false);
+ film->examine_and_add_content (content);
+ BOOST_REQUIRE (!wait_for_jobs());
+ content->set_position (film, DCPTime());
+ }
make_and_verify_dcp (film, {dcp::VerificationNote::Code::INVALID_STANDARD});
check_dcp ("test/data/xml_subtitle_test2", film->dir (film->dcp_name ()));
using std::make_shared;
using std::shared_ptr;
+using std::vector;
BOOST_AUTO_TEST_CASE (test_write_odd_amount_of_silence)
cl.run ();
}
+
+BOOST_AUTO_TEST_CASE (writer_disambiguate_font_ids1)
+{
+ auto film = new_test_film2("writer_disambiguate_font_ids1", {});
+ Writer writer(film, {});
+
+ auto fonts = vector<shared_ptr<dcpomatic::Font>> {
+ make_shared<dcpomatic::Font>("a"),
+ make_shared<dcpomatic::Font>("b"),
+ make_shared<dcpomatic::Font>("c")
+ };
+
+ writer.write(fonts);
+
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[0]), "a");
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[1]), "b");
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[2]), "c");
+}
+
+
+BOOST_AUTO_TEST_CASE (writer_disambiguate_font_ids2)
+{
+ auto film = new_test_film2("writer_disambiguate_font_ids2", {});
+ Writer writer(film, {});
+
+ auto fonts = vector<shared_ptr<dcpomatic::Font>> {
+ make_shared<dcpomatic::Font>("a"),
+ make_shared<dcpomatic::Font>("a"),
+ make_shared<dcpomatic::Font>("a")
+ };
+
+ writer.write(fonts);
+
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[0]), "a");
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[1]), "a_0");
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[2]), "a_1");
+}
+
+
+BOOST_AUTO_TEST_CASE (writer_disambiguate_font_ids3)
+{
+ auto film = new_test_film2("writer_disambiguate_font_ids3", {});
+ Writer writer(film, {});
+
+ auto fonts = vector<shared_ptr<dcpomatic::Font>> {
+ make_shared<dcpomatic::Font>("a_2"),
+ make_shared<dcpomatic::Font>("a_1"),
+ make_shared<dcpomatic::Font>("a_1"),
+ make_shared<dcpomatic::Font>("b")
+ };
+
+ writer.write(fonts);
+
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[1]), "a_1");
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[0]), "a_2");
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[2]), "a_3");
+ BOOST_CHECK_EQUAL(writer._fonts.get(fonts[3]), "b");
+}
+