2009/01/20 19:23
[프로그래밍]
아주 가끔 문자열의 Bezier Path를 얻어오고 싶을 때가 있다.
여기저기 참고해서 한번 만들어 보았다.
#import <Cocoa/Cocoa.h>
@interface LayoutManager : NSLayoutManager
{
NSBezierPath *bezierPath;
}
+ (NSBezierPath *)bezierPathWithAttributedString:(NSAttributedString *)string inRect:(NSRect)rect;
@end
@implementation LayoutManager
- (id)init
{
self = [super init];
if (self)
{
bezierPath = [[NSBezierPath bezierPath] retain];
}
return self;
}
- (void)dealloc
{
[bezierPath release];
[super dealloc];
}
- (NSBezierPath *)bezierPath
{
return [[bezierPath retain] autorelease];
}
- (void)showPackedGlyphs:(char *)glyphs length:(NSUInteger)glyphLen glyphRange:(NSRange)glyphRange atPoint:(NSPoint)point font:(NSFont *)font color:(NSColor *)color printingAdjustment:(NSSize)printingAdjustment
{
/*
* 이 메소드는 LayoutManager가 글자를 그리기 위해 호출한다.
* 여기에서 그냥 Bezier Path에다 바로 그린다.
*/
[bezierPath moveToPoint:point];
[bezierPath appendBezierPathWithPackedGlyphs:glyphs];
}
+ (NSBezierPath *)bezierPathWithAttributedString:(NSAttributedString *)string inRect:(NSRect)rect
{
NSTextContainer *textContainer;
NSTextStorage *textStorage;
LayoutManager *layoutManager;
NSImage *tmpImage;
NSBezierPath *theBezierPath;
NSAffineTransform *transform;
NSAffineTransformStruct tfStruct;
NSRect usedRect;
/*
* Bezier Path를 그리기 위해 LayoutManager를 구성한다.
* 이 때, NSTextContainer는 인자로 받은 rect의 size를 사용한다.
*/
textContainer = [[NSTextContainer alloc] initWithContainerSize:rect.size];
textStorage = [[NSTextStorage alloc] initWithAttributedString:string];
layoutManager = [[LayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
/*
* 화면용 폰트의 힌트를 사용하지 않게 한다.
*/
[layoutManager setUsesScreenFonts:NO];
/*
* LayoutManager가 글자를 그릴 공간을 만든다.
*/
tmpImage = [[NSImage alloc] initWithSize:NSMakeSize(10.0, 10.0)];
/*
* LayoutManager의 좌표계는 뒤집혀 있으므로 이미지의 좌표계를 뒤집어 준다.
*/
[tmpImage setFlipped:YES];
/*
* 이미지에 글자를 그린다.
* 실제로는 Bezier Path에 그려지므로 이 이미지에는 아무것도 그려지지 않는다.
*/
[tmpImage lockFocus];
[layoutManager drawGlyphsForGlyphRange:[layoutManager glyphRangeForTextContainer:textContainer] atPoint:NSZeroPoint];
[tmpImage unlockFocus];
/*
* 그려진 Bezier Path를 얻어온다.
*/
theBezierPath = [layoutManager bezierPath];
/*
* 좌표계에서 그려진 범위를 가져온다.
*/
usedRect = [layoutManager usedRectForTextContainer:textContainer];
/*
* Bezier Path에 적용할 AffineTransform을 만든다.
* 다음의 transform을 수행하게 된다.
* 1. 뒤집힌 좌표계를 다시 뒤집어 복구 (flip)
* 2. 인자로 받은 rect안으로 translate (이 때, X축은 기준점, Y축은 중앙으로 정렬)
*/
transform = [NSAffineTransform transform];
tfStruct.m11 = 1.0;
tfStruct.m12 = 0.0;
tfStruct.tX = NSMinX(rect);
tfStruct.m21 = 0.0;
tfStruct.m22 = -1.0;
tfStruct.tY = (NSHeight(rect) + NSHeight(usedRect)) / 2.0 + NSMinY(rect);
[transform setTransformStruct:tfStruct];
/*
* Bezier Path에 생성한 AffineTransform을 적용한다.
*/
[theBezierPath transformUsingAffineTransform:transform];
/*
* 생성한 객체들을 소멸시킨다.
*/
[tmpImage release];
[textStorage release];
[textContainer release];
[layoutManager release];
/*
* Bezier Path를 리턴한다.
*/
return theBezierPath;
}
@end
만약, 인자로 넘긴 rect안에서 X축 정렬이나 Y축 정렬을 바꾸고 싶으면 AffineTransformStruct의 멤버값을 적당히 바꿔주면 되겠다. 나머지는 주석으로 충분히 설명되었으리라 믿고 추가 설명은 생략... 흐흐..
이 글은 스프링노트에서 작성되었습니다.


