finish basic text support on iOS

This commit is contained in:
Horcrux
2016-09-17 15:42:27 +08:00
parent d549f6ca9c
commit a37c3ceee6
11 changed files with 150 additions and 75 deletions

View File

@@ -2,7 +2,7 @@ import React, {PropTypes} from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import {SpanAttributes} from '../lib/attributes';
import Shape from './Shape';
import {numberProp} from '../lib/props';
import {pathProps, numberProp, fontProps} from '../lib/props';
// Span components are only for internal use for Text.
@@ -10,15 +10,26 @@ class Span extends Shape {
static displayName = 'Span';
static propTypes = {
...pathProps,
frame: PropTypes.shape({
content: PropTypes.string.isRequired,
dx: numberProp,
dy: numberProp,
px: numberProp,
py: numberProp
py: numberProp,
font: PropTypes.shape(fontProps)
})
};
render() {
return <RNSVGSpan {...this.props}/>;
return <RNSVGSpan
{...this.extractProps({
...this.props,
x: null,
y: null
})}
{...this.props.frame}
/>;
}
}

View File

@@ -14,4 +14,5 @@
#import "RNSVGRenderable.h"
@interface RNSVGGroup : RNSVGRenderable <RNSVGContainer>
@end

View File

@@ -14,7 +14,6 @@
{
RNSVGSvgView* svg = [self getSvgView];
[self clip:context];
[self traverseSubviews:^(RNSVGNode *node) {
if (node.responsible && !svg.responsible) {
svg.responsible = YES;
@@ -83,7 +82,7 @@
}
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
- (void)mergeProperties:(RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
{
[self traverseSubviews:^(RNSVGNode *node) {
[node mergeProperties:target mergeList:mergeList];
@@ -99,7 +98,7 @@
}];
}
- (void)traverseSubviews:(BOOL (^)(RNSVGNode *node))block
- (void)traverseSubviews:(BOOL (^)(__kindof RNSVGNode *node))block
{
for (RNSVGNode *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {

View File

@@ -30,6 +30,7 @@
{
// todo: add detection if path has changed since last update.
self.d = [self getPath:context];
CGPathRef path = self.d;
if ((!self.fill && !self.stroke) || !path) {
return;

View File

@@ -30,6 +30,7 @@
- (instancetype)initWithBezierCurves:(NSArray *)bezierCurves
{
if (self = [super init]) {
_bezierCurves = bezierCurves;
_bezierIndex = 0;

View File

@@ -9,14 +9,14 @@
#import <Foundation/Foundation.h>
#import <CoreText/CoreText.h>
#import "RNSVGPath.h"
#import "RNSVGTextFrame.h"
@interface RNSVGSpan : RNSVGPath
@property (nonatomic, assign) CGFloat *dx;
@property (nonatomic, assign) CGFloat *dy;
@property (nonatomic, assign) NSString *px;
@property (nonatomic, assign) NSString *py;
@property (nonatomic, strong) NSString *px;
@property (nonatomic, strong) NSString *py;
@property (nonatomic, assign) CTFontRef font;
@property (nonatomic, strong) NSString *content;
@end

View File

@@ -6,17 +6,82 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGSpan.h"
#import "RNSVGBezierPath.h"
#import <CoreText/CoreText.h>
@implementation RNSVGSpan
- (CGPathRef)getPath:(CGContextRef)context
{
[self setBoundingBox:CGContextGetClipBoundingBox(context)];
CGMutablePathRef path = CGPathCreateMutable();
if (![self.content isEqualToString:@""]) {
// 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;
CGAffineTransform offset = CGAffineTransformMakeTranslation(px, size + py);
CGPathAddPath(path, &offset, linePath);
CGPathRelease(linePath);
}
return (CGPathRef)CFAutorelease(path);
}
- (CGMutablePathRef)setLinePath:(CTLineRef)line
{
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
CGMutablePathRef path = CGPathCreateMutable();
CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, 0);
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);
CFDictionaryRef attributes = CTRunGetAttributes(run);
CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName);
for(CFIndex i = 0; i < runGlyphCount; ++i) {
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[i], nil);
CGPoint point = positions[i];
if (letter) {
CGAffineTransform transform;
transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
CGPathAddPath(path, &transform, letter);
}
CGPathRelease(letter);
}
return path;
}
@end

View File

@@ -7,10 +7,9 @@
*/
#import <Foundation/Foundation.h>
#import "RNSVGPath.h"
#import "RNSVGTextFrame.h"
#import "RNSVGGroup.h"
@interface RNSVGText : RNSVGRenderable
@interface RNSVGText : RNSVGGroup
@property (nonatomic, assign) CTTextAlignment alignment;
@property (nonatomic, copy) NSArray<NSArray *> *path;

View File

@@ -39,9 +39,56 @@
// RNSVGFreeTextFrame(_textFrame);
//}
- (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 = [super getPath:context];
CGFloat shift = [self getShift:context path:collection];
CGAffineTransform align = CGAffineTransformMakeTranslation(shift, 0);
CGPathAddPath(path, &align, collection);
CGPathRelease(collection);
return (CGPathRef)CFAutorelease(path);
}
- (CGFloat)getShift:(CGContextRef)context path:(CGPathRef)path
{
if (!path) {
path = [super getPath: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;
}
//- (CGPathRef)getPath:(CGContextRef)context
//{
// CGMutablePathRef path = CGPathCreateMutable();
// RNSVGTextFrame frame = self.textFrame;
// for (int i = 0; i < frame.count; i++) {
// CGFloat shift;
@@ -66,59 +113,6 @@
// CGPathRelease(line);
// }
return (CGPathRef)CFAutorelease(path);
}
- (CGMutablePathRef)setLinePath:(CTLineRef)line
{
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
CGMutablePathRef path = CGPathCreateMutable();
CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, 0);
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);
CFDictionaryRef attributes = CTRunGetAttributes(run);
CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName);
RNSVGBezierPath *bezierPath = [[RNSVGBezierPath alloc] initWithBezierCurves:self.path];
for(CFIndex i = 0; i < runGlyphCount; ++i) {
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[i], nil);
CGPoint point = positions[i];
if (letter) {
CGAffineTransform transform;
// draw glyphs along path
if (self.path) {
transform = [bezierPath transformAtDistance:point.x];
// break loop if line reaches the end of the Path.
if (!transform.a || !transform.d) {
CGPathRelease(letter);
break;
}
transform = CGAffineTransformScale(transform, 1.0, -1.0);
} else {
transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
}
CGPathAddPath(path, &transform, letter);
}
CGPathRelease(letter);
}
return path;
}
// return (CGPathRef)CFAutorelease(path);
//}
@end

View File

@@ -25,5 +25,6 @@ RCT_EXPORT_VIEW_PROPERTY(dy, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(px, NSString)
RCT_EXPORT_VIEW_PROPERTY(py, NSString)
RCT_REMAP_VIEW_PROPERTY(font, font, RNSVGFont)
RCT_EXPORT_VIEW_PROPERTY(content, NSString)
@end

View File

@@ -1,7 +1,7 @@
import SerializablePath from '../SerializablePath';
import _ from 'lodash';
import React, {Children} from 'react';
import {fontAndRenderPropsKeys} from '../props';
import {fontAndRenderPropsKeys, fontPropsKeys} from '../props';
import Span from '../../elements/Span';
const fontRegExp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm%]*(?:\s*\/.*?)?\s+)?\s*"?([^"]*)/i;
@@ -210,7 +210,10 @@ export default function(props) {
}
};
return <Span {...spanProps} />;
return <Span
{..._.omit(frame.props, fontPropsKeys)}
frame={spanProps}
/>;
});
return {