1/**
2 * Copyright (c) 2015-present, Horcrux.
3 * All rights reserved.
4 *
5 * This source code is licensed under the MIT-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9#import "RNSVGViewBox.h"
10#import <math.h>
11#import "RNSVGUse.h"
12
13@implementation RNSVGViewBox
14
15+ (CGAffineTransform)getTransform:(CGRect)vbRect
16                            eRect:(CGRect)eRect
17                            align:(NSString *)align
18                      meetOrSlice:(RNSVGVBMOS)meetOrSlice
19{
20  // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
21
22  // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute
23  // respectively.
24  CGFloat vbX = CGRectGetMinX(vbRect);
25  CGFloat vbY = CGRectGetMinY(vbRect);
26  CGFloat vbWidth = CGRectGetWidth(vbRect);
27  CGFloat vbHeight = CGRectGetHeight(vbRect);
28
29  // Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
30  CGFloat eX = CGRectGetMinX(eRect);
31  CGFloat eY = CGRectGetMinY(eRect);
32  CGFloat eWidth = CGRectGetWidth(eRect);
33  CGFloat eHeight = CGRectGetHeight(eRect);
34
35  // Let align be the align value of preserveAspectRatio, or 'xMidyMid' if preserveAspectRatio is not defined.
36
37  // Let meetOrSlice be the meetOrSlice value of preserveAspectRatio, or 'meet' if preserveAspectRatio is not defined or
38  // if meetOrSlice is missing from this value.
39
40  // Initialize scale-x to e-width/vb-width.
41  CGFloat scaleX = eWidth / vbWidth;
42
43  // Initialize scale-y to e-height/vb-height.
44  CGFloat scaleY = eHeight / vbHeight;
45
46  // Initialize translate-x to e-x - (vb-x * scale-x).
47  // Initialize translate-y to e-y - (vb-y * scale-y).
48  CGFloat translateX = eX - (vbX * scaleX);
49  CGFloat translateY = eY - (vbY * scaleY);
50
51  // If align is 'none'
52  if (meetOrSlice == kRNSVGVBMOSNone) {
53    // Let scale be set the smaller value of scale-x and scale-y.
54    // Assign scale-x and scale-y to scale.
55    CGFloat scale = scaleX = scaleY = fmin(scaleX, scaleY);
56
57    // If scale is greater than 1
58    if (scale > 1) {
59      // Minus translateX by (eWidth / scale - vbWidth) / 2
60      // Minus translateY by (eHeight / scale - vbHeight) / 2
61      translateX -= (eWidth / scale - vbWidth) / 2;
62      translateY -= (eHeight / scale - vbHeight) / 2;
63    } else {
64      translateX -= (eWidth - vbWidth * scale) / 2;
65      translateY -= (eHeight - vbHeight * scale) / 2;
66    }
67  } else {
68    // If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller.
69    // Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the
70    // larger.
71    if (![align isEqualToString:@"none"] && meetOrSlice == kRNSVGVBMOSMeet) {
72      scaleX = scaleY = fmin(scaleX, scaleY);
73    } else if (![align isEqualToString:@"none"] && meetOrSlice == kRNSVGVBMOSSlice) {
74      scaleX = scaleY = fmax(scaleX, scaleY);
75    }
76
77    // If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
78    if ([align containsString:@"xMid"]) {
79      translateX += (eWidth - vbWidth * scaleX) / 2.0;
80    }
81
82    // If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
83    if ([align containsString:@"xMax"]) {
84      translateX += (eWidth - vbWidth * scaleX);
85    }
86
87    // If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
88    if ([align containsString:@"YMid"]) {
89      translateY += (eHeight - vbHeight * scaleY) / 2.0;
90    }
91
92    // If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
93    if ([align containsString:@"YMax"]) {
94      translateY += (eHeight - vbHeight * scaleY);
95    }
96  }
97
98  // The transform applied to content contained by the element is given by
99  // translate(translate-x, translate-y) scale(scale-x, scale-y).
100  CGAffineTransform transform = CGAffineTransformMakeTranslation(translateX, translateY);
101  return CGAffineTransformScale(transform, scaleX, scaleY);
102}
103
104@end
105