20#include <CoreFoundation/CoreFoundation.h>
47 std::vector<GlyphID> glyphs;
48 std::vector<Position> positions;
49 std::vector<int> glyph_to_char;
51 int total_advance = 0;
56 CoreTextVisualRun(CoreTextVisualRun &&other) =
default;
58 std::span<const GlyphID> GetGlyphs()
const override {
return this->glyphs; }
59 std::span<const Position> GetPositions()
const override {
return this->positions; }
60 std::span<const int> GetGlyphToCharMap()
const override {
return this->glyph_to_char; }
62 const Font *GetFont()
const override {
return this->font; }
63 int GetLeading()
const override {
return this->font->
fc->
GetHeight(); }
64 int GetGlyphCount()
const override {
return (
int)this->glyphs.size(); }
65 int GetAdvance()
const {
return this->total_advance; }
73 CFArrayRef runs = CTLineGetGlyphRuns(line.get());
74 for (CFIndex i = 0; i < CFArrayGetCount(runs); i++) {
75 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i);
78 CFRange chars = CTRunGetStringRange(run);
79 auto map = std::ranges::upper_bound(font_mapping, chars.location, std::less{}, &std::pair<int, Font *>::first);
81 this->emplace_back(run, map->second, buff);
87 int CountRuns()
const override {
return this->size(); }
88 const VisualRun &GetVisualRun(
int run)
const override {
return this->at(run); }
90 int GetInternalCharLength(
char32_t c)
const override
93 return c >= 0x010000U ? 2 : 1;
102 void Reflow()
override
104 this->cur_offset = 0;
107 std::unique_ptr<const Line> NextLine(
int max_width)
override;
115 char32_t c = (char32_t)((
size_t)ref_con & 0xFFFFFF);
120static const CTRunDelegateCallbacks _sprite_font_callback = {
121 kCTRunDelegateCurrentVersion,
nullptr,
nullptr,
nullptr,
128 ptrdiff_t length = buff_end - buff;
129 if (length == 0)
return nullptr;
132 for (
const auto &[position, font] : font_mapping) {
133 if (font->fc->IsBuiltInFont())
return nullptr;
138 CFAttributedStringBeginEditing(str.get());
141 CFAttributedStringReplaceString(str.get(), CFRangeMake(0, 0), base.get());
143 const UniChar replacement_char = 0xFFFC;
149 for (
const auto &[position, font] : font_mapping) {
150 if (position - last == 0)
continue;
152 CTFontRef font_handle =
static_cast<CTFontRef
>(font->fc->GetOSHandle());
153 if (font_handle ==
nullptr) {
156 CFAutoRelease<CFStringRef> font_name(CFStringCreateWithCString(kCFAllocatorDefault, font->fc->GetFontName().c_str(), kCFStringEncodingUTF8));
157 _font_cache[font->fc->GetSize()].reset(CTFontCreateWithName(font_name.get(), font->fc->GetFontSize(),
nullptr));
159 font_handle =
_font_cache[font->fc->GetSize()].get();
161 CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, position - last), kCTFontAttributeName, font_handle);
163 CGColorRef colour = CGColorCreateGenericGray((uint8_t)font->colour / 255.0f, 1.0f);
164 CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, position - last), kCTForegroundColorAttributeName, colour);
165 CGColorRelease(colour);
168 for (ssize_t c = last; c < position; c++) {
169 if (buff[c] >= SCC_SPRITE_START && buff[c] <= SCC_SPRITE_END && font->fc->MapCharToGlyph(buff[c],
false) == 0) {
172 CFAttributedStringReplaceString(str.get(), CFRangeMake(c, 1), replacement_str.get());
173 CFAttributedStringSetAttribute(str.get(), CFRangeMake(c, 1), kCTRunDelegateAttributeName, del.get());
179 CFAttributedStringEndEditing(str.get());
184 return typesetter ? std::make_unique<CoreTextParagraphLayout>(std::move(typesetter), buff, length, font_mapping) :
nullptr;
187 std::unique_ptr<const ParagraphLayouter::Line> CoreTextParagraphLayout::NextLine(
int max_width)
189 if (this->
cur_offset >= this->length)
return nullptr;
192 CFIndex len = CTTypesetterSuggestLineBreak(this->typesetter.get(), this->cur_offset, max_width);
193 if (len <= 0) len = CTTypesetterSuggestClusterBreak(this->typesetter.get(), this->cur_offset, max_width);
199 if (!line)
return nullptr;
200 return std::make_unique<CoreTextLine>(std::move(line), this->font_map, this->text_buffer);
205 this->glyphs.resize(CTRunGetGlyphCount(run));
208 auto map = std::make_unique<CFIndex[]>(this->glyphs.size());
209 CTRunGetStringIndices(run, CFRangeMake(0, 0), map.get());
211 this->glyph_to_char.resize(this->glyphs.size());
212 for (
size_t i = 0; i < this->glyph_to_char.size(); i++) this->glyph_to_char[i] = (
int)map[i];
214 auto pts = std::make_unique<CGPoint[]>(this->glyphs.size());
215 CTRunGetPositions(run, CFRangeMake(0, 0), pts.get());
216 auto advs = std::make_unique<CGSize[]>(this->glyphs.size());
217 CTRunGetAdvances(run, CFRangeMake(0, 0), advs.get());
218 this->positions.reserve(this->glyphs.size());
222 auto gl = std::make_unique<CGGlyph[]>(this->glyphs.size());
223 CTRunGetGlyphs(run, CFRangeMake(0, 0), gl.get());
224 for (
size_t i = 0; i < this->glyphs.size(); i++) {
225 if (buff[this->glyph_to_char[i]] >= SCC_SPRITE_START && buff[this->glyph_to_char[i]] <= SCC_SPRITE_END && (gl[i] == 0 || gl[i] == 3)) {
230 this->glyphs[i] = gl[i];
231 this->positions.emplace_back(pts[i].x, pts[i].x + advs[i].width - 1, pts[i].y);
234 this->total_advance = (int)std::ceil(CTRunGetTypographicBounds(run, CFRangeMake(0, 0),
nullptr,
nullptr,
nullptr));
244 for (
const auto &run : *
this) {
245 leading = std::max(leading, run.GetLeading());
257 if (this->empty())
return 0;
260 for (
const auto &run : *
this) {
261 total_width += run.GetAdvance();
277 CFAutoRelease<CFStringRef> path(CFStringCreateWithBytes(kCFAllocatorDefault,
reinterpret_cast<const UInt8 *
>(file_path.data()), file_path.size(), kCFStringEncodingUTF8,
false));
278 CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle,
false));
280 CTFontManagerRegisterFontsForURL(url.get(), kCTFontManagerScopeProcess,
nullptr);
286 CFAutoRelease<CFStringRef> iso(CFStringCreateWithBytes(kCFAllocatorDefault,
reinterpret_cast<const UInt8 *
>(iso_code.data()), iso_code.size(), kCFStringEncodingUTF8,
false));
287 _osx_locale.reset(CFLocaleCreate(kCFAllocatorDefault, iso.get()));
299 CFStringCompareFlags flags = kCFCompareCaseInsensitive | kCFCompareNumerically | kCFCompareLocalized | kCFCompareWidthInsensitive | kCFCompareForcedOrdering;
301 CFAutoRelease<CFStringRef> cf1(CFStringCreateWithBytes(kCFAllocatorDefault, (
const UInt8 *)s1.data(), s1.size(), kCFStringEncodingUTF8,
false));
302 CFAutoRelease<CFStringRef> cf2(CFStringCreateWithBytes(kCFAllocatorDefault, (
const UInt8 *)s2.data(), s2.size(), kCFStringEncodingUTF8,
false));
305 if (cf1 ==
nullptr || cf2 ==
nullptr)
return 0;
307 return (
int)CFStringCompareWithOptionsAndLocale(cf1.get(), cf2.get(), CFRangeMake(0, CFStringGetLength(cf1.get())), flags,
_osx_locale.get()) + 2;
320 CFStringCompareFlags flags = kCFCompareLocalized | kCFCompareWidthInsensitive;
321 if (case_insensitive) flags |= kCFCompareCaseInsensitive;
323 CFAutoRelease<CFStringRef> cf_str(CFStringCreateWithBytes(kCFAllocatorDefault, (
const UInt8 *)str.data(), str.size(), kCFStringEncodingUTF8,
false));
324 CFAutoRelease<CFStringRef> cf_value(CFStringCreateWithBytes(kCFAllocatorDefault, (
const UInt8 *)value.data(), value.size(), kCFStringEncodingUTF8,
false));
327 if (cf_str ==
nullptr || cf_value ==
nullptr)
return -1;
329 return CFStringFindWithOptionsAndLocale(cf_str.get(), cf_value.get(), CFRangeMake(0, CFStringGetLength(cf_str.get())), flags,
_osx_locale.get(),
nullptr) ? 1 : 0;
341 std::vector<UniChar> utf16_str;
343 for (
auto it = view.begin(), end = view.end(); it != end; ++it) {
344 size_t idx = it.GetByteOffset();
347 utf16_str.push_back((UniChar)c);
350 utf16_str.push_back((UniChar)(0xD800 + ((c - 0x10000) >> 10)));
351 utf16_str.push_back((UniChar)(0xDC00 + ((c - 0x10000) & 0x3FF)));
361 if (!utf16_str.empty()) {
362 CFAutoRelease<CFStringRef> str(CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, &utf16_str[0], utf16_str.size(), kCFAllocatorNull));
365 for (CFIndex i = 0; i < CFStringGetLength(str.get()); ) {
366 CFRange r = CFStringGetRangeOfComposedCharactersAtIndex(str.get(), i);
367 this->
str_info[r.location].char_stop =
true;
375 CFStringTokenizerTokenType tokenType = kCFStringTokenizerTokenNone;
376 while ((tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer.get())) != kCFStringTokenizerTokenNone) {
378 if ((tokenType & kCFStringTokenizerTokenHasNonLettersMask) != kCFStringTokenizerTokenHasNonLettersMask) {
379 CFRange r = CFStringTokenizerGetCurrentTokenRange(tokenizer.get());
380 this->
str_info[r.location].word_stop =
true;
386 this->
str_info.back().char_stop =
true;
387 this->
str_info.back().word_stop =
true;
393 size_t utf16_pos = 0;
402 while (utf16_pos > 0 && !this->
str_info[utf16_pos].char_stop) utf16_pos--;
417 }
while (this->cur_pos < this->
utf16_to_utf8.size() && (what ==
ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
438 return std::make_unique<OSXStringIterator>();
UniChar CharType
Helper for GetLayouter, to get the right type.
static std::unique_ptr< ParagraphLayouter > GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
Get the actual ParagraphLayout for the given buffer.
int GetWidth() const override
Get the width of this line.
int GetLeading() const override
Get the height of the line.
CFIndex cur_offset
Offset from the start of the current run from where to output.
int GetHeight() const
Get the height of the font.
virtual GlyphID MapCharToGlyph(char32_t key, bool fallback=true)=0
Map a character into a glyph.
FontSize GetSize() const
Get the FontSize of the font.
Container with information about a font.
FontCache * fc
The font we are using.
size_t Prev(IterType what) override
Move the cursor back by one iteration unit.
size_t Next(IterType what) override
Advance the cursor by one iteration unit.
std::vector< CharInfo > str_info
Break information for each code point.
size_t SetCurPosition(size_t pos) override
Change the current string cursor.
size_t cur_pos
Current iteration position.
std::vector< size_t > utf16_to_utf8
Mapping from UTF-16 code point position to index in the UTF-8 source string.
void SetString(std::string_view s) override
Set a new iteration string.
A single line worth of VisualRuns.
Visual run contains data about the bit of text with the same font.
Interface to glue fallback and normal layouter into one.
static const size_t END
Sentinel to indicate end-of-iteration.
static std::unique_ptr< StringIterator > Create()
Create a new iterator instance.
IterType
Type of the iterator.
@ ITER_WORD
Iterate over words.
@ ITER_CHARACTER
Iterate over characters (or more exactly grapheme clusters).
Constant span of UTF-8 encoded data.
Control codes that are embedded in the translation strings.
Functions to read fonts from files and cache them.
uint GetGlyphWidth(FontSize size, char32_t key)
Get the width of a glyph.
std::vector< std::pair< int, Font * > > FontMap
Mapping from index to font.
FontSize
Available font sizes.
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.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
void MacOSRegisterExternalFont(std::string_view file_path)
Register an external font file with the CoreText system.
int MacOSStringCompare(std::string_view s1, std::string_view s2)
Compares two strings using case insensitive natural sort.
void MacOSSetCurrentLocaleName(std::string_view iso_code)
Store current language locale as a CoreFoundation locale.
static CGFloat SpriteFontGetWidth(void *ref_con)
Get the width of an encoded sprite font character.
int MacOSStringContains(std::string_view str, std::string_view value, bool case_insensitive)
Search if a string is contained in another string using the current locale.
static CFAutoRelease< CFLocaleRef > _osx_locale
Cached current locale.
void MacOSResetScriptCache(FontSize size)
Delete CoreText font reference for a specific font size.
static CFAutoRelease< CTFontRef > _font_cache[FS_END]
CoreText cache for font information, cleared when OTTD changes fonts.
Functions related to localized text support on OSX.
Functions related to OTTD's strings.
Handling of UTF-8 encoded data.
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.