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:
Mikael Sand 2017-08-16 19:06:23 +03:00
Родитель f1657d9ddf
Коммит 3cafc34cb2
9 изменённых файлов: 200 добавлений и 133 удалений

Просмотреть файл

@ -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