Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add format and quality parameters toDataURL function #2431

Open
wants to merge 50 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9cc26da
feat: add type for toDataUrl options
bohdanprog Aug 28, 2024
b3c411a
feat: export ToDataUrlOptions type
bohdanprog Aug 28, 2024
20b35ce
feat: update Test2233, add data for test two options image format
bohdanprog Aug 28, 2024
b5c6cd7
feat: add implementation for iOS platform, now it's available to pass…
bohdanprog Aug 28, 2024
57be53a
feat: update iOS implementation, check quality value, and update func…
bohdanprog Aug 28, 2024
f93ea7a
feat: add android platform implementation for format and quality para…
bohdanprog Aug 28, 2024
b8aa9cd
feat: update test2233 example
bohdanprog Aug 28, 2024
f731f28
feat: extract out of class the function checkOptions, and add the fun…
bohdanprog Aug 28, 2024
3b4f12b
feat: add support for Macos platform format and quality parameters to…
bohdanprog Aug 28, 2024
78e31ad
feat: update test2233 example
bohdanprog Aug 28, 2024
6e762b9
feat: add to validateJpegQualityParameter check for macos
bohdanprog Aug 28, 2024
f525454
feat: update checkOptions and validateJpegQualityParameter functions
bohdanprog Aug 28, 2024
471c2db
feat: update web implementation add new parameters
bohdanprog Aug 28, 2024
ead5351
feat: update ToDataUrlOptions types, made validate function more unde…
bohdanprog Aug 29, 2024
5d07869
fix: upate web implementation and removed unnecessary comments
bohdanprog Sep 3, 2024
624f9f1
refactor: rename type from ToDataUrlOptions to DataUrlOptions
bohdanprog Sep 3, 2024
6720611
refactor: rename variable
bohdanprog Sep 3, 2024
683dcce
refactor: restore Test2233 example
bohdanprog Sep 3, 2024
88d412f
feat: update interface for JpegOptions and PngOptions
bohdanprog Sep 3, 2024
5fb2f9f
feat: update Svg example
bohdanprog Sep 3, 2024
5421aaf
feat: removed unimplemented method
bohdanprog Sep 3, 2024
26b15e2
feat: simplified getDataURLWithBounds function
bohdanprog Sep 3, 2024
85ce71c
feat: update Svg example
bohdanprog Sep 3, 2024
2b268ad
fix: fixed implementation without options, pass quality case it requi…
bohdanprog Sep 3, 2024
632d8d3
feat: modify the check for the lowest quality in validateJpegQualityP…
bohdanprog Sep 3, 2024
b6ea5b2
feat: removed extra check for the Android platform in the validateJpe…
bohdanprog Sep 4, 2024
1b722b9
feat: update Android implementation to make width and height paramete…
bohdanprog Sep 4, 2024
cf32be3
feat: update iOS implementation to make width and height parameters n…
bohdanprog Sep 4, 2024
f58e649
feat: update DataUrlOptions type,make width and height parameters not…
bohdanprog Sep 4, 2024
33c70db
feat: leave out quality value on web platform, don't pass default value
bohdanprog Sep 9, 2024
3edce70
Merge branch 'main' of github.com:software-mansion/react-native-svg i…
bohdanprog Sep 20, 2024
992d003
feat: update type safety, check if format value doesn't equal null, r…
bohdanprog Sep 20, 2024
ea3f8ac
feat: update Svg example
bohdanprog Sep 20, 2024
a91999c
feat: update type check on web version, also update types for native …
bohdanprog Sep 20, 2024
89963ce
feat: update svg example
bohdanprog Sep 20, 2024
9d7fef7
Merge branch 'main' of github.com:software-mansion/react-native-svg i…
bohdanprog Sep 24, 2024
d0f44b8
feat: create toDataUrlUtils for toDataUrl function
bohdanprog Sep 24, 2024
a7a77d1
chore: update DataUrlOptions imports
bohdanprog Sep 24, 2024
d84b048
chore: update Svg example
bohdanprog Sep 24, 2024
1ae4bdf
feat: add check if field size exist in options object
bohdanprog Sep 24, 2024
887eb51
fix: ios add new variable size, and extract width and height from siz…
bohdanprog Sep 24, 2024
13d38ec
chore: change JSdoc for toDataURL function
bohdanprog Sep 24, 2024
c2e2389
chore: changed quality check on web platform
bohdanprog Sep 24, 2024
f5c6464
Merge branch 'main' into feat/toDataURL-add-format-and-quality-parame…
bohdanprog Oct 2, 2024
ea9564f
Update JSDoc part of deprecated parameters
bohdanprog Oct 9, 2024
517a18e
Update JSDoc
bohdanprog Oct 9, 2024
21c5315
update description about function toDataUrl
bohdanprog Oct 9, 2024
211f7e9
chore: update properties description
bohdanprog Oct 9, 2024
3cdc9a0
Merge branch 'main' of github.com:software-mansion/react-native-svg i…
bohdanprog Oct 9, 2024
595d5d5
chore: update Podfile.lock
bohdanprog Oct 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions android/src/main/java/com/horcrux/svg/SvgView.java
Original file line number Diff line number Diff line change
Expand Up @@ -356,15 +356,20 @@ String toDataURL() {
return Base64.encodeToString(bitmapBytes, Base64.NO_WRAP);
}

String toDataURL(int width, int height) {
String toDataURL(int width, int height, String format, Integer quality) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

clearChildCache();
drawChildren(new Canvas(bitmap));
clearChildCache();
this.invalidate();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
if (format.equals("jpeg")) {
int qualityValue = (quality != null) ? quality : 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);
Expand Down
4 changes: 3 additions & 1 deletion android/src/main/java/com/horcrux/svg/SvgViewModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ public void run() {
});
} else {
if (options != null) {
Integer quality = options.hasKey("quality") ? options.getInt("quality") : null;
String format = options.hasKey("format") ? options.getString("format") : null;
successCallback.invoke(
svg.toDataURL(options.getInt("width"), options.getInt("height")));
svg.toDataURL(options.getInt("width"), options.getInt("height"), format, quality));
} else {
successCallback.invoke(svg.toDataURL());
}
Expand Down
2 changes: 1 addition & 1 deletion apple/Elements/RNSVGSvgView.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@

- (RNSVGFilter *)getDefinedFilter:(NSString *)filterName;

- (NSString *)getDataURLWithBounds:(CGRect)bounds;
- (NSString *)getDataURLWithBounds:(CGRect)bounds format:(NSString *)format quality:(CGFloat)quality;

- (CGRect)getContextBounds;

Expand Down
17 changes: 11 additions & 6 deletions apple/Elements/RNSVGSvgView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,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];
Expand All @@ -373,12 +373,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;
Expand Down
7 changes: 5 additions & 2 deletions apple/RNSVGSvgViewModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ - (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"];
Expand All @@ -52,7 +52,10 @@ - (void)toDataURL:(nonnull NSNumber *)reactTag
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);
Expand Down
29 changes: 25 additions & 4 deletions apps/examples/src/examples/Svg.tsx
Original file line number Diff line number Diff line change
@@ -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: {
Expand Down Expand Up @@ -131,14 +139,27 @@ class SvgNativeMethods extends Component {
state = {
base64: null,
};

optionsWithJPEGFormat: DataUrlOptions = {
format: 'jpeg',
width: 150,
height: 100,
quality: 1,
};

optionsWithPNGFormat: DataUrlOptions = {
format: 'png',
width: 150,
height: 100,
};

alert = () => {
console.log('PRESSED');
this.root?.toDataURL((base64: string) => {
this.setState({
base64,
});
});

}, this.optionsWithJPEGFormat);
console.log(this.circle?.isPointInFill({x: 200, y: 100}));
console.log(this.circle?.isPointInStroke({x: 200, y: 100}));
console.log(this.circle?.getTotalLength());
Expand Down
1 change: 1 addition & 0 deletions apps/test-examples/src/Test2233.tsx
bohdanprog marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Svg, {Path} from 'react-native-svg';

const SvgLogoWelcome = () => {
const ref = React.useRef<Svg | null>(null);

return (
<View>
<Button
Expand Down
2 changes: 1 addition & 1 deletion src/ReactNativeSVG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export type { PolylineProps } from './elements/Polyline';
export type { RadialGradientProps } from './elements/RadialGradient';
export type { RectProps } from './elements/Rect';
export type { StopProps } from './elements/Stop';
export type { SvgProps } from './elements/Svg';
export type { SvgProps, DataUrlOptions } from './elements/Svg';
export type { SymbolProps } from './elements/Symbol';
export type { TextProps } from './elements/Text';
export type { TextPathProps } from './elements/TextPath';
Expand Down
17 changes: 11 additions & 6 deletions src/elements.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import type { PolylineProps } from './elements/Polyline';
import type { RadialGradientProps } from './elements/RadialGradient';
import type { RectProps } from './elements/Rect';
import type { StopProps } from './elements/Stop';
import type { SvgProps } from './elements/Svg';
import type { SvgProps, DataUrlOptions } from './elements/Svg';
import type { SymbolProps } from './elements/Symbol';
import type { TextProps } from './elements/Text';
import type { TextPathProps } from './elements/TextPath';
Expand Down Expand Up @@ -250,10 +250,7 @@ export class Stop extends WebShape<BaseProps & StopProps> {

export class Svg extends WebShape<BaseProps & SvgProps> {
tag = 'svg' as const;
toDataURL(
callback: (data: string) => void,
options: { width?: number; height?: number } = {}
) {
toDataURL(callback: (data: string) => void, options: DataUrlOptions) {
const ref = this.elementRef.current;

if (ref === null) {
Expand All @@ -264,6 +261,12 @@ export class Svg extends WebShape<BaseProps & SvgProps> {

const width = Number(options.width) || rect.width;
const height = Number(options.height) || rect.height;
const format = options.format === 'jpeg' ? 'image/jpeg' : 'image/png';

let quality = 0.92;
if (options && options.format === 'jpeg' && options.quality) {
bohdanprog marked this conversation as resolved.
Show resolved Hide resolved
quality = options.quality;
}

const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', `0 0 ${rect.width} ${rect.height}`);
Expand All @@ -278,7 +281,9 @@ export class Svg extends WebShape<BaseProps & SvgProps> {
canvas.height = height;
const context = canvas.getContext('2d');
context?.drawImage(img, 0, 0);
callback(canvas.toDataURL().replace('data:image/png;base64,', ''));
const dataURL = canvas.toDataURL(format, quality);
const base64Data = dataURL.replace(`data:${format};base64,`, '');
callback(base64Data);
};

img.src = `data:image/svg+xml;utf8,${encodeSvg(
Expand Down
40 changes: 39 additions & 1 deletion src/elements/Svg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,40 @@
title?: string;
}

export type DataUrlOptions = JpegOptions | PngOptions;

interface JpegOptions {
format: 'jpeg';
quality?: number;
width: NumberProp;
height: NumberProp;
}

interface PngOptions {
format: 'png';
width: NumberProp;
height: NumberProp;
}
bohdanprog marked this conversation as resolved.
Show resolved Hide resolved

function checkOptions(options?: DataUrlOptions) {
bohdanprog marked this conversation as resolved.
Show resolved Hide resolved
if (options && options?.format === 'jpeg') {
if (!validateJpegQualityParameter(options)) {
throw new Error('toDataURL: Invalid quality parameter for JPEG format.');
}
bohdanprog marked this conversation as resolved.
Show resolved Hide resolved
}
}

function validateJpegQualityParameter(options: JpegOptions): boolean {
if (options.quality && (options.quality < 0 || options.quality > 1)) {
return false;
}
// Android requires quality to be a number between 0 and 100
if (Platform.OS === 'android' && options.quality) {
bohdanprog marked this conversation as resolved.
Show resolved Hide resolved
options.quality = Math.round(options.quality * 100);
}
bohdanprog marked this conversation as resolved.
Show resolved Hide resolved
return true;
}

export default class Svg extends Shape<SvgProps> {
static displayName = 'Svg';

Expand Down Expand Up @@ -81,10 +115,14 @@
root && root.setNativeProps(props);
};

toDataURL = (callback: (base64: string) => void, options?: object) => {
toDataURL = (
callback: (base64: string) => void,
options?: DataUrlOptions
) => {
if (!callback) {
return;
}
checkOptions(options);
const handle = findNodeHandle(this.root as Component);
const RNSVGSvgViewModule: Spec =
// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand Down Expand Up @@ -185,7 +223,7 @@
props.transform = gStyle.transform;
gStyle.transform = undefined;
}
props.transform = extractTransformSvgView(props as any);

Check warning on line 226 in src/elements/Svg.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
}

const RNSVGSvg = Platform.OS === 'android' ? RNSVGSvgAndroid : RNSVGSvgIOS;
Expand Down
Loading