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;
line->ParseASSTags();
StyleInfo style = styles[line->Style];
StyleInfo initial = style;
bool overriden = false;
for (size_t i = 0; i < line->Blocks.size(); ++i) {
if (AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride *>(line->Blocks[i])) {
for (size_t j = 0; j < ovr->Tags.size(); ++j) {
AssOverrideTag *tag = ovr->Tags[j];
wxString name = tag->Name;
if (name == "\\r")
if (name == "\\r") {
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);
else if (name == "\\i")
overriden = true;
}
else if (name == "\\i") {
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);
else
continue;
overriden = true;
}
}
}
else if (AssDialogueBlockPlain *txt = dynamic_cast<AssDialogueBlockPlain *>(line->Blocks[i])) {
wxString text = txt->GetText();
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)
chars.insert(text[i]);
}
@ -81,39 +91,64 @@ void FontCollector::ProcessDialogueLine(AssDialogue *line) {
line->ClearBlocks();
}
void FontCollector::ProcessChunk(std::pair<StyleInfo, std::set<wxUniChar> > const& style) {
std::vector<wxString> paths = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second);
void FontCollector::ProcessChunk(std::pair<StyleInfo, UsageData> const& style) {
FontFileLister::CollectionResult res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars);
if (paths.empty()) {
status_callback(wxString::Format("Could not find font '%s'\n", style.first.facename), 2);
if (res.paths.empty()) {
status_callback(wxString::Format(_("Could not find font '%s'\n"), style.first.facename), 2);
PrintUsage(style.second);
++missing;
}
else {
for (size_t i = 0; i < paths.size(); ++i) {
if (results.insert(paths[i]).second)
status_callback(wxString::Format("Found '%s' at '%s'\n", style.first.facename, paths[i]), 0);
for (size_t i = 0; i < res.paths.size(); ++i) {
if (results.insert(res.paths[i]).second)
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) {
missing = 0;
missing_glyphs = 0;
status_callback(_("Parsing file\n"), 0);
int index = 0;
for (std::list<AssEntry*>::const_iterator cur = file.begin(); cur != file.end(); ++cur) {
if (AssStyle *style = dynamic_cast<AssStyle*>(*cur)) {
StyleInfo &info = styles[style->name];
info.facename = style->font;
info.bold = style->bold;
info.italic = style->italic;
used_styles[info].styles.insert(style->name);
}
else if (AssDialogue *diag = dynamic_cast<AssDialogue*>(*cur))
ProcessDialogueLine(diag);
}
if (used_styles.empty()) {
status_callback(_("No non-empty dialogue lines found"), 2);
return std::vector<wxString>();
ProcessDialogueLine(diag, ++index);
}
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);
else
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;
}

View file

@ -42,18 +42,26 @@ typedef std::tr1::function<void (wxString, int)> FontCollectorStatusCallback;
/// @brief Font lister interface
class FontFileLister {
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
/// @param facename Name of font face
/// @param bold ASS font weight
/// @param italic Italic?
/// @param characters Characters in this style
/// @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
/// @brief Class which collects the paths to all fonts used in a script
class FontCollector {
/// All data needed to find the font file used to render text
struct StyleInfo {
wxString facename;
int bold;
@ -61,24 +69,37 @@ class FontCollector {
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
FontCollectorStatusCallback status_callback;
/// The actual lister to use to get font paths
FontFileLister &lister;
/// 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
std::map<wxString, StyleInfo> styles;
/// Paths to found required font files
std::set<wxString> results;
/// Number of fonts which could not be found
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
void ProcessDialogueLine(AssDialogue *line);
void ProcessDialogueLine(AssDialogue *line, int index);
/// 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:
/// Constructor

View file

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

View file

@ -42,7 +42,6 @@ class FontConfigFontFileLister : public FontFileLister {
};
scoped<FcConfig*> config;
FontCollectorStatusCallback cb;
/// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans")
/// @param family font fullname
@ -55,7 +54,7 @@ public:
/// @param cb Callback for status logging
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

View file

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

View file

@ -57,7 +57,7 @@ public:
/// @param cb Callback for status logging
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