10#include "../../stdafx.h"
12#include "../../string_func.h"
13#include "../../strings_func.h"
14#include "../../core/utf8.hpp"
15#include "../../table/control_codes.h"
16#include "../../fontcache.h"
17#include "../../zoom_func.h"
20#include <CoreFoundation/CoreFoundation.h>
21#include "../../safeguards.h"
47 std::vector<GlyphID> glyphs;
48 std::vector<Position> positions;
49 std::vector<int> glyph_to_char;
51 int total_advance = 0;
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;
335 this->utf16_to_utf8.clear();
336 this->str_info.clear();
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)));
352 this->utf16_to_utf8.push_back(idx);
354 this->utf16_to_utf8.push_back(idx);
356 this->utf16_to_utf8.push_back(s.size());
359 this->str_info.resize(utf16_to_utf8.size());
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;
394 for (
size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
395 if (this->utf16_to_utf8[i] == pos) {
402 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
403 this->cur_pos = utf16_pos;
405 return this->utf16_to_utf8[this->cur_pos];
410 assert(this->cur_pos <= this->utf16_to_utf8.size());
413 if (this->cur_pos == this->utf16_to_utf8.size())
return END;
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));
419 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
424 assert(this->cur_pos <= this->utf16_to_utf8.size());
427 if (this->cur_pos == 0)
return END;
431 }
while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
433 return this->utf16_to_utf8[this->cur_pos];
436 std::unique_ptr<StringIterator> OSXStringIterator::Create()
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.
A single line worth of VisualRuns.
int GetWidth() const override
Get the width of this line.
int GetLeading() const override
Get the height of the line.
Visual run contains data about the bit of text with the same font.
Wrapper for doing layouts with CoreText.
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.
size_t SetCurPosition(size_t pos) override
Change the current string cursor.
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.
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.
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.
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.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.