Reputation: 609
I am trying to divide text into all the different parts it has though CGPathRefs. For example, L has one path and ? has two (the dot and the rest).
Currently, I am able to get each letter as a path however in the example of "?" the path contains both the dot and the rest in one. I want to divide them up but I can't seem to get it to work.
To divide text into paths I used some answer from stack overflow, it works well and here is the part where I got each letter as a path. Here I also run it through the "CGPathApply" function to look at each individual point
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
NSMutableArray *pathElements = [NSMutableArray array];
CGPathApply(letter, (__bridge void * _Nullable)(pathElements), getPointsFromBezier);
I then look at each point in the getPointsFromBezier
like this:
void getPointsFromBezier(void *info, const CGPathElement *element){
CGPoint *p = element->points;
NSString *name;
switch (element->type) {
case kCGPathElementMoveToPoint: name = @"kCGPathElementMoveToPoint"; break;
case kCGPathElementAddLineToPoint: name = @"kCGPathElementAddLineToPoint"; break;
case kCGPathElementAddQuadCurveToPoint: name = @"kCGPathElementAddQuadCurveToPoint"; break;
case kCGPathElementAddCurveToPoint: name = @"kCGPathElementAddCurveToPoint"; break;
case kCGPathElementCloseSubpath: name = @"kCGPathElementCloseSubpath"; break;
default: name = @"default"; break;
}
NSLog(@"Type: %@ || Point: %@", name, NSStringFromCGPoint(*p));
}
This gives me output like this:
Type: kCGPathElementMoveToPoint || Point: {11.75, 0}
Type: kCGPathElementAddLineToPoint || Point: {11.75, 9.729}
Type: kCGPathElementAddQuadCurveToPoint || Point: {11.75, 11.26}
..
..
Type: kCGPathElementAddQuadCurveToPoint || Point: {13.00, 10.52}
Type: kCGPathElementAddLineToPoint || Point: {13.00, 0}
Type: kCGPathElementCloseSubpath || Point: {13.00, 0}
Type: kCGPathElementMoveToPoint || Point: {12.75, 18.75}
Type: kCGPathElementAddQuadCurveToPoint || Point: {11.5, 18.94}
Type: kCGPathElementAddLineToPoint || Point: {11.5, 19.95}
Type: kCGPathElementAddQuadCurveToPoint || Point: {11.5, 20.41}
..
..
Type: kCGPathElementAddQuadCurveToPoint || Point: {12.75, 18.169}
Type: kCGPathElementCloseSubpath || Point: {12.75, 18.169}
The character I inputted here was ก็ and we can clearly see that it has the two different paths in the output (i.e. two kCGPathElementMoveToPoint and two kCGPathElementCloseSubpath).
I am not sure how I can properly or nicely create two separate CGPathRefs from this.
Upvotes: 1
Views: 844
Reputation: 609
Actually, while writing the question I almost solved it.
I create a static mutable CGPath, then I add points as I go in the function called through CGPathApply. Like this:
static CGMutablePathRef path;
void getPointsFromBezier(void *info, const CGPathElement *element){
CGPoint *p = element->points;
if ( element->type == kCGPathElementMoveToPoint ){
path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, p->x, p->y);
}
else if ( element->type == kCGPathElementAddLineToPoint ){
CGPathAddLineToPoint(path, nil, p->x, p->y);
}
else if ( element->type == kCGPathElementAddQuadCurveToPoint ){
CGPathAddQuadCurveToPoint(path, nil, p->x, p->y, p->x, p->y);
}
else if ( element->type == kCGPathElementAddCurveToPoint ){
// ...
}
else if ( element->type == kCGPathElementCloseSubpath ){
CGPathCloseSubpath(path);
[((__bridge NSMutableArray*)info) addObject:(__bridge id _Nonnull)(path)];
path = nil;
CGPathRelease(path);
}
}
Then I simply loop through each path in the original method and draw them instead of the original "letter" CGPath.
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
NSMutableArray *pathElements = [NSMutableArray array];
CGPathApply(letter, (__bridge void * _Nullable)(pathElements), getPointsFromBezier);
for (id e in pathElements) {
CGContextSaveGState(ctx);
CGContextTranslateCTM(ctx,position.x,0 - 40 + CGRectGetMidY(rect) - self.font.descender + position.y);
CGContextScaleCTM(ctx, 1, -1);
CGContextAddPath(ctx, (CGPathRef)e);
[[self r] setFill];
CGContextFillPath(ctx);
CGContextRestoreGState(ctx);
}
CGPathRelease(letter);
This works kind of. One issue is that I don't get the proper curves, so the resulting paths are not as smooth as they should be. I will update if I solve it.
This is the result so far and you can see that the image is a bit blocky compared to the bottom original one. However, they have two different colors which was the initial problem to solve.
UPDATE: I found that each point (element->points) contains the remaining data (x and y's) to properly draw the points. For example:
if ( element->type == kCGPathElementAddQuadCurveToPoint ){
CGPoint *p0 = &points[0]; // Control points for bezier path
CGPoint *p1 = &points[1]; // Actual points
CGPathAddQuadCurveToPoint(path,
nil,
p0->x,
p0->y,
p1->x,
p1->y);
}
This will draw each glyph properly.
Upvotes: 2