mirror of
https://github.com/zhigang1992/CurvyText.git
synced 2026-04-30 10:22:34 +08:00
Handle combining characters
This commit is contained in:
@@ -48,16 +48,45 @@ public struct PathText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutating private func updatePositions() {
|
mutating private func updatePositions() {
|
||||||
let line = CTLineCreateWithAttributedString(text)
|
let layoutManager = NSLayoutManager()
|
||||||
|
let textStorage = NSTextStorage(attributedString: text)
|
||||||
|
let textContainer = NSTextContainer()
|
||||||
|
layoutManager.addTextContainer(textContainer)
|
||||||
|
textStorage.addLayoutManager(layoutManager)
|
||||||
|
|
||||||
|
var positions: [GlyphPosition] = []
|
||||||
|
|
||||||
|
var characterIndex = 0
|
||||||
|
|
||||||
|
while characterIndex < textStorage.length {
|
||||||
|
let string = textStorage.string as NSString
|
||||||
|
let characterRange = string.rangeOfComposedCharacterSequence(at: characterIndex)
|
||||||
|
var actualCharacterRange = NSRange()
|
||||||
|
let glyphRange = layoutManager.glyphRange(forCharacterRange: characterRange, actualCharacterRange: &actualCharacterRange)
|
||||||
|
assert(characterRange == actualCharacterRange) // It shouldn't be possible for this to mismatch since we composed the character already
|
||||||
|
|
||||||
|
let glyphString = textStorage.attributedSubstring(from: characterRange)
|
||||||
|
|
||||||
|
let font = glyphString.attribute(.font, at: 0, effectiveRange: nil) as? PlatformFont
|
||||||
|
?? .systemFont(ofSize: PlatformFont.systemFontSize)
|
||||||
|
|
||||||
|
let baseline = font.descender
|
||||||
|
|
||||||
|
let line = CTLineCreateWithAttributedString(glyphString)
|
||||||
|
let origin = layoutManager.location(forGlyphAt: glyphRange.location)
|
||||||
|
let bounds = CTLineGetBoundsWithOptions(line, [])
|
||||||
|
|
||||||
|
let position = GlyphPosition(attributedString: glyphString,
|
||||||
|
baseline: baseline,
|
||||||
|
rect: CGRect(origin: origin, size: CGSize(width: bounds.width, height: bounds.height)))
|
||||||
|
|
||||||
|
print(position)
|
||||||
|
positions.append(position)
|
||||||
|
characterIndex = NSMaxRange(characterRange)
|
||||||
|
|
||||||
|
|
||||||
glyphPositions = line.glyphRuns.flatMap { run -> [GlyphPosition] in
|
|
||||||
let baseline = run.font.descender
|
|
||||||
return run.glyphCharacterMapping.map { (glyphRange, characterRange) in
|
|
||||||
GlyphPosition(attributedString: text.attributedSubstring(from: characterRange),
|
|
||||||
baseline: baseline,
|
|
||||||
rect: run.typographicFrame(glyphRange: glyphRange))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.glyphPositions = positions
|
||||||
updateRuns()
|
updateRuns()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +120,7 @@ extension PathText: View {
|
|||||||
ForEach(runs) { run in
|
ForEach(runs) { run in
|
||||||
Text(verbatim: run.position.attributedString.string)
|
Text(verbatim: run.position.attributedString.string)
|
||||||
.font(.system(size: 48))
|
.font(.system(size: 48))
|
||||||
|
.frame(width: run.position.rect.size.width, height: run.position.rect.size.height, alignment: .bottom)
|
||||||
.padding(EdgeInsets(top: -run.position.baseline, leading: 0, bottom: run.position.baseline, trailing: 0))
|
.padding(EdgeInsets(top: -run.position.baseline, leading: 0, bottom: run.position.baseline, trailing: 0))
|
||||||
// .border(Color.green)
|
// .border(Color.green)
|
||||||
.rotationEffect(.radians(run.angle), anchor: .bottom)
|
.rotationEffect(.radians(run.angle), anchor: .bottom)
|
||||||
@@ -110,7 +140,7 @@ extension PathText: View {
|
|||||||
@available(iOS 13.0.0, *)
|
@available(iOS 13.0.0, *)
|
||||||
struct PathText_Previews: PreviewProvider {
|
struct PathText_Previews: PreviewProvider {
|
||||||
static let text: NSAttributedString = {
|
static let text: NSAttributedString = {
|
||||||
let string = NSString("You can display text along a curve, with bold, color, and big text.")
|
let string = NSString("You can d\u{030a}isplay text along a curve, with bold, color, and big text.")
|
||||||
|
|
||||||
let s = NSMutableAttributedString(string: string as String,
|
let s = NSMutableAttributedString(string: string as String,
|
||||||
attributes: [.font: UIFont.systemFont(ofSize: 48)])
|
attributes: [.font: UIFont.systemFont(ofSize: 48)])
|
||||||
@@ -234,10 +264,10 @@ struct PathText_Previews: PreviewProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return VStack {
|
return VStack {
|
||||||
Text("X\u{030A}")
|
Text("ÅX̊") // "X\u{030A}")
|
||||||
.font(.system(size: 48))
|
.font(.system(size: 48))
|
||||||
ZStack {
|
ZStack {
|
||||||
PathText(text: NSAttributedString(string: "X\u{030A}",
|
PathText(text: NSAttributedString(string: "ÅX̊Z",
|
||||||
attributes: [.font: UIFont.systemFont(ofSize: 48)]), path: path)
|
attributes: [.font: UIFont.systemFont(ofSize: 48)]), path: path)
|
||||||
path.stroke(Color.blue, lineWidth: 2)
|
path.stroke(Color.blue, lineWidth: 2)
|
||||||
}
|
}
|
||||||
@@ -245,12 +275,12 @@ struct PathText_Previews: PreviewProvider {
|
|||||||
}
|
}
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
Group {
|
Group {
|
||||||
// CurveView()
|
CurveView()
|
||||||
// LineView()
|
LineView()
|
||||||
// LinesView()
|
LinesView()
|
||||||
// LineAndCurveView()
|
LineAndCurveView()
|
||||||
// QuadCurveView()
|
QuadCurveView()
|
||||||
// RoundedRectView()
|
RoundedRectView()
|
||||||
TwoGlyphCharacter()
|
TwoGlyphCharacter()
|
||||||
}.previewLayout(.fixed(width: 700, height: 500))
|
}.previewLayout(.fixed(width: 700, height: 500))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user