@@ -1039,6 +1039,52 @@ - (void)batchDrawData:(NSData *)data
10391039 return newFontRef;
10401040}
10411041
1042+ static CFAttributedStringRef
1043+ attributedStringForString (NSString *string, const CTFontRef font, BOOL useLigatures)
1044+ {
1045+ NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
1046+ (id )font, kCTFontAttributeName ,
1047+ // 2 - full ligatures including rare
1048+ // 0 - no ligatures
1049+ [NSNumber numberWithInteger: (useLigatures) ? 2 : 0 ], kCTLigatureAttributeName ,
1050+ nil
1051+ ];
1052+
1053+ return CFAttributedStringCreate (NULL , string, attrs);
1054+ }
1055+
1056+ static UniCharCount
1057+ fetchGlyphsAndAdvances (const CTLineRef line, CGGlyph *glyphs, CGSize *advances, UniCharCount length)
1058+ {
1059+ NSArray *glyphRuns = (NSArray *)CTLineGetGlyphRuns (line);
1060+
1061+ // get a hold on the actual character widths and glyphs in line
1062+ UniCharCount offset = 0 ;
1063+ for (id item in glyphRuns) {
1064+ CTRunRef run = (CTRunRef)item;
1065+ CFIndex count = CTRunGetGlyphCount (run);
1066+
1067+ if (count > 0 && count - offset > length) {
1068+ count = length - offset;
1069+ }
1070+
1071+ CFRange range = CFRangeMake (0 , count);
1072+
1073+ if ( glyphs != NULL ) {
1074+ CTRunGetGlyphs (run, range, &glyphs[offset]);
1075+ }
1076+ if ( advances != NULL ) {
1077+ CTRunGetAdvances (run, range, &advances[offset]);
1078+ }
1079+
1080+ offset += count;
1081+ if (offset >= length) {
1082+ break ;
1083+ }
1084+ }
1085+ return offset;
1086+ }
1087+
10421088 static UniCharCount
10431089gatherGlyphs (CGGlyph glyphs[], UniCharCount count)
10441090{
@@ -1062,55 +1108,59 @@ - (void)batchDrawData:(NSData *)data
10621108 * The way proposed on the CoreText ML is to convert the text to an attributed
10631109 * string, create a CTLine from it and retrieve the Glyphs from the CTRuns in it.
10641110 */
1065- NSString *text = [NSString stringWithCharacters: chars
1066- length: *length];
1111+ NSString *plainText = [NSString stringWithCharacters: chars length: *length];
10671112
1068- NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
1069- (id )font, kCTFontAttributeName ,
1070- // 2 - full ligatures including rare
1071- [NSNumber numberWithInteger: 2 ], kCTLigatureAttributeName ,
1072- nil
1073- ];
1113+ CFAttributedStringRef regularText = attributedStringForString (plainText, font, NO );
1114+ CFAttributedStringRef ligatureText = attributedStringForString (plainText, font, YES );
10741115
1075- NSAttributedString *attrText = [[ NSAttributedString alloc ] initWithString: text
1076- attributes: attrs] ;
1116+ CGPoint refPositions[*length];
1117+ memcpy (refPositions, positions, sizeof (CGSize) * (*length)) ;
10771118
1078- CGPoint refPos = positions[0 ];
1119+ CTLineRef regular = CTLineCreateWithAttributedString ((CFAttributedStringRef)regularText);
1120+ CTLineRef ligature = CTLineCreateWithAttributedString ((CFAttributedStringRef)ligatureText);
10791121
1080- CTLineRef line = CTLineCreateWithAttributedString ((CFAttributedStringRef)attrText);
1122+ CFRelease (regularText);
1123+ CFRelease (ligatureText);
10811124
1082- UniCharCount offset = 0 ;
1083- NSArray *glyphRuns = (NSArray *)CTLineGetGlyphRuns (line);
1125+ CGSize ligatureRanges[*length], regularRanges[*length];
10841126
1085- for (id item in glyphRuns) {
1086- CTRunRef run = (CTRunRef)item;
1087- CFIndex count = CTRunGetGlyphCount (run);
1127+ // get the (ligature)glyphs and advances for the new text
1128+ UniCharCount offset = fetchGlyphsAndAdvances (ligature, glyphs, ligatureRanges, length);
1129+ // do the same but only for the advances for the base text
1130+ fetchGlyphsAndAdvances (regular, NULL , regularRanges, length);
10881131
1089- if (count > 0 ) {
1090- if (count - offset > *length) {
1091- count = (*length) - offset;
1092- }
1132+ // tricky part: compare both advance ranges and chomp positions which
1133+ // are covered by a single ligature
1134+ #define fequal (a, b ) (fabs( (a) - (b) ) < FLT_EPSILON)
1135+ CFIndex skip = 0 ;
1136+ for ( CFIndex i = 0 ; i < offset && skip + i < *length; ++i ) {
1137+ memcpy (&positions[i], &refPositions[skip + i], sizeof (CGSize));
1138+
1139+ if ( fequal (ligatureRanges[i].width , regularRanges[skip + i].width ) ) {
1140+ // [mostly] same width
1141+ continue ;
10931142 }
10941143
1095- CFRange range = CFRangeMake (0 , count);
1096- CTRunGetGlyphs (run, range, &glyphs[offset]);
1097- CTRunGetPositions (run, range, &positions[offset]);
1144+ // no, that's a ligature
1145+ // count how many positions this glyph would take up in the base text
1146+ CFIndex j = 0 ;
1147+ float width = ceil (regularRanges[skip + i].width );
10981148
1099- offset += count;
1100- if (offset >= *length) {
1101- // don't copy more glyphs then there is space for
1102- break ;
1149+ while ( ( int )width < ( int )ligatureRanges[i]. width
1150+ && skip + i + j < *length )
1151+ {
1152+ width += ceil (regularRanges[++j + skip + i]. width ) ;
11031153 }
1154+ skip += j;
11041155 }
1105- // fixup relative positioning
1106- CFIndex i;
1107- for ( i = 0 ; i < offset; ++i ) {
1108- positions[i].x += refPos.x ;
1109- positions[i].y += refPos.y ;
1110- }
1156+ #undef fequal
1157+
11111158 // as ligatures combine characters it is required to adjust the
11121159 // original length value
11131160 *length = offset;
1161+
1162+ CFRelease (regular);
1163+ CFRelease (ligature);
11141164}
11151165
11161166 static void
0 commit comments