Rewrite the font collector

The fontconfig collector should now always pick the exact font files
used by libass rather than a giant mishmash of vaguely related files
which may or may not include the correct font.

Make the freetype font collector windows-only, as it's far inferior to
the fontconfig collector and is only present as a fallback.

Add option to copy the fonts to the script's folder.

Closes #1059.

Originally committed to SVN as r6279.
This commit is contained in:
Thomas Goyne 2012-01-12 22:32:09 +00:00
parent 7dd6cfe37d
commit 988ade0c00
9 changed files with 879 additions and 1317 deletions

View file

@ -98,8 +98,7 @@ audio_player.o: CXXFLAGS += $(CFLAGS_ALSA) $(CFLAGS_PORTAUDIO) $(
audio_provider.o: CXXFLAGS += $(CFLAGS_FFMS2) audio_provider.o: CXXFLAGS += $(CFLAGS_FFMS2)
auto4_base.o: CXXFLAGS += $(CFLAGS_FREETYPE) auto4_base.o: CXXFLAGS += $(CFLAGS_FREETYPE)
charset_detect.o: CXXFLAGS += -D_X86_ charset_detect.o: CXXFLAGS += -D_X86_
font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG) $(CFLAGS_FREETYPE) font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG)
font_file_lister.o: CXXFLAGS += $(CFLAGS_FREETYPE)
text_file_reader.o: CXXFLAGS += -D_X86_ text_file_reader.o: CXXFLAGS += -D_X86_
video_provider_manager.o: CXXFLAGS += $(CFLAGS_FFMS2) video_provider_manager.o: CXXFLAGS += $(CFLAGS_FFMS2)

File diff suppressed because it is too large Load diff

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -35,72 +22,16 @@
/// ///
#ifndef AGI_PRE #ifndef AGI_PRE
#include <wx/button.h>
#include <wx/dialog.h> #include <wx/dialog.h>
#include <wx/radiobox.h>
#include <wx/stattext.h>
#include <wx/stc/stc.h>
#include <wx/textctrl.h>
#endif #endif
class AssFile; class AssFile;
class AssStyle;
class AssOverrideParameter;
class DialogFontsCollector;
class wxZipOutputStream;
class ScintillaTextCtrl; class ScintillaTextCtrl;
class wxButton;
/// DOCME class wxRadioBox;
/// @class FontsCollectorThread class wxStaticText;
/// @brief DOCME class wxTextCtrl;
/// class wxThreadEvent;
/// DOCME
class FontsCollectorThread : public wxThread {
/// DOCME
AssFile *subs;
/// DOCME
AssStyle *curStyle;
/// DOCME
wxString destination;
/// DOCME
DialogFontsCollector *collector;
/// DOCME
wxZipOutputStream *zip;
/// DOCME
int curLine;
/// DOCME
wxString destFolder;
/// DOCME
static FontsCollectorThread *instance;
/// DOCME
wxArrayString fonts;
bool ProcessFont(wxString fontname);
int CopyFont(wxString filename);
bool ArchiveFont(wxString filename);
bool AttachFont(wxString filename);
void Collect();
void AddFont(wxString fontname,int mode);
void CollectFontData();
void AppendText(wxString text,int colour=0);
public:
FontsCollectorThread(AssFile *subs,wxString destination,DialogFontsCollector *collector);
wxThread::ExitCode Entry();
static void GetFonts (wxString tagName,int par_n,AssOverrideParameter *param,void *usr);
};
/// DOCME /// DOCME
/// @class DialogFontsCollector /// @class DialogFontsCollector
@ -108,52 +39,25 @@ public:
/// ///
/// DOCME /// DOCME
class DialogFontsCollector : public wxDialog { class DialogFontsCollector : public wxDialog {
friend class FontsCollectorThread;
AssFile *subs; AssFile *subs;
/// DOCME ScintillaTextCtrl *collection_log;
wxTextCtrl *DestBox; wxButton *close_btn;
wxButton *dest_browse_button;
wxButton *start_btn;
wxRadioBox *collection_mode;
wxStaticText *dest_label;
wxTextCtrl *dest_ctrl;
/// DOCME void OnStart(wxCommandEvent &);
ScintillaTextCtrl *LogBox; void OnBrowse(wxCommandEvent &);
void OnRadio(wxCommandEvent &e);
/// DOCME /// Append text to log message from worker thread
wxButton *BrowseButton; void OnAddText(wxThreadEvent &event);
/// Collection complete notification from the worker thread to reenable buttons
/// DOCME void OnCollectionComplete(wxThreadEvent &);
wxButton *StartButton;
/// DOCME
wxButton *CloseButton;
/// DOCME
wxStaticText *DestLabel;
/// DOCME
wxRadioBox *CollectAction;
void OnStart(wxCommandEvent &event);
void OnClose(wxCommandEvent &event);
void OnBrowse(wxCommandEvent &event);
void OnRadio(wxCommandEvent &event);
void OnAddText(wxCommandEvent &event);
public: public:
DialogFontsCollector(wxWindow *parent, AssFile *subs); DialogFontsCollector(wxWindow *parent, AssFile *subs);
~DialogFontsCollector();
DECLARE_EVENT_TABLE()
};
/// DOCME
struct ColourString {
/// DOCME
wxString text;
/// DOCME
int colour;
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,201 +21,130 @@
/// @ingroup font_collector /// @ingroup font_collector
/// ///
////////////
// Includes
#include "config.h" #include "config.h"
#ifndef AGI_PRE #include "ass_dialogue.h"
#include <wx/tokenzr.h> #include "ass_override.h"
#endif #include "ass_style.h"
#ifdef WITH_FONTCONFIG
#include "font_file_lister_fontconfig.h" #include "font_file_lister_fontconfig.h"
#define FontListerClass FontConfigFontFileLister
#elif defined(WITH_FREETYPE2) #ifndef AGI_PRE
#include "font_file_lister_freetype.h" #include <algorithm>
#define FontListerClass FreetypeFontFileLister
#else #include <wx/intl.h>
#include "font_file_lister.h"
#endif #endif
#include "standard_paths.h" using namespace std::tr1::placeholders;
#include "text_file_reader.h"
#include "text_file_writer.h"
/// DOCME FontCollector::FontCollector(FontCollectorStatusCallback status_callback, FontFileLister &lister)
FontFileLister *FontFileLister::instance = NULL; : status_callback(status_callback)
, lister(lister)
/// @brief Constructor {
///
FontFileLister::FontFileLister() {
} }
void FontCollector::ProcessDialogueLine(AssDialogue *line) {
if (line->Comment) return;
line->ParseASSTags();
bool text = false;
StyleInfo style = styles[line->Style];
StyleInfo initial = style;
/// @brief Destructor for (size_t i = 0; i < line->Blocks.size(); ++i) {
/// if (AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride *>(line->Blocks[i])) {
FontFileLister::~FontFileLister() { if (text) {
used_styles.insert(style);
text = false;
}
for (size_t j = 0; j < ovr->Tags.size(); ++j) {
AssOverrideTag *tag = ovr->Tags[j];
wxString name = tag->Name;
if (name == "\\r")
style = styles[tag->Params[0]->Get(line->Style)];
if (name == "\\b")
style.bold = tag->Params[0]->Get(initial.bold);
else if (name == "\\i")
style.italic = tag->Params[0]->Get(initial.italic);
else if (name == "\\fn")
style.facename = tag->Params[0]->Get(initial.facename);
else
continue;
}
}
else if (AssDialogueBlockPlain *txt = dynamic_cast<AssDialogueBlockPlain *>(line->Blocks[i])) {
text = text || !txt->GetText().empty();
}
// Do nothing with drawing blocks
}
if (text)
used_styles.insert(style);
line->ClearBlocks();
} }
void FontCollector::ProcessChunk(StyleInfo const& style) {
std::vector<wxString> paths = lister.GetFontPaths(style.facename, style.bold, style.italic);
if (paths.empty()) {
/// @brief Get instance status_callback(wxString::Format("Could not find font '%s'\n", style.facename), 2);
/// ++missing;
void FontFileLister::GetInstance() { }
#ifdef FontListerClass
if (!instance) instance = new FontListerClass();
#endif
}
/// @brief Redirect statics to the instance
/// @param facename
/// @return
///
wxArrayString FontFileLister::GetFilesWithFace(wxString facename) {
GetInstance();
if (instance)
return instance->DoGetFilesWithFace(facename);
else { else {
wxArrayString ret; for (size_t i = 0; i < paths.size(); ++i) {
return ret; if (results.insert(paths[i]).second)
status_callback(wxString::Format("Found '%s' at '%s'\n", style.facename, paths[i]), 0);
}
} }
} }
/// @brief DOCME std::vector<wxString> FontCollector::GetFontPaths(std::list<AssEntry*> const& file) {
/// missing = 0;
void FontFileLister::Initialize() {
GetInstance(); status_callback(_("Parsing file\n"), 0);
if (instance) instance->DoInitialize(); 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;
}
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>();
}
status_callback(_("Searching for font files\n"), 0);
for_each(used_styles.begin(), used_styles.end(), bind(&FontCollector::ProcessChunk, this, _1));
status_callback(_("Done\n\n"), 0);
std::vector<wxString> paths;
paths.reserve(results.size());
paths.insert(paths.end(), results.begin(), results.end());
if (missing == 0)
status_callback(_("All fonts found.\n"), 1);
else
status_callback(wxString::Format(_("%d fonts could not be found.\n"), missing), 2);
return paths;
} }
/// @brief DOCME bool FontCollector::StyleInfo::operator<(StyleInfo const& rgt) const {
/// #define CMP(field) \
void FontFileLister::ClearData() { if (field < rgt.field) return true; \
GetInstance(); if (field > rgt.field) return false
if (instance) instance->DoClearData();
} CMP(facename);
CMP(bold);
CMP(italic);
/// @brief Get list of files that match a specific face #undef CMP
/// @param facename
/// @return return false;
///
wxArrayString FontFileLister::CacheGetFilesWithFace(wxString facename) {
FontMap::iterator iter = fontTable.find(facename);
if (iter != fontTable.end()) return iter->second;
else {
iter = fontTable.find("*"+facename);
if (iter != fontTable.end()) return iter->second;
return wxArrayString();
}
}
/// @brief Clear data
///
void FontFileLister::ClearCache() {
fontFiles.clear();
fontTable.clear();
}
/// @brief Add font
/// @param filename
/// @param facename
/// @return
///
void FontFileLister::AddFont(wxString filename,wxString facename) {
// See if it's a valid facename
facename.Trim(true).Trim(false);
if (facename.IsEmpty()) return;
if (facename.Lower().StartsWith("copyright ")) return;
// Add filename to general list
if (fontFiles.Index(filename) == wxNOT_FOUND) {
fontFiles.Add(filename);
}
// Add filename to mapping of this face
wxArrayString &arr = fontTable[facename];
if (arr.Index(filename) == wxNOT_FOUND) arr.Add(filename);
}
/// @brief Check if a filename is cached
/// @param filename
/// @return
///
bool FontFileLister::IsFilenameCached(wxString filename) {
return fontFiles.Index(filename) != wxNOT_FOUND;
}
/// @brief Save cache
///
void FontFileLister::SaveCache() {
try {
// Open file
TextFileWriter file(StandardPaths::DecodePath("?user/fontscache.dat"));
// For each face...
for (FontMap::iterator iter = fontTable.begin();iter!=fontTable.end();iter++) {
// Write face name
wxString line = iter->first + "?";
size_t len = iter->second.Count();
// Write file names
for (size_t i=0;i<len;i++) {
line += iter->second[i];
if (i != len-1) line += "|";
}
// Write line
file.WriteLineToFile(line);
}
}
catch (...) {
}
}
/// @brief Load cache
///
void FontFileLister::LoadCache() {
try {
// Load cache
TextFileReader file(StandardPaths::DecodePath("?user/fontscache.dat"));
// Read each line
while (file.HasMoreLines()) {
// Read line
wxString line = file.ReadLineFromFile();
int pos = line.Find('?');
// Get face name
wxString face = line.Left(pos);
if (face.IsEmpty()) continue;
// Get files
wxStringTokenizer tkn(line.Mid(pos+1),"|");
while (tkn.HasMoreTokens()) {
wxString file = tkn.GetNextToken();
if (!file.IsEmpty()) {
AddFont(file,face);
}
}
}
}
catch (...) {
}
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,71 +21,73 @@
/// @ingroup font_collector /// @ingroup font_collector
/// ///
#pragma once
////////////
// Includes
#ifndef AGI_PRE #ifndef AGI_PRE
#include <list>
#include <map> #include <map>
#include <tr1/functional>
#include <set>
#include <vector>
#include <wx/arrstr.h>
#include <wx/string.h> #include <wx/string.h>
#endif #endif
#ifdef WITH_FREETYPE2 class AssEntry;
/// DOCME class AssDialogue;
typedef struct FT_LibraryRec_ *FT_Library;
#endif
/// DOCME typedef std::tr1::function<void (wxString, int)> FontCollectorStatusCallback;
typedef std::map<wxString,wxArrayString> FontMap;
/// DOCME
/// @class FontFileLister /// @class FontFileLister
/// @brief DOCME /// @brief Font lister interface
///
/// DOCME
class FontFileLister { class FontFileLister {
private:
/// DOCME
static FontFileLister *instance;
static void GetInstance();
/// DOCME
FontMap fontTable;
/// DOCME
wxArrayString fontFiles;
protected:
/// @brief DOCME
/// @param facename
/// @return
///
virtual wxArrayString DoGetFilesWithFace(wxString facename) { return CacheGetFilesWithFace(facename); }
virtual void DoInitialize()=0;
/// @brief DOCME
///
virtual void DoClearData() { ClearCache(); }
FontFileLister();
virtual ~FontFileLister();
wxArrayString CacheGetFilesWithFace(wxString facename);
bool IsFilenameCached(wxString filename);
void AddFont(wxString filename,wxString facename);
void SaveCache();
void LoadCache();
void ClearCache();
public: public:
static wxArrayString GetFilesWithFace(wxString facename); /// @brief Get the path to the font with the given styles
static void Initialize(); /// @param facename Name of font face
static void ClearData(); /// @param bold ASS font weight
/// @param italic Italic?
/// @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) = 0;
}; };
/// @class FontCollector
/// @brief Class which collects the paths to all fonts used in a script
class FontCollector {
struct StyleInfo {
wxString facename;
int bold;
bool italic;
bool operator<(StyleInfo const& rgt) const;
};
/// Message callback provider by caller
FontCollectorStatusCallback status_callback;
/// The actual lister to use to get font paths
FontFileLister &lister;
/// A set of each combination of styles used in the file
std::set<StyleInfo> 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;
/// Gather all of the unique styles with text on a line
void ProcessDialogueLine(AssDialogue *line);
/// Get the font for a single style
void ProcessChunk(StyleInfo const& style);
public:
/// Constructor
/// @param status_callback Function to pass status updates to
/// @param lister The actual font file lister
FontCollector(FontCollectorStatusCallback status_callback, FontFileLister &lister);
/// @brief Get a list of the locations of all font files used in the file
/// @param file Lines in the subtitle file to check
/// @param status Callback function for messages
/// @return List of paths to fonts
std::vector<wxString> GetFontPaths(std::list<AssEntry*> const& file);
};

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, David Lamparter, Rodrigo Braz Monteiro // Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -38,73 +25,169 @@
#ifdef WITH_FONTCONFIG #ifdef WITH_FONTCONFIG
#include "font_file_lister_fontconfig.h" #include "font_file_lister_fontconfig.h"
#include "charset_conv.h"
#include <fontconfig/fontconfig.h>
#include "compat.h"
/// @brief Get files that contain the face #include <libaegisub/util.h>
/// @param facename
/// @return
///
wxArrayString FontConfigFontFileLister::DoGetFilesWithFace(wxString facename) {
wxArrayString results;
// Code stolen from asa #ifndef AGI_PRE
FcPattern *final, *tmp1, *tmp2; #include <wx/intl.h>
FcResult res; #endif
FcChar8 *filename,*gotfamily;
int fontindex;
char buffer[1024];
strcpy(buffer,facename.mb_str(wxConvUTF8));
// Get data from fconfig or something namespace {
tmp1 = FcPatternBuild(NULL,FC_FAMILY, FcTypeString,buffer,NULL); // In SSA/ASS fonts are sometimes referenced by their "full name",
if (!tmp1) return results; // which is usually a concatenation of family name and font
tmp2 = FcFontRenderPrepare(fontconf, tmp1, aux); // style (ex. Ottawa Bold). Full name is available from
FcPatternDestroy(tmp1); // FontConfig pattern element FC_FULLNAME, but it is never
FcDefaultSubstitute(tmp2); // used for font matching.
FcConfigSubstitute(fontconf, tmp2, FcMatchPattern); // Therefore, I'm removing words from the end of the name one
final = FcFontMatch(fontconf, tmp2, &res); // by one, and adding shortened names to the pattern. It seems
FcPatternDestroy(tmp2); // that the first value (full name in this case) has
if (!final) return results; // precedence in matching.
if (FcPatternGetString(final, FC_FILE, 0, &filename) == FcResultMatch && FcPatternGetInteger(final, FC_INDEX, 0, &fontindex) == FcResultMatch) { // An alternative approach could be to reimplement FcFontSort
FcPatternGetString(final, FC_FAMILY, fontindex, &gotfamily); // using FC_FULLNAME instead of FC_FAMILY.
if (strcmp((const char*)gotfamily,buffer) == 0) { int add_families(FcPattern *pat, std::string family) {
results.Add(wxString((char*) filename)); int family_cnt = 1;
FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *)family.c_str());
for (std::string::reverse_iterator p = family.rbegin(); p != family.rend(); ++p) {
if (*p == ' ' || *p == '-') {
*p = '\0';
FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *)family.c_str());
++family_cnt;
} }
} }
FcPatternDestroy(final); return family_cnt;
}
return results; int strcasecmp(std::string a, std::string b) {
agi::util::str_lower(a);
agi::util::str_lower(b);
return a.compare(b);
}
} }
FontConfigFontFileLister::FontConfigFontFileLister(FontCollectorStatusCallback cb)
: config(FcInitLoadConfig(), FcConfigDestroy)
/// @brief Constructor
///
FontConfigFontFileLister::FontConfigFontFileLister()
: fontconf(0), aux(0)
{ {
cb(_("Updating font cache\n"), 0);
FcConfigBuildFonts(config);
} }
FcFontSet *FontConfigFontFileLister::MatchFullname(const char *family, int weight, int slant) {
FcFontSet *sets[2];
FcFontSet *result = FcFontSetCreate();
int nsets = 0;
if ((sets[nsets] = FcConfigGetFonts(config, FcSetSystem)))
nsets++;
if ((sets[nsets] = FcConfigGetFonts(config, FcSetApplication)))
nsets++;
/// @brief Initialize // Run over font sets and patterns and try to match against full name
/// for (int i = 0; i < nsets; i++) {
void FontConfigFontFileLister::DoInitialize() { FcFontSet *set = sets[i];
fontconf = FcInitLoadConfigAndFonts(); for (int fi = 0; fi < set->nfont; fi++) {
aux = FcPatternCreate(); FcPattern *pat = set->fonts[fi];
char *fullname;
int pi = 0, at;
FcBool ol;
while (FcPatternGetString(pat, FC_FULLNAME, pi++, (FcChar8 **)&fullname) == FcResultMatch) {
if (FcPatternGetBool(pat, FC_OUTLINE, 0, &ol) != FcResultMatch || ol != FcTrue)
continue;
if (FcPatternGetInteger(pat, FC_SLANT, 0, &at) != FcResultMatch || at < slant)
continue;
if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &at) != FcResultMatch || at < weight)
continue;
if (strcasecmp(fullname, family) == 0) {
FcFontSetAdd(result, FcPatternDuplicate(pat));
break;
}
}
}
}
return result;
} }
std::vector<wxString> FontConfigFontFileLister::GetFontPaths(wxString const& facename, int bold, bool italic) {
std::vector<wxString> ret;
std::string family = STD_STR(facename);
if (family[0] == '@')
family.erase(0, 1);
/// @brief Clean up int weight = bold == 0 ? 80 :
/// bold == 1 ? 200 :
void FontConfigFontFileLister::DoClearData() { bold;
if (aux) FcPatternDestroy(aux); int slant = italic ? 110 : 0;
#ifdef HAVE_FCFINI
FcFini(); scoped<FcPattern*> pat(FcPatternCreate(), FcPatternDestroy);
#endif if (!pat) return ret;
}
int family_cnt = add_families(pat, family);
FcPatternAddBool(pat, FC_OUTLINE, true);
FcPatternAddInteger(pat, FC_SLANT, slant);
FcPatternAddInteger(pat, FC_WEIGHT, weight);
FcDefaultSubstitute(pat);
if (!FcConfigSubstitute(config, pat, FcMatchPattern)) return ret;
FcResult result;
scoped<FcFontSet*> fsorted(FcFontSort(config, pat, true, NULL, &result), FcFontSetDestroy);
scoped<FcFontSet*> ffullname(MatchFullname(family.c_str(), weight, slant), FcFontSetDestroy);
if (!fsorted || !ffullname) return ret;
scoped<FcFontSet*> fset(FcFontSetCreate(), FcFontSetDestroy);
for (int cur_font = 0; cur_font < ffullname->nfont; ++cur_font) {
FcPattern *curp = ffullname->fonts[cur_font];
FcPatternReference(curp);
FcFontSetAdd(fset, curp);
}
for (int cur_font = 0; cur_font < fsorted->nfont; ++cur_font) {
FcPattern *curp = fsorted->fonts[cur_font];
FcPatternReference(curp);
FcFontSetAdd(fset, curp);
}
int cur_font;
for (cur_font = 0; cur_font < fset->nfont; ++cur_font) {
FcBool outline;
FcResult result = FcPatternGetBool(fset->fonts[cur_font], FC_OUTLINE, 0, &outline);
if (result == FcResultMatch && outline) break;
}
if (cur_font >= fset->nfont) return ret;
// Remove all extra family names from original pattern.
// After this, FcFontRenderPrepare will select the most relevant family
// name in case there are more than one of them.
for (; family_cnt > 1; --family_cnt)
FcPatternRemove(pat, FC_FAMILY, family_cnt - 1);
scoped<FcPattern*> rpat(FcFontRenderPrepare(config, pat, fset->fonts[cur_font]), FcPatternDestroy);
if (!rpat) return ret;
FcChar8 *r_family;
if (FcPatternGetString(rpat, FC_FAMILY, 0, &r_family) != FcResultMatch)
return ret;
FcChar8 *r_fullname;
if (FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname) != FcResultMatch)
return ret;
if (strcasecmp(family, (char*)r_family) && strcasecmp(family, (char*)r_fullname))
return ret;
FcChar8 *file;
if(FcPatternGetString(rpat, FC_FILE, 0, &file) != FcResultMatch)
return ret;
ret.push_back((const char *)file);
return ret;
}
#endif #endif

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,37 +21,40 @@
/// @ingroup font_collector /// @ingroup font_collector
/// ///
#ifdef WITH_FONTCONFIG
////////////
// Includes
#include <fontconfig/fontconfig.h>
#include <fontconfig/fcfreetype.h>
#include "font_file_lister.h" #include "font_file_lister.h"
typedef struct _FcConfig FcConfig;
typedef struct _FcFontSet FcFontSet;
/// DOCME
/// @class FontConfigFontFileLister /// @class FontConfigFontFileLister
/// @brief DOCME /// @brief fontconfig powered font lister
///
/// DOCME
class FontConfigFontFileLister : public FontFileLister { class FontConfigFontFileLister : public FontFileLister {
friend class FontFileLister; template<typename T> class scoped {
private: T data;
void (*destructor)(T);
public:
scoped(T data, void (*destructor)(T)) : data(data), destructor(destructor) { }
~scoped() { if (data) destructor(data); }
operator T() { return data; }
T operator->() { return data; }
};
/// DOCME scoped<FcConfig*> config;
FcConfig *fontconf;
/// DOCME /// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans")
FcPattern *aux; /// @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);
FontConfigFontFileLister(); std::vector<wxString> GetFontPaths(wxString const& facename, int bold, bool italic);
wxArrayString DoGetFilesWithFace(wxString facename);
void DoInitialize();
void DoClearData();
}; };
#endif

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Niels Martin Hansen, Rodrigo Braz Monteiro // Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,154 +21,162 @@
/// @ingroup font_collector /// @ingroup font_collector
/// ///
////////////
// Includes
#include "config.h" #include "config.h"
#ifdef WITH_FREETYPE2 #ifdef WITH_FREETYPE2
#include "font_file_lister_freetype.h"
#ifndef AGI_PRE
#include <wx/dir.h>
#endif
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
#include FT_GLYPH_H #include FT_GLYPH_H
#include FT_SFNT_NAMES_H #include FT_SFNT_NAMES_H
#include FT_TRUETYPE_IDS_H
#ifdef WIN32
#include <shlobj.h> #include <shlobj.h>
#endif
#include "charset_conv.h" #include "charset_conv.h"
#include "font_file_lister_freetype.h" #include "standard_paths.h"
#include "text_file_reader.h"
#include "text_file_writer.h"
namespace {
typedef std::map<wxString, std::set<wxString> > FontMap;
/// @brief Constructor wxString get_font_folder() {
/// wchar_t szPath[MAX_PATH];
FreetypeFontFileLister::FreetypeFontFileLister() { if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, 0, szPath)))
// Initialize freetype2 return wxString(szPath) + "\\";
FT_Init_FreeType(&ft2lib); else
} return wxGetOSDirectory() + "\\fonts\\";
/// @brief Destructor
///
FreetypeFontFileLister::~FreetypeFontFileLister() {
}
/// @brief Get name from face
/// @param face
/// @param id
/// @return
///
wxArrayString GetName(FT_Face &face,int id) {
// Get name
wxArrayString final;
int count = FT_Get_Sfnt_Name_Count(face);
for (int i=0;i<count;i++) {
FT_SfntName name;
FT_Get_Sfnt_Name(face,i,&name);
if (name.name_id == id) {
char *str = new char[name.string_len+2];
memcpy(str,name.string,name.string_len);
str[name.string_len] = 0;
str[name.string_len+1] = 0;
if (name.encoding_id == 0) final.Add(wxString(str));
else if (name.encoding_id == 1) {
wxMBConvUTF16BE conv;
wxString string(str,conv);
final.Add(string);
} }
delete [] str;
FT_UInt get_name_count(wxString const& filename, FT_Face face) {
wxString ext = filename.Right(4).Lower();
if (ext == ".otf" || ext == ".ttf" || ext == ".ttc_")
return FT_Get_Sfnt_Name_Count(face);
return 0;
}
std::map<FT_UShort, std::vector<wxString> > get_names(FT_Face face) {
std::map<FT_UShort, std::vector<wxString> > final;
FT_UInt count = FT_Get_Sfnt_Name_Count(face);
for (FT_UInt i = 0; i < count; ++i) {
FT_SfntName name;
FT_Get_Sfnt_Name(face, i, &name);
// truetype string encoding is an absolute nightmare, so just try
// UTF-16BE and local charsets
if (name.string_len) {
if ((name.platform_id == TT_PLATFORM_MICROSOFT && name.encoding_id == TT_MS_ID_UNICODE_CS) ||
name.platform_id == TT_PLATFORM_APPLE_UNICODE ||
name.string[0])
{
final[name.name_id].push_back(wxString(name.string, wxMBConvUTF16BE(), name.string_len));
}
else
final[name.name_id].push_back(wxString(name.string, name.string_len));
} }
} }
return final; return final;
}
void load_cache(FontMap &font_files, std::set<wxString> &indexed_files) {
try {
TextFileReader file(StandardPaths::DecodePath("?local/freetype_collector_index.dat"), "utf-16le");
while (file.HasMoreLines()) {
wxString face = file.ReadLineFromFile();
std::set<wxString>& files = font_files[face];
while (file.HasMoreLines()) {
wxString filename = file.ReadLineFromFile();
if (filename.empty()) break;
if (wxFileExists(filename)) {
files.insert(filename);
indexed_files.insert(filename);
}
}
}
}
catch (agi::FileNotAccessibleError const&) { }
}
void save_cache(FontMap &font_files) {
TextFileWriter file(StandardPaths::DecodePath("?local/freetype_collector_index.dat"), "utf-16le");
for (FontMap::iterator face_it = font_files.begin(); face_it != font_files.end(); ++face_it) {
file.WriteLineToFile(face_it->first);
for_each(face_it->second.begin(), face_it->second.end(),
bind(&TextFileWriter::WriteLineToFile, &file, std::tr1::placeholders::_1, true));
file.WriteLineToFile("");
}
}
} }
FreetypeFontFileLister::FreetypeFontFileLister(FontCollectorStatusCallback AppendText) {
AppendText(_("Collecting font data from system. This might take a while, depending on the number of fonts installed. Results are cached and subsequent executions will be faster...\n"), 0);
load_cache(font_files, indexed_files);
FT_Library ft2lib;
FT_Init_FreeType(&ft2lib);
/// @brief Gather data from system
///
void FreetypeFontFileLister::DoInitialize() {
// Load cache
LoadCache();
// Get fonts folder
wxString source;
#ifdef WIN32
TCHAR szPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS,NULL,0,szPath))) {
source = wxString(szPath);
}
else source = wxGetOSDirectory() + _T("\\fonts");
source += _T("\\");
#else
# ifdef __APPLE__
// XXXHACK: Is this always a correct assumption?
// Fonts might be instaled in more places, I think...
source = _T("/Library/Fonts/");
# endif
#endif
// Get the list of fonts in the fonts folder
wxArrayString fontfiles; wxArrayString fontfiles;
wxDir::GetAllFiles(source, &fontfiles, wxEmptyString, wxDIR_FILES); wxDir::GetAllFiles(get_font_folder(), &fontfiles, "", wxDIR_FILES);
// Loop through each file for (size_t i = 0; i < fontfiles.size(); ++i) {
int fterr; if (indexed_files.count(fontfiles[i])) continue;
for (unsigned int i=0;i<fontfiles.Count(); i++) {
// Check if it's cached
if (IsFilenameCached(fontfiles[i])) continue;
// Loop through each face in the file
for (int facenum=0;true;facenum++) {
// Get font face
FT_Face face; FT_Face face;
fterr = FT_New_Face(ft2lib, fontfiles[i].mb_str(*wxConvFileName), facenum, &face); for (FT_Long i = 0; FT_New_Face(ft2lib, fontfiles[i].mb_str(*wxConvFileName), i, &face) == 0; ++i) {
if (fterr) break; if (get_name_count(fontfiles[i], face) > 0) {
std::map<FT_UShort, std::vector<wxString> > names = get_names(face);
std::vector<wxString>& family = names[1];
std::vector<wxString>& style = names[2];
std::vector<wxString>& full_name = names[4];
// Special names for TTF and OTF for (size_t j = 0; j < family.size() && j < style.size(); ++j) {
int nameCount = 0; if (style[j] != "Regular")
wxString ext = fontfiles[i].Right(4).Lower(); AddFont(fontfiles[i], family[j], style[j]);
if (ext == _T(".otf") || ext == _T(".ttf") || ext == _T(".ttc_")) nameCount = FT_Get_Sfnt_Name_Count(face); else
if (nameCount > 0) { AddFont(fontfiles[i], family[j]);
wxArrayString family = GetName(face,1);
wxArrayString subFamily = GetName(face,2);
wxArrayString fullName = GetName(face,4);
for (size_t j=0;j<family.Count() && j<subFamily.Count();j++) {
if (subFamily[j] != _T("Regular")) {
AddFont(fontfiles[i],family[j] + _T(" ") + subFamily[j]);
AddFont(fontfiles[i],_T("*")+family[j]);
} }
else AddFont(fontfiles[i],family[j]); for (size_t j = 0; j < full_name.size(); ++j)
AddFont(fontfiles[i], full_name[j]);
} }
for (size_t j=0;j<fullName.Count();j++) AddFont(fontfiles[i],fullName[j]);
}
// Ordinary fonts
else { else {
if (face->style_name) { if (face->style_name)
AddFont(fontfiles[i],wxString(face->family_name, csConvLocal) + _T(" ") + wxString(face->style_name, csConvLocal)); AddFont(fontfiles[i], face->family_name, face->style_name);
AddFont(fontfiles[i],_T("*")+wxString(face->family_name, csConvLocal)); else
} AddFont(fontfiles[i], wxString(face->family_name));
else AddFont(fontfiles[i],wxString(face->family_name, csConvLocal));
} }
FT_Done_Face(face); FT_Done_Face(face);
} }
} }
// Save cache FT_Done_FreeType(ft2lib);
SaveCache();
AppendText(_("Done collecting font data.\n"), 0);
save_cache(font_files);
}
void FreetypeFontFileLister::AddFont(wxString const& filename, wxString facename) {
facename.Trim(true).Trim(false);
if (facename.size() && !facename.Lower().StartsWith("copyright "))
font_files[facename].insert(filename);
}
void FreetypeFontFileLister::AddFont(wxString const& filename, wxString const& family, wxString const& style) {
AddFont(filename, family + " " + style);
AddFont(filename, "*" + family);
}
std::vector<wxString> FreetypeFontFileLister::GetFontPaths(wxString const& facename, int, bool) {
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());
return ret;
} }
#endif WITH_FREETYPE2 #endif WITH_FREETYPE2

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// //
// * Redistributions of source code must retain the above copyright notice, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,35 +21,43 @@
/// @ingroup font_collector /// @ingroup font_collector
/// ///
#ifdef WITH_FREETYPE2
////////////
// Includes
#include "font_file_lister.h" #include "font_file_lister.h"
#ifndef AGI_PRE
#include <map>
#include <set>
/// DOCME #include <wx/string.h>
typedef struct FT_LibraryRec_ *FT_Library; #endif
/// DOCME
/// @class FreetypeFontFileLister /// @class FreetypeFontFileLister
/// @brief DOCME /// @brief Freetype2-based font collector
///
/// DOCME
class FreetypeFontFileLister : public FontFileLister { class FreetypeFontFileLister : public FontFileLister {
friend class FontFileLister; /// Map of face name -> possible containing files
private: std::map<wxString, std::set<wxString> > font_files;
/// DOCME /// Set of files which were loaded from the cache
FT_Library ft2lib; std::set<wxString> indexed_files;
void DoInitialize(); /// Add a font to the list of mappings
/// @param filename Full path to font file
/// @param facename Font name
void AddFont(wxString const& filename, wxString facename);
FreetypeFontFileLister(); /// Add a font to the list of mappings
~FreetypeFontFileLister(); /// @param filename Full path to font file
/// @param family Family name
/// @param style Style name
void AddFont(wxString const& filename, wxString const& family, wxString const& style);
public:
/// Constructor
/// @param cb Callback for status logging
FreetypeFontFileLister(FontCollectorStatusCallback cb);
std::vector<wxString> GetFontPaths(wxString const& facename, int, bool);
}; };
#endif