Search code examples
cocoacgpathnsbezierpath

How to get the path Quartz is using to stroke a NSBezierPath


A dashed line

I'm using this code to stroke a NSBezierPath with a wide, dashed, black line.

(c and strForBezier are defined somewhere else)

NSGlyph glyph;
for(n = 0; n < len; n++) {
    glyph = [font glyphWithName: [strForBezier substringWithRange:NSMakeRange(n, 1)]];
    [path appendBezierPathWithGlyph: glyph inFont: font];
}

CGFloat pattern2[4]; pattern2[0]=5*c; pattern2[1]=2*c; pattern2[2]=2*c; pattern2[3]=2*c;
[path setLineDash:pattern2 count:4 phase:0];

[path setLineWidth:c];
[path stroke];
[[NSColor blueColor] set ];
[path fill];

How can I get the black NSBezierPath ? I'm making the assumption that a NSBezierPath is built and filled to stroke the initial curve.


Solution

  • You can create a path by dashing and stroking an existing path, but that requires to use CGPathRef instead of NSBezierPath.

    CGMutablePathRef path0 = CGPathCreateMutable();
    CGAffineTransform transform = CGAffineTransformMakeTranslation(20, 20);
    // initial position is {20, 20}
    
    CGGlyph glyph;
    for(n = 0; n < len; n++)
    {
        glyph = [font glyphWithName: [strForBezier substringWithRange:NSMakeRange(n, 1)]];
        CGPathRef glyphPath = CTFontCreatePathForGlyph((__bridge CTFontRef) font, glyph, NULL);
        CGPathAddPath(path0, &transform, glyphPath);
        CGPathRelease(glyphPath);
    
        // append the glyph advance to the transform
        CGSize advance;
        CTFontGetAdvancesForGlyphs((__bridge CTFontRef) font, kCTFontDefaultOrientation, &glyph, &advance, 1);
        transform.tx += advance.width;
        transform.ty += advance.height;
    }
    
    CGFloat pattern2[4]; pattern2[0]=5*c; pattern2[1]=2*c; pattern2[2]=2*c; pattern2[3]=2*c;
    CGPathRef path1 = CGPathCreateCopyByDashingPath(path0, NULL, 0, pattern2, 4);
    CGPathRef path2 = CGPathCreateCopyByStrokingPath(path1, NULL, c, kCGLineCapButt, kCGLineJoinMiter, CGFLOAT_MAX);
    
    CGContextRef context = [NSGraphicsContext currentContext].graphicsPort;
    CGContextAddPath(context, path2);
    CGContextDrawPath(context, kCGPathFill);
    
    CGPathRelease(path0);
    CGPathRelease(path1);
    CGPathRelease(path2);
    

    If you're not comfortable with CGPathRef, you may create a NSBezierPath then convert it to a CGPathRef using the method described in Apple documentation Building a CGPathRef From a NSBezierPath Object (the reverse is also possible).