Fix ViewBox, images, transforms, and, gradients; on ios.
Make getContextWidth/Height and setupGlyphContext take clip bounding box and affine transform matrix into consideration.
This commit is contained in:
Родитель
f1657d9ddf
Коммит
3cafc34cb2
|
@ -50,7 +50,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
// todo: throw error
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_type = kRNSVGLinearGradient;
|
||||
_colors = colors;
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
// todo: throw error
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_type = kRNSVGRadialGradient;
|
||||
_colors = colors;
|
||||
}
|
||||
|
@ -84,27 +84,27 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
float width = CGRectGetWidth(rect);
|
||||
float x = 0.0;
|
||||
float y = 0.0;
|
||||
|
||||
|
||||
if (_useObjectBoundingBox) {
|
||||
x = CGRectGetMinX(rect);
|
||||
y = CGRectGetMinY(rect);
|
||||
}
|
||||
|
||||
|
||||
return CGRectMake(x, y, width, height);
|
||||
}
|
||||
|
||||
- (void)paintLinearGradient:(CGContextRef)context
|
||||
{
|
||||
|
||||
|
||||
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors offset:0]);
|
||||
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||
|
||||
|
||||
CGRect rect = [self getPaintRect:context];
|
||||
float height = CGRectGetHeight(rect);
|
||||
float width = CGRectGetWidth(rect);
|
||||
float offsetX = CGRectGetMinX(rect);
|
||||
float offsetY = CGRectGetMinY(rect);
|
||||
|
||||
|
||||
CGFloat x1 = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:0]
|
||||
relative:width
|
||||
offset:offsetX];
|
||||
|
@ -117,8 +117,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
CGFloat y2 = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:3]
|
||||
relative:height
|
||||
offset:offsetY];
|
||||
|
||||
|
||||
|
||||
|
||||
CGContextConcatCTM(context, _transform);
|
||||
CGContextDrawLinearGradient(context, gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions);
|
||||
CGGradientRelease(gradient);
|
||||
}
|
||||
|
@ -127,13 +128,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
{
|
||||
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors offset:0]);
|
||||
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||
|
||||
|
||||
CGRect rect = [self getPaintRect:context];
|
||||
float height = CGRectGetHeight(rect);
|
||||
float width = CGRectGetWidth(rect);
|
||||
float offsetX = CGRectGetMinX(rect);
|
||||
float offsetY = CGRectGetMinY(rect);
|
||||
|
||||
|
||||
CGFloat rx = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:2]
|
||||
relative:width
|
||||
offset:0];
|
||||
|
@ -152,10 +153,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
CGFloat cy = [RNSVGPercentageConverter stringToFloat:(NSString *)[_points objectAtIndex:5]
|
||||
relative:height
|
||||
offset:offsetY] / (ry / rx);
|
||||
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformMakeScale(1, ry / rx);
|
||||
CGContextConcatCTM(context, transform);
|
||||
|
||||
|
||||
CGContextConcatCTM(context, _transform);
|
||||
CGContextDrawRadialGradient(context, gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions);
|
||||
CGGradientRelease(gradient);
|
||||
}
|
||||
|
|
|
@ -28,17 +28,17 @@
|
|||
if (node.responsible && !svg.responsible) {
|
||||
svg.responsible = YES;
|
||||
}
|
||||
|
||||
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)node mergeProperties:self];
|
||||
}
|
||||
|
||||
|
||||
[node renderTo:context];
|
||||
|
||||
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)node resetProperties];
|
||||
}
|
||||
|
||||
|
||||
return YES;
|
||||
}];
|
||||
[self popGlyphContext];
|
||||
|
@ -46,8 +46,13 @@
|
|||
|
||||
- (void)setupGlyphContext:(CGContextRef)context
|
||||
{
|
||||
_glyphContext = [[RNSVGGlyphContext alloc] initWithDimensions:[self getContextWidth]
|
||||
height:[self getContextHeight]];
|
||||
CGRect clipBounds = CGContextGetClipBoundingBox(context);
|
||||
clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix);
|
||||
CGFloat width = CGRectGetWidth(clipBounds);
|
||||
CGFloat height = CGRectGetHeight(clipBounds);
|
||||
|
||||
_glyphContext = [[RNSVGGlyphContext alloc] initWithDimensions:width
|
||||
height:height];
|
||||
}
|
||||
|
||||
- (RNSVGGlyphContext *)getGlyphContext
|
||||
|
@ -88,7 +93,7 @@
|
|||
if (hitSelf) {
|
||||
return hitSelf;
|
||||
}
|
||||
|
||||
|
||||
CGAffineTransform matrix = CGAffineTransformConcat(self.matrix, transform);
|
||||
|
||||
CGPathRef clip = [self getClipPath];
|
||||
|
@ -96,26 +101,26 @@
|
|||
CGPathRef transformedClipPath = CGPathCreateCopyByTransformingPath(clip, &matrix);
|
||||
BOOL insideClipPath = CGPathContainsPoint(clip, nil, point, self.clipRule == kRNSVGCGFCRuleEvenodd);
|
||||
CGPathRelease(transformedClipPath);
|
||||
|
||||
|
||||
if (!insideClipPath) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) {
|
||||
if (![node isKindOfClass:[RNSVGNode class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (event) {
|
||||
node.active = NO;
|
||||
} else if (node.active) {
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
UIView *hitChild = [node hitTest: point withEvent:event withTransform:matrix];
|
||||
|
||||
|
||||
if (hitChild) {
|
||||
node.active = YES;
|
||||
return (node.responsible || (node != hitChild)) ? hitChild : self;
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
} else {
|
||||
_imageRatio = 0.0;
|
||||
}
|
||||
|
||||
|
||||
_image = CGImageRetain([RCTConvert CGImage:src]);
|
||||
[self invalidate];
|
||||
}
|
||||
|
@ -102,11 +102,11 @@
|
|||
CGPathRef hitArea = CGPathCreateWithRect(rect, nil);
|
||||
[self setHitArea:hitArea];
|
||||
CGPathRelease(hitArea);
|
||||
|
||||
|
||||
CGContextSaveGState(context);
|
||||
CGContextTranslateCTM(context, 0, rect.size.height + 2 * rect.origin.y);
|
||||
CGContextScaleCTM(context, 1, -1);
|
||||
|
||||
|
||||
// apply viewBox transform on Image render.
|
||||
CGFloat imageRatio = _imageRatio;
|
||||
CGFloat rectWidth = CGRectGetWidth(rect);
|
||||
|
@ -115,7 +115,7 @@
|
|||
CGFloat rectY = CGRectGetMinY(rect);
|
||||
CGFloat rectRatio = rectWidth / rectHeight;
|
||||
CGRect renderRect;
|
||||
|
||||
|
||||
if (!imageRatio || imageRatio == rectRatio) {
|
||||
renderRect = rect;
|
||||
} else if (imageRatio < rectRatio) {
|
||||
|
@ -123,29 +123,34 @@
|
|||
} else {
|
||||
renderRect = CGRectMake(0, 0, rectWidth, rectWidth / imageRatio);
|
||||
}
|
||||
|
||||
|
||||
CGFloat canvasLeft = [self getContextLeft];
|
||||
CGFloat canvasTop = [self getContextTop];
|
||||
CGRect vbRect = CGRectMake(0, 0, CGRectGetWidth(renderRect), CGRectGetHeight(renderRect));
|
||||
CGRect eRect = CGRectMake([self getContextLeft], [self getContextTop], rectWidth, rectHeight);
|
||||
|
||||
CGRect eRect = CGRectMake(canvasLeft, canvasTop, rectWidth, rectHeight);
|
||||
|
||||
CGAffineTransform transform = [RNSVGViewBox getTransform:vbRect eRect:eRect align:self.align meetOrSlice:self.meetOrSlice fromSymbol:NO];
|
||||
|
||||
|
||||
CGFloat dx = rectX + canvasLeft;
|
||||
CGFloat dy = rectY + canvasTop;
|
||||
renderRect = CGRectApplyAffineTransform(renderRect, CGAffineTransformMakeTranslation(-dx, -dy));
|
||||
renderRect = CGRectApplyAffineTransform(renderRect, transform);
|
||||
renderRect = CGRectApplyAffineTransform(renderRect, CGAffineTransformMakeTranslation(rectX, rectY));
|
||||
|
||||
|
||||
[self clip:context];
|
||||
CGContextClipToRect(context, rect);
|
||||
|
||||
|
||||
CGContextDrawImage(context, renderRect, _image);
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (CGRect)getRect:(CGContextRef)context
|
||||
{
|
||||
return CGRectMake([self relativeOnWidth:self.x],
|
||||
[self relativeOnHeight:self.y],
|
||||
[self relativeOnWidth:self.width],
|
||||
[self relativeOnHeight:self.height]);
|
||||
CGFloat x = [self relativeOnWidth:self.x];
|
||||
CGFloat y = [self relativeOnHeight:self.y];
|
||||
CGFloat width = [self relativeOnWidth:self.width];
|
||||
CGFloat height = [self relativeOnHeight:self.height];
|
||||
return CGRectMake(x, y, x + width, y + height);
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
|
|
|
@ -65,9 +65,9 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
_textRoot = (RNSVGGroup*)node;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
UIView* parent = [node superview];
|
||||
|
||||
|
||||
if (![node isKindOfClass:[RNSVGNode class]]) {
|
||||
node = nil;
|
||||
} else {
|
||||
|
@ -75,7 +75,7 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return _textRoot;
|
||||
}
|
||||
|
||||
|
@ -95,11 +95,11 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
if (root == nil) {
|
||||
return DEFAULT_FONT_SIZE;
|
||||
}
|
||||
|
||||
|
||||
if (glyphContext == nil) {
|
||||
glyphContext = [root getGlyphContext];
|
||||
}
|
||||
|
||||
|
||||
return [glyphContext getFontSize];
|
||||
}
|
||||
|
||||
|
@ -113,13 +113,13 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
if (opacity == _opacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (opacity <= 0) {
|
||||
opacity = 0;
|
||||
} else if (opacity > 1) {
|
||||
opacity = 1;
|
||||
}
|
||||
|
||||
|
||||
[self invalidate];
|
||||
_transparent = opacity < 1;
|
||||
_opacity = opacity;
|
||||
|
@ -175,14 +175,14 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
CGPathRelease(_cachedClipPath);
|
||||
_cachedClipPath = CGPathRetain([[[self getSvgView] getDefinedClipPath:self.clipPath] getPath:context]);
|
||||
}
|
||||
|
||||
|
||||
return [self getClipPath];
|
||||
}
|
||||
|
||||
- (void)clip:(CGContextRef)context
|
||||
{
|
||||
CGPathRef clipPath = [self getClipPath:context];
|
||||
|
||||
|
||||
if (clipPath) {
|
||||
CGContextAddPath(context, clipPath);
|
||||
if (self.clipRule == kRNSVGCGFCRuleEvenodd) {
|
||||
|
@ -207,7 +207,7 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
// hitTest delagate
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
|
||||
|
||||
// abstract
|
||||
return nil;
|
||||
}
|
||||
|
@ -223,9 +223,9 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
if (_svgView) {
|
||||
return _svgView;
|
||||
}
|
||||
|
||||
|
||||
__kindof UIView *parent = self.superview;
|
||||
|
||||
|
||||
if ([parent class] == [RNSVGSvgView class]) {
|
||||
_svgView = parent;
|
||||
} else if ([parent isKindOfClass:[RNSVGNode class]]) {
|
||||
|
@ -234,7 +234,7 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
} else {
|
||||
RCTLogError(@"RNSVG: %@ should be descendant of a SvgViewShadow.", NSStringFromClass(self.class));
|
||||
}
|
||||
|
||||
|
||||
return _svgView;
|
||||
}
|
||||
|
||||
|
@ -259,12 +259,22 @@ CGFloat const DEFAULT_FONT_SIZE = 12;
|
|||
|
||||
- (CGFloat)getContextWidth
|
||||
{
|
||||
return CGRectGetWidth([[self getSvgView] getContextBounds]);
|
||||
RNSVGGroup * root = [self getTextRoot];
|
||||
if (root == nil) {
|
||||
return CGRectGetWidth([[self getSvgView] getContextBounds]);
|
||||
} else {
|
||||
return [[root getGlyphContext] getWidth];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)getContextHeight
|
||||
{
|
||||
return CGRectGetHeight([[self getSvgView] getContextBounds]);
|
||||
RNSVGGroup * root = [self getTextRoot];
|
||||
if (root == nil) {
|
||||
return CGRectGetHeight([[self getSvgView] getContextBounds]);
|
||||
} else {
|
||||
return [[root getGlyphContext] getHeight];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)getContextLeft
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
- (instancetype)initWithDimensions:(CGFloat)width height:(CGFloat)height;
|
||||
|
||||
- (CGFloat)getWidth;
|
||||
- (CGFloat)getHeight;
|
||||
- (CGFloat)getFontSize;
|
||||
- (void)pushContext:(NSDictionary *)font;
|
||||
- (void)pushContext:(NSDictionary *)font deltaX:(NSArray<NSString *> *)deltaX deltaY:(NSArray<NSString *> *)deltaY positionX:(NSArray<NSString *> *)positionX positionY:(NSArray<NSString *> *)positionY;
|
||||
|
|
|
@ -41,6 +41,15 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (CGFloat) getWidth
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
- (CGFloat) getHeight {
|
||||
return _height;
|
||||
}
|
||||
|
||||
- (CGFloat) getFontSize
|
||||
{
|
||||
return _fontSize;
|
||||
|
@ -49,7 +58,7 @@
|
|||
- (void)pushContext:(NSDictionary *)font
|
||||
{
|
||||
CGPoint location = _currentLocation;
|
||||
|
||||
|
||||
[_locationContext addObject:[NSValue valueWithCGPoint:location]];
|
||||
[_fontContext addObject:font ? font : @{}];
|
||||
[_xContext addObject:[NSNumber numberWithFloat:location.x]];
|
||||
|
@ -59,15 +68,15 @@
|
|||
- (void)pushContext:(NSDictionary *)font deltaX:(NSArray<NSString *> *)deltaX deltaY:(NSArray<NSString *> *)deltaY positionX:(NSArray<NSString *> *)positionX positionY:(NSArray<NSString *> *)positionY
|
||||
{
|
||||
CGPoint location = _currentLocation;
|
||||
|
||||
|
||||
if (positionX) {
|
||||
location.x = [RNSVGPercentageConverter stringToFloat:[positionX firstObject] relative:_width offset:0];
|
||||
}
|
||||
|
||||
|
||||
if (positionY) {
|
||||
location.y = [RNSVGPercentageConverter stringToFloat:[positionY firstObject] relative:_height offset:0];
|
||||
}
|
||||
|
||||
|
||||
[_locationContext addObject:[NSValue valueWithCGPoint:location]];
|
||||
[_fontContext addObject:font ? font : @{}];
|
||||
[_deltaXContext addObject:deltaX ? deltaX : @[]];
|
||||
|
@ -84,11 +93,11 @@
|
|||
[_deltaXContext removeLastObject];
|
||||
[_deltaYContext removeLastObject];
|
||||
[_xContext removeLastObject];
|
||||
|
||||
|
||||
if (_xContext.count) {
|
||||
[_xContext replaceObjectAtIndex:_xContext.count - 1 withObject:x];
|
||||
}
|
||||
|
||||
|
||||
if (_locationContext.count) {
|
||||
_currentLocation = [[_locationContext lastObject] CGPointValue];
|
||||
_currentLocation.x = [x floatValue];
|
||||
|
@ -102,17 +111,17 @@
|
|||
CGPoint currentLocation = _currentLocation;
|
||||
NSNumber *dx = [self getNextDelta:_deltaXContext];
|
||||
currentLocation.x += [dx floatValue];
|
||||
|
||||
|
||||
NSNumber *dy = [self getNextDelta:_deltaYContext];
|
||||
currentLocation.y += [dy floatValue];
|
||||
|
||||
|
||||
for (NSUInteger i = 0; i < _locationContext.count; i++) {
|
||||
CGPoint point = [[_locationContext objectAtIndex:i] CGPointValue];
|
||||
point.x += [dx floatValue];
|
||||
point.y += [dy floatValue];
|
||||
[_locationContext replaceObjectAtIndex:i withObject:[NSValue valueWithCGPoint:point]];
|
||||
}
|
||||
|
||||
|
||||
_currentLocation = currentLocation;
|
||||
NSNumber *x = [NSNumber numberWithFloat:currentLocation.x + offset.x + glyphWidth];
|
||||
[_xContext replaceObjectAtIndex:_xContext.count - 1 withObject:x];
|
||||
|
@ -128,14 +137,14 @@
|
|||
if (value == nil) {
|
||||
value = [delta firstObject];
|
||||
}
|
||||
|
||||
|
||||
if (delta.count) {
|
||||
NSMutableArray *mutableDelta = [delta mutableCopy];
|
||||
[mutableDelta removeObjectAtIndex:0];
|
||||
[deltaContext replaceObjectAtIndex:index withObject:[mutableDelta copy]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -152,26 +161,26 @@
|
|||
if (!fontFamily) {
|
||||
fontFamily = font[@"fontFamily"];
|
||||
}
|
||||
|
||||
|
||||
if (fontSize == nil) {
|
||||
fontSize = [f numberFromString:font[@"fontSize"]];
|
||||
}
|
||||
|
||||
|
||||
if (!fontWeight) {
|
||||
fontWeight = font[@"fontWeight"];
|
||||
}
|
||||
if (!fontStyle) {
|
||||
fontStyle = font[@"fontStyle"];
|
||||
}
|
||||
|
||||
|
||||
if (fontFamily && fontSize && fontWeight && fontStyle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOL fontFamilyFound = NO;
|
||||
NSArray *supportedFontFamilyNames = [UIFont familyNames];
|
||||
|
||||
|
||||
if ([supportedFontFamilyNames containsObject:fontFamily]) {
|
||||
fontFamilyFound = YES;
|
||||
} else {
|
||||
|
@ -183,7 +192,7 @@
|
|||
}
|
||||
}
|
||||
fontFamily = fontFamilyFound ? fontFamily : nil;
|
||||
|
||||
|
||||
return (__bridge CTFontRef)[RCTFont updateFont:nil
|
||||
withFamily:fontFamily
|
||||
size:fontSize
|
||||
|
|
|
@ -53,22 +53,22 @@
|
|||
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;
|
||||
if (font != nil) {
|
||||
|
@ -85,19 +85,19 @@
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -107,23 +107,23 @@
|
|||
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) {
|
||||
|
@ -135,19 +135,19 @@
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@
|
|||
}
|
||||
return YES;
|
||||
}];
|
||||
|
||||
|
||||
_bezierTransformer = bezierTransformer;
|
||||
}
|
||||
|
||||
|
@ -169,13 +169,13 @@
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
#import "RNSVGGlyphContext.h"
|
||||
|
||||
@implementation RNSVGText
|
||||
{
|
||||
RNSVGText *_textRoot;
|
||||
RNSVGGlyphContext *_glyphContext;
|
||||
}
|
||||
|
||||
- (void)setTextAnchor:(RNSVGTextAnchor)textAnchor
|
||||
{
|
||||
|
@ -24,20 +28,27 @@
|
|||
{
|
||||
[self clip:context];
|
||||
CGContextSaveGState(context);
|
||||
|
||||
[self setupGlyphContext:context];
|
||||
|
||||
CGPathRef path = [self getGroupPath:context];
|
||||
CGAffineTransform transform = [self getAlignTransform:path];
|
||||
CGContextConcatCTM(context, transform);
|
||||
[self renderGroupTo:context];
|
||||
[self releaseCachedPath];
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
|
||||
|
||||
|
||||
CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &transform);
|
||||
[self setHitArea:transformedPath];
|
||||
CGPathRelease(transformedPath);
|
||||
}
|
||||
|
||||
- (void)setupGlyphContext:(CGContextRef)context
|
||||
{
|
||||
_glyphContext = [[RNSVGGlyphContext alloc] initWithDimensions:[self getContextWidth]
|
||||
height:[self getContextHeight]];
|
||||
}
|
||||
|
||||
// release the cached CGPathRef for RNSVGTSpan
|
||||
- (void)releaseCachedPath
|
||||
{
|
||||
|
@ -53,16 +64,17 @@
|
|||
[self pushGlyphContext];
|
||||
CGPathRef groupPath = [super getPath:context];
|
||||
[self popGlyphContext];
|
||||
|
||||
|
||||
return groupPath;
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
[self setupGlyphContext:context];
|
||||
CGPathRef groupPath = [self getGroupPath:context];
|
||||
CGAffineTransform transform = [self getAlignTransform:groupPath];
|
||||
[self releaseCachedPath];
|
||||
|
||||
|
||||
return (CGPathRef)CFAutorelease(CGPathCreateCopyByTransformingPath(groupPath, &transform));
|
||||
}
|
||||
|
||||
|
@ -77,7 +89,7 @@
|
|||
{
|
||||
CGFloat width = CGRectGetWidth(CGPathGetBoundingBox(path));
|
||||
CGFloat x = 0;
|
||||
|
||||
|
||||
switch ([self getComputedTextAnchor]) {
|
||||
case kRNSVGTextAnchorMiddle:
|
||||
x = -width / 2;
|
||||
|
@ -87,7 +99,7 @@
|
|||
break;
|
||||
default: ;
|
||||
}
|
||||
|
||||
|
||||
return CGAffineTransformMakeTranslation(x, 0);
|
||||
}
|
||||
|
||||
|
@ -96,7 +108,7 @@
|
|||
RNSVGTextAnchor anchor = self.textAnchor;
|
||||
if (self.subviews.count > 0) {
|
||||
RNSVGText *child = [self.subviews firstObject];
|
||||
|
||||
|
||||
while (child.subviews.count && anchor == kRNSVGTextAnchorAuto) {
|
||||
anchor = child.textAnchor;
|
||||
child = [child.subviews firstObject];
|
||||
|
@ -105,6 +117,27 @@
|
|||
return anchor;
|
||||
}
|
||||
|
||||
- (RNSVGText *)getTextRoot
|
||||
{
|
||||
if (!_textRoot) {
|
||||
_textRoot = self;
|
||||
while (_textRoot && [_textRoot class] != [RNSVGText class]) {
|
||||
if (![_textRoot isKindOfClass:[RNSVGText class]]) {
|
||||
//todo: throw exception here
|
||||
break;
|
||||
}
|
||||
_textRoot = (RNSVGText*)[_textRoot superview];
|
||||
}
|
||||
}
|
||||
|
||||
return _textRoot;
|
||||
}
|
||||
|
||||
- (RNSVGGlyphContext *)getGlyphContext
|
||||
{
|
||||
return _glyphContext;
|
||||
}
|
||||
|
||||
- (void)pushGlyphContext
|
||||
{
|
||||
[[[self getTextRoot] getGlyphContext] pushContext:self.font
|
||||
|
|
|
@ -15,41 +15,40 @@
|
|||
+ (CGAffineTransform)getTransform:(CGRect)vbRect eRect:(CGRect)eRect align:(NSString *)align meetOrSlice:(RNSVGVBMOS)meetOrSlice fromSymbol:(BOOL)fromSymbol
|
||||
{
|
||||
// based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
|
||||
|
||||
|
||||
// Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively.
|
||||
CGFloat vbX = CGRectGetMinX(vbRect);
|
||||
CGFloat vbY = CGRectGetMinY(vbRect);
|
||||
CGFloat vbWidth = CGRectGetWidth(vbRect);
|
||||
CGFloat vbHeight = CGRectGetHeight(vbRect);
|
||||
|
||||
|
||||
// Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
|
||||
CGFloat eX = CGRectGetMinX(eRect);
|
||||
CGFloat eY = CGRectGetMinY(eRect);
|
||||
CGFloat eWidth = CGRectGetWidth(eRect);
|
||||
CGFloat eHeight = CGRectGetHeight(eRect);
|
||||
|
||||
|
||||
// Let align be the align value of preserveAspectRatio, or 'xMidyMid' if preserveAspectRatio is not defined.
|
||||
|
||||
|
||||
// Let meetOrSlice be the meetOrSlice value of preserveAspectRatio, or 'meet' if preserveAspectRatio is not defined or if meetOrSlice is missing from this value.
|
||||
|
||||
|
||||
// Initialize scale-x to e-width/vb-width.
|
||||
CGFloat scaleX = eWidth / vbWidth;
|
||||
|
||||
|
||||
// Initialize scale-y to e-height/vb-height.
|
||||
CGFloat scaleY = eHeight / vbHeight;
|
||||
|
||||
|
||||
// Initialize translate-x to vb-x - e-x.
|
||||
// Initialize translate-y to vb-y - e-y.
|
||||
CGFloat translateX = vbX - eX;
|
||||
CGFloat translateY = vbY - eY;
|
||||
|
||||
|
||||
// Initialize translate-x to e-x - (vb-x * scale-x).
|
||||
// Initialize translate-y to e-y - (vb-y * scale-y).
|
||||
CGFloat translateX = eX - (vbX * scaleX);
|
||||
CGFloat translateY = eY - (vbY * scaleY);
|
||||
|
||||
// If align is 'none'
|
||||
if (meetOrSlice == kRNSVGVBMOSNone) {
|
||||
// Let scale be set the smaller value of scale-x and scale-y.
|
||||
// Assign scale-x and scale-y to scale.
|
||||
CGFloat scale = scaleX = scaleY = fmin(scaleX, scaleY);
|
||||
|
||||
|
||||
// If scale is greater than 1
|
||||
if (scale > 1) {
|
||||
// Minus translateX by (eWidth / scale - vbWidth) / 2
|
||||
|
@ -68,30 +67,32 @@
|
|||
} else if (![align isEqualToString: @"none"] && meetOrSlice == kRNSVGVBMOSSlice) {
|
||||
scaleX = scaleY = fmax(scaleX, scaleY);
|
||||
}
|
||||
|
||||
// If align contains 'xMid', minus (e-width / scale-x - vb-width) / 2 from transform-x.
|
||||
|
||||
// If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
|
||||
if ([align containsString:@"xMid"]) {
|
||||
translateX -= (eWidth / scaleX - vbWidth) / 2;
|
||||
translateX += (eWidth - vbWidth * scaleX) / 2.0;
|
||||
}
|
||||
|
||||
// If align contains 'xMax', minus (e-width / scale-x - vb-width) from transform-x.
|
||||
|
||||
// If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
|
||||
if ([align containsString:@"xMax"]) {
|
||||
translateX -= eWidth / scaleX - vbWidth;
|
||||
translateX += (eWidth - vbWidth * scaleX);
|
||||
}
|
||||
|
||||
// If align contains 'yMid', minus (e-height / scale-y - vb-height) / 2 from transform-y.
|
||||
|
||||
// If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
|
||||
if ([align containsString:@"YMid"]) {
|
||||
translateY -= (eHeight / scaleY - vbHeight) / 2;
|
||||
translateY += (eHeight - vbHeight * scaleY) / 2.0;
|
||||
}
|
||||
|
||||
// If align contains 'yMax', minus (e-height / scale-y - vb-height) from transform-y.
|
||||
|
||||
// If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
|
||||
if ([align containsString:@"YMax"]) {
|
||||
translateY -= eHeight / scaleY - vbHeight;
|
||||
translateY += (eHeight - vbHeight * scaleY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The transform applied to content contained by the element is given by
|
||||
// translate(translate-x, translate-y) scale(scale-x, scale-y).
|
||||
CGAffineTransform transform = CGAffineTransformMakeScale(scaleX, scaleY);
|
||||
return CGAffineTransformTranslate(transform, -translateX * (fromSymbol ? scaleX : 1), -translateY * (fromSymbol ? scaleY : 1));
|
||||
return CGAffineTransformTranslate(transform, translateX, translateY);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Загрузка…
Ссылка в новой задаче