2015-10-28 20:44:22 +01:00
|
|
|
// Copyright (c) 2016, Thomas Goyne <plorkyeran@aegisub.org>
|
|
|
|
//
|
|
|
|
// 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/
|
|
|
|
|
|
|
|
#include "font_file_lister.h"
|
|
|
|
|
|
|
|
#include <AppKit/AppKit.h>
|
|
|
|
#include <CoreText/CoreText.h>
|
|
|
|
|
|
|
|
namespace {
|
2015-12-02 20:44:55 +01:00
|
|
|
struct FontMatch {
|
|
|
|
NSURL *url;
|
|
|
|
NSCharacterSet *codepoints;
|
2015-12-11 20:45:14 +01:00
|
|
|
int weight;
|
|
|
|
int width;
|
2015-12-02 20:44:55 +01:00
|
|
|
bool italic;
|
|
|
|
};
|
|
|
|
|
|
|
|
void process_descriptor(NSFontDescriptor *font, std::vector<FontMatch>& out) {
|
2015-10-28 20:44:22 +01:00
|
|
|
// For whatever reason there is no NSFontURLAttribute
|
|
|
|
NSURL *url = [font objectForKey:(__bridge NSString *)kCTFontURLAttribute];
|
|
|
|
if (!url)
|
|
|
|
return;
|
|
|
|
|
2015-12-11 20:45:14 +01:00
|
|
|
NSFont *nsfont = [NSFont fontWithDescriptor:font size:10];
|
2015-12-12 20:45:17 +01:00
|
|
|
int weight = 400;
|
|
|
|
int width = 5;
|
|
|
|
|
2015-12-11 20:45:14 +01:00
|
|
|
auto traits = CTFontGetSymbolicTraits((__bridge CTFontRef)nsfont);
|
|
|
|
bool italic = !!(traits & kCTFontItalicTrait);
|
2015-10-28 20:44:22 +01:00
|
|
|
|
2015-12-02 20:44:55 +01:00
|
|
|
// CoreText sometimes reports a slant of zero (or a very small slant)
|
|
|
|
// despite the macStyle field indicating that the font is italic, so
|
|
|
|
// double check by reading that field from the ttf
|
2015-12-11 20:45:14 +01:00
|
|
|
if (!italic) {
|
2015-12-02 20:44:55 +01:00
|
|
|
auto data = (__bridge_transfer NSData *)CTFontCopyTable((__bridge CTFontRef)nsfont, kCTFontTableHead, 0);
|
|
|
|
if (data.length > 45) {
|
|
|
|
italic = static_cast<const char *>(data.bytes)[45] & 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-11 20:45:14 +01:00
|
|
|
auto data = (__bridge_transfer NSData *)CTFontCopyTable((__bridge CTFontRef)nsfont, kCTFontTableOS2, 0);
|
2015-12-12 20:45:17 +01:00
|
|
|
if (data.length > 7) {
|
|
|
|
auto bytes = static_cast<const uint8_t *>(data.bytes);
|
|
|
|
weight = (bytes[4] << 8) + bytes[5];
|
|
|
|
width = (bytes[6] << 8) + bytes[7];
|
|
|
|
}
|
2015-12-11 20:45:14 +01:00
|
|
|
|
2015-12-02 20:44:55 +01:00
|
|
|
out.push_back({url, [font objectForKey:NSFontCharacterSetAttribute], weight, width, italic});
|
|
|
|
}
|
|
|
|
|
2015-12-11 20:45:14 +01:00
|
|
|
void select_best(CollectionResult& ret, std::vector<FontMatch>& options, int bold,
|
2015-12-02 20:44:55 +01:00
|
|
|
bool italic, std::vector<int> const& characters) {
|
|
|
|
if (options.empty())
|
2015-10-28 20:44:22 +01:00
|
|
|
return;
|
|
|
|
|
2015-12-02 20:44:55 +01:00
|
|
|
// Prioritize fonts with the width closest to normal
|
|
|
|
sort(begin(options), end(options), [](FontMatch const& lft, FontMatch const& rgt) {
|
2015-12-11 20:45:14 +01:00
|
|
|
return std::abs(lft.width - 100) < std::abs(rgt.width - 100);
|
2015-12-02 20:44:55 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
const FontMatch *match = nullptr;
|
|
|
|
|
|
|
|
for (auto const& m : options) {
|
2015-12-11 20:45:14 +01:00
|
|
|
if (italic == m.italic && (!match || std::abs(match->weight - bold + 1) > std::abs(m.weight - bold + 1)))
|
2015-12-02 20:44:55 +01:00
|
|
|
match = &m;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match) {
|
|
|
|
ret.fake_italic = italic;
|
|
|
|
for (auto const& m : options) {
|
2015-12-11 20:45:14 +01:00
|
|
|
if (!match || std::abs(match->weight - bold + 1) > std::abs(m.weight - bold + 1))
|
2015-12-02 20:44:55 +01:00
|
|
|
match = &m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-11 20:45:14 +01:00
|
|
|
ret.fake_bold = bold > 400 && match->weight < bold;
|
2015-12-02 20:44:55 +01:00
|
|
|
|
|
|
|
auto& best = *match;
|
|
|
|
ret.paths.push_back(best.url.fileSystemRepresentation);
|
2015-10-28 20:44:22 +01:00
|
|
|
for (int chr : characters) {
|
2015-12-02 20:44:55 +01:00
|
|
|
if (![best.codepoints longCharacterIsMember:chr]) {
|
2015-10-28 20:44:22 +01:00
|
|
|
ret.missing += chr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CollectionResult CoreTextFontFileLister::GetFontPaths(std::string const& facename,
|
|
|
|
int bold, bool italic,
|
|
|
|
std::vector<int> const& characters) {
|
|
|
|
CollectionResult ret;
|
|
|
|
|
2015-12-02 20:44:55 +01:00
|
|
|
if (bold == 0)
|
2015-12-11 20:45:14 +01:00
|
|
|
bold = 400;
|
2015-12-02 20:44:55 +01:00
|
|
|
else if (bold == 1)
|
2015-12-11 20:45:14 +01:00
|
|
|
bold = 700;
|
2015-12-02 20:44:55 +01:00
|
|
|
|
|
|
|
std::vector<FontMatch> fonts;
|
2015-10-28 20:44:22 +01:00
|
|
|
@autoreleasepool {
|
|
|
|
NSString *name = @(facename.c_str() + (facename[0] == '@'));
|
2015-12-02 20:44:55 +01:00
|
|
|
|
2015-10-28 20:44:22 +01:00
|
|
|
NSArray *attrs = @[
|
|
|
|
[NSFontDescriptor fontDescriptorWithFontAttributes:@{NSFontFamilyAttribute: name}],
|
|
|
|
[NSFontDescriptor fontDescriptorWithFontAttributes:@{NSFontFaceAttribute: name}],
|
|
|
|
[NSFontDescriptor fontDescriptorWithFontAttributes:@{NSFontNameAttribute: name}]
|
|
|
|
];
|
|
|
|
|
|
|
|
auto font_collection = [NSFontCollection fontCollectionWithDescriptors:attrs];
|
|
|
|
for (NSFontDescriptor *desc in font_collection.matchingDescriptors) {
|
2015-12-02 20:44:55 +01:00
|
|
|
process_descriptor(desc, fonts);
|
2015-10-28 20:44:22 +01:00
|
|
|
}
|
2015-12-11 20:45:14 +01:00
|
|
|
select_best(ret, fonts, bold, italic, characters);
|
2015-10-28 20:44:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|