166 lines
4.2 KiB
Objective-C
166 lines
4.2 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Horcrux.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the MIT-style license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#import "RNSVGText.h"
|
|
#import "RNSVGTextPath.h"
|
|
#import <React/RCTFont.h>
|
|
#import <CoreText/CoreText.h>
|
|
#import "RNSVGGlyphContext.h"
|
|
|
|
@implementation RNSVGText
|
|
{
|
|
RNSVGText *_textRoot;
|
|
RNSVGGlyphContext *_glyphContext;
|
|
}
|
|
|
|
- (void)setTextAnchor:(RNSVGTextAnchor)textAnchor
|
|
{
|
|
[self invalidate];
|
|
_textAnchor = textAnchor;
|
|
}
|
|
|
|
- (void)renderLayerTo:(CGContextRef)context
|
|
{
|
|
[self clip:context];
|
|
CGContextSaveGState(context);
|
|
[self setupGlyphContext:context];
|
|
|
|
CGPathRef path = [self getGroupPath:context];
|
|
CGAffineTransform transform = [self getAlignTransform:path];
|
|
CGContextConcatCTM(context, transform);
|
|
[self renderGroupTo:context];
|
|
[self releaseCachedPath];
|
|
CGContextRestoreGState(context);
|
|
|
|
|
|
CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &transform);
|
|
[self setHitArea:transformedPath];
|
|
CGPathRelease(transformedPath);
|
|
}
|
|
|
|
- (void)setupGlyphContext:(CGContextRef)context
|
|
{
|
|
_glyphContext = [[RNSVGGlyphContext alloc] initWithDimensions:[self getContextWidth]
|
|
height:[self getContextHeight]];
|
|
}
|
|
|
|
// release the cached CGPathRef for RNSVGTSpan
|
|
- (void)releaseCachedPath
|
|
{
|
|
[self traverseSubviews:^BOOL(__kindof RNSVGNode *node) {
|
|
RNSVGText *text = node;
|
|
[text releaseCachedPath];
|
|
return YES;
|
|
}];
|
|
}
|
|
|
|
- (CGPathRef)getGroupPath:(CGContextRef)context
|
|
{
|
|
[self pushGlyphContext];
|
|
CGPathRef groupPath = [super getPath:context];
|
|
[self popGlyphContext];
|
|
|
|
return groupPath;
|
|
}
|
|
|
|
- (CGPathRef)getPath:(CGContextRef)context
|
|
{
|
|
[self setupGlyphContext:context];
|
|
CGPathRef groupPath = [self getGroupPath:context];
|
|
CGAffineTransform transform = [self getAlignTransform:groupPath];
|
|
[self releaseCachedPath];
|
|
|
|
return (CGPathRef)CFAutorelease(CGPathCreateCopyByTransformingPath(groupPath, &transform));
|
|
}
|
|
|
|
- (void)renderGroupTo:(CGContextRef)context
|
|
{
|
|
[self pushGlyphContext];
|
|
[super renderGroupTo:context];
|
|
[self popGlyphContext];
|
|
}
|
|
|
|
- (CGAffineTransform)getAlignTransform:(CGPathRef)path
|
|
{
|
|
CGFloat width = CGRectGetWidth(CGPathGetBoundingBox(path));
|
|
CGFloat x = 0;
|
|
|
|
switch ([self getComputedTextAnchor]) {
|
|
case kRNSVGTextAnchorMiddle:
|
|
x = -width / 2;
|
|
break;
|
|
case kRNSVGTextAnchorEnd:
|
|
x = -width;
|
|
break;
|
|
default: ;
|
|
}
|
|
|
|
return CGAffineTransformMakeTranslation(x, 0);
|
|
}
|
|
|
|
- (RNSVGTextAnchor)getComputedTextAnchor
|
|
{
|
|
RNSVGTextAnchor anchor = self.textAnchor;
|
|
if (self.subviews.count > 0) {
|
|
RNSVGText *child = [self.subviews firstObject];
|
|
|
|
while (child.subviews.count && anchor == kRNSVGTextAnchorAuto) {
|
|
anchor = child.textAnchor;
|
|
child = [child.subviews firstObject];
|
|
}
|
|
}
|
|
return anchor;
|
|
}
|
|
|
|
- (RNSVGText *)getTextRoot
|
|
{
|
|
if (!_textRoot) {
|
|
_textRoot = self;
|
|
while (_textRoot && [_textRoot class] != [RNSVGText class]) {
|
|
if (![_textRoot isKindOfClass:[RNSVGText class]]) {
|
|
//todo: throw exception here
|
|
break;
|
|
}
|
|
_textRoot = (RNSVGText*)[_textRoot superview];
|
|
}
|
|
}
|
|
|
|
return _textRoot;
|
|
}
|
|
|
|
- (RNSVGGlyphContext *)getGlyphContext
|
|
{
|
|
return _glyphContext;
|
|
}
|
|
|
|
- (void)pushGlyphContext
|
|
{
|
|
[[[self getTextRoot] getGlyphContext] pushContext:self.font
|
|
deltaX:self.deltaX
|
|
deltaY:self.deltaY
|
|
positionX:self.positionX
|
|
positionY:self.positionY];
|
|
}
|
|
|
|
- (void)popGlyphContext
|
|
{
|
|
[[[self getTextRoot] getGlyphContext] popContext];
|
|
}
|
|
|
|
- (CTFontRef)getFontFromContext
|
|
{
|
|
return [[[self getTextRoot] getGlyphContext] getGlyphFont];
|
|
}
|
|
|
|
- (CGPoint)getGlyphPointFromContext:(CGPoint)offset glyphWidth:(CGFloat)glyphWidth
|
|
{
|
|
return [[[self getTextRoot] getGlyphContext] getNextGlyphPoint:(CGPoint)offset glyphWidth:glyphWidth];
|
|
}
|
|
|
|
@end
|