SVGLength.m 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #import "SVGLength.h"
  2. #import "CSSPrimitiveValue.h"
  3. #import "CSSPrimitiveValue_ConfigurablePixelsPerInch.h"
  4. #import "SVGUtils.h"
  5. #import "SVGKDefine_Private.h"
  6. #include <sys/types.h>
  7. #include <sys/sysctl.h>
  8. @interface SVGLength()
  9. @property(nonatomic,strong) CSSPrimitiveValue* internalCSSPrimitiveValue;
  10. @end
  11. @implementation SVGLength
  12. @synthesize unitType;
  13. @synthesize value;
  14. @synthesize valueInSpecifiedUnits;
  15. @synthesize valueAsString;
  16. @synthesize internalCSSPrimitiveValue;
  17. - (id)init
  18. {
  19. NSAssert(FALSE, @"This class must not be init'd. Use the static hepler methods to instantiate it instead");
  20. return nil;
  21. }
  22. - (id)initWithCSSPrimitiveValue:(CSSPrimitiveValue*) pv
  23. {
  24. self = [super init];
  25. if (self) {
  26. self.internalCSSPrimitiveValue = pv;
  27. }
  28. return self;
  29. }
  30. -(float)value
  31. {
  32. return [self.internalCSSPrimitiveValue getFloatValue:self.internalCSSPrimitiveValue.primitiveType];
  33. }
  34. -(SVG_LENGTH_TYPE)unitType
  35. {
  36. switch( self.internalCSSPrimitiveValue.primitiveType )
  37. {
  38. case CSS_CM:
  39. return SVG_LENGTHTYPE_CM;
  40. case CSS_EMS:
  41. return SVG_LENGTHTYPE_EMS;
  42. case CSS_EXS:
  43. return SVG_LENGTHTYPE_EXS;
  44. case CSS_IN:
  45. return SVG_LENGTHTYPE_IN;
  46. case CSS_MM:
  47. return SVG_LENGTHTYPE_MM;
  48. case CSS_PC:
  49. return SVG_LENGTHTYPE_PC;
  50. case CSS_PERCENTAGE:
  51. return SVG_LENGTHTYPE_PERCENTAGE;
  52. case CSS_PT:
  53. return SVG_LENGTHTYPE_PT;
  54. case CSS_PX:
  55. return SVG_LENGTHTYPE_PX;
  56. case CSS_NUMBER:
  57. case CSS_DIMENSION:
  58. return SVG_LENGTHTYPE_NUMBER;
  59. default:
  60. return SVG_LENGTHTYPE_UNKNOWN;
  61. }
  62. }
  63. -(void) newValueSpecifiedUnits:(SVG_LENGTH_TYPE) unitType valueInSpecifiedUnits:(float) valueInSpecifiedUnits
  64. {
  65. NSAssert(FALSE, @"Not supported yet");
  66. }
  67. -(void) convertToSpecifiedUnits:(SVG_LENGTH_TYPE) unitType
  68. {
  69. NSAssert(FALSE, @"Not supported yet");
  70. }
  71. /** Apple calls this method when the class is loaded; that's as good a time as any to calculate the device / screen's PPI */
  72. +(void)initialize
  73. {
  74. cachedDevicePixelsPerInch = [self pixelsPerInchForCurrentDevice];
  75. }
  76. +(SVGLength*) svgLengthZero
  77. {
  78. SVGLength* result = [[SVGLength alloc] initWithCSSPrimitiveValue:nil];
  79. return result;
  80. }
  81. static float cachedDevicePixelsPerInch;
  82. +(SVGLength*) svgLengthFromNSString:(NSString*) s
  83. {
  84. CSSPrimitiveValue* pv = [[CSSPrimitiveValue alloc] init];
  85. pv.pixelsPerInch = cachedDevicePixelsPerInch;
  86. pv.cssText = s;
  87. SVGLength* result = [[SVGLength alloc] initWithCSSPrimitiveValue:pv];
  88. return result;
  89. }
  90. -(float) pixelsValue
  91. {
  92. return [self.internalCSSPrimitiveValue getFloatValue:CSS_PX];
  93. }
  94. -(float) pixelsValueWithDimension:(float)dimension
  95. {
  96. if (self.internalCSSPrimitiveValue.primitiveType == CSS_PERCENTAGE)
  97. return dimension * self.value / 100.0;
  98. return [self pixelsValue];
  99. }
  100. -(float) pixelsValueWithGradientDimension:(float)dimension
  101. {
  102. if (self.internalCSSPrimitiveValue.primitiveType == CSS_PERCENTAGE) {
  103. return dimension * self.value / 100.0;
  104. } else if (self.internalCSSPrimitiveValue.primitiveType == CSS_NUMBER) {
  105. if (self.value >= 0 && self.value <= 1) {
  106. return dimension * self.value;
  107. }
  108. }
  109. return [self pixelsValue];
  110. }
  111. -(float) numberValue
  112. {
  113. return [self.internalCSSPrimitiveValue getFloatValue:CSS_NUMBER];
  114. }
  115. #pragma mark - secret methods needed to provide an implementation on ObjectiveC
  116. +(float) pixelsPerInchForCurrentDevice
  117. {
  118. /** Using this as reference: http://en.wikipedia.org/wiki/Retina_Display and https://www.theiphonewiki.com/wiki/Models
  119. */
  120. size_t size;
  121. sysctlbyname("hw.machine", NULL, &size, NULL, 0);
  122. char *machine = malloc(size);
  123. sysctlbyname("hw.machine", machine, &size, NULL, 0);
  124. NSString *platform = [NSString stringWithUTF8String:machine];
  125. free(machine);
  126. if( [platform hasPrefix:@"iPhone1"]
  127. || [platform hasPrefix:@"iPhone2"]
  128. || [platform hasPrefix:@"iPhone3"])
  129. return 163.0f;
  130. if( [platform hasPrefix:@"iPhone4"]
  131. || [platform hasPrefix:@"iPhone5"]
  132. || [platform hasPrefix:@"iPhone6"]
  133. || [platform hasPrefix:@"iPhone7,2"]
  134. || [platform hasPrefix:@"iPhone8,1"]
  135. || [platform hasPrefix:@"iPhone8,4"]
  136. || [platform hasPrefix:@"iPhone9,1"]
  137. || [platform hasPrefix:@"iPhone9,3"]) {
  138. return 326.0f;
  139. }
  140. if ( [platform hasPrefix:@"iPhone7,1"]
  141. || [platform hasPrefix:@"iPhone8,2"]
  142. || [platform hasPrefix:@"iPhone9,2"]
  143. || [platform hasPrefix:@"iPhone9,4"]) {
  144. return 401.0f;
  145. }
  146. if( [platform hasPrefix:@"iPhone"]) // catch-all for higher-end devices not yet existing
  147. {
  148. NSAssert(FALSE, @"Update your source code or disable assertions: you are using an iPhone that didn't exist when this code was written, we have no idea what the pixel count per inch is!");
  149. return 401.0f;
  150. }
  151. if( [platform hasPrefix:@"iPod1"]
  152. || [platform hasPrefix:@"iPod2"]
  153. || [platform hasPrefix:@"iPod3"])
  154. return 163.0f;
  155. if( [platform hasPrefix:@"iPod4"]
  156. || [platform hasPrefix:@"iPod5"]
  157. || [platform hasPrefix:@"iPod7"])
  158. return 326.0f;
  159. if( [platform hasPrefix:@"iPod"]) // catch-all for higher-end devices not yet existing
  160. {
  161. NSAssert(FALSE, @"Update your source code or disable assertions: you are using an iPod that didn't exist when this code was written, we have no idea what the pixel count per inch is!");
  162. return 326.0f;
  163. }
  164. if( [platform hasPrefix:@"iPad5,1"]
  165. || [platform hasPrefix:@"iPad5,2"])
  166. return 326.0f;
  167. if( [platform hasPrefix:@"iPad1"]
  168. || [platform hasPrefix:@"iPad2"])
  169. return 132.0f;
  170. if( [platform hasPrefix:@"iPad3"]
  171. || [platform hasPrefix:@"iPad4"]
  172. || [platform hasPrefix:@"iPad5,3"]
  173. || [platform hasPrefix:@"iPad5,4"]
  174. || [platform hasPrefix:@"iPad6"]
  175. || [platform hasPrefix:@"iPad7"]
  176. || [platform hasPrefix:@"iPad8"])
  177. return 264.0f;
  178. if( [platform hasPrefix:@"iPad"]) // catch-all for higher-end devices not yet existing
  179. {
  180. NSAssert(FALSE, @"Update your source code or disable assertions: you are using an iPad that didn't exist when this code was written, we have no idea what the pixel count per inch is!");
  181. return 264.0f;
  182. }
  183. if( [platform hasPrefix:@"iWatch1"])
  184. return 326.0f;
  185. if( [platform hasPrefix:@"iWatch"]) // catch-all for higher-end devices not yet existing
  186. {
  187. NSAssert(FALSE, @"Update your source code or disable assertions: you are using an iWatch that didn't exist when this code was written, we have no idea what the pixel count per inch is!");
  188. return 326.0f;
  189. }
  190. if( [platform hasPrefix:@"x86_64"])
  191. {
  192. SVGKitLogWarn(@"[%@] WARNING: you are running on the simulator; it's impossible for us to calculate centimeter/millimeter/inches units correctly", [self class]);
  193. return 132.0f; // Simulator, running on desktop machine
  194. }
  195. NSAssert(FALSE, @"Cannot determine the PPI values for current device; returning 0.0f - hopefully this will crash your code (you CANNOT run SVG's that use CM/IN/MM etc until you fix this)" );
  196. return 0.0f; // Bet you'll get a divide by zero here...
  197. }
  198. @end