Increase the amount of information reported when fonts can't be found

List the styles using the font along with lines which use the font via
overrides, and add a warning at the end when some glyphs could not be
found to reduce the chance of the user failing to notice it.

Originally committed to SVN as r6434.
This commit is contained in:
Thomas Goyne 2012-02-02 19:18:40 +00:00
parent 6c365f0e6a
commit 6652ef40e9
6 changed files with 94 additions and 45 deletions

View File

@ -43,35 +43,45 @@ FontCollector::FontCollector(FontCollectorStatusCallback status_callback, FontFi
{ {
} }
void FontCollector::ProcessDialogueLine(AssDialogue *line) { void FontCollector::ProcessDialogueLine(AssDialogue *line, int index) {
if (line->Comment) return; if (line->Comment) return;
line->ParseASSTags(); line->ParseASSTags();
StyleInfo style = styles[line->Style]; StyleInfo style = styles[line->Style];
StyleInfo initial = style; StyleInfo initial = style;
bool overriden = false;
for (size_t i = 0; i < line->Blocks.size(); ++i) { for (size_t i = 0; i < line->Blocks.size(); ++i) {
if (AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride *>(line->Blocks[i])) { if (AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride *>(line->Blocks[i])) {
for (size_t j = 0; j < ovr->Tags.size(); ++j) { for (size_t j = 0; j < ovr->Tags.size(); ++j) {
AssOverrideTag *tag = ovr->Tags[j]; AssOverrideTag *tag = ovr->Tags[j];
wxString name = tag->Name; wxString name = tag->Name;
if (name == "\\r") if (name == "\\r") {
style = styles[tag->Params[0]->Get(line->Style)]; style = styles[tag->Params[0]->Get(line->Style)];
if (name == "\\b") overriden = false;
}
else if (name == "\\b") {
style.bold = tag->Params[0]->Get(initial.bold); style.bold = tag->Params[0]->Get(initial.bold);
else if (name == "\\i") overriden = true;
}
else if (name == "\\i") {
style.italic = tag->Params[0]->Get(initial.italic); style.italic = tag->Params[0]->Get(initial.italic);
else if (name == "\\fn") overriden = true;
}
else if (name == "\\fn") {
style.facename = tag->Params[0]->Get(initial.facename); style.facename = tag->Params[0]->Get(initial.facename);
else overriden = true;
continue; }
} }
} }
else if (AssDialogueBlockPlain *txt = dynamic_cast<AssDialogueBlockPlain *>(line->Blocks[i])) { else if (AssDialogueBlockPlain *txt = dynamic_cast<AssDialogueBlockPlain *>(line->Blocks[i])) {
wxString text = txt->GetText(); wxString text = txt->GetText();
if (text.size()) { if (text.size()) {
std::set<wxUniChar>& chars = used_styles[style]; if (overriden)
used_styles[style].lines.insert(index);
std::set<wxUniChar>& chars = used_styles[style].chars;
for (size_t i = 0; i < text.size(); ++i) for (size_t i = 0; i < text.size(); ++i)
chars.insert(text[i]); chars.insert(text[i]);
} }
@ -81,39 +91,64 @@ void FontCollector::ProcessDialogueLine(AssDialogue *line) {
line->ClearBlocks(); line->ClearBlocks();
} }
void FontCollector::ProcessChunk(std::pair<StyleInfo, std::set<wxUniChar> > const& style) { void FontCollector::ProcessChunk(std::pair<StyleInfo, UsageData> const& style) {
std::vector<wxString> paths = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second); FontFileLister::CollectionResult res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars);
if (paths.empty()) { if (res.paths.empty()) {
status_callback(wxString::Format("Could not find font '%s'\n", style.first.facename), 2); status_callback(wxString::Format(_("Could not find font '%s'\n"), style.first.facename), 2);
PrintUsage(style.second);
++missing; ++missing;
} }
else { else {
for (size_t i = 0; i < paths.size(); ++i) { for (size_t i = 0; i < res.paths.size(); ++i) {
if (results.insert(paths[i]).second) if (results.insert(res.paths[i]).second)
status_callback(wxString::Format("Found '%s' at '%s'\n", style.first.facename, paths[i]), 0); status_callback(wxString::Format(_("Found '%s' at '%s'\n"), style.first.facename, res.paths[i]), 0);
}
if (res.missing.size()) {
if (res.missing.size() > 50)
status_callback(wxString::Format(_("'%s' is missing %d glyphs used.\n"), style.first.facename, (int)res.missing.size()), 2);
else if (res.missing.size() > 0)
status_callback(wxString::Format(_("'%s' is missing the following glyphs used: %s\n"), style.first.facename, res.missing), 2);
PrintUsage(style.second);
++missing_glyphs;
} }
} }
} }
void FontCollector::PrintUsage(UsageData const& data) {
if (data.styles.size()) {
status_callback(wxString::Format(_("Used in styles:\n")), 2);
for (std::set<wxString>::const_iterator it = data.styles.begin(); it != data.styles.end(); ++it)
status_callback(wxString::Format(" - %s\n", *it), 2);
}
if (data.lines.size()) {
status_callback(wxString::Format(_("Used on lines:")), 2);
for (std::set<int>::const_iterator it = data.lines.begin(); it != data.lines.end(); ++it)
status_callback(wxString::Format(" %d", *it), 2);
status_callback("\n", 2);
}
status_callback("\n", 2);
}
std::vector<wxString> FontCollector::GetFontPaths(std::list<AssEntry*> const& file) { std::vector<wxString> FontCollector::GetFontPaths(std::list<AssEntry*> const& file) {
missing = 0; missing = 0;
missing_glyphs = 0;
status_callback(_("Parsing file\n"), 0); status_callback(_("Parsing file\n"), 0);
int index = 0;
for (std::list<AssEntry*>::const_iterator cur = file.begin(); cur != file.end(); ++cur) { for (std::list<AssEntry*>::const_iterator cur = file.begin(); cur != file.end(); ++cur) {
if (AssStyle *style = dynamic_cast<AssStyle*>(*cur)) { if (AssStyle *style = dynamic_cast<AssStyle*>(*cur)) {
StyleInfo &info = styles[style->name]; StyleInfo &info = styles[style->name];
info.facename = style->font; info.facename = style->font;
info.bold = style->bold; info.bold = style->bold;
info.italic = style->italic; info.italic = style->italic;
used_styles[info].styles.insert(style->name);
} }
else if (AssDialogue *diag = dynamic_cast<AssDialogue*>(*cur)) else if (AssDialogue *diag = dynamic_cast<AssDialogue*>(*cur))
ProcessDialogueLine(diag); ProcessDialogueLine(diag, ++index);
}
if (used_styles.empty()) {
status_callback(_("No non-empty dialogue lines found"), 2);
return std::vector<wxString>();
} }
status_callback(_("Searching for font files\n"), 0); status_callback(_("Searching for font files\n"), 0);
@ -128,6 +163,8 @@ std::vector<wxString> FontCollector::GetFontPaths(std::list<AssEntry*> const& fi
status_callback(_("All fonts found.\n"), 1); status_callback(_("All fonts found.\n"), 1);
else else
status_callback(wxString::Format(_("%d fonts could not be found.\n"), missing), 2); status_callback(wxString::Format(_("%d fonts could not be found.\n"), missing), 2);
if (missing_glyphs != 0)
status_callback(wxString::Format(_("%d fonts were found, but were missing glyphs used in the script.\n"), missing_glyphs), 2);
return paths; return paths;
} }

View File

@ -42,18 +42,26 @@ typedef std::tr1::function<void (wxString, int)> FontCollectorStatusCallback;
/// @brief Font lister interface /// @brief Font lister interface
class FontFileLister { class FontFileLister {
public: public:
struct CollectionResult {
/// Characters which could not be found in any font files
wxString missing;
/// Paths to the file(s) containing the requested font
std::vector<wxString> paths;
};
/// @brief Get the path to the font with the given styles /// @brief Get the path to the font with the given styles
/// @param facename Name of font face /// @param facename Name of font face
/// @param bold ASS font weight /// @param bold ASS font weight
/// @param italic Italic? /// @param italic Italic?
/// @param characters Characters in this style /// @param characters Characters in this style
/// @return Path to the matching font file(s), or empty if not found /// @return Path to the matching font file(s), or empty if not found
virtual std::vector<wxString> GetFontPaths(wxString const& facename, int bold, bool italic, std::set<wxUniChar> const& characters) = 0; virtual CollectionResult GetFontPaths(wxString const& facename, int bold, bool italic, std::set<wxUniChar> const& characters) = 0;
}; };
/// @class FontCollector /// @class FontCollector
/// @brief Class which collects the paths to all fonts used in a script /// @brief Class which collects the paths to all fonts used in a script
class FontCollector { class FontCollector {
/// All data needed to find the font file used to render text
struct StyleInfo { struct StyleInfo {
wxString facename; wxString facename;
int bold; int bold;
@ -61,24 +69,37 @@ class FontCollector {
bool operator<(StyleInfo const& rgt) const; bool operator<(StyleInfo const& rgt) const;
}; };
/// Data about where each style is used
struct UsageData {
std::set<wxUniChar> chars; ///< Characters used in this style which glyphs will be needed for
std::set<int> lines; ///< Lines on which this style is used via overrides
std::set<wxString> styles; ///< ASS styles which use this style
};
/// Message callback provider by caller /// Message callback provider by caller
FontCollectorStatusCallback status_callback; FontCollectorStatusCallback status_callback;
/// The actual lister to use to get font paths /// The actual lister to use to get font paths
FontFileLister &lister; FontFileLister &lister;
/// The set of all glyphs used in the file /// The set of all glyphs used in the file
std::map<StyleInfo, std::set<wxUniChar> > used_styles; std::map<StyleInfo, UsageData> used_styles;
/// Style name -> ASS style definition /// Style name -> ASS style definition
std::map<wxString, StyleInfo> styles; std::map<wxString, StyleInfo> styles;
/// Paths to found required font files /// Paths to found required font files
std::set<wxString> results; std::set<wxString> results;
/// Number of fonts which could not be found /// Number of fonts which could not be found
int missing; int missing;
/// Number of fonts which were found, but did not contain all used glyphs
int missing_glyphs;
/// Gather all of the unique styles with text on a line /// Gather all of the unique styles with text on a line
void ProcessDialogueLine(AssDialogue *line); void ProcessDialogueLine(AssDialogue *line, int index);
/// Get the font for a single style /// Get the font for a single style
void ProcessChunk(std::pair<StyleInfo, std::set<wxUniChar> > const& style); void ProcessChunk(std::pair<StyleInfo, UsageData> const& style);
/// Print the lines and styles on which a missing font is used
void PrintUsage(UsageData const& data);
public: public:
/// Constructor /// Constructor

View File

@ -71,7 +71,6 @@ namespace {
FontConfigFontFileLister::FontConfigFontFileLister(FontCollectorStatusCallback cb) FontConfigFontFileLister::FontConfigFontFileLister(FontCollectorStatusCallback cb)
: config(FcInitLoadConfig(), FcConfigDestroy) : config(FcInitLoadConfig(), FcConfigDestroy)
, cb(cb)
{ {
cb(_("Updating font cache\n"), 0); cb(_("Updating font cache\n"), 0);
FcConfigBuildFonts(config); FcConfigBuildFonts(config);
@ -113,8 +112,8 @@ FcFontSet *FontConfigFontFileLister::MatchFullname(const char *family, int weigh
return result; return result;
} }
std::vector<wxString> FontConfigFontFileLister::GetFontPaths(wxString const& facename, int bold, bool italic, std::set<wxUniChar> const& characters) { FontFileLister::CollectionResult FontConfigFontFileLister::GetFontPaths(wxString const& facename, int bold, bool italic, std::set<wxUniChar> const& characters) {
std::vector<wxString> ret; CollectionResult ret;
std::string family = STD_STR(facename); std::string family = STD_STR(facename);
if (family[0] == '@') if (family[0] == '@')
@ -188,22 +187,15 @@ std::vector<wxString> FontConfigFontFileLister::GetFontPaths(wxString const& fac
if(FcPatternGetString(rpat, FC_FILE, 0, &file) != FcResultMatch) if(FcPatternGetString(rpat, FC_FILE, 0, &file) != FcResultMatch)
return ret; return ret;
wxString missing;
FcCharSet *charset; FcCharSet *charset;
if (FcPatternGetCharSet(rpat, FC_CHARSET, 0, &charset) == FcResultMatch) { if (FcPatternGetCharSet(rpat, FC_CHARSET, 0, &charset) == FcResultMatch) {
for (std::set<wxUniChar>::const_iterator it = characters.begin(); it != characters.end(); ++it) { for (std::set<wxUniChar>::const_iterator it = characters.begin(); it != characters.end(); ++it) {
if (!FcCharSetHasChar(charset, *it)) if (!FcCharSetHasChar(charset, *it))
missing += *it; ret.missing += *it;
} }
} }
if (missing.size() > 50) ret.paths.push_back((const char *)file);
cb(wxString::Format(_("'%s' is missing %d glyphs used.\n"), facename, (int)missing.size()), 2);
else if (missing.size() > 0)
cb(wxString::Format(_("'%s' is missing the following glyphs used: %s\n"), facename, missing), 2);
ret.push_back((const char *)file);
return ret; return ret;
} }
#endif #endif

View File

@ -42,7 +42,6 @@ class FontConfigFontFileLister : public FontFileLister {
}; };
scoped<FcConfig*> config; scoped<FcConfig*> config;
FontCollectorStatusCallback cb;
/// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans") /// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans")
/// @param family font fullname /// @param family font fullname
@ -55,7 +54,7 @@ public:
/// @param cb Callback for status logging /// @param cb Callback for status logging
FontConfigFontFileLister(FontCollectorStatusCallback cb); FontConfigFontFileLister(FontCollectorStatusCallback cb);
std::vector<wxString> GetFontPaths(wxString const& facename, int bold, bool italic, std::set<wxUniChar> const& characters); CollectionResult GetFontPaths(wxString const& facename, int bold, bool italic, std::set<wxUniChar> const& characters);
}; };
#endif #endif

View File

@ -171,11 +171,11 @@ void FreetypeFontFileLister::AddFont(wxString const& filename, wxString const& f
AddFont(filename, "*" + family); AddFont(filename, "*" + family);
} }
std::vector<wxString> FreetypeFontFileLister::GetFontPaths(wxString const& facename, int, bool, std::set<wxUniChar> const&) { FontFileLister::CollectionResult FreetypeFontFileLister::GetFontPaths(wxString const& facename, int, bool, std::set<wxUniChar> const&) {
std::vector<wxString> ret; CollectionResult ret;
ret.insert(ret.end(), font_files[facename].begin(), font_files[facename].end()); ret.paths.insert(ret.paths.end(), font_files[facename].begin(), font_files[facename].end());
if (ret.empty()) if (ret.paths.empty())
ret.insert(ret.end(), font_files["*" + facename].begin(), font_files["*" + facename].end()); ret.paths.insert(ret.paths.end(), font_files["*" + facename].begin(), font_files["*" + facename].end());
return ret; return ret;
} }

View File

@ -57,7 +57,7 @@ public:
/// @param cb Callback for status logging /// @param cb Callback for status logging
FreetypeFontFileLister(FontCollectorStatusCallback cb); FreetypeFontFileLister(FontCollectorStatusCallback cb);
std::vector<wxString> GetFontPaths(wxString const& facename, int, bool, std::set<wxUniChar> const&); CollectionResult GetFontPaths(wxString const& facename, int, bool, std::set<wxUniChar> const&);
}; };
#endif #endif