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 {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}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
#import "RNSVGRenderable.h"
|
||||
|
||||
@interface RNSVGGroup : RNSVGRenderable <RNSVGContainer>
|
||||
|
||||
@end
|
||||
|
||||
@@ -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]]) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
- (instancetype)initWithBezierCurves:(NSArray *)bezierCurves
|
||||
{
|
||||
|
||||
if (self = [super init]) {
|
||||
_bezierCurves = bezierCurves;
|
||||
_bezierIndex = 0;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user