暗号だと思った?
残念Core Textちゃんでした。
せっかくCore Text扱ってるので、文字の形状を定義したベジェ曲線を取り出して使ってみマッスル。
いわゆるグリフ(Glyph:字体)というやつです。
例えば、世界には同じ「A」を意味する図形がいろいろあるわけですが、この図形それぞれを「A」のグリフというわけですYooo
「A」を意味するいろいろなグリフちゃん
で、OS XやiOSでは、このグリフがベジェ曲線でフォントごとに提供されてまして、Core Textを使うと、指定したフォントから指定した文字のベジェ曲線が取り出せるようになってるんですな。
注意)ベジェ曲線は、数個の座標点を指定して定義する曲線で、iOSやOS Xで曲線を描くのに使う。これ常識な→(iPhoneアプリ開発、その(229) ワン・フロム・ザ・ハート)。
それが
CTFontCreatePathForGlyph
で、こいつに取り出したい文字と、どのフォントから取り出したいかを指定することで、指定した文字に対応するグリフが取り出せるようになっている。
取り出したい文字は番号で指定します。
この番号はフォントごとに固有であることには注意が必要。
例えばASCIIコードやunicodeでは、アルファベットの「A」を数値の65と定義してるんですが、これをそのまま番号として渡しても「A」のグリフを指定したことにはなりません。
グリフを指定するための番号はフォントごとに固有に決められていて、その値を知るためには
CTFontGetGlyphsForCharacters
を使えってことになってます。
こいつに、どのフォントかと、どの文字かを指定することで、知りたいグリフ番号を取り出せます。
ここでようやく文字の指定としてunicodeを使うことになる。
unicodeは今のところ符号なし16ビット整数で指定します。UInt16型でもいいんだけど、UniCharという型が用意されているんでそっちを使いましょう。NSUIntegerみたいに将来変更されるかもしれんでな。
unicodeの値も、グリフの番号も同じく符号なし16ビット整数なんだけど、グリフの番号の型はCGGlyph型として用意されているんでこっちを使う。グリフを指定する番号、ちゅ~意味でCGGlyphなんでしょう。
あと、フォントの指定にUIFontは使えません。
Core TextはCTFontという専用のオブジェクトが用意されていて、そっちを使います。
UIFontが示すフォントと同じものを、CTFontととして作りたい場合に使うのは
CTFontCreateWithFontDescriptor
swiftでは
func CTFontCreateWithFontDescriptor(_ descriptor: CTFontDescriptor, _ size: CGFloat, _ matrix: UnsafePointer) -> CTFont
と定義されていて、第1引数descriptorにはCTFontDescriptorを渡す必要があるけど、こいつにはUIFontから-fontDescriptorメッセージで取り出したUIFontDescriptorをそのまま渡せます。
sizeには文字のポイント数を指定し、matrixには取り出したグリフの座標系を変更したい時に指定する。そのまま使うならnilを指定。これで新しく作成されたCTFontが戻される。
fontは事前に用意したUIFont
let ctfont = CTFontCreateWithFontDescriptor(font.fontDescriptor(), font.pointSize, nil)
これでCTFontGetGlyphsForCharactersを使ってグリフ番号を手に入れる準備は完了。例えばアルファベットの「A」のグリフ番号を手に入れたいなら、「A」を意味するunicodeは65なので、上で紹介したCTFontGetGlyphsForCharacters にCTFontと65を渡せば、「A」のグリフを取り出すための番号が手に入る。
このCTFontGetGlyphsForCharacters、swiftでは
func CTFontGetGlyphsForCharacters(_ font: CTFont, _ characters: UnsafePointer, _ glyphs: UnsafeMutablePointer, _ count: CFIndex) -> Bool
と定義されていて、第1引数fontにCTFontを渡し、次のcharactersにUniChar型の変数の番地か、UniChar型配列の先頭番地を渡すことになってます。
具体的には配列だと
var unicodes:[UniChar] = [65, 66, 67]
var glyphs:[CGGlyph] = [0, 0, 0]
CTFontGetGlyphsForCharacters(ctfont, &unicodes, &glyphs, unicodes.count)
なんてすることで、「A」、「B」、「C」それぞれのグリフ番号が一気に手に入ることになる。
成功するとtrue、失敗するとfalseが戻ります。
ちなみにUniCharの配列であるunicodesは、UnsafePointerとして渡すわけで、これは指し示される内容を変更しないという意味なので、varじゃなくletで用意してもいいんじゃないかと思ったけど実際やると怒られます。
受け取る方のCGGlyph配列であるglyphsはUnsafeMutablePointerで正真正銘ポイント先の内容が変更されるのでvarにする必要がある。
上ではCGGlyph配列をリテラル表記で[0,0,0]と3個分羅列して用意してるけど、init(count:, repeatedValue:)を使って用意してもかまいません。ここらへんは好き好きで。初期値なんて設定してもしょうがないけどswiftの決まりなんで0で初期化。あとはcountに確保したい要素数を指定する。今回ならunicodesの要素数分必要なんでunicodes.countを使う。
var glyphs = [CGGlyph](count:unicodes.count, repeatedValue:0)
それと、ここでは「A」のunicode値として直接65って指定してるけど「A」がunicodeの値でいくつなのかを知る方法もあります。
let unicode = UniChar("A".unicodeScalars.first!.value) unicodeに65が入る
ま、それは別の機会で触れるとして、CTFontGetGlyphsForCharactersから戻されたCGGlyph配列の番号を使えば、それぞれのベジェ曲線をCTFontCreatePathForGlyphで取り出せます。こいつは
func CTFontCreatePathForGlyph(font: CTFont, _ glyph: CGGlyph, _ matrix:
UnsafePointer) -> CGPath?
と定義されていて、第1引数fontにはCTFont、次のglyphに番号を渡せばCGPathが戻される。
matrixはCTFontCreateWithFontDescriptorと同じなのでnilを指定。
例えば、さっきの「A」、「B」、「C」を取り出したglyphsならglyphs[1]とすることで、「B」のベジェ曲線が戻されるってわけです。
CTFontCreatePathForGlyph(ctfont, glyphs[1], nil)
戻り値はCGPathに?が付いてる事からわかりますがオプショナルです。パスが作れなかった場合はnilが戻されるので、その点のチェックも忘れないように。
サンプルでは指定したunicode1文字のパスを取り出すメソッドを用意して使っています。
サンプル:https://github.com/Takahiro-Kunii/TextToPath.gitのBranch:v1
// 指定したfontから、指定したunicodeに対応するCGPathを取り出す
func unicodeToCGPath(var unicode:UniChar, font:UIFont) -> CGPath? {
// 取り出しにはCore TextのAPIを使うのでUIFontではなくCTFontを用意しなければいけない
let ctfont = CTFontCreateWithFontDescriptor(font.fontDescriptor(), font.pointSize, nil)
// CTFontが管理する文字形状(CGPath)群は、そのCTFont独自のインディックスで
// 管理される。このインディックスはCGGlyph型として定義されていて、指定した
// unicode列に対応するCGGlyph列を返すAPI
// CTFontGetGlyphsForCharacters
// を使い取り出せる
var glyph:CGGlyph = 0 // 指定されたunicodeに対応するインディックス
if CTFontGetGlyphsForCharacters(ctfont, &unicode, &glyph, 1) {
// インディックス(CGGlyph)が取り出せたので、これを指定して対応する
// 文字形状(CGPath)を取り出す
return CTFontCreatePathForGlyph(ctfont, glyph, nil)
}
// インディックスが取り出せなかった(指定したフォントに、指定したunicodeの
// 文字形状が用意されていない等)
return nil // 失敗
}
上のglyphのように、1文字なら、CGGlyph配列使わずにCGGlyph変数を使うこともできる。
サンプルでは取り出したベジェ曲線を枠線を描いたり、塗りつぶしたりできるCALayer派性クラスのオブジェクト
CAShapeLayer
に設定して画面に表示させてます。
あとオプショナルが戻されるので、アンラップしてnilでない時だけ実行するようにしてる。
if let path = self.unicodeToCGPath(0x65, font:font) {
このif let path = てのが、unicodeToCGPathからの戻り値、CGPath?をアンラップしてnilでないか確認する作業。nilでなければpathにCGPathが設定される。
if let path = self.unicodeToCGPath(0x65, font:font) {
let shape = CAShapeLayer()
shape.path = path // ベジェ曲線設定
shape.frame = CGPathGetBoundingBox(path) // 矩形設定(レイヤなので
// 設定しなくても表示されるが…)
// サブレイヤーとして登録
self.view.layer.addSublayer(shape)
// 位置調整 ほぼ真ん中に配置
shape.position = self.view.center
}
ちなみにiOSと文字のベジェ曲線の座標系が違うんで上下逆転してる。
ま、それは後で対応するとして、グリフ形状のCAShapeLayerが作れたことで何ができるかというと画面のマスキングができるんですな。
例えばUIImageViewなんかで画像を表示しておいて、このUIImageViewのlayerのmaskプロパティに指定するとこうなる。
でもって、CAShapeLayerはCALayer派性なんでアニメーションもでき。
前回のその(239)で使った領域指定にも使える、はず。
というところで、以下次回!
残念Core Textちゃんでした。
せっかくCore Text扱ってるので、文字の形状を定義したベジェ曲線を取り出して使ってみマッスル。
いわゆるグリフ(Glyph:字体)というやつです。
例えば、世界には同じ「A」を意味する図形がいろいろあるわけですが、この図形それぞれを「A」のグリフというわけですYooo
「A」を意味するいろいろなグリフちゃん
で、OS XやiOSでは、このグリフがベジェ曲線でフォントごとに提供されてまして、Core Textを使うと、指定したフォントから指定した文字のベジェ曲線が取り出せるようになってるんですな。
注意)ベジェ曲線は、数個の座標点を指定して定義する曲線で、iOSやOS Xで曲線を描くのに使う。これ常識な→(iPhoneアプリ開発、その(229) ワン・フロム・ザ・ハート)。
それが
CTFontCreatePathForGlyph
で、こいつに取り出したい文字と、どのフォントから取り出したいかを指定することで、指定した文字に対応するグリフが取り出せるようになっている。
取り出したい文字は番号で指定します。
この番号はフォントごとに固有であることには注意が必要。
例えばASCIIコードやunicodeでは、アルファベットの「A」を数値の65と定義してるんですが、これをそのまま番号として渡しても「A」のグリフを指定したことにはなりません。
グリフを指定するための番号はフォントごとに固有に決められていて、その値を知るためには
CTFontGetGlyphsForCharacters
を使えってことになってます。
こいつに、どのフォントかと、どの文字かを指定することで、知りたいグリフ番号を取り出せます。
ここでようやく文字の指定としてunicodeを使うことになる。
unicodeは今のところ符号なし16ビット整数で指定します。UInt16型でもいいんだけど、UniCharという型が用意されているんでそっちを使いましょう。NSUIntegerみたいに将来変更されるかもしれんでな。
unicodeの値も、グリフの番号も同じく符号なし16ビット整数なんだけど、グリフの番号の型はCGGlyph型として用意されているんでこっちを使う。グリフを指定する番号、ちゅ~意味でCGGlyphなんでしょう。
あと、フォントの指定にUIFontは使えません。
Core TextはCTFontという専用のオブジェクトが用意されていて、そっちを使います。
UIFontが示すフォントと同じものを、CTFontととして作りたい場合に使うのは
CTFontCreateWithFontDescriptor
swiftでは
func CTFontCreateWithFontDescriptor(_ descriptor: CTFontDescriptor, _ size: CGFloat, _ matrix: UnsafePointer
と定義されていて、第1引数descriptorにはCTFontDescriptorを渡す必要があるけど、こいつにはUIFontから-fontDescriptorメッセージで取り出したUIFontDescriptorをそのまま渡せます。
sizeには文字のポイント数を指定し、matrixには取り出したグリフの座標系を変更したい時に指定する。そのまま使うならnilを指定。これで新しく作成されたCTFontが戻される。
fontは事前に用意したUIFont
let ctfont = CTFontCreateWithFontDescriptor(font.fontDescriptor(), font.pointSize, nil)
これでCTFontGetGlyphsForCharactersを使ってグリフ番号を手に入れる準備は完了。例えばアルファベットの「A」のグリフ番号を手に入れたいなら、「A」を意味するunicodeは65なので、上で紹介したCTFontGetGlyphsForCharacters にCTFontと65を渡せば、「A」のグリフを取り出すための番号が手に入る。
このCTFontGetGlyphsForCharacters、swiftでは
func CTFontGetGlyphsForCharacters(_ font: CTFont, _ characters: UnsafePointer
と定義されていて、第1引数fontにCTFontを渡し、次のcharactersにUniChar型の変数の番地か、UniChar型配列の先頭番地を渡すことになってます。
具体的には配列だと
var unicodes:[UniChar] = [65, 66, 67]
var glyphs:[CGGlyph] = [0, 0, 0]
CTFontGetGlyphsForCharacters(ctfont, &unicodes, &glyphs, unicodes.count)
なんてすることで、「A」、「B」、「C」それぞれのグリフ番号が一気に手に入ることになる。
成功するとtrue、失敗するとfalseが戻ります。
ちなみにUniCharの配列であるunicodesは、UnsafePointerとして渡すわけで、これは指し示される内容を変更しないという意味なので、varじゃなくletで用意してもいいんじゃないかと思ったけど実際やると怒られます。
受け取る方のCGGlyph配列であるglyphsはUnsafeMutablePointerで正真正銘ポイント先の内容が変更されるのでvarにする必要がある。
上ではCGGlyph配列をリテラル表記で[0,0,0]と3個分羅列して用意してるけど、init(count:, repeatedValue:)を使って用意してもかまいません。ここらへんは好き好きで。初期値なんて設定してもしょうがないけどswiftの決まりなんで0で初期化。あとはcountに確保したい要素数を指定する。今回ならunicodesの要素数分必要なんでunicodes.countを使う。
var glyphs = [CGGlyph](count:unicodes.count, repeatedValue:0)
それと、ここでは「A」のunicode値として直接65って指定してるけど「A」がunicodeの値でいくつなのかを知る方法もあります。
let unicode = UniChar("A".unicodeScalars.first!.value) unicodeに65が入る
ま、それは別の機会で触れるとして、CTFontGetGlyphsForCharactersから戻されたCGGlyph配列の番号を使えば、それぞれのベジェ曲線をCTFontCreatePathForGlyphで取り出せます。こいつは
func CTFontCreatePathForGlyph(font: CTFont, _ glyph: CGGlyph, _ matrix:
UnsafePointer
と定義されていて、第1引数fontにはCTFont、次のglyphに番号を渡せばCGPathが戻される。
matrixはCTFontCreateWithFontDescriptorと同じなのでnilを指定。
例えば、さっきの「A」、「B」、「C」を取り出したglyphsならglyphs[1]とすることで、「B」のベジェ曲線が戻されるってわけです。
CTFontCreatePathForGlyph(ctfont, glyphs[1], nil)
戻り値はCGPathに?が付いてる事からわかりますがオプショナルです。パスが作れなかった場合はnilが戻されるので、その点のチェックも忘れないように。
サンプルでは指定したunicode1文字のパスを取り出すメソッドを用意して使っています。
サンプル:https://github.com/Takahiro-Kunii/TextToPath.gitのBranch:v1
// 指定したfontから、指定したunicodeに対応するCGPathを取り出す
func unicodeToCGPath(var unicode:UniChar, font:UIFont) -> CGPath? {
// 取り出しにはCore TextのAPIを使うのでUIFontではなくCTFontを用意しなければいけない
let ctfont = CTFontCreateWithFontDescriptor(font.fontDescriptor(), font.pointSize, nil)
// CTFontが管理する文字形状(CGPath)群は、そのCTFont独自のインディックスで
// 管理される。このインディックスはCGGlyph型として定義されていて、指定した
// unicode列に対応するCGGlyph列を返すAPI
// CTFontGetGlyphsForCharacters
// を使い取り出せる
var glyph:CGGlyph = 0 // 指定されたunicodeに対応するインディックス
if CTFontGetGlyphsForCharacters(ctfont, &unicode, &glyph, 1) {
// インディックス(CGGlyph)が取り出せたので、これを指定して対応する
// 文字形状(CGPath)を取り出す
return CTFontCreatePathForGlyph(ctfont, glyph, nil)
}
// インディックスが取り出せなかった(指定したフォントに、指定したunicodeの
// 文字形状が用意されていない等)
return nil // 失敗
}
上のglyphのように、1文字なら、CGGlyph配列使わずにCGGlyph変数を使うこともできる。
サンプルでは取り出したベジェ曲線を枠線を描いたり、塗りつぶしたりできるCALayer派性クラスのオブジェクト
CAShapeLayer
に設定して画面に表示させてます。
あとオプショナルが戻されるので、アンラップしてnilでない時だけ実行するようにしてる。
if let path = self.unicodeToCGPath(0x65, font:font) {
このif let path = てのが、unicodeToCGPathからの戻り値、CGPath?をアンラップしてnilでないか確認する作業。nilでなければpathにCGPathが設定される。
if let path = self.unicodeToCGPath(0x65, font:font) {
let shape = CAShapeLayer()
shape.path = path // ベジェ曲線設定
shape.frame = CGPathGetBoundingBox(path) // 矩形設定(レイヤなので
// 設定しなくても表示されるが…)
// サブレイヤーとして登録
self.view.layer.addSublayer(shape)
// 位置調整 ほぼ真ん中に配置
shape.position = self.view.center
}
ちなみにiOSと文字のベジェ曲線の座標系が違うんで上下逆転してる。
ま、それは後で対応するとして、グリフ形状のCAShapeLayerが作れたことで何ができるかというと画面のマスキングができるんですな。
例えばUIImageViewなんかで画像を表示しておいて、このUIImageViewのlayerのmaskプロパティに指定するとこうなる。
でもって、CAShapeLayerはCALayer派性なんでアニメーションもでき。
前回のその(239)で使った領域指定にも使える、はず。
というところで、以下次回!