mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-21 14:25:14 +00:00
finish basic text support on iOS
This commit is contained in:
@@ -2,7 +2,7 @@ import React, {PropTypes} from 'react';
|
|||||||
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
|
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
|
||||||
import {SpanAttributes} from '../lib/attributes';
|
import {SpanAttributes} from '../lib/attributes';
|
||||||
import Shape from './Shape';
|
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.
|
// Span components are only for internal use for Text.
|
||||||
@@ -10,15 +10,26 @@ class Span extends Shape {
|
|||||||
static displayName = 'Span';
|
static displayName = 'Span';
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
...pathProps,
|
||||||
|
frame: PropTypes.shape({
|
||||||
content: PropTypes.string.isRequired,
|
content: PropTypes.string.isRequired,
|
||||||
dx: numberProp,
|
dx: numberProp,
|
||||||
dy: numberProp,
|
dy: numberProp,
|
||||||
px: numberProp,
|
px: numberProp,
|
||||||
py: numberProp
|
py: numberProp,
|
||||||
|
font: PropTypes.shape(fontProps)
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <RNSVGSpan {...this.props}/>;
|
return <RNSVGSpan
|
||||||
|
{...this.extractProps({
|
||||||
|
...this.props,
|
||||||
|
x: null,
|
||||||
|
y: null
|
||||||
|
})}
|
||||||
|
{...this.props.frame}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,4 +14,5 @@
|
|||||||
#import "RNSVGRenderable.h"
|
#import "RNSVGRenderable.h"
|
||||||
|
|
||||||
@interface RNSVGGroup : RNSVGRenderable <RNSVGContainer>
|
@interface RNSVGGroup : RNSVGRenderable <RNSVGContainer>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
{
|
{
|
||||||
RNSVGSvgView* svg = [self getSvgView];
|
RNSVGSvgView* svg = [self getSvgView];
|
||||||
[self clip:context];
|
[self clip:context];
|
||||||
|
|
||||||
[self traverseSubviews:^(RNSVGNode *node) {
|
[self traverseSubviews:^(RNSVGNode *node) {
|
||||||
if (node.responsible && !svg.responsible) {
|
if (node.responsible && !svg.responsible) {
|
||||||
svg.responsible = YES;
|
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) {
|
[self traverseSubviews:^(RNSVGNode *node) {
|
||||||
[node mergeProperties:target mergeList:mergeList];
|
[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) {
|
for (RNSVGNode *node in self.subviews) {
|
||||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
{
|
{
|
||||||
// todo: add detection if path has changed since last update.
|
// todo: add detection if path has changed since last update.
|
||||||
self.d = [self getPath:context];
|
self.d = [self getPath:context];
|
||||||
|
|
||||||
CGPathRef path = self.d;
|
CGPathRef path = self.d;
|
||||||
if ((!self.fill && !self.stroke) || !path) {
|
if ((!self.fill && !self.stroke) || !path) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
- (instancetype)initWithBezierCurves:(NSArray *)bezierCurves
|
- (instancetype)initWithBezierCurves:(NSArray *)bezierCurves
|
||||||
{
|
{
|
||||||
|
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_bezierCurves = bezierCurves;
|
_bezierCurves = bezierCurves;
|
||||||
_bezierIndex = 0;
|
_bezierIndex = 0;
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <CoreText/CoreText.h>
|
#import <CoreText/CoreText.h>
|
||||||
#import "RNSVGPath.h"
|
#import "RNSVGPath.h"
|
||||||
#import "RNSVGTextFrame.h"
|
|
||||||
|
|
||||||
@interface RNSVGSpan : RNSVGPath
|
@interface RNSVGSpan : RNSVGPath
|
||||||
|
|
||||||
@property (nonatomic, assign) CGFloat *dx;
|
@property (nonatomic, assign) CGFloat *dx;
|
||||||
@property (nonatomic, assign) CGFloat *dy;
|
@property (nonatomic, assign) CGFloat *dy;
|
||||||
@property (nonatomic, assign) NSString *px;
|
@property (nonatomic, strong) NSString *px;
|
||||||
@property (nonatomic, assign) NSString *py;
|
@property (nonatomic, strong) NSString *py;
|
||||||
@property (nonatomic, assign) CTFontRef font;
|
@property (nonatomic, assign) CTFontRef font;
|
||||||
|
@property (nonatomic, strong) NSString *content;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -6,17 +6,82 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#import "RNSVGSpan.h"
|
#import "RNSVGSpan.h"
|
||||||
#import "RNSVGBezierPath.h"
|
#import "RNSVGBezierPath.h"
|
||||||
#import <CoreText/CoreText.h>
|
|
||||||
|
|
||||||
@implementation RNSVGSpan
|
@implementation RNSVGSpan
|
||||||
|
|
||||||
- (CGPathRef)getPath:(CGContextRef)context
|
- (CGPathRef)getPath:(CGContextRef)context
|
||||||
{
|
{
|
||||||
|
[self setBoundingBox:CGContextGetClipBoundingBox(context)];
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
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);
|
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
|
@end
|
||||||
|
|||||||
@@ -7,10 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "RNSVGPath.h"
|
#import "RNSVGGroup.h"
|
||||||
#import "RNSVGTextFrame.h"
|
|
||||||
|
|
||||||
@interface RNSVGText : RNSVGRenderable
|
@interface RNSVGText : RNSVGGroup
|
||||||
|
|
||||||
@property (nonatomic, assign) CTTextAlignment alignment;
|
@property (nonatomic, assign) CTTextAlignment alignment;
|
||||||
@property (nonatomic, copy) NSArray<NSArray *> *path;
|
@property (nonatomic, copy) NSArray<NSArray *> *path;
|
||||||
|
|||||||
@@ -39,9 +39,56 @@
|
|||||||
// RNSVGFreeTextFrame(_textFrame);
|
// 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
|
- (CGPathRef)getPath:(CGContextRef)context
|
||||||
{
|
{
|
||||||
|
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
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;
|
// RNSVGTextFrame frame = self.textFrame;
|
||||||
// for (int i = 0; i < frame.count; i++) {
|
// for (int i = 0; i < frame.count; i++) {
|
||||||
// CGFloat shift;
|
// CGFloat shift;
|
||||||
@@ -66,59 +113,6 @@
|
|||||||
// CGPathRelease(line);
|
// CGPathRelease(line);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return (CGPathRef)CFAutorelease(path);
|
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -25,5 +25,6 @@ RCT_EXPORT_VIEW_PROPERTY(dy, CGFloat)
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(px, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(px, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(py, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(py, NSString)
|
||||||
RCT_REMAP_VIEW_PROPERTY(font, font, RNSVGFont)
|
RCT_REMAP_VIEW_PROPERTY(font, font, RNSVGFont)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(content, NSString)
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import SerializablePath from '../SerializablePath';
|
import SerializablePath from '../SerializablePath';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, {Children} from 'react';
|
import React, {Children} from 'react';
|
||||||
import {fontAndRenderPropsKeys} from '../props';
|
import {fontAndRenderPropsKeys, fontPropsKeys} from '../props';
|
||||||
import Span from '../../elements/Span';
|
import Span from '../../elements/Span';
|
||||||
|
|
||||||
const fontRegExp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm%]*(?:\s*\/.*?)?\s+)?\s*"?([^"]*)/i;
|
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 {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user