12 #include "../../stdafx.h" 14 #include "../../string_func.h" 15 #include "../../strings_func.h" 16 #include "../../table/control_codes.h" 17 #include "../../fontcache.h" 20 #include <CoreFoundation/CoreFoundation.h> 23 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 39 CTTypesetterRef typesetter;
47 std::vector<GlyphID> glyphs;
48 std::vector<float> positions;
49 std::vector<int> glyph_to_char;
51 int total_advance = 0;
58 const GlyphID *GetGlyphs()
const override {
return &this->glyphs[0]; }
59 const float *GetPositions()
const override {
return &this->positions[0]; }
60 const int *GetGlyphToCharMap()
const override {
return &this->glyph_to_char[0]; }
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);
74 for (CFIndex i = 0; i < CFArrayGetCount(runs); i++) {
75 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i);
78 CFRange chars = CTRunGetStringRange(run);
79 auto map = fontMapping.begin();
80 while (map < fontMapping.end() - 1 && map->first <= chars.location) map++;
82 this->emplace_back(run, map->second, buff);
87 int GetLeading()
const override;
88 int GetWidth()
const override;
89 int CountRuns()
const override {
return this->size(); }
90 const VisualRun &GetVisualRun(
int run)
const override {
return this->at(run); }
92 int GetInternalCharLength(
WChar c)
const override 95 return c >= 0x010000U ? 2 : 1;
106 CFRelease(this->typesetter);
109 void Reflow()
override 111 this->cur_offset = 0;
114 std::unique_ptr<const Line> NextLine(
int max_width)
override;
122 WChar c = (
WChar)((
size_t)ref_con & 0xFFFFFF);
127 static CTRunDelegateCallbacks _sprite_font_callback = {
128 kCTRunDelegateCurrentVersion,
nullptr,
nullptr,
nullptr,
137 ptrdiff_t length = buff_end - buff;
138 if (length == 0)
return nullptr;
141 for (
const auto &i : fontMapping) {
142 if (i.second->fc->IsBuiltInFont())
return nullptr;
146 CFMutableAttributedStringRef str = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
147 CFAttributedStringBeginEditing(str);
149 CFStringRef base = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buff, length, kCFAllocatorNull);
150 CFAttributedStringReplaceString(str, CFRangeMake(0, 0), base);
156 for (
const auto &i : fontMapping) {
157 if (i.first - last == 0)
continue;
159 if (
_font_cache[i.second->fc->GetSize()] ==
nullptr) {
161 CFStringRef font_name = CFStringCreateWithCString(kCFAllocatorDefault, i.second->fc->GetFontName(), kCFStringEncodingUTF8);
162 _font_cache[i.second->fc->GetSize()] = CTFontCreateWithName(font_name, i.second->fc->GetFontSize(),
nullptr);
163 CFRelease(font_name);
165 CFAttributedStringSetAttribute(str, CFRangeMake(last, i.first - last), kCTFontAttributeName,
_font_cache[i.second->fc->GetSize()]);
167 CGColorRef color = CGColorCreateGenericGray((uint8)i.second->colour / 255.0f, 1.0f);
168 CFAttributedStringSetAttribute(str, CFRangeMake(last, i.first - last), kCTForegroundColorAttributeName, color);
169 CGColorRelease(color);
172 for (ssize_t c = last; c < i.first; c++) {
173 if (buff[c] >= SCC_SPRITE_START && buff[c] <= SCC_SPRITE_END) {
174 CTRunDelegateRef del = CTRunDelegateCreate(&_sprite_font_callback, (
void *)(
size_t)(buff[c] | (i.second->fc->GetSize() << 24)));
175 CFAttributedStringSetAttribute(str, CFRangeMake(c, 1), kCTRunDelegateAttributeName, del);
182 CFAttributedStringEndEditing(str);
185 CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString(str);
191 std::unique_ptr<const ParagraphLayouter::Line> CoreTextParagraphLayout::NextLine(
int max_width)
193 if (this->
cur_offset >= this->length)
return nullptr;
196 CFIndex len = CTTypesetterSuggestLineBreak(this->typesetter, this->
cur_offset, max_width);
197 if (len <= 0) len = CTTypesetterSuggestClusterBreak(this->typesetter, this->
cur_offset, max_width);
200 CTLineRef line = CTTypesetterCreateLine(this->typesetter, CFRangeMake(this->
cur_offset, len));
203 return std::unique_ptr<const Line>(line !=
nullptr ?
new CoreTextLine(line, this->font_map, this->text_buffer) :
nullptr);
208 this->glyphs.resize(CTRunGetGlyphCount(run));
211 CFIndex map[this->glyphs.size()];
212 CTRunGetStringIndices(run, CFRangeMake(0, 0), map);
214 this->glyph_to_char.resize(this->glyphs.size());
215 for (
size_t i = 0; i < this->glyph_to_char.size(); i++) this->glyph_to_char[i] = (
int)map[i];
217 CGPoint pts[this->glyphs.size()];
218 CTRunGetPositions(run, CFRangeMake(0, 0), pts);
219 this->positions.resize(this->glyphs.size() * 2 + 2);
223 CGGlyph gl[this->glyphs.size()];
224 CTRunGetGlyphs(run, CFRangeMake(0, 0), gl);
225 for (
size_t i = 0; i < this->glyphs.size(); i++) {
226 if (buff[this->glyph_to_char[i]] >= SCC_SPRITE_START && buff[this->glyph_to_char[i]] <= SCC_SPRITE_END) {
227 this->glyphs[i] = font->fc->MapCharToGlyph(buff[this->glyph_to_char[i]]);
228 this->positions[i * 2 + 0] = pts[i].x;
229 this->positions[i * 2 + 1] = font->fc->GetAscender() - font->fc->GetGlyph(this->glyphs[i])->height - 1;
231 this->glyphs[i] = gl[i];
232 this->positions[i * 2 + 0] = pts[i].x;
233 this->positions[i * 2 + 1] = pts[i].y;
236 this->total_advance = (int)CTRunGetTypographicBounds(run, CFRangeMake(0, 0),
nullptr,
nullptr,
nullptr);
237 this->positions[this->glyphs.size() * 2] = this->positions[0] + this->total_advance;
247 for (
const auto &run : *
this) {
248 leading =
max(leading, run.GetLeading());
260 if (this->size() == 0)
return 0;
263 for (
const auto &run : *
this) {
264 total_width += run.GetAdvance();
287 CFStringRef iso = CFStringCreateWithCString(kCFAllocatorDefault, iso_code, kCFStringEncodingUTF8);
288 _osx_locale = CFLocaleCreate(kCFAllocatorDefault, iso);
302 if (!supported)
return 0;
304 CFStringCompareFlags flags = kCFCompareCaseInsensitive | kCFCompareNumerically | kCFCompareLocalized | kCFCompareWidthInsensitive | kCFCompareForcedOrdering;
306 CFStringRef cf1 = CFStringCreateWithCString(kCFAllocatorDefault, s1, kCFStringEncodingUTF8);
307 CFStringRef cf2 = CFStringCreateWithCString(kCFAllocatorDefault, s2, kCFStringEncodingUTF8);
310 if (cf1 ==
nullptr || cf2 ==
nullptr) {
311 if (cf1 !=
nullptr) CFRelease(cf1);
312 if (cf2 !=
nullptr) CFRelease(cf2);
316 CFComparisonResult res = CFStringCompareWithOptionsAndLocale(cf1, cf2, CFRangeMake(0, CFStringGetLength(cf1)), flags,
_osx_locale);
327 const char *string_base = s;
329 this->utf16_to_utf8.clear();
330 this->str_info.clear();
335 std::vector<UniChar> utf16_str;
337 size_t idx = s - string_base;
339 WChar c = Utf8Consume(&s);
341 utf16_str.push_back((UniChar)c);
344 utf16_str.push_back((UniChar)(0xD800 + ((c - 0x10000) >> 10)));
345 utf16_str.push_back((UniChar)(0xDC00 + ((c - 0x10000) & 0x3FF)));
346 this->utf16_to_utf8.push_back(idx);
348 this->utf16_to_utf8.push_back(idx);
350 this->utf16_to_utf8.push_back(s - string_base);
353 this->str_info.resize(utf16_to_utf8.size());
355 if (utf16_str.size() > 0) {
356 CFStringRef str = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, &utf16_str[0], utf16_str.size(), kCFAllocatorNull);
359 for (CFIndex i = 0; i < CFStringGetLength(str); ) {
360 CFRange r = CFStringGetRangeOfComposedCharactersAtIndex(str, i);
361 this->str_info[r.location].char_stop =
true;
367 CFStringTokenizerRef tokenizer = CFStringTokenizerCreate(kCFAllocatorDefault, str, CFRangeMake(0, CFStringGetLength(str)), kCFStringTokenizerUnitWordBoundary,
_osx_locale);
369 CFStringTokenizerTokenType tokenType = kCFStringTokenizerTokenNone;
370 while ((tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)) != kCFStringTokenizerTokenNone) {
372 if ((tokenType & kCFStringTokenizerTokenHasNonLettersMask) != kCFStringTokenizerTokenHasNonLettersMask) {
373 CFRange r = CFStringTokenizerGetCurrentTokenRange(tokenizer);
374 this->str_info[r.location].word_stop =
true;
378 CFRelease(tokenizer);
383 this->str_info.back().char_stop =
true;
384 this->str_info.back().word_stop =
true;
390 size_t utf16_pos = 0;
391 for (
size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
392 if (this->utf16_to_utf8[i] == pos) {
399 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
400 this->cur_pos = utf16_pos;
402 return this->utf16_to_utf8[this->cur_pos];
407 assert(this->cur_pos <= this->utf16_to_utf8.size());
410 if (this->cur_pos == this->utf16_to_utf8.size())
return END;
414 }
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));
416 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
421 assert(this->cur_pos <= this->utf16_to_utf8.size());
424 if (this->cur_pos == 0)
return END;
428 }
while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
430 return this->utf16_to_utf8[this->cur_pos];
void SetString(const char *s) override
Set a new iteration string.
Wrapper for doing layouts with CoreText.
int GetLeading() const override
Get the height of the line.
int MacOSStringCompare(const char *s1, const char *s2)
Compares two strings using case insensitive natural sort.
Implementation of simple mapping class.
String iterator using CoreText as a backend.
static bool MacOSVersionIsAtLeast(long major, long minor, long bugfix)
Check if we are at least running on the specified version of Mac OS.
Visual run contains data about the bit of text with the same font.
void MacOSResetScriptCache(FontSize size)
Delete CoreText font reference for a specific font size.
static T max(const T a, const T b)
Returns the maximum of two values.
static uint GetGlyphWidth(FontSize size, WChar key)
Get the width of a glyph.
Iterate over characters (or more exactly grapheme clusters).
Visual run contains data about the bit of text with the same font.
A single line worth of VisualRuns.
static CTFontRef _font_cache[FS_END]
CoreText cache for font information, cleared when OTTD changes fonts.
A single line worth of VisualRuns.
Interface to glue fallback and normal layouter into one.
size_t Prev(IterType what) override
Move the cursor back by one iteration unit.
CFIndex cur_offset
Offset from the start of the current run from where to output.
IterType
Type of the iterator.
Functions related to localized text support on OSX.
size_t Next(IterType what) override
Advance the cursor by one iteration unit.
static CFLocaleRef _osx_locale
Cached current locale.
FontSize
Available font sizes.
Functions related to MacOS support.
Class for iterating over different kind of parts of a string.
static ParagraphLayouter * GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping)
Get the actual ParagraphLayout for the given buffer.
UniChar CharType
Helper for GetLayouter, to get the right type.
int GetWidth() const override
Get the width of this line.
static CGFloat SpriteFontGetWidth(void *ref_con)
Get the width of an encoded sprite font character.
uint32 GlyphID
Glyphs are characters from a font.
size_t SetCurPosition(size_t pos) override
Change the current string cursor.
uint32 WChar
Type for wide characters, i.e.
void MacOSSetCurrentLocaleName(const char *iso_code)
Store current language locale as a CoreFounation locale.