Finish TSpan

This commit is contained in:
Horcrux
2016-11-11 00:48:55 +08:00
parent a305a33985
commit aae482236d
23 changed files with 379 additions and 262 deletions
+212 -100
View File
@@ -8,10 +8,13 @@
#import "RNSVGText.h"
#import "RNSVGBezierPath.h"
#import "RCTConvert+RNSVG.h"
#import "RCTFont.h"
#import <CoreText/CoreText.h>
@implementation RNSVGText
{
RNSVGText *_textRoot;
}
- (void)setTextAnchor:(RNSVGTextAnchor)textAnchor
{
@@ -19,112 +22,221 @@
_textAnchor = textAnchor;
}
//- (void)renderLayerTo:(CGContextRef)context
//{
//CGFloat shift = [self getShift:context path:nil];
// Translate path by alignment offset
//CGContextSaveGState(context);
//CGContextConcatCTM(context, CGAffineTransformMakeTranslation(-shift, 0));
//[super renderLayerTo:context];
//CGContextRestoreGState(context);
//}
//- (CGPathRef)getPath:(CGContextRef)context
//{
// CGMutablePathRef path = CGPathCreateMutable();
// CGPathRef collection = [self getPathFromSuper:context];
//
// // get alignment shift and Translate CGPath by it.
// CGFloat shift = [self getShift:context path:collection];
// CGAffineTransform align = CGAffineTransformMakeTranslation(shift, 0);
// CGPathAddPath(path, &align, collection);
// CGPathRelease(collection);
// return (CGPathRef)CFAutorelease(path);
//}
- (CGPathRef)getContentPath:(CGContextRef)context
- (void)renderLayerTo:(CGContextRef)context
{
[self setBoundingBox:CGContextGetClipBoundingBox(context)];
CGMutablePathRef path = CGPathCreateMutable();
// if (![self.content isEqualToString:@""]) {
// CGFontRef *font = [RCTConvert RNSVGFont:self.font];
// NSLog(@"font: %@", font);
// Create a dictionary for this font
// CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
// (NSString *)kCTFontAttributeName: (__bridge id)self.font,
// (NSString *)kCTForegroundColorFromContextAttributeName: @YES
// };
//
// CFStringRef string = (__bridge CFStringRef)self.content;
// CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
// CTLineRef line = CTLineCreateWithAttributedString(attrString);
// CFRelease(attrString);
//
// CGMutablePathRef linePath = [self setLinePath:line];
//
// // Set up text frame with font metrics
// CGFloat size = CTFontGetSize(self.font);
// CGFloat px = self.px ? [self getWidthRelatedValue:self.px] : 0;
// CGFloat py = self.py ? [self getHeightRelatedValue:self.py] : 0;
//
// if (self.px) {
// text.offsetX = px;
// }
//
// if (self.py) {
// text.offsetY = py + size * 1.1;
// }
//
// text.offsetX += self.dx;
// text.offsetY += self.dy;
//
// CGAffineTransform offset = CGAffineTransformMakeTranslation(text.offsetX, text.offsetY);
//
// text.offsetX += CTLineGetTypographicBounds(line, nil, nil, nil);
//
// CGPathAddPath(path, &offset, linePath);
// CGPathRelease(linePath);
// } else {
// text.offsetX += self.dx;
// text.offsetY += self.dy;
// }
return path;
CGContextSaveGState(context);
CGContextConcatCTM(context, CGAffineTransformMakeTranslation([self getShift:context path:nil], 0));
[super renderLayerTo:context];
CGContextRestoreGState(context);
}
- (CGPathRef)getPathFromSuper:(CGContextRef)context
- (CGPathRef)getPath:(CGContextRef)context
{
CGMutablePathRef shape = [self getTextGroupPath:context];
CGAffineTransform translation = CGAffineTransformMakeTranslation([self getShift:context path:shape], 0);
CGMutablePathRef path = CGPathCreateCopyByTransformingPath(shape, &translation);
CGPathRelease(shape);
return (CGPathRef)CFAutorelease(path);
}
- (CGPathRef)getTextGroupPath:(CGContextRef)context
{
CGPathRef path = [super getPath:context];
// reset offsetX and offsetY
self.offsetX = self.offsetY = 0;
[self resetTextPathAttributes];
return path;
}
//- (CGFloat)getShift:(CGContextRef)context path:(CGPathRef)path
//{
// if (!path) {
// path = [self getPathFromSuper:context];
// }
//
// CGFloat width = CGPathGetBoundingBox(path).size.width;
// CGFloat shift;
// switch (self.alignment) {
// case kCTTextAlignmentRight:
// shift = width;
// break;
// case kCTTextAlignmentCenter:
// shift = width / 2;
// break;
// default:
// shift = 0;
// break;
// }
//
// return shift;
//}
- (CGFloat)getShift:(CGContextRef)context path:(CGPathRef)path
{
if (!path) {
path = [self getTextGroupPath:context];
}
CGFloat width = CGRectGetWidth(CGPathGetBoundingBox(path));
switch ([self getComputedTextAnchor]) {
case kRNSVGTextAnchorMiddle:
return -width / 2;
case kRNSVGTextAnchorEnd:
return -width;
default:
return 0;
}
}
- (RNSVGTextAnchor)getComputedTextAnchor
{
RNSVGTextAnchor anchor = self.textAnchor;
RNSVGText *child = [self.subviews objectAtIndex:0];
while (child.subviews.count && anchor == kRNSVGTextAnchorAuto) {
anchor = child.textAnchor;
child = [child.subviews objectAtIndex:0];
}
return anchor;
}
- (BOOL)extendFontFromInheritedFont:(NSMutableDictionary *)font inheritedFont:(NSDictionary *)inheritedFont
{
NSString *fontFamily = font[@"fontFamily"];
NSNumber *fontSize = font[@"fontSize"];
NSString *fontWeight = font[@"fontWeight"];
NSString *fontStyle = font[@"fontStyle"];
BOOL fontAttributesSet = YES;
if (!fontFamily && inheritedFont[@"fontFamily"]) {
[font setObject:inheritedFont[@"fontFamily"] forKey:@"fontFamily"];
fontAttributesSet = NO;
}
if (fontSize == nil && inheritedFont[@"fontSize"] != nil) {
[font setObject:inheritedFont[@"fontSize"] forKey:@"fontSize"];
fontAttributesSet = NO;
}
if (!fontWeight && inheritedFont[@"fontWeight"]) {
[font setObject:inheritedFont[@"fontWeight"] forKey:@"fontWeight"];
fontAttributesSet = NO;
}
if (!fontStyle && inheritedFont[@"fontStyle"]) {
[font setObject:inheritedFont[@"fontStyle"] forKey:@"fontStyle"];
fontAttributesSet = NO;
}
return fontAttributesSet;
}
- (CTFontRef)getComputedFont
{
NSMutableDictionary *fontDict = [[NSMutableDictionary alloc] init];
[self traverseTextSuperviews:^(RNSVGText *node) {
return [self extendFontFromInheritedFont:fontDict inheritedFont:node.font];
}];
NSString *fontFamily = fontDict[@"fontFamily"];
BOOL fontFamilyFound = NO;
NSArray *supportedFontFamilyNames = [UIFont familyNames];
if ([supportedFontFamilyNames containsObject:fontFamily]) {
fontFamilyFound = YES;
} else {
for (NSString *fontFamilyName in supportedFontFamilyNames) {
if ([[UIFont fontNamesForFamilyName: fontFamilyName] containsObject:fontFamily]) {
fontFamilyFound = YES;
break;
}
}
}
fontFamily = fontFamilyFound ? fontFamily : nil;
return (__bridge CTFontRef)[RCTFont updateFont:nil withFamily:fontFamily size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] variant:nil scaleMultiplier:1.0];
}
- (RNSVGGlyphPoint)getComputedGlyphPoint:(NSUInteger *)index glyphOffset:(CGPoint)glyphOffset
{
RNSVGGlyphPoint __block point;
point.isDeltaXSet = point.isDeltaYSet = point.isPositionXSet = point.isPositionYSet = NO;
[self traverseTextSuperviews:^(RNSVGText *node) {
NSUInteger index = node.lastIndex;
if (!point.isPositionXSet && node.positionX && !index) {
point.positionX = [self getWidthRelatedValue:node.positionX];
point.isPositionXSet = YES;
}
if (!point.isDeltaXSet && node.deltaX.count > index) {
point.deltaX = [[node.deltaX objectAtIndex:index] floatValue];
point.isDeltaXSet = YES;
}
if (!point.isPositionYSet && node.positionY && !index) {
point.positionY = [self getHeightRelatedValue:node.positionY];
point.isPositionYSet = YES;
}
if (!point.isDeltaYSet && node.deltaY.count > index) {
point.deltaY = [[node.deltaY objectAtIndex:index] floatValue];
point.isDeltaYSet = YES;
}
node.lastIndex++;
return YES;
}];
CGPoint lineOffset = [self getGlyphLineOffset];
CGFloat lastX = lineOffset.x;
CGFloat lastY = lineOffset.y;
if (point.isPositionXSet) {
lastX = point.positionX;
}
if (point.isPositionYSet) {
lastY = point.positionY;
}
if (point.isDeltaXSet) {
lastX += point.deltaX;
}
if (point.isDeltaYSet) {
lastY += point.deltaY;
}
[self getTextRoot].lastX = lastX;
[self getTextRoot].lastY = lastY;
point.x = lastX + glyphOffset.x;
point.y = lastY + glyphOffset.y;
return point;
}
- (void)traverseTextSuperviews:(BOOL (^)(__kindof RNSVGText *node))block
{
RNSVGText *targetView = self;
block(targetView);
while (targetView && [targetView class] != [RNSVGText class]) {
if (![targetView isKindOfClass:[RNSVGText class]]) {
//todo: throw exception here
break;
}
targetView = [targetView superview];
block(targetView);
}
}
- (RNSVGText *)getTextRoot
{
if (!_textRoot) {
_textRoot = self;
while (_textRoot && [_textRoot class] != [RNSVGText class]) {
if (![_textRoot isKindOfClass:[RNSVGText class]]) {
//todo: throw exception here
break;
}
_textRoot = [_textRoot superview];
}
}
return _textRoot;
}
- (CGPoint)getGlyphLineOffset
{
RNSVGText *text = [self getTextRoot];
return CGPointMake(text.lastX, text.lastY);
}
// reset Text path related attributes
- (void)resetTextPathAttributes
{
self.lastIndex = 0;
self.lastX = self.lastY = 0;
}
@end