★ 縦書き文章にルビをふる
import UIKit
extension String {
func find(pattern: String) -> NSTextCheckingResult? {
do {
let re = try NSRegularExpression(pattern: pattern, options: [])
return re.firstMatch(
in: self,
options: [],
range: NSMakeRange(0, self.utf16.count))
} catch {
return nil
}
}
func replace(pattern: String, template: String) -> String {
do {
let re = try NSRegularExpression(pattern: pattern, options: [])
return re.stringByReplacingMatches(
in: self,
options: [],
range: NSMakeRange(0, self.utf16.count),
withTemplate: template)
} catch {
return self
}
}
}
class View: UIView {
override func draw(_ rect: CGRect) {
let text = [
"「まさか、|後罪《クライム》の|触媒《カタリスト》を〈|讃来歌《オラトリオ》〉無しで?」",
"教師たちの狼狽した声が次々と上がる。",
"……なんでだろう。何を驚いているんだろう。",
"ただ普通に、この|触媒《カタリスト》を使って|名詠門《チャネル》を開かせただけなのに。",
"そう言えば、何を|詠《よ》ぼう。",
"自分の一番好きな花でいいかな。",
"どんな宝石より素敵な、わたしの大好きな緋色の花。",
"――『|Keinez《赤》』――",
"そして、少女の口ずさんだその後に――",
]
.joined(separator: "\n")
let attributed =
text
.replace(pattern: "(|.+?《.+?》)", template: ",$1,")
.components(separatedBy: ",")
.map { x -> NSAttributedString in
if let pair = x.find(pattern: "|(.+?)《(.+?)》") {
let string = (x as NSString).substring(with: pair.rangeAt(1))
let ruby = (x as NSString).substring(with: pair.rangeAt(2))
var text: [Unmanaged<CFString>?] = [Unmanaged<CFString>.passRetained(ruby as CFString) as Unmanaged<CFString>, .none, .none, .none]
let annotation = CTRubyAnnotationCreate(.auto, .auto, 0.5, &text[0]!)
return NSAttributedString(
string: string,
attributes: [kCTRubyAnnotationAttributeName as String: annotation])
} else {
return NSAttributedString(string: x, attributes: nil)
}
}
.reduce(NSMutableAttributedString()) { $0.append($1); return $0 }
var height = 28.0
let settings = [
CTParagraphStyleSetting(
spec: .minimumLineHeight,
valueSize: Int(MemoryLayout.size(ofValue: height)),
value: &height)
]
let style = CTParagraphStyleCreate(settings, Int(settings.count))
attributed.addAttributes([
NSFontAttributeName: UIFont(name: "HiraMinProN-W3", size: 14.0)!,
NSVerticalGlyphFormAttributeName: true,
kCTParagraphStyleAttributeName as String: style,
],
range: NSMakeRange(0, attributed.length))
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
context!.addRect(rect)
context?.fillPath()
context!.rotate(by: CGFloat(M_PI_2))
context!.translateBy(x: 30.0, y: 35.0)
context!.scaleBy(x: 1.0, y: -1.0)
let framesetter = CTFramesetterCreateWithAttributedString(attributed)
let path = CGPath(rect: CGRect(x: 0.0, y: 0.0, width: rect.height, height: rect.width), transform: nil)
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)
CTFrameDraw(frame, context!)
}
}
class ViewController: UIViewController {
override func loadView() {
super.loadView()
self.view = View()
}
}