28 this->SetFontSize(pixels);
42void CoreTextFontCache::SetFontSize(
int pixels)
46 int scaled_height =
ScaleGUITrad(FontCache::GetDefaultFontHeight(this->
fs));
47 pixels = scaled_height;
51 float min_size = 0.0f;
60 uint16_t lowestRecPPEM;
61 CFDataGetBytes(data.get(), CFRangeMake(46,
sizeof(lowestRecPPEM)), (UInt8 *)&lowestRecPPEM);
62 min_size = CFSwapInt16BigToHost(lowestRecPPEM);
65 CFNumberGetValue(size.get(), kCFNumberFloatType, &min_size);
79 this->
font.reset(CTFontCreateWithFontDescriptor(this->
font_desc.get(), pixels,
nullptr));
84 this->
ascender = (int)std::ceil(CTFontGetAscent(this->
font.get()));
85 this->
descender = -(int)std::ceil(CTFontGetDescent(this->
font.get()));
94 Debug(fontcache, 2,
"Loaded font '{}' with size {}", this->
font_name, pixels);
99 assert(IsPrintable(key));
103 if (key >= 0x010000U) {
104 chars[0] = (UniChar)(((key - 0x010000U) >> 10) + 0xD800);
105 chars[1] = (UniChar)(((key - 0x010000U) & 0x3FF) + 0xDC00);
107 chars[0] = (UniChar)(key & 0xFFFF);
110 CGGlyph glyph[2] = {0, 0};
111 if (CTFontGetGlyphsForCharacters(this->
font.get(), chars, glyph, key >= 0x010000U ? 2 : 1)) {
115 if (allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
116 return this->
parent->MapCharToGlyph(key);
125 CGGlyph glyph = (CGGlyph)key;
126 CGRect bounds = CTFontGetOpticalBoundsForGlyphs(this->
font.get(), &glyph,
nullptr, 1, 0);
127 if (CGRectIsNull(bounds)) UserError(
"Unable to render font glyph");
129 uint bb_width = (uint)std::ceil(bounds.size.width) + 1;
130 uint bb_height = (uint)std::ceil(bounds.size.height);
134 uint width = std::max(1U, bb_width + shadow);
135 uint
height = std::max(1U, bb_height + shadow);
145 sprite.
width = width;
147 sprite.
x_offs = (int16_t)std::round(CGRectGetMinX(bounds));
148 sprite.
y_offs = this->
ascender - (int16_t)std::ceil(CGRectGetMaxY(bounds));
150 if (bounds.size.width > 0) {
154 int pitch =
Align(bb_width, 16);
156 const uint8_t *bmp =
static_cast<uint8_t *
>(CGBitmapContextGetData(context.get()));
158 CGContextSetAllowsAntialiasing(context.get(), use_aa);
159 CGContextSetAllowsFontSubpixelPositioning(context.get(), use_aa);
160 CGContextSetAllowsFontSubpixelQuantization(context.get(), !use_aa);
161 CGContextSetShouldSmoothFonts(context.get(),
false);
163 CGPoint pos{-bounds.origin.x, -bounds.origin.y};
164 CTFontDrawGlyphs(this->
font.get(), &glyph, &pos, 1, context.get());
168 for (uint y = 0; y < bb_height; y++) {
169 for (uint x = 0; x < bb_width; x++) {
170 if (bmp[y * pitch + x] > 0) {
171 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
m = SHADOW_COLOUR;
172 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
a = use_aa ? bmp[x + y * pitch] : 0xFF;
179 for (uint y = 0; y < bb_height; y++) {
180 for (uint x = 0; x < bb_width; x++) {
181 if (bmp[y * pitch + x] > 0) {
182 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
183 sprite.
data[x + y * sprite.
width].
a = use_aa ? bmp[x + y * pitch] : 0xFF;
193 new_glyph.
data = std::move(allocator.data);
194 new_glyph.
width = (uint8_t)std::round(CTFontGetAdvancesForGlyphs(this->
font.get(), kCTFontOrientationDefault, &glyph,
nullptr, 1));
196 return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
199class CoreTextFontCacheFactory :
public FontCacheFactory {
201 CoreTextFontCacheFactory() : FontCacheFactory(
"coretext",
"CoreText font loader") {}
203 std::unique_ptr<FontCache>
LoadFont(
FontSize fs,
FontType fonttype,
bool search,
const std::string &font_name,
const std::any &)
const override
209 if (!font_ref) ShowInfo(
"Unable to load file '{}' for {} font, using default OS font selection instead", font_name, FontSizeToName(fs));
211 if (!font_ref && search) {
219 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
220 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
223 if (descs && CFArrayGetCount(descs.get()) > 0) {
224 font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
225 CFRetain(font_ref.get());
230 ShowInfo(
"Unable to use '{}' for {} font, using sprite font instead", font_name, FontSizeToName(fs));
242 if (language_isocode ==
"zh_TW") {
245 }
else if (language_isocode ==
"zh_CN") {
250 lang = language_isocode.substr(0, language_isocode.find(
'_'));
255 CFStringRef lang_codes[2];
256 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang.c_str(), kCFStringEncodingUTF8);
257 lang_codes[1] = CFSTR(
"en");
258 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (
const void **)lang_codes,
lengthof(lang_codes), &kCFTypeArrayCallBacks);
259 CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontLanguagesAttribute)), (
const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
262 CFRelease(lang_codes[0]);
265 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
266 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
269 for (
int tries = 0; tries < 2; tries++) {
270 for (CFIndex i = 0; descs.get() !=
nullptr && i < CFArrayGetCount(descs.get()); i++) {
271 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
275 CTFontSymbolicTraits symbolic_traits;
276 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
279 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait))
continue;
281 if (symbolic_traits & kCTFontBoldTrait)
continue;
288 CFStringGetCString(font_name.get(), buffer, std::size(buffer), kCFStringEncodingUTF8);
293 if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass)
continue;
297 std::string_view
name{buffer};
298 if (
name.starts_with(
".") ||
name.starts_with(
"LastResort"))
continue;
304 Debug(fontcache, 2,
"CT-Font for {}: {}", language_isocode,
name);
323 static CTFontDescriptorRef LoadFontFromFile(
const std::string &font_name)
331 path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8));
335 if (!full_font.empty()) {
336 path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8));
342 CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle,
false));
345 if (descs && CFArrayGetCount(descs.get()) > 0) {
346 CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0);
356 static CoreTextFontCacheFactory instance;
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
const std::string_view name
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
bool FindFallbackFont(const std::string &language_isocode, MissingGlyphSearcher *callback) const override
We would like to have a fallback font as the current one doesn't contain all characters we need.
std::unique_ptr< FontCache > LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font_name, const std::any &) const override
Try loading a font with this factory.
CFAutoRelease< CTFontDescriptorRef > font_desc
Font descriptor excluding font size.
std::string font_name
Cached font name.
const Sprite * InternalGetGlyph(GlyphID key, bool use_aa) override
Load the glyph as a sprite.
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.
std::unique_ptr< FontCache > parent
The parent of this font cache.
static void AddFallback(FontSizes fontsizes, std::string_view name, const std::any &os_handle={})
Add a fallback font, with optional OS-specific handle.
const FontSize fs
The size of the font.
int descender
The descender value of the font.
static bool TryFallback(FontSizes fontsizes, const std::set< char32_t > &glyphs, const std::string &name, const std::any &os_handle={})
Test a fallback font, with optional OS-specific handle, for specific glyphs.
int ascender
The ascender value of the font.
A searcher for missing glyphs.
std::set< char32_t > missing_glyphs
Glyphs to search for.
FontSizes missing_fontsizes
Font sizes to actually search for.
virtual Sprite * Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)=0
Convert a sprite from the loader to our own format.
SpriteCollMap< Sprite > 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.
Control codes that are embedded in the translation strings.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Error reporting related functions.
Factory to 'query' all available blitters.
bool FileExists(std::string_view filename)
Test whether the given filename exists.
std::string FioFindFullPath(Subdirectory subdir, std::string_view filename)
Find a path to the filename in one of the search directories.
Functions for standard in/out file operations.
@ Base
Base directory for all subdirectories.
Functions related to font handling on MacOS.
uint GetFontCacheFontSize(FontSize fs)
Get the scalable font size to use for a FontSize.
FontType
Different types of font that can be loaded.
@ TrueType
Scalable TrueType fonts.
@ Font
A sprite used for fonts.
FontSize
Available font sizes.
@ Small
Index of the small font in the font tables.
@ Normal
Index of the normal font in the font tables.
@ Monospace
Index of the monospaced 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.
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.
@ Palette
Sprite has palette data.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Functions related to low-level strings.
Functions related to OTTD's strings.
Structure for passing information from the sprite loader to the blitter.
SpriteComponents colours
The colour components of the sprite with useful information.
void AllocateData(ZoomLevel zoom, size_t size)
Allocate the sprite data of this sprite.
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.
int16_t y_offs
The y-offset of where the sprite will be drawn.
Data structure describing a sprite.
Container for information about a glyph.
std::unique_ptr< std::byte[]> data
The loaded sprite.
uint8_t width
The width of the glyph.
static const int MAX_FONT_SIZE
Maximum font size.
Functions related to zooming.