if (smpte) {
verify_smpte_timed_text_asset (smpte, reel_asset_duration, notes);
verify_smpte_subtitle_asset (smpte, notes, state);
- if (namespace_count(asset, "SubtitleReel") > 1) {
+ /* This asset may be encrypted and in that case we'll have no raw_xml() */
+ if (asset->raw_xml() && namespace_count(asset, "SubtitleReel") > 1) {
notes.push_back({ VerificationNote::Type::WARNING, VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()});
}
}
}
-/** Check the timing of the individual subtitles and make sure there are no empty <Text> nodes */
+/** Check the timing of the individual subtitles and make sure there are no empty <Text> nodes etc. */
static
void
verify_text_details (
auto empty_text = false;
/* current reel start time (in editable units) */
int64_t reel_offset = 0;
+ vector<string> font_ids;
+ optional<string> missing_load_font_id;
std::function<void (cxml::ConstNodePtr, optional<int>, optional<Time>, int, bool)> parse;
- parse = [&parse, &last_out, &too_short, &too_close, &too_early, &empty_text, &reel_offset](cxml::ConstNodePtr node, optional<int> tcr, optional<Time> start_time, int er, bool first_reel) {
+ parse = [&parse, &last_out, &too_short, &too_close, &too_early, &empty_text, &reel_offset, &font_ids, &missing_load_font_id](cxml::ConstNodePtr node, optional<int> tcr, optional<Time> start_time, int er, bool first_reel) {
if (node->name() == "Subtitle") {
Time in (node->string_attribute("TimeIn"), tcr);
if (start_time) {
if (!node_has_content(node)) {
empty_text = true;
}
+ } else if (node->name() == "LoadFont") {
+ if (auto const id = node->optional_string_attribute("Id")) {
+ font_ids.push_back(*id);
+ }
+ } else if (node->name() == "Font") {
+ if (auto const font_id = node->optional_string_attribute("Id")) {
+ if (std::find_if(font_ids.begin(), font_ids.end(), [font_id](string const& id) { return id == font_id; }) == font_ids.end()) {
+ missing_load_font_id = font_id;
+ }
+ }
}
-
for (auto i: node->node_children()) {
parse(i, tcr, start_time, er, first_reel);
}
VerificationNote::Type::WARNING, VerificationNote::Code::EMPTY_TEXT
});
}
+
+ if (missing_load_font_id) {
+ notes.push_back(dcp::VerificationNote(VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_id(*missing_load_font_id));
+ }
}
notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())});
} catch (MXFFileError& e) {
notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())});
+ } catch (BadURNUUIDError& e) {
+ notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())});
} catch (cxml::Error& e) {
notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())});
}
case VerificationNote::Code::INVALID_XML:
return String::compose("An XML file is badly formed: %1 (%2:%3)", note.note().get(), note.file()->filename(), note.line().get());
case VerificationNote::Code::MISSING_ASSETMAP:
- return "No ASSETMAP or ASSETMAP.xml was found.";
+ return "No valid ASSETMAP or ASSETMAP.xml was found.";
case VerificationNote::Code::INVALID_INTRINSIC_DURATION:
return String::compose("The intrinsic duration of the asset %1 is less than 1 second.", note.note().get());
case VerificationNote::Code::INVALID_DURATION:
);
case VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT:
return String::compose("The XML in the subtitle asset %1 has more than one namespace declaration.", note.note().get());
+ case VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT:
+ return String::compose("A subtitle or closed caption refers to a font with ID %1 that does not have a corresponding <LoadFont> node", note.id().get());
}
return "";