21

iOS アプリで使用しているカスタム フォント「Foo」があるとします。私は自分のプロジェクト、plistなどにそれを追加しました.sなどをUILabelうまくレンダリングできます。

では、そのフォントの文字「P」を「トレース」するポイントのシーケンスを見つけたい場合、そのポイントのシーケンスを取得するにはどうすればよいでしょうか? たとえば、a を使用しCGPathて、プロッターのようにゆっくりと文字 'P' を描画したいとします...

CGPointsパスとして描画された場合に「P」を描画する配列内のシーケンスが必要なだけです。

「T」のような一部の文字では、「T」を横切るためにペンを持ち上げる必要があるため、これは意味をなさない可能性があることを認識しています. だから多分私はCGPathsのシーケンスが必要です...

これを達成する方法に関するヒントはありますか?

ありがとう!

4

3 に答える 3

40

文字「P」から一連のポイントに移動するには、いくつかの手順が必要です。Core Text を使用する必要があります。

  1. を作成しますCTFontUIFontiOS 7 以降、 a が必要な場所で aを使用できますCTFont(それらは「無料でブリッジ」されています)。関数を使用してaCTFontから直接作成することも、名前を使用して(または他のいくつかの方法を使用して) 作成することもできます。CGFontCTFontCreateWithGraphicsFontCTFontCreateWithName

  2. 関数を使用して文字のグリフを取得しCTFontGetGlyphsForCharactersます。文字「P」の場合、グリフは 1 つだけにする必要があります。英語以外のスクリプトの一部の文字では、複数の (組み合わせた) グリフが得られる場合があります。

  3. CTFontCreatePathForGlyph関数を使用しCGPathて、グリフの を取得します。

  4. CGPathApplyパスの要素を列挙するために使用します。

  5. パスの各ライン、クアッド カーブ、キュービック カーブ要素を一連のポイントに変換します。Apple は、これを行うためのパブリック API を提供していません。自分で行う必要があります。直線要素の場合は簡単です。曲線要素の場合、ベジエ曲線をレンダリングする方法をまだ知らない場合は、調査を行う必要があります。たとえば、ベジエ曲線をポリゴン チェーンに変換するを参照してください。.

これは、Swift プレイグラウンドで簡単に試すことができます。

import UIKit
import CoreText
import XCPlayground

let font = UIFont(name: "HelveticaNeue", size: 64)!

var unichars = [UniChar]("P".utf16)
var glyphs = [CGGlyph](count: unichars.count, repeatedValue: 0)
let gotGlyphs = CTFontGetGlyphsForCharacters(font, &unichars, &glyphs, unichars.count)
if gotGlyphs {
    let cgpath = CTFontCreatePathForGlyph(font, glyphs[0], nil)!
    let path = UIBezierPath(CGPath: cgpath)
    print(path)
    XCPlaygroundPage.currentPage.captureValue(path, withIdentifier: "glyph \(glyphs[0])")
}

結果:

<UIBezierPath: 0x7fbc89e0d370; <MoveTo {11.072000000000001, 23.808}>,
 <LineTo {11.072000000000001, 40.576000000000001}>,
 <LineTo {22.975999999999999, 40.576000000000001}>,
 <QuadCurveTo {30.560000000000002, 38.432000000000002} - {28.16, 40.576000000000001}>,
 <QuadCurveTo {32.960000000000001, 32.192} - {32.960000000000001, 36.288000000000004}>,
 <QuadCurveTo {30.560000000000002, 25.920000000000002} - {32.960000000000001, 28.096}>,
 <QuadCurveTo {22.975999999999999, 23.808} - {28.16, 23.744}>,
 <Close>,
 <MoveTo {4.992, 45.695999999999998}>,
 <LineTo {4.992, 0}>,
 <LineTo {11.072000000000001, 0}>,
 <LineTo {11.072000000000001, 18.687999999999999}>,
 <LineTo {25.024000000000001, 18.687999999999999}>,
 <QuadCurveTo {35.488, 22.208000000000002} - {31.936, 18.623999999999999}>,
 <QuadCurveTo {39.039999999999999, 32.192} - {39.039999999999999, 25.792000000000002}>,
 <QuadCurveTo {35.488, 42.143999999999998} - {39.039999999999999, 38.591999999999999}>,
 <QuadCurveTo {25.024000000000001, 45.695999999999998} - {31.936, 45.695999999999998}>,
 <Close>

P グリフ パス

于 2012-06-23T20:00:33.180 に答える
3

Swift 4 + CoreGraphics の垂直反転用に更新された rob の回答では、反転座標系が使用されます。

    var unichars = [UniChar]("P".utf16)
    var glyphs = [CGGlyph](repeating: 0, count: unichars.count)
    let gotGlyphs = CTFontGetGlyphsForCharacters(font, &unichars, &glyphs, unichars.count)
    if gotGlyphs {
        let cgpath = CTFontCreatePathForGlyph(font, glyphs[0], nil)!
        var inverse = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -cgpath.boundingBox.height - 1)
        let path = UIBezierPath(cgPath: cgpath.copy(using: &inverse)!)
        print(path)
于 2019-03-01T15:02:15.833 に答える
2

Objective Cで同じソリューションを探している人向け

    UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:64];
    CGFontRef fontref = CGFontCreateWithFontName((__bridge CFStringRef)font.fontName);
    NSString *unichars = @"I";
    CFStringRef yourFriendlyCFString = (__bridge CFStringRef)unichars;
    CGGlyph glyphs = CGFontGetGlyphWithGlyphName(fontref, yourFriendlyCFString);
    CTFontRef fontCT = CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL);
    CGPathRef cgpath = CTFontCreatePathForGlyph(fontCT, glyphs, nil);
    UIBezierPath *path = [UIBezierPath bezierPathWithCGPath:cgpath];
    NSLog(@"Complete path For p is %@", path);
    CGPathApply(cgpath, (__bridge void * _Nullable)(bezierPoints), MyCGPathApplierFunc);
    NSLog(@"Points on path %@", bezierPoints);
    for(NSValue *point in bezierPoints){

       //Do your work
    }
于 2016-09-27T11:43:19.223 に答える