-
Notifications
You must be signed in to change notification settings - Fork 5
/
String+BezierPath.swift
79 lines (60 loc) · 2.72 KB
/
String+BezierPath.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//
// String+BezierPath.swift
// BezierPathSample
//
// Created by Volodymyr Boichentsov on 16/11/2017.
// Copyright © 2017 Volodymyr Boichentsov. All rights reserved.
//
import Foundation
import CoreText
func bridge<T : AnyObject>(obj : T) -> UnsafeRawPointer {
return UnsafeRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}
func bridge<T : AnyObject>(ptr : UnsafeRawPointer) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
}
func bridgeRetained<T : AnyObject>(obj : T) -> UnsafeRawPointer {
return UnsafeRawPointer(Unmanaged.passRetained(obj).toOpaque())
}
func bridgeTransfer<T : AnyObject>(ptr : UnsafeRawPointer) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()
}
extension String {
func bezierPath(font:OSFont) -> OSBezierPath {
let ctFont = CTFontCreateWithName((font.familyName as CFString?)!, font.pointSize, nil)
let attributed = NSAttributedString.init(string: self, attributes: [NSAttributedString.Key(rawValue: kCTFontAttributeName as String as String): ctFont])
let letters = CGMutablePath()
let line = CTLineCreateWithAttributedString(attributed)
let runArray = CTLineGetGlyphRuns(line)
for runIndex in 0..<CFArrayGetCount(runArray) {
let runRef = CFArrayGetValueAtIndex(runArray, runIndex)
let run:CTRun = bridge(ptr: runRef!)
let atts = CTRunGetAttributes(run)
let key = kCTFontAttributeName
let keyPtr = bridgeRetained(obj: key)
let runFontPtr = CFDictionaryGetValue(atts, keyPtr)
let runFont:CTFont = bridge(ptr: runFontPtr!)
for runGlyphIndex in 0..<CTRunGetGlyphCount(run) {
let thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
var glyph = CGGlyph()
var position = CGPoint()
CTRunGetGlyphs(run, thisGlyphRange, &glyph);
CTRunGetPositions(run, thisGlyphRange, &position);
let letter = CTFontCreatePathForGlyph(runFont, glyph, nil);
let t = CGAffineTransform(translationX: position.x, y: position.y);
if letter != nil {
letters.addPath(letter!, transform: t)
}
}
}
let path = OSBezierPath.init(cgPath:letters)
let boundingBox = letters.boundingBox;
#if os(macOS)
// The path is upside down (CG coordinate system)
path.transform(using: OSAffineTransform.init(scaleByX: 1.0, byY: -1.0))
path.transform(using: OSAffineTransform.init(translationByX: 0.0, byY: boundingBox.size.height))
#endif
return path
// return OSBezierPath.init()
}
}