-
Notifications
You must be signed in to change notification settings - Fork 40
/
Copy pathFontRenderer+singleLineSize.swift
124 lines (95 loc) · 4.21 KB
/
FontRenderer+singleLineSize.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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//
// FontRenderer+singleLineSize.swift
// UIKit
//
// Created by Geordie Jay on 17.05.18.
// Copyright © 2018 flowkey. All rights reserved.
//
internal import SDL_ttf
extension FontRenderer {
// From `TTF_HANDLE_STYLE_BOLD`
var fontRequiresManualBoldHandling: Bool {
return (
rawPointer.pointee.style & TTF_STYLE_BOLD != 0
&& rawPointer.pointee.face_style & TTF_STYLE_BOLD == 0
)
}
// From `TTF_HANDLE_STYLE_UNDERLINE`
var fontRequiresManualUnderlineHandling: Bool {
return rawPointer.pointee.style & TTF_STYLE_UNDERLINE != 0
}
private var fontUnderlineTopYPosition: Int32 {
// With outline, the underline_offset is underline_offset + outline.
// So, we don't have to remove the top part of the outline height.
return rawPointer.pointee.ascent - rawPointer.pointee.underline_offset - 1
}
private var fontUnderlineBottomYPosition: Int32 {
let row = fontUnderlineTopYPosition + rawPointer.pointee.underline_height
return row + (rawPointer.pointee.outline * 2)
}
// From `FT_HAS_KERNING`
var fontHasKerning: Bool {
return (
rawPointer.pointee.face.pointee.face_flags & FT_FACE_FLAG_KERNING != 0
&& rawPointer.pointee.kerning != 0
)
}
func size(_ text: String) -> (width: Int32, height: Int32) {
var minX: Int32 = 0
var maxX: Int32 = 0
var minY: Int32 = 0
var maxY: Int32 = 0
let outlineWidth = rawPointer.pointee.outline
let outlineDelta = outlineWidth * 2 // could be 0
var x: Int32 = 0
var previousGlyphIndex: FT_UInt? = nil
// Load each character and sum its bounding box
for currentGlyph in freetypeGlyphs(in: text) {
x += getFontKerningOffset(between: previousGlyphIndex, and: currentGlyph.index)
previousGlyphIndex = currentGlyph.index
minX = min(minX, x + currentGlyph.minx)
if fontRequiresManualBoldHandling {
x += rawPointer.pointee.glyph_overhang
}
maxX = max(maxX, x + max(currentGlyph.advance, currentGlyph.maxx))
minY = min(minY, currentGlyph.miny)
maxY = max(maxY, currentGlyph.maxy)
x += currentGlyph.advance
}
let width = (maxX - minX) + outlineDelta
// Some fonts descend below font height (e.g. FletcherGothicFLF)
let fontHeight = rawPointer.pointee.height
let measuredHeight = max(fontHeight, (rawPointer.pointee.ascent - minY) + outlineDelta)
// Update height if necessary according to underline style
if fontRequiresManualUnderlineHandling {
return (width: width, height: max(fontUnderlineBottomYPosition, measuredHeight))
}
return (width: width, height: measuredHeight)
}
func freetypeGlyphs(in string: String) -> UnfoldSequence<c_glyph, String.UnicodeScalarView.Iterator> {
return sequence(state: string.unicodeScalars.makeIterator(), next: { [weak self] unicodeScalars -> c_glyph? in
guard let `self` = self else { return nil }
var characterCode: UInt32 = 0
while true {
guard let nextScalar = unicodeScalars.next() else { return nil }
characterCode = nextScalar.value
// Skip BOM characters:
if characterCode != UNICODE_BOM_NATIVE && characterCode != UNICODE_BOM_SWAPPED {
break
}
}
guard Find_Glyph(self.rawPointer, characterCode, CACHED_METRICS) == 0 else {
assertionFailure("Glyph \(characterCode) ('\(Character(UnicodeScalar(characterCode)!))') could not be found")
return nil
}
let glyph = self.rawPointer.pointee.current.pointee
let spaceCharacterCode = 32
let newLineCharacterCode = 10
if characterCode != spaceCharacterCode, characterCode != newLineCharacterCode, glyph.maxx - glyph.minx <= 0 {
assertionFailure("Glyph \(characterCode) ('\(Character(UnicodeScalar(characterCode)!))') has no width")
return nil
}
return glyph
})
}
}