CTRunDraw() does implement correct behaviour

428 views Asked by At

I am implementing a custom text layout algorithm on MAC OS X using CoreText. I have to partially render a portion of CTRun in different locations inside a custom NSView subclass object.

Here is my implementation of drawRect: method

- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
CGContextRef context =
(CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(context); {
    [[NSColor whiteColor] set];
    NSRectFill(dirtyRect);


    CTFontRef font = CTFontCreateWithName(CFSTR("Menlo"), 20, &CGAffineTransformIdentity);

    CFTypeRef values[] = {font};
    CFStringRef keys[] = {kCTFontAttributeName};

    CFDictionaryRef dictionary =
    CFDictionaryCreate(NULL,
                       (const void **)&keys,
                       (const void **)&values,
                       sizeof(keys) / sizeof(keys[0]),
                       &kCFTypeDictionaryKeyCallBacks,
                       &kCFTypeDictionaryValueCallBacks);

    CFAttributedStringRef longString =
    CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("this_is_a_very_long_string_that_compromises_many_glyphs,we_wil_see_it:)"), dictionary);
    CTLineRef lineRef = CTLineCreateWithAttributedString(longString);

    CFArrayRef runsArray = CTLineGetGlyphRuns(lineRef);
    CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runsArray, 0);

    CGAffineTransform textTransform = CGAffineTransformIdentity;
    textTransform = CGAffineTransformScale(textTransform, 1.0, -1.0);
    CGContextSetTextMatrix(context, textTransform);

    CGAffineTransform sequenceTransform =
    CGAffineTransformIdentity;
    sequenceTransform = CGAffineTransformTranslate(sequenceTransform, 0, 23.2818);


    CGPoint firstPoint = CGPointApplyAffineTransform(CGPointMake(0, 0), sequenceTransform);
    CFRange firstRange = CFRangeMake(0, 24);
    CGContextSetTextPosition(context, firstPoint.x, firstPoint.y);
    CTRunDraw(run, context, firstRange);

    CGPoint secondPoint = CGPointApplyAffineTransform(CGPointMake(0, 26.2812), sequenceTransform);
    CFRange secondRange = CFRangeMake(24, 24);
    CGContextSetTextPosition(context, secondPoint.x, secondPoint.y);
    CTRunDraw(run, context, secondRange);

    CGPoint thirdPoint = CGPointApplyAffineTransform(CGPointMake(0, 52.5625), sequenceTransform);
    CFRange thirdRange = CFRangeMake(48, 23);
    CGContextSetTextPosition(context, thirdPoint.x, thirdPoint.y);
    CTRunDraw(run, context, thirdRange);

}
CGContextRestoreGState(context);

}

Here is the output of this code https://docs.google.com/open?id=0B8df1OdxKw4FYkE5Z1d1VUZQYWs

The problem is CTRunDraw() method inserts blank spaces on the positions other than the range specified.

What i want is it should render the part of run at its correct position. Here is the correct out put which i want.(The correct output is photoshop of original output). https://docs.google.com/open?id=0B8df1OdxKw4FcFRnS0p1cFBfa28

Note: I am using flipped coordinate system in my custom NSView subclass.

- (BOOL)isFlipped {
return YES;

}

1

There are 1 answers

0
al45tair On

You're misusing CTRun here. A CTRun is a horizontal box containing laid-out glyphs. It doesn't really make sense to try to draw portions of it underneath one another (certainly such typesetting would be wrong in any even slightly complicated case). Why? Well, because there are situations where the glyphs that are chosen might differ if there was a line break at a given position (this can happen, for instance, with ligatures). Also, note that there is not necessarily a 1:1 mapping between characters and glyphs.

My guess is that you probably don’t need your own full custom typesetter (and trust me, writing one is complicated, so if you don’t need one, don’t write one). Instead, just use CTTypesetter and/or CTFramesetter to get the output you want.