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 "../../string_func.h"
18#include "../../strings_func.h"
19#include "../../zoom_func.h"
22#include "../../table/control_codes.h"
24#include "../../safeguards.h"
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)) {
118const Sprite *CoreTextFontCache::InternalGetGlyph(
GlyphID key,
bool use_aa)
121 CGGlyph glyph = (CGGlyph)key;
122 CGRect bounds = CGRectNull;
124 bounds = CTFontGetOpticalBoundsForGlyphs(this->
font.get(), &glyph,
nullptr, 1, 0);
126 bounds = CTFontGetBoundingRectsForGlyphs(this->
font.get(), kCTFontOrientationDefault, &glyph,
nullptr, 1);
128 if (CGRectIsNull(bounds)) UserError(
"Unable to render font glyph");
130 uint bb_width = (uint)std::ceil(bounds.size.width) + 1;
131 uint bb_height = (uint)std::ceil(bounds.size.height);
135 uint width = std::max(1U, bb_width + shadow);
136 uint
height = std::max(1U, bb_height + shadow);
146 sprite.
width = width;
148 sprite.
x_offs = (int16_t)std::round(CGRectGetMinX(bounds));
149 sprite.
y_offs = this->
ascender - (int16_t)std::ceil(CGRectGetMaxY(bounds));
151 if (bounds.size.width > 0) {
155 int pitch =
Align(bb_width, 16);
157 const uint8_t *bmp =
static_cast<uint8_t *
>(CGBitmapContextGetData(context.get()));
159 CGContextSetAllowsAntialiasing(context.get(), use_aa);
160 CGContextSetAllowsFontSubpixelPositioning(context.get(), use_aa);
161 CGContextSetAllowsFontSubpixelQuantization(context.get(), !use_aa);
162 CGContextSetShouldSmoothFonts(context.get(),
false);
164 CGPoint pos{-bounds.origin.x, -bounds.origin.y};
165 CTFontDrawGlyphs(this->
font.get(), &glyph, &pos, 1, context.get());
169 for (uint y = 0; y < bb_height; y++) {
170 for (uint x = 0; x < bb_width; x++) {
171 if (bmp[y * pitch + x] > 0) {
172 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
m = SHADOW_COLOUR;
173 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
a = use_aa ? bmp[x + y * pitch] : 0xFF;
180 for (uint y = 0; y < bb_height; y++) {
181 for (uint x = 0; x < bb_width; x++) {
182 if (bmp[y * pitch + x] > 0) {
183 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
184 sprite.
data[x + y * sprite.
width].
a = use_aa ? bmp[x + y * pitch] : 0xFF;
193 GlyphEntry new_glyph;
194 new_glyph.
data = std::move(allocator.data);
195 new_glyph.width = (uint8_t)std::round(CTFontGetAdvancesForGlyphs(this->
font.get(), kCTFontOrientationDefault, &glyph,
nullptr, 1));
197 return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
210 std::unique_ptr<FontCache>
LoadFont(
FontSize fs,
FontType fonttype,
bool search,
const std::string &font, std::span<const std::byte>)
const override
218 font_ref.reset(LoadFontFromFile(font));
219 if (!font_ref) ShowInfo(
"Unable to load file '{}' for {} font, using default OS font selection instead", font, FontSizeToName(fs));
222 if (!font_ref && search) {
230 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
231 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
234 if (descs && CFArrayGetCount(descs.get()) > 0) {
235 font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
236 CFRetain(font_ref.get());
241 ShowInfo(
"Unable to use '{}' for {} font, using sprite font instead", font, FontSizeToName(fs));
253 if (language_isocode ==
"zh_TW") {
256 }
else if (language_isocode ==
"zh_CN") {
261 lang = language_isocode.substr(0, language_isocode.find(
'_'));
266 CFStringRef lang_codes[2];
267 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang.c_str(), kCFStringEncodingUTF8);
268 lang_codes[1] = CFSTR(
"en");
269 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (
const void **)lang_codes,
lengthof(lang_codes), &kCFTypeArrayCallBacks);
270 CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontLanguagesAttribute)), (
const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
273 CFRelease(lang_codes[0]);
276 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
277 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
280 for (
int tries = 0; tries < 2; tries++) {
281 for (CFIndex i = 0; descs.get() !=
nullptr && i < CFArrayGetCount(descs.get()); i++) {
282 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
286 CTFontSymbolicTraits symbolic_traits;
287 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
290 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait))
continue;
292 if (symbolic_traits & kCTFontBoldTrait)
continue;
294 if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != fontsizes.
Test(
FS_MONO))
continue;
299 CFStringGetCString(font_name.get(), buffer, std::size(buffer), kCFStringEncodingUTF8);
304 if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass)
continue;
308 std::string_view name{buffer};
309 if (name.starts_with(
".") || name.starts_with(
"LastResort"))
continue;
315 Debug(fontcache, 2,
"CT-Font for {}: {}", language_isocode, name);
334 static CTFontDescriptorRef LoadFontFromFile(
const std::string &font_name)
344 path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8));
348 if (!full_font.empty()) {
349 path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8));
355 CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle,
false));
358 if (descs && CFArrayGetCount(descs.get()) > 0) {
359 CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0);
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr bool None() const
Test if none of the values are set.
constexpr Timpl & Set()
Set all bits.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
std::unique_ptr< FontCache > LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font, std::span< const std::byte >) const override
Loads the TrueType font.
CFAutoRelease< CTFontDescriptorRef > font_desc
Font descriptor excluding font size.
std::string font_name
Cached font name.
CFAutoRelease< CTFontRef > font
CoreText font handle.
GlyphID MapCharToGlyph(char32_t key) override
Map a character into a glyph.
void ClearFontCache() override
Reset cached glyphs.
int height
The height of the font.
const FontSize fs
The size of the font.
int descender
The descender value of the font.
int ascender
The ascender value of the font.
static void AddFallback(FontSizes fontsizes, FontLoadReason load_reason, std::string_view name, std::span< const std::byte > os_data={})
Add a fallback font, with optional OS-specific handle.
A searcher for missing glyphs.
FontSizes FindMissingGlyphs()
Test if any glyphs are missing.
virtual Sprite * Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)=0
Convert a sprite from the loader to our own format.
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,...)
Output a line of debugging information.
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.
@ BASE_DIR
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.
uint32_t GlyphID
Glyphs are characters from a font.
@ Font
A sprite used for fonts.
FontSize
Available font sizes.
@ FS_MONO
Index of the monospaced font in the font tables.
@ 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.
@ Palette
Sprite has palette data.
#define lengthof(array)
Return the length of an fixed size array.
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.
std::byte data[]
Sprite data.
static const int MAX_FONT_SIZE
Maximum font size.