10#include "../../stdafx.h"
11#include "../../debug.h"
13#include "../../core/math_func.hpp"
14#include "../../blitter/factory.hpp"
15#include "../../error_func.h"
16#include "../../fileio_func.h"
17#include "../../fontdetection.h"
18#include "../../string_func.h"
19#include "../../strings_func.h"
20#include "../../zoom_func.h"
23#include "../../table/control_codes.h"
32 if (language_isocode ==
"zh_TW") {
35 }
else if (language_isocode ==
"zh_CN") {
40 lang = language_isocode.substr(0, language_isocode.find(
'_'));
45 CFStringRef lang_codes[2];
46 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang.c_str(), kCFStringEncodingUTF8);
47 lang_codes[1] = CFSTR(
"en");
48 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (
const void **)lang_codes,
lengthof(lang_codes), &kCFTypeArrayCallBacks);
49 CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontLanguagesAttribute)), (
const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
52 CFRelease(lang_codes[0]);
55 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
59 for (
int tries = 0; tries < 2; tries++) {
60 for (CFIndex i = 0; descs.get() !=
nullptr && i < CFArrayGetCount(descs.get()); i++) {
61 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
65 CTFontSymbolicTraits symbolic_traits;
66 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
69 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait))
continue;
71 if (symbolic_traits & kCTFontBoldTrait)
continue;
73 if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->
Monospace())
continue;
78 CFStringGetCString(font_name.get(), name,
lengthof(name), kCFStringEncodingUTF8);
83 if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass)
continue;
87 if (name[0] ==
'.' || strncmp(name,
"LastResort", 10) == 0)
continue;
92 Debug(fontcache, 2,
"CT-Font for {}: {}", language_isocode, name);
113 this->SetFontSize(pixels);
127void CoreTextFontCache::SetFontSize(
int pixels)
131 int scaled_height =
ScaleGUITrad(FontCache::GetDefaultFontHeight(this->
fs));
132 pixels = scaled_height;
136 float min_size = 0.0f;
145 uint16_t lowestRecPPEM;
146 CFDataGetBytes(data.get(), CFRangeMake(46,
sizeof(lowestRecPPEM)), (UInt8 *)&lowestRecPPEM);
147 min_size = CFSwapInt16BigToHost(lowestRecPPEM);
150 CFNumberGetValue(size.get(), kCFNumberFloatType, &min_size);
164 this->
font.reset(CTFontCreateWithFontDescriptor(this->
font_desc.get(), pixels,
nullptr));
169 this->
ascender = (int)std::ceil(CTFontGetAscent(this->
font.get()));
170 this->
descender = -(int)std::ceil(CTFontGetDescent(this->
font.get()));
176 CFStringGetCString(
font_name.get(), name,
lengthof(name), kCFStringEncodingUTF8);
179 Debug(fontcache, 2,
"Loaded font '{}' with size {}", this->
font_name, pixels);
184 assert(IsPrintable(key));
188 if (key >= 0x010000U) {
189 chars[0] = (UniChar)(((key - 0x010000U) >> 10) + 0xD800);
190 chars[1] = (UniChar)(((key - 0x010000U) & 0x3FF) + 0xDC00);
192 chars[0] = (UniChar)(key & 0xFFFF);
195 CGGlyph glyph[2] = {0, 0};
196 if (CTFontGetGlyphsForCharacters(this->
font.get(), chars, glyph, key >= 0x010000U ? 2 : 1)) {
200 if (allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
207const Sprite *CoreTextFontCache::InternalGetGlyph(
GlyphID key,
bool use_aa)
210 CGGlyph glyph = (CGGlyph)key;
211 CGRect bounds = CGRectNull;
213 bounds = CTFontGetOpticalBoundsForGlyphs(this->
font.get(), &glyph,
nullptr, 1, 0);
215 bounds = CTFontGetBoundingRectsForGlyphs(this->
font.get(), kCTFontOrientationDefault, &glyph,
nullptr, 1);
217 if (CGRectIsNull(bounds)) UserError(
"Unable to render font glyph");
219 uint bb_width = (uint)std::ceil(bounds.size.width) + 1;
220 uint bb_height = (uint)std::ceil(bounds.size.height);
224 uint width = std::max(1U, bb_width + shadow);
225 uint
height = std::max(1U, bb_height + shadow);
235 sprite.
width = width;
237 sprite.
x_offs = (int16_t)std::round(CGRectGetMinX(bounds));
238 sprite.
y_offs = this->
ascender - (int16_t)std::ceil(CGRectGetMaxY(bounds));
240 if (bounds.size.width > 0) {
244 int pitch =
Align(bb_width, 16);
246 const uint8_t *bmp =
static_cast<uint8_t *
>(CGBitmapContextGetData(context.get()));
248 CGContextSetAllowsAntialiasing(context.get(), use_aa);
249 CGContextSetAllowsFontSubpixelPositioning(context.get(), use_aa);
250 CGContextSetAllowsFontSubpixelQuantization(context.get(), !use_aa);
251 CGContextSetShouldSmoothFonts(context.get(),
false);
253 CGPoint pos{-bounds.origin.x, -bounds.origin.y};
254 CTFontDrawGlyphs(this->
font.get(), &glyph, &pos, 1, context.get());
258 for (uint y = 0; y < bb_height; y++) {
259 for (uint x = 0; x < bb_width; x++) {
260 if (bmp[y * pitch + x] > 0) {
261 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
m = SHADOW_COLOUR;
262 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
a = use_aa ? bmp[x + y * pitch] : 0xFF;
269 for (uint y = 0; y < bb_height; y++) {
270 for (uint x = 0; x < bb_width; x++) {
271 if (bmp[y * pitch + x] > 0) {
272 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
273 sprite.
data[x + y * sprite.
width].
a = use_aa ? bmp[x + y * pitch] : 0xFF;
282 GlyphEntry new_glyph;
283 new_glyph.
data = std::move(allocator.data);
284 new_glyph.width = (uint8_t)std::round(CTFontGetAdvancesForGlyphs(this->
font.get(), kCTFontOrientationDefault, &glyph,
nullptr, 1));
286 return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
289static CTFontDescriptorRef LoadFontFromFile(
const std::string &font_name)
299 path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8));
303 if (!full_font.empty()) {
304 path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8));
310 CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle,
false));
313 if (descs && CFArrayGetCount(descs.get()) > 0) {
314 CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0);
334 if (font.empty())
return;
338 if (
settings->os_handle !=
nullptr) {
339 font_ref.reset(
static_cast<CTFontDescriptorRef
>(
const_cast<void *
>(
settings->os_handle)));
340 CFRetain(font_ref.get());
345 font_ref.reset(LoadFontFromFile(font));
346 if (!font_ref) ShowInfo(
"Unable to load file '{}' for {} font, using default OS font selection instead", font, FontSizeToName(fs));
357 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
358 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
361 if (descs && CFArrayGetCount(descs.get()) > 0) {
362 font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
363 CFRetain(font_ref.get());
368 ShowInfo(
"Unable to use '{}' for {} font, using sprite font instead", font, FontSizeToName(fs));
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
CFAutoRelease< CTFontDescriptorRef > font_desc
Font descriptor exlcuding font size.
std::string font_name
Cached font name.
CFAutoRelease< CTFontRef > font
CoreText font handle.
void ClearFontCache() override
Reset cached glyphs.
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback=true) override
Map a character into a glyph.
int height
The height of the font.
FontCache * parent
The parent of this font cache.
virtual GlyphID MapCharToGlyph(char32_t key, bool fallback=true)=0
Map a character into a glyph.
const FontSize fs
The size of the font.
int descender
The descender value of the font.
int ascender
The ascender value of the font.
A searcher for missing glyphs.
virtual void SetFontNames(struct FontCacheSettings *settings, const char *font_name, const void *os_data=nullptr)=0
Set the right font names.
bool FindMissingGlyphs()
Check whether there are glyphs missing in the current language.
virtual bool Monospace()=0
Whether to search for a monospace font or not.
virtual Sprite * Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)=0
Convert a sprite from the loader to our own format.
std::array< Sprite, ZOOM_LVL_END > SpriteCollection
Type defining a collection of sprites, one for each zoom level.
Font cache for fonts that are based on a TrueType font.
static constexpr int MAX_GLYPH_DIM
Maximum glyph dimensions.
int used_size
Used font size.
int req_size
Requested font size.
void ClearFontCache() override
Reset cached glyphs.
static constexpr uint MAX_FONT_MIN_REC_SIZE
Upper limit for the recommended font size in case a font file contains nonsensical values.
SpriteAllocator that allocates memory via a unique_ptr array.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
std::string FioFindFullPath(Subdirectory subdir, const std::string &filename)
Find a path to the filename in one of the search directories.
bool FileExists(const std::string &filename)
Test whether the given filename exists.
@ BASE_DIR
Base directory for all subdirectories.
fluid_settings_t * settings
FluidSynth settings handle.
void LoadCoreTextFont(FontSize fs)
Loads the TrueType font.
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, int, MissingGlyphSearcher *callback)
We would like to have a fallback font as the current one doesn't contain all characters we need.
Functions related to font handling on MacOS.
uint GetFontCacheFontSize(FontSize fs)
Get the scalable font size to use for a FontSize.
std::string GetFontCacheFontName(FontSize fs)
Get font to use for a given font size.
FontCacheSubSetting * GetFontCacheSubSetting(FontSize fs)
Get the settings of a given font size.
uint32_t GlyphID
Glyphs are characters from a font.
@ Font
A sprite used for fonts.
FontSize
Available font sizes.
@ FS_SMALL
Index of the small font in the font tables.
@ FS_NORMAL
Index of the normal font in the font tables.
Functions related to MacOS support.
std::unique_ptr< typename std::remove_pointer< T >::type, CFDeleter< typename std::remove_pointer< T >::type > > CFAutoRelease
Specialisation of std::unique_ptr for CoreFoundation objects.
bool MacOSVersionIsAtLeast(long major, long minor, long bugfix)
Check if we are at least running on the specified version of Mac OS.
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
A number of safeguards to prevent using unsafe methods.
@ SCC_ALPHA
Sprite has alpha.
@ SCC_PAL
Sprite has palette data.
#define lengthof(array)
Return the length of an fixed size array.
Settings for the four different fonts.
Settings for a single font.
Structure for passing information from the sprite loader to the blitter.
void AllocateData(ZoomLevel zoom, size_t size)
Allocate the sprite data of this sprite.
SpriteColourComponent colours
The colour components of the sprite with useful information.
uint16_t width
Width of the sprite.
int16_t x_offs
The x-offset of where the sprite will be drawn.
SpriteLoader::CommonPixel * data
The sprite itself.
uint16_t height
Height of the sprite.
SpriteType type
The sprite type.
int16_t y_offs
The y-offset of where the sprite will be drawn.
Data structure describing a sprite.
uint8_t data[]
Sprite data.
static const int MAX_FONT_SIZE
Maximum font size.
@ ZOOM_LVL_MIN
Minimum zoom level.