年中、ルイボスティーを飲み続ける 見習いプログラマーの naopon です!
ごくたま〜〜〜に。
テキストの輪郭パスがほしい時ってありませんか?
画像にテキストを合成したり?
画像からテキストの輪郭を切り抜いたり?
Objective-C で何かしら描画をする場合など、よく UIBezierPath を使用するケースがあるので、
今回はこのクラスを機能拡張してみます。
※一部コードに「正規表現のパターンで文字列を分割」で紹介したカテゴリーを使っています
【輪郭パス】
1 2 3 4 5 6 7 8 9 10 11 |
#import <UIKit/UIKit.h> @interface UIBezierPath (TextPath) + (UIBezierPath *)fromText:(NSString *)text fontSize:(float)fontSize fontName:(NSString *)fontName lineSpacing:(CGFloat)lineSpacing textAlignment:(NSTextAlignment)textAlignment; @end |
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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
#import "UIBezierPath+TextPath.h" #import <CoreText/CoreText.h> #import "NSString+SplitPattern.h" @implementation UIBezierPath (TextPath) /** * テキストの輪郭パス取得 * @param text テキスト * @param fontSize フォントサイズ * @param fontName フォント名 * @param lineSpacing 行間 * @param textAlignment Left or Center or Right * @return テキストの輪郭パス */ + (UIBezierPath *)fromText:(NSString *)text fontSize:(float)fontSize fontName:(NSString *)fontName lineSpacing:(CGFloat)lineSpacing textAlignment:(NSTextAlignment)textAlignment { UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointZero]; @autoreleasepool { //改行で分割して行毎の文字列でループ NSArray *textAry = [[[text splitNewLine] reverseObjectEnumerator] allObjects]; //最大幅 CGFloat maxWidth = 0.f; for (int i=0; i<textAry.count; i++) { @autoreleasepool { //装飾文字列 CTFontRef font = CTFontCreateWithName((CFStringRef)fontName, fontSize, NULL); NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)font, kCTFontAttributeName, nil]; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:textAry[i] attributes:attrs]; //文字列の行(Line)情報取得 CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString); //文字列の行(Line)の幅 CGRect rect = CTLineGetBoundsWithOptions(line, kCTLineBoundsUseGlyphPathBounds); CGFloat width = CGRectGetWidth(rect); //最大幅を保持 if (maxWidth < width) { maxWidth = width; } //メモリ解放 CFRelease(line); CFRelease(font); } } for (int i=0; i<textAry.count; i++) { @autoreleasepool { //行(Line)パス保持用 CGMutablePathRef letters = CGPathCreateMutable(); //装飾文字列 CTFontRef font = CTFontCreateWithName((CFStringRef)fontName, fontSize, NULL); NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)font, kCTFontAttributeName, nil]; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:textAry[i] attributes:attrs]; //文字列の行(Line)情報取得 CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString); //文字列の行(Line)の幅 CGRect rect = CTLineGetBoundsWithOptions(line, kCTLineBoundsUseGlyphPathBounds); CGFloat width = CGRectGetWidth(rect); //マージン計算 CGFloat margin = 0.f; switch (textAlignment) { case NSTextAlignmentCenter: margin = (maxWidth - width)/2.; break; case NSTextAlignmentRight: margin = maxWidth - width; break; default: break; } //行(Line)情報から一文字毎の情報(RUN)を取得してループ CFArrayRef runArray = CTLineGetGlyphRuns(line); for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++) { @autoreleasepool { //一文字の情報(RUN)を取得 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex); CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); //RUNのGLYPH毎にループ for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++) { @autoreleasepool { //GLYPH情報取得 CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1); CGGlyph glyph; CGPoint position; CTRunGetGlyphs(run, thisGlyphRange, &glyph); CTRunGetPositions(run, thisGlyphRange, &position); //輪郭(outline)のパスを保持していく { CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL); CGFloat px = position.x + margin; CGFloat py = position.y + path.bounds.size.height + ((i==0)?0:lineSpacing); CGAffineTransform t = CGAffineTransformMakeTranslation(px, py); CGPathAddPath(letters, &t, letter); CGPathRelease(letter); } } } } } CFRelease(line); //一行分のパスを格納 [path appendPath:[UIBezierPath bezierPathWithCGPath:letters]]; CGPathRelease(letters); CFRelease(font); } } } return path; } |
これもカテゴリーにして機能拡張しちゃえば超便利でいいですね!
UIBezierPath+TextPath.h
UIBezierPath+TextPath.m
これでテキストの輪郭パスが手に入りました。
次回は、このカテゴリーを使って、実際に画像からテキストを切り抜いてみます!
先人たちは素晴らしいですね。
いつもありがとうございます!