diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj index 718bf1010..7a8fce47f 100644 --- a/build/Aegisub/Aegisub.vcxproj +++ b/build/Aegisub/Aegisub.vcxproj @@ -157,7 +157,6 @@ - diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters index 8e7224267..1ea6273f1 100644 --- a/build/Aegisub/Aegisub.vcxproj.filters +++ b/build/Aegisub/Aegisub.vcxproj.filters @@ -225,9 +225,6 @@ Features\Font collector - - Features\Font collector - Video\UI @@ -1117,4 +1114,4 @@ - \ No newline at end of file + diff --git a/src/dialog_fonts_collector.cpp b/src/dialog_fonts_collector.cpp index ed012837f..1e92b4d97 100644 --- a/src/dialog_fonts_collector.cpp +++ b/src/dialog_fonts_collector.cpp @@ -15,7 +15,6 @@ // Aegisub Project http://www.aegisub.org/ #include "font_file_lister.h" -#include "font_file_lister_fontconfig.h" #include "compat.h" #include "dialog_manager.h" @@ -91,8 +90,7 @@ void FontsCollectorThread(AssFile *subs, agi::fs::path const& destination, FcMod collector->AddPendingEvent(event); }; - FontConfigFontFileLister lister(AppendText); - auto paths = FontCollector(AppendText, lister).GetFontPaths(subs); + auto paths = FontCollector(AppendText).GetFontPaths(subs); if (paths.empty()) { collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE)); return; diff --git a/src/font_file_lister.cpp b/src/font_file_lister.cpp index 8c0b6becb..de8203165 100644 --- a/src/font_file_lister.cpp +++ b/src/font_file_lister.cpp @@ -14,11 +14,6 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file font_file_lister.cpp -/// @brief Base-class for font collector implementations -/// @ingroup font_collector -/// - #include "font_file_lister.h" #include "ass_dialogue.h" @@ -36,31 +31,31 @@ #include namespace { - wxString format_missing(wxString const& str) { - wxString printable; - wxString unprintable; - for (wxUniChar c : str) { - if (!u_isUWhiteSpace(c.GetValue())) - printable += c; - else { - unprintable += fmt_wx("\n - U+%04X ", c.GetValue()); - UErrorCode ec; - char buf[1024]; - auto len = u_charName(c.GetValue(), U_EXTENDED_CHAR_NAME, buf, sizeof buf, &ec); - if (len != 0 && U_SUCCESS(ec)) - unprintable += to_wx(buf); - if (c.GetValue() == 0xA0) - unprintable += " (\\h)"; - } +wxString format_missing(wxString const& str) { + wxString printable; + wxString unprintable; + for (wxUniChar c : str) { + if (!u_isUWhiteSpace(c.GetValue())) + printable += c; + else { + unprintable += fmt_wx("\n - U+%04X ", c.GetValue()); + UErrorCode ec; + char buf[1024]; + auto len = u_charName(c.GetValue(), U_EXTENDED_CHAR_NAME, buf, sizeof buf, &ec); + if (len != 0 && U_SUCCESS(ec)) + unprintable += to_wx(buf); + if (c.GetValue() == 0xA0) + unprintable += " (\\h)"; } - - return printable + unprintable; } + + return printable + unprintable; +} } -FontCollector::FontCollector(FontCollectorStatusCallback status_callback, FontFileLister &lister) +FontCollector::FontCollector(FontCollectorStatusCallback status_callback) : status_callback(std::move(status_callback)) -, lister(lister) +, lister(this->status_callback) { } @@ -80,50 +75,66 @@ void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index) { bool overriden = false; for (auto& block : line->ParseTags()) { - if (AssDialogueBlockOverride *ovr = dynamic_cast(block.get())) { + if (auto ovr = dynamic_cast(block.get())) { for (auto const& tag : ovr->Tags) { - std::string const& name = tag.Name; - - if (name == "\\r") { + if (tag.Name == "\\r") { style = styles[tag.Params[0].Get(line->Style.get())]; overriden = false; } - else if (name == "\\b") { + else if (tag.Name == "\\b") { style.bold = tag.Params[0].Get(initial.bold); overriden = true; } - else if (name == "\\i") { + else if (tag.Name == "\\i") { style.italic = tag.Params[0].Get(initial.italic); overriden = true; } - else if (name == "\\fn") { + else if (tag.Name == "\\fn") { style.facename = tag.Params[0].Get(initial.facename); overriden = true; } } } - else if (AssDialogueBlockPlain *txt = dynamic_cast(block.get())) { - wxString text(to_wx(txt->GetText())); + else if (auto txt = dynamic_cast(block.get())) { + auto text = txt->GetText(); if (text.empty()) continue; - if (overriden) - used_styles[style].lines.insert(index); - std::set& chars = used_styles[style].chars; - for (auto it = text.begin(); it != text.end(); ++it) { - wxUniChar cur = *it; - if (cur == L'\\' && it + 1 != text.end()) { - wxUniChar next = *++it; - if (next == 'N' || next == 'n') - continue; - if (next == 'h') - cur = 0xA0; - else - --it; - } - chars.insert(cur); + auto& usage = used_styles[style]; + + if (overriden) { + auto& lines = usage.lines; + if (lines.empty() || lines.back() != index) + lines.push_back(index); } + + auto& chars = usage.chars; + auto size = static_cast(text.size()); + for (int i = 0; i < size; ) { + if (text[i] == '\\' && i + 1 < size) { + char next = text[++i]; + if (next == 'N' || next == 'n') { + ++i; + continue; + } + if (next == 'h') { + ++i; + chars.push_back(0xA0); + continue; + } + + chars.push_back('\\'); + continue; + } + + UChar32 c; + U8_NEXT(&text[0], i, size, c); + chars.push_back(c); + } + + sort(begin(chars), end(chars)); + chars.erase(unique(chars.begin(), chars.end()), chars.end()); } // Do nothing with drawing and comment blocks } @@ -132,7 +143,7 @@ void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index) { void FontCollector::ProcessChunk(std::pair const& style) { if (style.second.chars.empty()) return; - FontFileLister::CollectionResult res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars); + auto res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars); if (res.paths.empty()) { status_callback(fmt_tl("Could not find font '%s'\n", style.first.facename), 2); @@ -141,8 +152,11 @@ void FontCollector::ProcessChunk(std::pair const& style) { } else { for (auto& elem : res.paths) { - if (results.insert(elem).second) - status_callback(fmt_tl("Found '%s' at '%s'\n", style.first.facename, elem.make_preferred()), 0); + elem.make_preferred(); + if (std::find(begin(results), end(results), elem) == end(results)) { + status_callback(fmt_tl("Found '%s' at '%s'\n", style.first.facename, elem), 0); + results.push_back(elem); + } } if (res.fake_bold) @@ -190,7 +204,7 @@ std::vector FontCollector::GetFontPaths(const AssFile *file) { info.facename = style.font; info.bold = style.bold; info.italic = style.italic; - used_styles[info].styles.insert(style.name); + used_styles[info].styles.push_back(style.name); } int index = 0; diff --git a/src/font_file_lister.h b/src/font_file_lister.h index 4d70ef624..7646552fd 100644 --- a/src/font_file_lister.h +++ b/src/font_file_lister.h @@ -14,19 +14,12 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file font_file_lister.h -/// @see font_file_lister.cpp -/// @ingroup font_collector -/// - -#pragma once - #include +#include #include #include #include -#include #include #include @@ -37,18 +30,33 @@ class AssFile; typedef std::function FontCollectorStatusCallback; -/// @class FontFileLister -/// @brief Font lister interface -class FontFileLister { +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 paths; + bool fake_bold = false; + bool fake_italic = false; +}; + +typedef struct _FcConfig FcConfig; +typedef struct _FcFontSet FcFontSet; + +/// @class FontConfigFontFileLister +/// @brief fontconfig powered font lister +class FontConfigFontFileLister { + agi::scoped_holder config; + + /// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans") + /// @param family font fullname + /// @param bold weight attribute + /// @param italic italic attribute + /// @return font set + FcFontSet *MatchFullname(const char *family, int weight, int slant); 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 paths; - bool fake_bold = false; - bool fake_italic = false; - }; + /// Constructor + /// @param cb Callback for status logging + FontConfigFontFileLister(FontCollectorStatusCallback &cb); /// @brief Get the path to the font with the given styles /// @param facename Name of font face @@ -56,7 +64,7 @@ public: /// @param italic Italic? /// @param characters Characters in this style /// @return Path to the matching font file(s), or empty if not found - virtual CollectionResult GetFontPaths(std::string const& facename, int bold, bool italic, std::set const& characters) = 0; + CollectionResult GetFontPaths(std::string const& facename, int bold, bool italic, std::vector const& characters); }; /// @class FontCollector @@ -72,22 +80,22 @@ class FontCollector { /// Data about where each style is used struct UsageData { - std::set chars; ///< Characters used in this style which glyphs will be needed for - std::set lines; ///< Lines on which this style is used via overrides - std::set styles; ///< ASS styles which use this style + std::vector chars; ///< Characters used in this style which glyphs will be needed for + std::vector lines; ///< Lines on which this style is used via overrides + std::vector 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; + + FontConfigFontFileLister lister; /// The set of all glyphs used in the file std::map used_styles; /// Style name -> ASS style definition std::map styles; /// Paths to found required font files - std::set results; + std::vector results; /// Number of fonts which could not be found int missing = 0; /// Number of fonts which were found, but did not contain all used glyphs @@ -106,7 +114,7 @@ public: /// Constructor /// @param status_callback Function to pass status updates to /// @param lister The actual font file lister - FontCollector(FontCollectorStatusCallback status_callback, FontFileLister &lister); + FontCollector(FontCollectorStatusCallback status_callback); /// @brief Get a list of the locations of all font files used in the file /// @param file Lines in the subtitle file to check diff --git a/src/font_file_lister_fontconfig.cpp b/src/font_file_lister_fontconfig.cpp index 28051040a..ba53b17f3 100644 --- a/src/font_file_lister_fontconfig.cpp +++ b/src/font_file_lister_fontconfig.cpp @@ -14,12 +14,7 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file font_file_lister_fontconfig.cpp -/// @brief Font Config-based font collector -/// @ingroup font_collector -/// - -#include "font_file_lister_fontconfig.h" +#include "font_file_lister.h" #include @@ -74,14 +69,14 @@ void find_font(FcFontSet *src, FcFontSet *dst, std::string const& family) { } -FontConfigFontFileLister::FontConfigFontFileLister(FontCollectorStatusCallback cb) +FontConfigFontFileLister::FontConfigFontFileLister(FontCollectorStatusCallback &cb) : config(init_fontconfig(), FcConfigDestroy) { cb(_("Updating font cache\n"), 0); FcConfigBuildFonts(config); } -FontFileLister::CollectionResult FontConfigFontFileLister::GetFontPaths(std::string const& facename, int bold, bool italic, std::set const& characters) { +CollectionResult FontConfigFontFileLister::GetFontPaths(std::string const& facename, int bold, bool italic, std::vector const& characters) { CollectionResult ret; std::string family = facename[0] == '@' ? facename.substr(1) : facename; @@ -124,7 +119,7 @@ FontFileLister::CollectionResult FontConfigFontFileLister::GetFontPaths(std::str FcCharSet *charset; if (FcPatternGetCharSet(match, FC_CHARSET, 0, &charset) == FcResultMatch) { - for (wxUniChar chr : characters) { + for (int chr : characters) { if (!FcCharSetHasChar(charset, chr)) ret.missing += chr; } diff --git a/src/font_file_lister_fontconfig.h b/src/font_file_lister_fontconfig.h deleted file mode 100644 index 9ff2d2a49..000000000 --- a/src/font_file_lister_fontconfig.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012, Thomas Goyne -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file font_file_lister_fontconfig.h -/// @see font_file_lister_fontconfig.cpp -/// @ingroup font_collector -/// - -#include "font_file_lister.h" - -#include - -typedef struct _FcConfig FcConfig; -typedef struct _FcFontSet FcFontSet; - -/// @class FontConfigFontFileLister -/// @brief fontconfig powered font lister -class FontConfigFontFileLister final : public FontFileLister { - agi::scoped_holder config; - - /// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans") - /// @param family font fullname - /// @param bold weight attribute - /// @param italic italic attribute - /// @return font set - FcFontSet *MatchFullname(const char *family, int weight, int slant); -public: - /// Constructor - /// @param cb Callback for status logging - FontConfigFontFileLister(FontCollectorStatusCallback cb); - - CollectionResult GetFontPaths(std::string const& facename, int bold, bool italic, std::set const& characters) override; -};