【问题标题】:Get path to trace out a character in an iOS UIFont获取在 iOS UIFont 中追踪字符的路径
【发布时间】:2012-06-25 16:44:24
【问题描述】:

假设我在我的 iOS 应用程序中使用了一个自定义字体“Foo”。我已将它添加到我的项目、plist 等中,并且我可以用它来渲染 UILabels 等。

现在,如果我想找出可以“追踪”该字体中字母“P”的点序列,我将如何获得该点序列?例如,假设我想使用CGPath 像绘图仪一样缓慢地绘制字母“P”......

我只需要一个数组中的 CGPoints 序列,如果将其绘制为路径将绘制一个“P”。

我意识到对于一些像“T”这样的字母,这可能没有意义,因为必须抬起笔才能越过“T”。所以也许我需要一系列CGPaths ...

关于如何做到这一点的任何提示?

谢谢!

【问题讨论】:

标签: iphone ios core-graphics uifont cgpath


【解决方案1】:

从字母“P”到一系列点涉及几个步骤。您需要使用 Core Text。

  1. 创建一个CTFont。从 iOS 7 开始,您可以在需要 CTFont 的地方使用 UIFont(它们是“免费桥接”)。您还可以使用CTFontCreateWithGraphicsFont 函数直接从CGFont 创建CTFont,或者使用CTFontCreateWithName(或使用其他一些方法)按名称创建。

  2. 使用CTFontGetGlyphsForCharacters 函数获取字母的字形。对于字母“P”,应该只有一个字形。对于非英语脚本中的某些字符,您可能会得到多个(组合)字形。

  3. 使用CTFontCreatePathForGlyph 函数为字形获取CGPath。

  4. 使用CGPathApply枚举路径的元素。

  5. 将路径的每条直线、四边形曲线和三次曲线元素转换为一系列点。 Apple 没有为此提供任何公共 API。你需要自己做。对于直线元素,这很容易。对于曲线元素,如果您还不知道如何渲染贝塞尔曲线,则需要进行一些研究。例如,请参阅convert bezier curve to polygonal chain?。

我们可以在 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>

【讨论】:

  • @RobMayoff 很好的答案!正是我也在寻找的东西!既然您似乎知道您的 CGPaths,您知道我如何在刚刚创建的路径中添加一行吗?比如说回到起点。只需添加:let pathP = CGPathCreateMutableCopy(CGPathCreateMutableCopy(cgpath)) CGPathAddLineToPoint(pathP, nil, 0, 0)似乎不起作用。`
  • “似乎不起作用”是什么意思?您需要发布一个新问题,其中包含您的代码、输出(转储您修改的路径),并解释输出有什么问题。
  • 我在我的项目中使用了你的代码,我得到了字母“P”,但它被翻转了。我打印了我的图层 contentsAreFlipped 属性,它返回 true。我该如何解决这个问题? let line : CAShapeLayer = CAShapeLayer() line.path = path.cgPath line.strokeColor = UIColor.blue.cgColor line.fillColor = UIColor.clear.cgColor line.position = self.view.center self.view.layer.addSublayer(行)
  • 已修复,我添加了此代码 line.isGeometryFlipped = true。
【解决方案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
    }

【讨论】:

    【解决方案3】:

    rob 为 swift 4 + CoreGraphics 的垂直翻转更新的答案使用倒置坐标系:

        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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-23
      • 2017-11-11
      • 1970-01-01
      • 2014-08-05
      • 1970-01-01
      • 2020-09-09
      • 2012-02-13
      相关资源
      最近更新 更多