GT2/GT2-Android/node_modules/react-native-svg/ios/Text/RNSVGTSpan.m

188 lines
5.5 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 "RNSVGTSpan.h"
#import "RNSVGBezierTransformer.h"
#import "RNSVGText.h"
#import "RNSVGTextPath.h"
@implementation RNSVGTSpan
{
RNSVGBezierTransformer *_bezierTransformer;
CGPathRef _cache;
}
- (void)setContent:(NSString *)content
{
if (content == _content) {
return;
}
[self invalidate];
_content = content;
}
- (void)renderLayerTo:(CGContextRef)context
{
if (self.content) {
[self renderPathTo:context];
} else {
[self clip:context];
[self renderGroupTo:context];
}
}
- (void)releaseCachedPath
{
CGPathRelease(_cache);
_cache = nil;
}
- (void)dealloc
{
CGPathRelease(_cache);
}
- (CGPathRef)getPath:(CGContextRef)context
{
if (_cache) {
return _cache;
}
NSString *text = self.content;
if (!text) {
return [self getGroupPath:context];
}
[self setupTextPath:context];
CGMutablePathRef path = CGPathCreateMutable();
// append spacing
text = [text stringByAppendingString:@" "];
[self pushGlyphContext];
CTFontRef font = [self getFontFromContext];
// Create a dictionary for this font
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
(NSString *)kCTFontAttributeName: (__bridge id)font,
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
};
CFStringRef string = (__bridge CFStringRef)text;
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CTLineRef line = CTLineCreateWithAttributedString(attrString);
CGMutablePathRef linePath = [self getLinePath:line];
CGAffineTransform offset = CGAffineTransformMakeTranslation(0, _bezierTransformer ? 0 : CTFontGetSize(font) * 1.1);
CGPathAddPath(path, &offset, linePath);
CGPathRelease(linePath);
_cache = CGPathRetain(CFAutorelease(CGPathCreateCopy(path)));
[self popGlyphContext];
// clean up
CFRelease(attrString);
CFRelease(line);
return (CGPathRef)CFAutorelease(path);
}
- (CGMutablePathRef)getLinePath:(CTLineRef)line
{
CGMutablePathRef path = CGPathCreateMutable();
CFArrayRef runs = CTLineGetGlyphRuns(line);
for (CFIndex i = 0; i < CFArrayGetCount(runs); i++) {
CTRunRef run = CFArrayGetValueAtIndex(CTLineGetGlyphRuns(line), i);
CFIndex runGlyphCount = CTRunGetGlyphCount(run);
CGPoint positions[runGlyphCount];
CGGlyph glyphs[runGlyphCount];
// Grab the glyphs, positions, and font
CTRunGetPositions(run, CFRangeMake(0, 0), positions);
CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs);
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
CGPoint glyphPoint;
for(CFIndex i = 0; i < runGlyphCount; i++) {
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[i], nil);
glyphPoint = [self getGlyphPointFromContext:positions[i] glyphWidth:CGRectGetWidth(CGPathGetBoundingBox(letter))];
CGAffineTransform textPathTransform = CGAffineTransformIdentity;
CGAffineTransform transform;
if (_bezierTransformer) {
textPathTransform = [_bezierTransformer getTransformAtDistance:glyphPoint.x];
if ([self textPathHasReachedEnd]) {
CGPathRelease(letter);
break;
} else if (![self textPathHasReachedStart]) {
CGPathRelease(letter);
continue;
}
textPathTransform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(0, glyphPoint.y), textPathTransform);
transform = CGAffineTransformScale(textPathTransform, 1.0, -1.0);
} else {
transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(1.0, -1.0), glyphPoint.x, -glyphPoint.y);
}
CGPathAddPath(path, &transform, letter);
CGPathRelease(letter);
}
}
return path;
}
- (void)setupTextPath:(CGContextRef)context
{
__block RNSVGBezierTransformer *bezierTransformer;
[self traverseTextSuperviews:^(__kindof RNSVGText *node) {
if ([node class] == [RNSVGTextPath class]) {
bezierTransformer = [(RNSVGTextPath*)node getBezierTransformer];
return NO;
}
return YES;
}];
_bezierTransformer = bezierTransformer;
}
- (void)traverseTextSuperviews:(BOOL (^)(__kindof RNSVGText *node))block
{
RNSVGText *targetView = self;
BOOL result = block(self);
while (targetView && [targetView class] != [RNSVGText class] && result) {
if (![targetView isKindOfClass:[RNSVGText class]]) {
//todo: throw exception here
break;
}
targetView = (RNSVGText*)[targetView superview];
result = block(targetView);
}
}
- (BOOL)textPathHasReachedEnd
{
return [_bezierTransformer hasReachedEnd];
}
- (BOOL)textPathHasReachedStart
{
return [_bezierTransformer hasReachedStart];
}
@end