From 776836d2700123f8a0f109d3a2ac00fad5cdfd48 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 13 Apr 2023 23:39:40 +0200 Subject: [PATCH] Add check for Interop font assets being present (in the ASSETMAP and on disk). --- src/interop_subtitle_asset.cc | 13 +++++++++++++ src/interop_subtitle_asset.h | 6 ++++++ src/verify.cc | 6 ++++++ src/verify.h | 4 ++++ test/verify_test.cc | 13 +++++++++++-- tools/common.cc | 4 +++- 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/interop_subtitle_asset.cc b/src/interop_subtitle_asset.cc index b815da55..24cd907d 100644 --- a/src/interop_subtitle_asset.cc +++ b/src/interop_subtitle_asset.cc @@ -309,3 +309,16 @@ InteropSubtitleAsset::set_font_file (string load_id, boost::filesystem::path fil } } + +vector +InteropSubtitleAsset::unresolved_fonts() const +{ + vector unresolved; + for (auto load_font_node: _load_font_nodes) { + if (std::find_if(_fonts.begin(), _fonts.end(), [load_font_node](Font const& font) { return font.load_id == load_font_node->id; }) == _fonts.end()) { + unresolved.push_back(load_font_node->id); + } + } + return unresolved; +} + diff --git a/src/interop_subtitle_asset.h b/src/interop_subtitle_asset.h index 23cf0b49..467728db 100644 --- a/src/interop_subtitle_asset.h +++ b/src/interop_subtitle_asset.h @@ -85,6 +85,12 @@ public: void add_font_assets (std::vector>& assets); void set_font_file (std::string load_id, boost::filesystem::path file); + /** @return the IDs of fonts for which we have not (yet) found a font asset. + * This could be because resolve_fonts() has not yet been called, or because there is + * a missing font file. + */ + std::vector unresolved_fonts() const; + /** Set the reel number or sub-element identifier * of these subtitles. * @param n New reel number. diff --git a/src/verify.cc b/src/verify.cc index ab66a00e..d43c7ff1 100644 --- a/src/verify.cc +++ b/src/verify.cc @@ -715,6 +715,10 @@ verify_interop_subtitle_asset(shared_ptr asset, vect if (asset->subtitles().empty()) { notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_SUBTITLE, asset->id(), asset->file().get() }); } + auto const unresolved = asset->unresolved_fonts(); + if (!unresolved.empty()) { + notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_FONT, unresolved.front() }); + } } @@ -2008,6 +2012,8 @@ dcp::note_to_string (VerificationNote note) return String::compose("The sound assets do not all have the same channel count; the first to differ is %1", note.file()->filename()); case VerificationNote::Code::INVALID_MAIN_SOUND_CONFIGURATION: return String::compose(" has an invalid value: %1", note.note().get()); + case VerificationNote::Code::MISSING_FONT: + return String::compose("The font file for font ID \"%1\" was not found, or was not referred to in the ASSETMAP.", note.note().get()); } return ""; diff --git a/src/verify.h b/src/verify.h index 7696ea85..ed5cac4c 100644 --- a/src/verify.h +++ b/src/verify.h @@ -432,6 +432,10 @@ public: * file contains the CPL filename */ INVALID_MAIN_SOUND_CONFIGURATION, + /** An interop subtitle file has a node which refers to a font file that is not found. + * note contains the ID + */ + MISSING_FONT }; VerificationNote (Type type, Code code) diff --git a/test/verify_test.cc b/test/verify_test.cc index 159c179c..62e386b7 100644 --- a/test/verify_test.cc +++ b/test/verify_test.cc @@ -770,7 +770,11 @@ BOOST_AUTO_TEST_CASE (verify_valid_interop_subtitles) auto reel_asset = make_shared(asset, dcp::Fraction(24, 1), 16 * 24, 0); write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP); - check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }}); + check_verify_result ( + {dir}, { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } + }); } @@ -801,7 +805,8 @@ BOOST_AUTO_TEST_CASE (verify_invalid_interop_subtitles) string("element 'Foo' is not allowed for content model '(SubtitleID,MovieTitle,ReelNumber,Language,LoadFont*,Font*,Subtitle*)'"), path(), 29 - } + }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } }); } @@ -820,6 +825,7 @@ BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_no_subtitles) { { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } }); } @@ -838,6 +844,7 @@ BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_single_space_subtitle) { dir }, { { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"Arial"} } }); } @@ -926,6 +933,7 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_child_nodes) { dir }, { { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"} } }); } @@ -946,6 +954,7 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_empty_child_nodes { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) }, { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"} }, }); } diff --git a/tools/common.cc b/tools/common.cc index 13ce9cb0..030a2ce7 100644 --- a/tools/common.cc +++ b/tools/common.cc @@ -49,7 +49,9 @@ dcp::filter_notes (vector& notes, bool ignore_missing_ass vector filtered; std::copy_if (notes.begin(), notes.end(), std::back_inserter(filtered), [](dcp::VerificationNote const& i) { - return i.code() != dcp::VerificationNote::Code::MISSING_ASSET && i.code() != dcp::VerificationNote::Code::EXTERNAL_ASSET; + return i.code() != dcp::VerificationNote::Code::MISSING_ASSET && + i.code() != dcp::VerificationNote::Code::EXTERNAL_ASSET && + i.code() != dcp::VerificationNote::Code::MISSING_FONT; }); notes = filtered; -- 2.30.2