15
15
// Aegisub Project http://www.aegisub.org/
17
/// @file font_file_lister.cpp
18
/// @brief Base-class for font collector implementations
19
/// @ingroup font_collector
24
17
#include "font_file_lister.h"
26
19
#include "ass_dialogue.h"
27
20
#include "ass_file.h"
28
21
#include "ass_style.h"
29
22
#include "compat.h"
32
#include <libaegisub/of_type_adaptor.h>
25
#include <libaegisub/format_flyweight.h>
26
#include <libaegisub/format_path.h>
34
28
#include <algorithm>
36
30
#include <unicode/uchar.h>
37
31
#include <wx/intl.h>
39
using namespace std::placeholders;
42
wxString format_missing(wxString const& str) {
45
for (wxUniChar c : str) {
46
if (!u_isUWhiteSpace(c.GetValue()))
49
unprintable += wxString::Format("\n - U+%04X ", c.GetValue());
52
auto len = u_charName(c.GetValue(), U_EXTENDED_CHAR_NAME, buf, sizeof buf, &ec);
53
if (len != 0 && U_SUCCESS(ec))
54
unprintable += to_wx(buf);
55
if (c.GetValue() == 0xA0)
56
unprintable += " (\\h)";
34
wxString format_missing(wxString const& str) {
37
for (wxUniChar c : str) {
38
if (!u_isUWhiteSpace(c.GetValue()))
41
unprintable += fmt_wx("\n - U+%04X ", c.GetValue());
44
auto len = u_charName(c.GetValue(), U_EXTENDED_CHAR_NAME, buf, sizeof buf, &ec);
45
if (len != 0 && U_SUCCESS(ec))
46
unprintable += to_wx(buf);
47
if (c.GetValue() == 0xA0)
48
unprintable += " (\\h)";
60
return printable + unprintable;
64
FontCollector::FontCollector(FontCollectorStatusCallback status_callback, FontFileLister &lister)
52
return printable + unprintable;
56
FontCollector::FontCollector(FontCollectorStatusCallback status_callback)
65
57
: status_callback(std::move(status_callback))
58
, lister(this->status_callback)
73
65
auto style_it = styles.find(line->Style);
74
66
if (style_it == end(styles)) {
75
status_callback(wxString::Format(_("Style '%s' does not exist\n"), to_wx(line->Style)), 2);
67
status_callback(fmt_tl("Style '%s' does not exist\n", line->Style), 2);
80
boost::ptr_vector<AssDialogueBlock> blocks(line->ParseTags());
81
72
StyleInfo style = style_it->second;
82
73
StyleInfo initial = style;
84
75
bool overriden = false;
86
for (auto& block : blocks) {
87
if (AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride *>(&block)) {
77
for (auto& block : line->ParseTags()) {
78
if (auto ovr = dynamic_cast<AssDialogueBlockOverride *>(block.get())) {
88
79
for (auto const& tag : ovr->Tags) {
89
std::string const& name = tag.Name;
80
if (tag.Name == "\\r") {
92
81
style = styles[tag.Params[0].Get(line->Style.get())];
95
else if (name == "\\b") {
84
else if (tag.Name == "\\b") {
96
85
style.bold = tag.Params[0].Get(initial.bold);
99
else if (name == "\\i") {
88
else if (tag.Name == "\\i") {
100
89
style.italic = tag.Params[0].Get(initial.italic);
103
else if (name == "\\fn") {
92
else if (tag.Name == "\\fn") {
104
93
style.facename = tag.Params[0].Get(initial.facename);
109
else if (AssDialogueBlockPlain *txt = dynamic_cast<AssDialogueBlockPlain *>(&block)) {
110
wxString text(to_wx(txt->GetText()));
98
else if (auto txt = dynamic_cast<AssDialogueBlockPlain *>(block.get())) {
99
auto text = txt->GetText();
112
101
if (text.empty())
116
used_styles[style].lines.insert(index);
117
std::set<wxUniChar>& chars = used_styles[style].chars;
118
for (auto it = text.begin(); it != text.end(); ++it) {
120
if (cur == L'\\' && it + 1 != text.end()) {
121
wxUniChar next = *++it;
122
if (next == 'N' || next == 'n')
104
auto& usage = used_styles[style];
107
auto& lines = usage.lines;
108
if (lines.empty() || lines.back() != index)
109
lines.push_back(index);
112
auto& chars = usage.chars;
113
auto size = static_cast<int>(text.size());
114
for (int i = 0; i < size; ) {
115
if (text[i] == '\\' && i + 1 < size) {
116
char next = text[++i];
117
if (next == 'N' || next == 'n') {
123
chars.push_back(0xA0);
127
chars.push_back('\\');
132
U8_NEXT(&text[0], i, size, c);
136
sort(begin(chars), end(chars));
137
chars.erase(unique(chars.begin(), chars.end()), chars.end());
132
139
// Do nothing with drawing and comment blocks
136
143
void FontCollector::ProcessChunk(std::pair<StyleInfo, UsageData> const& style) {
137
144
if (style.second.chars.empty()) return;
139
FontFileLister::CollectionResult res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars);
146
auto res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars);
141
148
if (res.paths.empty()) {
142
status_callback(wxString::Format(_("Could not find font '%s'\n"), to_wx(style.first.facename)), 2);
149
status_callback(fmt_tl("Could not find font '%s'\n", style.first.facename), 2);
143
150
PrintUsage(style.second);
147
154
for (auto& elem : res.paths) {
148
if (results.insert(elem).second)
149
status_callback(wxString::Format(_("Found '%s' at '%s'\n"), to_wx(style.first.facename), elem.make_preferred().wstring()), 0);
155
elem.make_preferred();
156
if (std::find(begin(results), end(results), elem) == end(results)) {
157
status_callback(fmt_tl("Found '%s' at '%s'\n", style.first.facename, elem), 0);
158
results.push_back(elem);
163
status_callback(fmt_tl("'%s' does not have a bold variant.\n", style.first.facename), 3);
165
status_callback(fmt_tl("'%s' does not have an italic variant.\n", style.first.facename), 3);
152
167
if (res.missing.size()) {
153
168
if (res.missing.size() > 50)
154
status_callback(wxString::Format(_("'%s' is missing %d glyphs used.\n"), to_wx(style.first.facename), (int)res.missing.size()), 2);
169
status_callback(fmt_tl("'%s' is missing %d glyphs used.\n", style.first.facename, res.missing.size()), 2);
155
170
else if (res.missing.size() > 0)
156
status_callback(wxString::Format(_("'%s' is missing the following glyphs used: %s\n"), to_wx(style.first.facename), format_missing(res.missing)), 2);
171
status_callback(fmt_tl("'%s' is missing the following glyphs used: %s\n", style.first.facename, format_missing(res.missing)), 2);
157
172
PrintUsage(style.second);
158
173
++missing_glyphs;
175
else if (res.fake_bold || res.fake_italic)
176
PrintUsage(style.second);
163
180
void FontCollector::PrintUsage(UsageData const& data) {
164
181
if (data.styles.size()) {
165
status_callback(wxString::Format(_("Used in styles:\n")), 2);
182
status_callback(_("Used in styles:\n"), 2);
166
183
for (auto const& style : data.styles)
167
status_callback(wxString::Format(" - %s\n", style), 2);
184
status_callback(fmt_wx(" - %s\n", style), 2);
170
187
if (data.lines.size()) {
171
status_callback(wxString::Format(_("Used on lines:")), 2);
188
status_callback(_("Used on lines:"), 2);
172
189
for (int line : data.lines)
173
status_callback(wxString::Format(" %d", line), 2);
190
status_callback(fmt_wx(" %d", line), 2);
174
191
status_callback("\n", 2);
176
193
status_callback("\n", 2);
183
200
status_callback(_("Parsing file\n"), 0);
185
for (auto style : file->Line | agi::of_type<const AssStyle>()) {
186
StyleInfo &info = styles[style->name];
187
info.facename = style->font;
188
info.bold = style->bold;
189
info.italic = style->italic;
190
used_styles[info].styles.insert(style->name);
202
for (auto const& style : file->Styles) {
203
StyleInfo &info = styles[style.name];
204
info.facename = style.font;
205
info.bold = style.bold;
206
info.italic = style.italic;
207
used_styles[info].styles.push_back(style.name);
194
for (auto diag : file->Line | agi::of_type<const AssDialogue>())
195
ProcessDialogueLine(diag, ++index);
211
for (auto const& diag : file->Events)
212
ProcessDialogueLine(&diag, ++index);
197
214
status_callback(_("Searching for font files\n"), 0);
198
for_each(used_styles.begin(), used_styles.end(), bind(&FontCollector::ProcessChunk, this, _1));
215
for (auto const& style : used_styles) ProcessChunk(style);
199
216
status_callback(_("Done\n\n"), 0);
201
218
std::vector<agi::fs::path> paths;
205
222
if (missing == 0)
206
223
status_callback(_("All fonts found.\n"), 1);
208
status_callback(wxString::Format(_("%d fonts could not be found.\n"), missing), 2);
225
status_callback(fmt_plural(missing, "One font could not be found\n", "%d fonts could not be found.\n", missing), 2);
209
226
if (missing_glyphs != 0)
210
status_callback(wxString::Format(_("%d fonts were found, but were missing glyphs used in the script.\n"), missing_glyphs), 2);
227
status_callback(fmt_plural(missing_glyphs,
228
"One font was found, but was missing glyphs used in the script.\n",
229
"%d fonts were found, but were missing glyphs used in the script.\n",