From 3cafc34cb2d87adf27596d769974c0d32e8dc8bd Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Wed, 16 Aug 2017 19:06:23 +0300 Subject: [PATCH] Fix ViewBox, images, transforms, and, gradients; on ios. Make getContextWidth/Height and setupGlyphContext take clip bounding box and affine transform matrix into consideration. --- ios/Brushes/RNSVGPainter.m | 28 ++++++++++-------- ios/Elements/RNSVGGroup.m | 31 ++++++++++++-------- ios/Elements/RNSVGImage.m | 37 +++++++++++++---------- ios/RNSVGNode.m | 40 +++++++++++++++---------- ios/Text/RNSVGGlyphContext.h | 2 ++ ios/Text/RNSVGGlyphContext.m | 43 ++++++++++++++++----------- ios/Text/RNSVGTSpan.m | 46 ++++++++++++++--------------- ios/Text/RNSVGText.m | 49 ++++++++++++++++++++++++++----- ios/Utils/RNSVGViewBox.m | 57 ++++++++++++++++++------------------ 9 files changed, 200 insertions(+), 133 deletions(-) diff --git a/ios/Brushes/RNSVGPainter.m b/ios/Brushes/RNSVGPainter.m index c49fd82e..1ba21880 100644 --- a/ios/Brushes/RNSVGPainter.m +++ b/ios/Brushes/RNSVGPainter.m @@ -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); } diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index d881981c..c093e90c 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -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; diff --git a/ios/Elements/RNSVGImage.m b/ios/Elements/RNSVGImage.m index 97ac9f62..9f3764a8 100644 --- a/ios/Elements/RNSVGImage.m +++ b/ios/Elements/RNSVGImage.m @@ -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 diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 09ef0e3a..bf2f8dd3 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -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 diff --git a/ios/Text/RNSVGGlyphContext.h b/ios/Text/RNSVGGlyphContext.h index 96cbc686..8fb0cf3b 100644 --- a/ios/Text/RNSVGGlyphContext.h +++ b/ios/Text/RNSVGGlyphContext.h @@ -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 *)deltaX deltaY:(NSArray *)deltaY positionX:(NSArray *)positionX positionY:(NSArray *)positionY; diff --git a/ios/Text/RNSVGGlyphContext.m b/ios/Text/RNSVGGlyphContext.m index 7f093f8f..b51d8cff 100644 --- a/ios/Text/RNSVGGlyphContext.m +++ b/ios/Text/RNSVGGlyphContext.m @@ -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 *)deltaX deltaY:(NSArray *)deltaY positionX:(NSArray *)positionX positionY:(NSArray *)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 diff --git a/ios/Text/RNSVGTSpan.m b/ios/Text/RNSVGTSpan.m index af973ee3..4aef6bfa 100644 --- a/ios/Text/RNSVGTSpan.m +++ b/ios/Text/RNSVGTSpan.m @@ -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); } diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m index 27205f6a..31fae9fa 100644 --- a/ios/Text/RNSVGText.m +++ b/ios/Text/RNSVGText.m @@ -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 diff --git a/ios/Utils/RNSVGViewBox.m b/ios/Utils/RNSVGViewBox.m index efe1faa3..29651cb8 100644 --- a/ios/Utils/RNSVGViewBox.m +++ b/ios/Utils/RNSVGViewBox.m @@ -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