diff --git a/android/src/main/java/com/horcrux/svg/SvgView.java b/android/src/main/java/com/horcrux/svg/SvgView.java index 346873cca..3bf2caff3 100644 --- a/android/src/main/java/com/horcrux/svg/SvgView.java +++ b/android/src/main/java/com/horcrux/svg/SvgView.java @@ -22,6 +22,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.ReactCompoundView; import com.facebook.react.uimanager.ReactCompoundViewGroup; @@ -29,6 +30,7 @@ import java.io.ByteArrayOutputStream; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -361,7 +363,17 @@ String toDataURL() { return Base64.encodeToString(bitmapBytes, Base64.NO_WRAP); } - String toDataURL(int width, int height) { + String toDataURL(final ReadableMap options) { + int width; + int height; + if (options.hasKey("size")) { + ReadableMap size = options.getMap("size"); + width = size.hasKey("width") ? size.getInt("width") : getWidth(); + height = size.hasKey("height") ? size.getInt("height") : getHeight(); + } else { + width = getWidth(); + height = getHeight(); + } Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); clearChildCache(); @@ -369,7 +381,15 @@ String toDataURL(int width, int height) { clearChildCache(); this.invalidate(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + + Integer quality = options.hasKey("quality") ? options.getInt("quality") : null; + String format = options.hasKey("format") ? options.getString("format") : null; + if (Objects.equals(format, "jpeg")) { + int qualityValue = (quality != null) ? Math.round(quality * 100) : 100; + bitmap.compress(Bitmap.CompressFormat.JPEG, qualityValue, stream); + } else { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + } bitmap.recycle(); byte[] bitmapBytes = stream.toByteArray(); return Base64.encodeToString(bitmapBytes, Base64.NO_WRAP); diff --git a/android/src/main/java/com/horcrux/svg/SvgViewModule.java b/android/src/main/java/com/horcrux/svg/SvgViewModule.java index 74c2ef382..042e6b37b 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewModule.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewModule.java @@ -68,7 +68,7 @@ public void run() { } else { if (options != null) { successCallback.invoke( - svg.toDataURL(options.getInt("width"), options.getInt("height"))); + svg.toDataURL(options)); } else { successCallback.invoke(svg.toDataURL()); } diff --git a/apple/Elements/RNSVGSvgView.h b/apple/Elements/RNSVGSvgView.h index 760c1f0e4..566412fab 100644 --- a/apple/Elements/RNSVGSvgView.h +++ b/apple/Elements/RNSVGSvgView.h @@ -62,7 +62,7 @@ - (RNSVGFilter *)getDefinedFilter:(NSString *)filterName; -- (NSString *)getDataURLWithBounds:(CGRect)bounds; +- (NSString *)getDataURLWithBounds:(CGRect)bounds format:(NSString *)format quality:(CGFloat)quality; - (CGRect)getContextBounds; diff --git a/apple/Elements/RNSVGSvgView.mm b/apple/Elements/RNSVGSvgView.mm index f5da0cf50..7d2f138d9 100644 --- a/apple/Elements/RNSVGSvgView.mm +++ b/apple/Elements/RNSVGSvgView.mm @@ -357,7 +357,7 @@ - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event return isPointInside ? self : nil; } -- (NSString *)getDataURLWithBounds:(CGRect)bounds +- (NSString *)getDataURLWithBounds:(CGRect)bounds format:(NSString *)format quality:(CGFloat)quality { #if !TARGET_OS_OSX // [macOS] UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:bounds.size]; @@ -372,12 +372,17 @@ - (NSString *)getDataURLWithBounds:(CGRect)bounds #if !TARGET_OS_OSX // [macOS] }]; #endif -#if !TARGET_OS_OSX // [macOS] - NSData *imageData = UIImagePNGRepresentation(image); - NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; -#else // [macOS - NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()); + NSData *imageData; +#if TARGET_OS_OSX // [macOS + NSImage *image = UIGraphicsGetImageFromCurrentImageContext(); +#endif // macOS] + if ([format isEqual:@"jpeg"]) { + imageData = UIImageJPEGRepresentation(image, quality); + } else { + imageData = UIImagePNGRepresentation(image); + } NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; +#if TARGET_OS_OSX // [macOS UIGraphicsEndImageContext(); #endif // macOS] return base64; diff --git a/apple/RNSVGSvgViewModule.mm b/apple/RNSVGSvgViewModule.mm index 5b72f0192..8038f2822 100644 --- a/apple/RNSVGSvgViewModule.mm +++ b/apple/RNSVGSvgViewModule.mm @@ -38,21 +38,25 @@ - (void)toDataURL:(nonnull NSNumber *)reactTag if ([view isKindOfClass:[RNSVGSvgView class]]) { RNSVGSvgView *svg = view; if (options == nil) { - b64 = [svg getDataURLWithBounds:svg.boundingBox]; + b64 = [svg getDataURLWithBounds:svg.boundingBox format:@"png" quality:1.0]; } else { - id width = [options objectForKey:@"width"]; - id height = [options objectForKey:@"height"]; - if (![width isKindOfClass:NSNumber.class] || ![height isKindOfClass:NSNumber.class]) { - RCTLogError(@"Invalid width or height given to toDataURL"); - return; + CGRect bounds; + NSDictionary *size = [options objectForKey:@"size"]; + id width = [size objectForKey:@"width"]; + id height = [size objectForKey:@"height"]; + if (![width isKindOfClass:NSNumber.class] && ![height isKindOfClass:NSNumber.class]) { + bounds = svg.boundingBox; + } else { + NSNumber *w = width; + NSInteger wi = w ? (NSInteger)[w intValue] : svg.boundingBox.size.width; + NSNumber *h = height; + NSInteger hi = h ? (NSInteger)[h intValue] : svg.boundingBox.size.height; + bounds = CGRectMake(0, 0, wi, hi); } - NSNumber *w = width; - NSInteger wi = (NSInteger)[w intValue]; - NSNumber *h = height; - NSInteger hi = (NSInteger)[h intValue]; - - CGRect bounds = CGRectMake(0, 0, wi, hi); - b64 = [svg getDataURLWithBounds:bounds]; + NSString *format = [options objectForKey:@"format"]; + NSNumber *qualityNumber = [options objectForKey:@"quality"]; + CGFloat quality = qualityNumber ? [qualityNumber doubleValue] : 1.0; + b64 = [svg getDataURLWithBounds:bounds format:format quality:quality]; } } else { RCTLogError(@"Invalid svg returned from registry, expecting RNSVGSvgView, got: %@", view); diff --git a/apps/examples/src/examples/Svg.tsx b/apps/examples/src/examples/Svg.tsx index 02ff95635..c11331d10 100644 --- a/apps/examples/src/examples/Svg.tsx +++ b/apps/examples/src/examples/Svg.tsx @@ -1,6 +1,14 @@ import React, {Component} from 'react'; -import {StyleSheet, View, Image} from 'react-native'; -import {Svg, Circle, G, Path, Line, Rect} from 'react-native-svg'; +import {StyleSheet, View, Image, Dimensions} from 'react-native'; +import { + Svg, + Circle, + G, + Path, + Line, + Rect, + DataUrlOptions, +} from 'react-native-svg'; const styles = StyleSheet.create({ container: { @@ -131,14 +139,19 @@ class SvgNativeMethods extends Component { state = { base64: null, }; + + optionsWithPNGFormat: DataUrlOptions = { + format: 'png', + size: {width: 150, height: 100}, + }; + alert = () => { console.log('PRESSED'); this.root?.toDataURL((base64: string) => { this.setState({ base64, }); - }); - + }, this.optionsWithPNGFormat); console.log(this.circle?.isPointInFill({x: 200, y: 100})); console.log(this.circle?.isPointInStroke({x: 200, y: 100})); console.log(this.circle?.getTotalLength()); diff --git a/apps/test-examples/src/Test2233.tsx b/apps/test-examples/src/Test2233.tsx index 4ba5dd14f..905069042 100644 --- a/apps/test-examples/src/Test2233.tsx +++ b/apps/test-examples/src/Test2233.tsx @@ -4,6 +4,7 @@ import Svg, {Path} from 'react-native-svg'; const SvgLogoWelcome = () => { const ref = React.useRef(null); + return (