CGPathRef from string
Asked Answered
U

2

7

Please how can we get path of particular arabic of french letter ? I've just found out that CTFontCreatePathForGlyph will give CGPathRef like, but its will be the outline of text .

I need this real text path for showing a text drawing animation..

any help please

Uvea answered 2/4, 2012 at 12:29 Comment(3)
Why you want to convert Path to string?? U can directly animate the task on the returned path :)Feeze
i won't convert am looking to get a the path of a specific string my objectif is to make an application for my project school like letter school or i writewordUvea
to explain look at this app letterschool.com i wanna the same think as they do it to write letter "Sorry for my bed english"Uvea
F
11

You dont require ur path to be converted into NSString at all.

You can create the path for text as follows:

    CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica-Bold"), 72.0f, NULL);
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
                       (id)font, kCTFontAttributeName,
                       nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:@"Hello World!"
                                                                 attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
CFArrayRef runArray = CTLineGetGlyphRuns(line);

// for each RUN
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
{
    // Get FONT for this run
    CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
    CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);

    // for each GLYPH in run
    for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++) 
    {
        // get Glyph & Glyph-data
        CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
        CGGlyph glyph;
        CGPoint position;
        CTRunGetGlyphs(run, thisGlyphRange, &glyph);
        CTRunGetPositions(run, thisGlyphRange, &position);

        // Get PATH of outline
        {
            CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
            CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
            CGPathAddPath(letters, &t, letter);
            CGPathRelease(letter);
        }
    }
}
CFRelease(line);

This is how you create a path, for sample code please refer this link. This code is a part of this sample project. Hope this helps you

Feeze answered 3/4, 2012 at 4:23 Comment(3)
thanks :) but using this method we will get the outline path.Uvea
Ya I know.. But this is the way you can get your work done. Obviously you need to change the code to accomplish your requirements.Feeze
how i can change it to get the right result for example letter A with you're method is composed from 12lines but in real is 3lines sorry for my bad englishUvea
J
6

I needed this in Swift but it was painful to work out. I hope this is useful to someone else! I've written it as extensions to String and NSAttributedString to be more versatile.

You might see the letters as upside-down depending on how you're drawing your path. You can fix this by adding a vertical flip transform to the call to CTFontCreatePathForGlyph() (a vertical flip is just a CGAffineTransform with a scaleY of -1).

public extension String {
    func path(withFont font: UIFont) -> CGPath {
        let attributedString = NSAttributedString(string: self, attributes: [.font: font])
        let path = attributedString.path()
        return path
    }
}

public extension NSAttributedString {
    func path() -> CGPath {
        let path = CGMutablePath()

        // Use CoreText to lay the string out as a line
        let line = CTLineCreateWithAttributedString(self as CFAttributedString)

        // Iterate the runs on the line
        let runArray = CTLineGetGlyphRuns(line)
        let numRuns = CFArrayGetCount(runArray)
        for runIndex in 0..<numRuns {

            // Get the font for this run
            let run = unsafeBitCast(CFArrayGetValueAtIndex(runArray, runIndex), to: CTRun.self)
            let runAttributes = CTRunGetAttributes(run) as Dictionary
            let runFont = runAttributes[kCTFontAttributeName] as! CTFont

            // Iterate the glyphs in this run
            let numGlyphs = CTRunGetGlyphCount(run)
            for glyphIndex in 0..<numGlyphs {
                let glyphRange = CFRangeMake(glyphIndex, 1)

                // Get the glyph
                var glyph : CGGlyph = 0
                withUnsafeMutablePointer(to: &glyph) { glyphPtr in
                    CTRunGetGlyphs(run, glyphRange, glyphPtr)
                }

                // Get the position
                var position : CGPoint = .zero
                withUnsafeMutablePointer(to: &position) {positionPtr in
                    CTRunGetPositions(run, glyphRange, positionPtr)
                }

                // Get a path for the glyph
                guard let glyphPath = CTFontCreatePathForGlyph(runFont, glyph, nil) else {
                    continue
                }

                // Transform the glyph as it is added to the final path
                let t = CGAffineTransform(translationX: position.x, y: position.y)
                path.addPath(glyphPath, transform: t)
            }
        }

        return path
    }
}
Joy answered 6/4, 2019 at 19:49 Comment(1)
Solved my life! Works awesome.Harquebus

© 2022 - 2024 — McMap. All rights reserved.