@lodev09/react-native-exify 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/com/{exify → lodev09/exify}/ExifyModule.kt +2 -2
- package/android/src/main/java/com/{exify → lodev09/exify}/ExifyPackage.kt +1 -1
- package/android/src/main/java/com/{exify → lodev09/exify}/ExifyTags.kt +1 -1
- package/android/src/main/java/com/{exify → lodev09/exify}/ExifyUtils.kt +1 -1
- package/ios/Exify.swift +20 -23
- package/ios/ExifyUtils.swift +19 -20
- package/package.json +3 -3
- package/ios/Exify.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/Exify.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/Exify.xcodeproj/project.xcworkspace/xcuserdata/lodev09.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/Exify.xcodeproj/xcuserdata/lodev09.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# React Native Exify
|
|
2
2
|
|
|
3
|
+
[](https://github.com/lodev09/react-native-exify/actions/workflows/ci.yml)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
3
7
|
A simple library to read and write image Exif metadata in React Native. Inspired from [this thread](https://github.com/mrousavy/react-native-vision-camera/issues/780).
|
|
4
8
|
|
|
5
9
|
## Features
|
package/android/build.gradle
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
package com.exify
|
|
1
|
+
package com.lodev09.exify
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.net.Uri
|
|
5
5
|
import androidx.exifinterface.media.ExifInterface
|
|
6
|
-
import com.exify.ExifyUtils.formatTags
|
|
6
|
+
import com.lodev09.exify.ExifyUtils.formatTags
|
|
7
7
|
import com.facebook.react.bridge.Arguments
|
|
8
8
|
import com.facebook.react.bridge.Promise
|
|
9
9
|
import com.facebook.react.bridge.ReactApplicationContext
|
package/ios/Exify.swift
CHANGED
|
@@ -11,19 +11,18 @@ import PhotosUI
|
|
|
11
11
|
|
|
12
12
|
@objc(Exify)
|
|
13
13
|
class Exify: NSObject {
|
|
14
|
-
|
|
15
|
-
func readExif(uri: String, resolve: @escaping RCTPromiseResolveBlock) -> Void {
|
|
14
|
+
func readExif(uri: String, resolve: @escaping RCTPromiseResolveBlock) {
|
|
16
15
|
guard let url = URL(string: uri) else {
|
|
17
16
|
resolve(nil)
|
|
18
17
|
return
|
|
19
18
|
}
|
|
20
|
-
|
|
19
|
+
|
|
21
20
|
readExifTags(from: url) { tags in
|
|
22
21
|
resolve(tags)
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
func readExif(assetId: String, resolve: @escaping RCTPromiseResolveBlock)
|
|
25
|
+
func readExif(assetId: String, resolve: @escaping RCTPromiseResolveBlock) {
|
|
27
26
|
guard let asset = getAssetBy(id: assetId) else {
|
|
28
27
|
resolve(nil)
|
|
29
28
|
return
|
|
@@ -32,62 +31,61 @@ class Exify: NSObject {
|
|
|
32
31
|
let imageOptions = PHContentEditingInputRequestOptions()
|
|
33
32
|
imageOptions.isNetworkAccessAllowed = true
|
|
34
33
|
|
|
35
|
-
asset.requestContentEditingInput(with: imageOptions) { contentInput,
|
|
34
|
+
asset.requestContentEditingInput(with: imageOptions) { contentInput, _ in
|
|
36
35
|
guard let url = contentInput?.fullSizeImageURL else {
|
|
37
36
|
resolve(nil)
|
|
38
37
|
return
|
|
39
38
|
}
|
|
40
|
-
|
|
39
|
+
|
|
41
40
|
readExifTags(from: url) { tags in
|
|
42
41
|
resolve(tags)
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
|
-
|
|
47
|
-
func writeExif(assetId: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock)
|
|
45
|
+
|
|
46
|
+
func writeExif(assetId: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
48
47
|
guard let asset = getAssetBy(id: assetId) else {
|
|
49
48
|
reject("Error", "Cannot retrieve asset.", nil)
|
|
50
49
|
return
|
|
51
50
|
}
|
|
52
|
-
|
|
51
|
+
|
|
53
52
|
let imageOptions = PHContentEditingInputRequestOptions()
|
|
54
53
|
imageOptions.isNetworkAccessAllowed = true
|
|
55
|
-
|
|
54
|
+
|
|
56
55
|
asset.requestContentEditingInput(with: imageOptions) { contentInput, _ in
|
|
57
56
|
guard let contentInput, let url = contentInput.fullSizeImageURL else {
|
|
58
57
|
reject("Error", "Unable to read metadata from asset", nil)
|
|
59
58
|
return
|
|
60
59
|
}
|
|
61
|
-
|
|
60
|
+
|
|
62
61
|
updateMetadata(url: url, with: tags) { metadata, data in
|
|
63
62
|
guard let metadata, let data else {
|
|
64
63
|
reject("Error", "Could not update metadata", nil)
|
|
65
64
|
return
|
|
66
65
|
}
|
|
67
|
-
|
|
66
|
+
|
|
68
67
|
do {
|
|
69
|
-
try PHPhotoLibrary.shared().performChangesAndWait{
|
|
68
|
+
try PHPhotoLibrary.shared().performChangesAndWait {
|
|
70
69
|
let request = PHAssetCreationRequest.forAsset()
|
|
71
70
|
request.addResource(with: .photo, data: data, options: nil)
|
|
72
71
|
request.creationDate = Date()
|
|
73
|
-
|
|
72
|
+
|
|
74
73
|
let newAssetId = request.placeholderForCreatedAsset!.localIdentifier
|
|
75
74
|
resolve([
|
|
76
75
|
"uri": "ph://\(newAssetId)",
|
|
77
76
|
"assetId": newAssetId,
|
|
78
77
|
"tags": getExifTags(from: metadata),
|
|
79
78
|
])
|
|
80
|
-
|
|
81
79
|
}
|
|
82
|
-
} catch
|
|
80
|
+
} catch {
|
|
83
81
|
reject("Error", "Could not save to image file", nil)
|
|
84
82
|
print(error)
|
|
85
83
|
}
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
86
|
}
|
|
89
|
-
|
|
90
|
-
func writeExif(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock)
|
|
87
|
+
|
|
88
|
+
func writeExif(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
91
89
|
guard let url = URL(string: uri) else {
|
|
92
90
|
reject("Error", "Invalid URL", nil)
|
|
93
91
|
return
|
|
@@ -98,7 +96,7 @@ class Exify: NSObject {
|
|
|
98
96
|
reject("Error", "Could not update metadata", nil)
|
|
99
97
|
return
|
|
100
98
|
}
|
|
101
|
-
|
|
99
|
+
|
|
102
100
|
do {
|
|
103
101
|
// Write to the current file
|
|
104
102
|
try data.write(to: url, options: .atomic)
|
|
@@ -107,8 +105,7 @@ class Exify: NSObject {
|
|
|
107
105
|
"uri": uri,
|
|
108
106
|
"tags": getExifTags(from: metadata),
|
|
109
107
|
])
|
|
110
|
-
|
|
111
|
-
} catch let error {
|
|
108
|
+
} catch {
|
|
112
109
|
reject("Error", "Could not save to image file", nil)
|
|
113
110
|
print(error)
|
|
114
111
|
}
|
|
@@ -116,7 +113,7 @@ class Exify: NSObject {
|
|
|
116
113
|
}
|
|
117
114
|
|
|
118
115
|
@objc(readAsync:withResolver:withRejecter:)
|
|
119
|
-
func readAsync(uri: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock)
|
|
116
|
+
func readAsync(uri: String, resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
|
|
120
117
|
if uri.starts(with: "ph://") {
|
|
121
118
|
let assetId = String(uri[uri.index(uri.startIndex, offsetBy: 5)...])
|
|
122
119
|
readExif(assetId: assetId, resolve: resolve)
|
|
@@ -126,7 +123,7 @@ class Exify: NSObject {
|
|
|
126
123
|
}
|
|
127
124
|
|
|
128
125
|
@objc(writeAsync:withExif:withResolver:withRejecter:)
|
|
129
|
-
func writeAsync(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock)
|
|
126
|
+
func writeAsync(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
130
127
|
if uri.starts(with: "ph://") {
|
|
131
128
|
let assetId = String(uri[uri.index(uri.startIndex, offsetBy: 5)...])
|
|
132
129
|
writeExif(assetId: assetId, tags: tags, resolve: resolve, reject: reject)
|
package/ios/ExifyUtils.swift
CHANGED
|
@@ -27,7 +27,7 @@ func getAssetsBy(assetIds: [String]) -> PHFetchResult<PHAsset> {
|
|
|
27
27
|
return PHAsset.fetchAssets(withLocalIdentifiers: assetIds, options: options)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
func addTagEntries(from dictionary: CFString, metadata: NSDictionary, to tags: NSMutableDictionary)
|
|
30
|
+
func addTagEntries(from dictionary: CFString, metadata: NSDictionary, to tags: NSMutableDictionary) {
|
|
31
31
|
if let entries = metadata[dictionary] as? [String: Any] {
|
|
32
32
|
tags.addEntries(from: entries)
|
|
33
33
|
}
|
|
@@ -35,56 +35,56 @@ func addTagEntries(from dictionary: CFString, metadata: NSDictionary, to tags: N
|
|
|
35
35
|
|
|
36
36
|
func getExifTags(from metadata: NSDictionary) -> [String: Any] {
|
|
37
37
|
let tags: NSMutableDictionary = [:]
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
// Add root non-dictionary properties
|
|
40
40
|
for (key, value) in metadata {
|
|
41
41
|
if value as? [String: Any] == nil {
|
|
42
42
|
tags[key] = value
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
// Append all {Exif} properties
|
|
47
47
|
addTagEntries(from: kCGImagePropertyExifDictionary, metadata: metadata, to: tags)
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
// Prefix {GPS} dictionary with "GPS"
|
|
50
50
|
if let gps = metadata[kCGImagePropertyGPSDictionary as String] as? [String: Any] {
|
|
51
51
|
for (key, value) in gps {
|
|
52
52
|
tags["GPS" + key] = value
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
// Include tags from formats
|
|
57
57
|
addTagEntries(from: kCGImagePropertyTIFFDictionary, metadata: metadata, to: tags)
|
|
58
58
|
addTagEntries(from: kCGImagePropertyPNGDictionary, metadata: metadata, to: tags)
|
|
59
59
|
addTagEntries(from: kCGImagePropertyHEICSDictionary, metadata: metadata, to: tags)
|
|
60
|
-
|
|
61
|
-
return tags as
|
|
60
|
+
|
|
61
|
+
return tags as? [String: Any] ?? [:]
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
func readExifTags(from url: URL?, completionHandler: ([String: Any]?) -> Void)
|
|
64
|
+
func readExifTags(from url: URL?, completionHandler: ([String: Any]?) -> Void) {
|
|
65
65
|
guard let url, let sourceImage = CGImageSourceCreateWithURL(url as CFURL, nil),
|
|
66
66
|
let metadataDict = CGImageSourceCopyPropertiesAtIndex(sourceImage, 0, nil) else {
|
|
67
67
|
completionHandler(nil)
|
|
68
68
|
return
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
let tags = getExifTags(from: metadataDict)
|
|
72
72
|
completionHandler(tags)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDictionary?, Data?) -> Void)
|
|
75
|
+
func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDictionary?, Data?) -> Void) {
|
|
76
76
|
guard let cgImageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else {
|
|
77
77
|
completionHanlder(nil, nil)
|
|
78
78
|
return
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
let metadataDict = CGImageSourceCopyPropertiesAtIndex(cgImageSource, 0, nil) ?? [:] as CFDictionary
|
|
82
82
|
let metadata = NSMutableDictionary(dictionary: metadataDict)
|
|
83
83
|
|
|
84
84
|
// Append additional Exif data
|
|
85
85
|
let exifDict = metadata[kCGImagePropertyExifDictionary as String] as? NSMutableDictionary
|
|
86
86
|
exifDict?.addEntries(from: tags)
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
// Handle GPS Tags
|
|
89
89
|
var gpsDict = [String: Any]()
|
|
90
90
|
|
|
@@ -102,15 +102,14 @@ func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDi
|
|
|
102
102
|
gpsDict[kCGImagePropertyGPSAltitude as String] = abs(altitude)
|
|
103
103
|
gpsDict[kCGImagePropertyGPSAltitudeRef as String] = altitude >= 0 ? 0 : 1
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
if let gpsDate = tags["GPSDateStamp"] as? String {
|
|
107
107
|
gpsDict[kCGImagePropertyGPSDateStamp as String] = gpsDate
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
if let gpsTime = tags["GPSTimeStamp"] as? String {
|
|
111
111
|
gpsDict[kCGImagePropertyGPSTimeStamp as String] = gpsTime
|
|
112
112
|
}
|
|
113
|
-
|
|
114
113
|
|
|
115
114
|
if metadata[kCGImagePropertyGPSDictionary as String] == nil {
|
|
116
115
|
metadata[kCGImagePropertyGPSDictionary as String] = gpsDict
|
|
@@ -119,20 +118,20 @@ func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDi
|
|
|
119
118
|
metadataGpsDict.addEntries(from: gpsDict)
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
|
-
|
|
121
|
+
|
|
123
122
|
metadata.setObject(NSNumber(value: 1), forKey: kCGImageDestinationLossyCompressionQuality as NSString)
|
|
124
|
-
|
|
123
|
+
|
|
125
124
|
let destinationData = NSMutableData()
|
|
126
125
|
|
|
127
126
|
guard let uiImage = UIImage(contentsOfFile: url.path),
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
let sourceType = CGImageSourceGetType(cgImageSource),
|
|
128
|
+
let destination = CGImageDestinationCreateWithData(destinationData, sourceType, 1, nil) else {
|
|
130
129
|
completionHanlder(nil, nil)
|
|
131
130
|
return
|
|
132
131
|
}
|
|
133
132
|
|
|
134
133
|
CGImageDestinationAddImage(destination, uiImage.cgImage!, metadata)
|
|
135
134
|
CGImageDestinationFinalize(destination)
|
|
136
|
-
|
|
135
|
+
|
|
137
136
|
completionHanlder(metadata, destinationData as Data)
|
|
138
137
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lodev09/react-native-exify",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Read and write exif data into an image
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Read and write exif data into an image 🏷️",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
7
7
|
"types": "lib/typescript/src/index.d.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"typecheck": "tsc --noEmit",
|
|
32
32
|
"lint": "eslint --fix \"**/*.{js,ts,tsx}\"",
|
|
33
33
|
"format": "prettier --write \"**/*.{js,ts,tsx}\"",
|
|
34
|
-
"tidy": "yarn typecheck && yarn lint && yarn format",
|
|
34
|
+
"tidy": "yarn typecheck && yarn lint && yarn format && scripts/swiftlint.sh && scripts/swiftformat.sh",
|
|
35
35
|
"clean": "del-cli android/build lib && yarn workspace exify-example clean",
|
|
36
36
|
"prepare": "bob build",
|
|
37
37
|
"release": "release-it"
|
|
Binary file
|
package/ios/Exify.xcodeproj/xcuserdata/lodev09.xcuserdatad/xcschemes/xcschememanagement.plist
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
-
<plist version="1.0">
|
|
4
|
-
<dict>
|
|
5
|
-
<key>SchemeUserState</key>
|
|
6
|
-
<dict>
|
|
7
|
-
<key>Exify.xcscheme_^#shared#^_</key>
|
|
8
|
-
<dict>
|
|
9
|
-
<key>orderHint</key>
|
|
10
|
-
<integer>0</integer>
|
|
11
|
-
</dict>
|
|
12
|
-
</dict>
|
|
13
|
-
</dict>
|
|
14
|
-
</plist>
|