Skip to content

Commit 9d6e28e

Browse files
Merge pull request #56 from Shirk/core_text_optional_ligatures
Support optional ligatures in CoreText
2 parents 9b55300 + e138842 commit 9d6e28e

File tree

7 files changed

+422
-1723
lines changed

7 files changed

+422
-1723
lines changed

src/MacVim/English.lproj/Preferences.nib/designable.nib

Lines changed: 321 additions & 1719 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.

src/MacVim/MMAppController.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ + (void)initialize
208208
[NSNumber numberWithBool:NO], MMNoFontSubstitutionKey,
209209
[NSNumber numberWithBool:YES], MMLoginShellKey,
210210
[NSNumber numberWithInt:2], MMRendererKey,
211+
[NSNumber numberWithBool:NO], MMRendererLigaturesSupportKey,
211212
[NSNumber numberWithInt:MMUntitledWindowAlways],
212213
MMUntitledWindowKey,
213214
[NSNumber numberWithBool:NO], MMTexturedWindowKey,
@@ -1225,6 +1226,27 @@ - (IBAction)atsuiButtonClicked:(id)sender
12251226
[self rebuildPreloadCache];
12261227
}
12271228

1229+
- (IBAction)ligaturesButtonClicked:(id)sender
1230+
{
1231+
ASLogDebug(@"Toggle CoreText ligatures");
1232+
BOOL enable = ([sender state] == NSOnState);
1233+
1234+
// Update the user default MMRendererLigaturesSupport and synchronize the
1235+
// change so that any new Vim process will pick up on the changed setting.
1236+
CFPreferencesSetAppValue(
1237+
(CFStringRef)MMRendererLigaturesSupportKey,
1238+
(CFPropertyListRef)[NSNumber numberWithBool:enable],
1239+
kCFPreferencesCurrentApplication);
1240+
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
1241+
1242+
ASLogInfo(@"Use ligatures=%ld", enable);
1243+
1244+
// This action is called when the user clicks the "enable support for ligatures"
1245+
// button in the advanced preferences pane.
1246+
[self rebuildPreloadCache];
1247+
}
1248+
1249+
12281250
- (IBAction)loginShellButtonClicked:(id)sender
12291251
{
12301252
ASLogDebug(@"Toggle login shell option");

src/MacVim/MMCoreTextView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
float fontDescent;
3333
BOOL antialias;
34+
BOOL useLigatures;
3435
NSMutableArray *drawData;
3536

3637
MMTextViewHelper *helper;

src/MacVim/MMCoreTextView.m

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ - (id)initWithFrame:(NSRect)frame
132132
[self registerForDraggedTypes:[NSArray arrayWithObjects:
133133
NSFilenamesPboardType, NSStringPboardType, nil]];
134134

135+
// Check if ligatures should be used or not
136+
{
137+
Boolean val;
138+
Boolean keyValid;
139+
val = CFPreferencesGetAppBooleanValue((CFStringRef)MMRendererLigaturesSupportKey,
140+
kCFPreferencesCurrentApplication,
141+
&keyValid);
142+
143+
useLigatures = NO;
144+
if(keyValid) { useLigatures = val; }
145+
}
135146
return self;
136147
}
137148

@@ -1017,14 +1028,75 @@ - (void)batchDrawData:(NSData *)data
10171028
return newFontRef;
10181029
}
10191030

1031+
static void
1032+
ligatureGlyphsForChars(const unichar *chars, CGGlyph *glyphs, CGPoint *positions, UniCharCount *length, CTFontRef font )
1033+
{
1034+
/* CoreText has no simple wait of retrieving a ligature for a set of UniChars.
1035+
* The way proposed on the CoreText ML is to convert the text to an attributed
1036+
* string, create a CTLine from it and retrieve the Glyphs from the CTRuns in it.
1037+
*/
1038+
NSString *text = [NSString stringWithCharacters:chars
1039+
length:*length];
1040+
1041+
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
1042+
(id)font, kCTFontAttributeName,
1043+
// 2 - full ligatures including rare
1044+
[NSNumber numberWithInteger: 2], kCTLigatureAttributeName,
1045+
nil
1046+
];
1047+
1048+
NSAttributedString *attrText = [[NSAttributedString alloc] initWithString:text
1049+
attributes:attrs];
1050+
1051+
CGPoint refPos = positions[0];
1052+
1053+
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrText);
1054+
1055+
UniCharCount offset = 0;
1056+
NSArray *glyphRuns = (NSArray*)CTLineGetGlyphRuns(line);
1057+
1058+
for (id item in glyphRuns) {
1059+
CTRunRef run = (CTRunRef)item;
1060+
CFIndex count = CTRunGetGlyphCount(run);
1061+
1062+
if(count > 0) {
1063+
if(count - offset > *length) {
1064+
count = (*length) - offset;
1065+
}
1066+
}
1067+
1068+
CFRange range = CFRangeMake(0, count);
1069+
CTRunGetGlyphs(run, range, &glyphs[offset]);
1070+
CTRunGetPositions(run, range, &positions[offset]);
1071+
1072+
offset += count;
1073+
if(offset >= *length) {
1074+
// don't copy more glyphs then there is space for
1075+
break;
1076+
}
1077+
}
1078+
// fixup relative positioning
1079+
for( CFIndex i = 0; i < offset; ++i ) {
1080+
positions[i].x += refPos.x;
1081+
positions[i].y += refPos.y;
1082+
}
1083+
// as ligatures combine characters it is required to adjust the
1084+
// original length value
1085+
*length = offset;
1086+
}
1087+
10201088
static void
10211089
recurseDraw(const unichar *chars, CGGlyph *glyphs, CGPoint *positions,
10221090
UniCharCount length, CGContextRef context, CTFontRef fontRef,
1023-
NSMutableArray *fontCache)
1091+
NSMutableArray *fontCache, BOOL useLigatures)
10241092
{
1025-
10261093
if (CTFontGetGlyphsForCharacters(fontRef, chars, glyphs, length)) {
10271094
// All chars were mapped to glyphs, so draw all at once and return.
1095+
if (useLigatures) {
1096+
memset(glyphs, 0, sizeof(CGGlyph) * length);
1097+
ligatureGlyphsForChars(chars, glyphs, positions, &length, fontRef);
1098+
}
1099+
10281100
CGFontRef cgFontRef = CTFontCopyGraphicsFont(fontRef, NULL);
10291101
CGContextSetFont(context, cgFontRef);
10301102
CGContextShowGlyphsAtPositions(context, glyphs, positions, length);
@@ -1077,7 +1149,7 @@ - (void)batchDrawData:(NSData *)data
10771149
}
10781150

10791151
recurseDraw(chars, glyphs, positions, attemptedCount, context,
1080-
fallback, fontCache);
1152+
fallback, fontCache, useLigatures);
10811153

10821154
// If only a portion of the invalid range was rendered above,
10831155
// the remaining range needs to be attempted by subsequent
@@ -1201,7 +1273,7 @@ - (void)drawString:(const UniChar *)chars length:(UniCharCount)length
12011273
}
12021274

12031275
CGContextSetTextPosition(context, x, y+fontDescent);
1204-
recurseDraw(chars, glyphs, positions, length, context, fontRef, fontCache);
1276+
recurseDraw(chars, glyphs, positions, length, context, fontRef, fontCache, useLigatures);
12051277

12061278
CFRelease(fontRef);
12071279
CGContextRestoreGState(context);

src/MacVim/MacVim.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ extern NSString *MMNoWindowKey;
274274
extern NSString *MMAutosaveRowsKey;
275275
extern NSString *MMAutosaveColumnsKey;
276276
extern NSString *MMRendererKey;
277+
extern NSString *MMRendererLigaturesSupportKey;
277278

278279
enum {
279280
MMRendererDefault = 0,

src/MacVim/MacVim.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
NSString *MMAutosaveRowsKey = @"MMAutosaveRows";
117117
NSString *MMAutosaveColumnsKey = @"MMAutosaveColumns";
118118
NSString *MMRendererKey = @"MMRenderer";
119+
NSString *MMRendererLigaturesSupportKey = @"MMRendererLigaturesSupport";
119120

120121
// Vim find pasteboard type (string contains Vim regex patterns)
121122
NSString *VimFindPboardType = @"VimFindPboardType";

0 commit comments

Comments
 (0)