2012-01-12 23:32:09 +01:00
|
|
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
2007-07-29 03:21:28 +02:00
|
|
|
//
|
2012-01-12 23:32:09 +01:00
|
|
|
// 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.
|
2007-07-29 03:21:28 +02:00
|
|
|
//
|
2012-01-12 23:32:09 +01:00
|
|
|
// 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.
|
2007-07-29 03:21:28 +02:00
|
|
|
//
|
2009-07-29 07:43:02 +02:00
|
|
|
// Aegisub Project http://www.aegisub.org/
|
|
|
|
|
|
|
|
/// @file font_file_lister_fontconfig.cpp
|
|
|
|
/// @brief Font Config-based font collector
|
|
|
|
/// @ingroup font_collector
|
|
|
|
///
|
2007-07-29 03:21:28 +02:00
|
|
|
|
2009-01-04 07:31:48 +01:00
|
|
|
#include "config.h"
|
|
|
|
|
2010-10-20 03:56:22 +02:00
|
|
|
#ifdef WITH_FONTCONFIG
|
2007-07-29 03:21:28 +02:00
|
|
|
#include "font_file_lister_fontconfig.h"
|
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
#include <fontconfig/fontconfig.h>
|
2007-07-29 03:21:28 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
#include "compat.h"
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2012-10-09 16:44:27 +02:00
|
|
|
#include <libaegisub/log.h>
|
2012-01-12 23:32:09 +01:00
|
|
|
#include <libaegisub/util.h>
|
|
|
|
|
2012-10-09 16:44:27 +02:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <libaegisub/util_osx.h>
|
|
|
|
#endif
|
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
#ifndef AGI_PRE
|
|
|
|
#include <wx/intl.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
// In SSA/ASS fonts are sometimes referenced by their "full name",
|
|
|
|
// which is usually a concatenation of family name and font
|
|
|
|
// style (ex. Ottawa Bold). Full name is available from
|
|
|
|
// FontConfig pattern element FC_FULLNAME, but it is never
|
|
|
|
// used for font matching.
|
|
|
|
// Therefore, I'm removing words from the end of the name one
|
|
|
|
// by one, and adding shortened names to the pattern. It seems
|
|
|
|
// that the first value (full name in this case) has
|
|
|
|
// precedence in matching.
|
|
|
|
// An alternative approach could be to reimplement FcFontSort
|
|
|
|
// using FC_FULLNAME instead of FC_FAMILY.
|
|
|
|
int add_families(FcPattern *pat, std::string family) {
|
|
|
|
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;
|
|
|
|
}
|
2007-07-30 02:56:54 +02:00
|
|
|
}
|
2012-01-12 23:32:09 +01:00
|
|
|
return family_cnt;
|
2007-07-29 03:47:18 +02:00
|
|
|
}
|
2007-07-29 03:21:28 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
int strcasecmp(std::string a, std::string b) {
|
|
|
|
agi::util::str_lower(a);
|
|
|
|
agi::util::str_lower(b);
|
|
|
|
return a.compare(b);
|
|
|
|
}
|
2012-10-09 16:44:27 +02:00
|
|
|
|
|
|
|
FcConfig *init_fontconfig() {
|
|
|
|
#ifdef __APPLE__
|
|
|
|
FcConfig *config = FcConfigCreate();
|
|
|
|
std::string conf_path = agi::util::OSX_GetBundleResourcesDirectory() + "/etc/fonts/fonts.conf";
|
|
|
|
if (FcConfigParseAndLoad(config, (unsigned char *)conf_path.c_str(), FcTrue))
|
|
|
|
return config;
|
|
|
|
|
|
|
|
LOG_E("font_collector/fontconfig") << "Loading fontconfig configuration file failed";
|
|
|
|
FcConfigDestroy(config);
|
|
|
|
#endif
|
|
|
|
return FcInitLoadConfig();
|
|
|
|
}
|
2007-07-29 03:21:28 +02:00
|
|
|
}
|
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
FontConfigFontFileLister::FontConfigFontFileLister(FontCollectorStatusCallback cb)
|
2012-10-09 16:44:27 +02:00
|
|
|
: config(init_fontconfig(), FcConfigDestroy)
|
2012-01-12 23:32:09 +01:00
|
|
|
{
|
|
|
|
cb(_("Updating font cache\n"), 0);
|
|
|
|
FcConfigBuildFonts(config);
|
|
|
|
}
|
2007-07-29 03:21:28 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
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++;
|
|
|
|
|
|
|
|
// Run over font sets and patterns and try to match against full name
|
|
|
|
for (int i = 0; i < nsets; i++) {
|
|
|
|
FcFontSet *set = sets[i];
|
|
|
|
for (int fi = 0; fi < set->nfont; fi++) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
return result;
|
2007-09-22 17:27:46 +02:00
|
|
|
}
|
|
|
|
|
2012-02-02 20:18:40 +01:00
|
|
|
FontFileLister::CollectionResult FontConfigFontFileLister::GetFontPaths(wxString const& facename, int bold, bool italic, std::set<wxUniChar> const& characters) {
|
|
|
|
CollectionResult ret;
|
2007-09-22 17:27:46 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
std::string family = STD_STR(facename);
|
|
|
|
if (family[0] == '@')
|
|
|
|
family.erase(0, 1);
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
int weight = bold == 0 ? 80 :
|
|
|
|
bold == 1 ? 200 :
|
|
|
|
bold;
|
|
|
|
int slant = italic ? 110 : 0;
|
2007-07-29 03:21:28 +02:00
|
|
|
|
2012-02-20 19:22:12 +01:00
|
|
|
agi::scoped_holder<FcPattern*> pat(FcPatternCreate(), FcPatternDestroy);
|
2012-01-12 23:32:09 +01:00
|
|
|
if (!pat) return ret;
|
2007-07-29 03:21:28 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
int family_cnt = add_families(pat, family);
|
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
FcPatternAddBool(pat, FC_OUTLINE, true);
|
|
|
|
FcPatternAddInteger(pat, FC_SLANT, slant);
|
|
|
|
FcPatternAddInteger(pat, FC_WEIGHT, weight);
|
2007-07-29 03:21:28 +02:00
|
|
|
|
2012-01-12 23:32:09 +01:00
|
|
|
FcDefaultSubstitute(pat);
|
|
|
|
|
|
|
|
if (!FcConfigSubstitute(config, pat, FcMatchPattern)) return ret;
|
|
|
|
|
|
|
|
FcResult result;
|
2012-11-13 17:51:01 +01:00
|
|
|
agi::scoped_holder<FcFontSet*> fsorted(FcFontSort(config, pat, true, nullptr, &result), FcFontSetDestroy);
|
2012-02-20 19:22:12 +01:00
|
|
|
agi::scoped_holder<FcFontSet*> ffullname(MatchFullname(family.c_str(), weight, slant), FcFontSetDestroy);
|
2012-01-12 23:32:09 +01:00
|
|
|
if (!fsorted || !ffullname) return ret;
|
|
|
|
|
2012-02-20 19:22:12 +01:00
|
|
|
agi::scoped_holder<FcFontSet*> fset(FcFontSetCreate(), FcFontSetDestroy);
|
2012-01-12 23:32:09 +01:00
|
|
|
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;
|
2012-03-13 00:34:16 +01:00
|
|
|
result = FcPatternGetBool(fset->fonts[cur_font], FC_OUTLINE, 0, &outline);
|
2012-01-12 23:32:09 +01:00
|
|
|
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);
|
|
|
|
|
2012-02-20 19:22:12 +01:00
|
|
|
agi::scoped_holder<FcPattern*> rpat(FcFontRenderPrepare(config, pat, fset->fonts[cur_font]), FcPatternDestroy);
|
2012-01-12 23:32:09 +01:00
|
|
|
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;
|
|
|
|
|
2012-02-01 05:17:33 +01:00
|
|
|
FcCharSet *charset;
|
|
|
|
if (FcPatternGetCharSet(rpat, FC_CHARSET, 0, &charset) == FcResultMatch) {
|
2012-11-04 04:53:03 +01:00
|
|
|
for (wxUniChar chr : characters) {
|
|
|
|
if (!FcCharSetHasChar(charset, chr))
|
|
|
|
ret.missing += chr;
|
2012-02-01 05:17:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-02 20:18:40 +01:00
|
|
|
ret.paths.push_back((const char *)file);
|
2012-01-12 23:32:09 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2007-07-29 03:21:28 +02:00
|
|
|
#endif
|