@lodev09/react-native-exify 0.2.7 → 1.0.1

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.
Files changed (37) hide show
  1. package/Exify.podspec +3 -23
  2. package/LICENSE +1 -1
  3. package/README.md +45 -18
  4. package/android/build.gradle +26 -53
  5. package/android/src/main/AndroidManifest.xml +2 -2
  6. package/android/src/main/java/com/lodev09/exify/ExifyModule.kt +154 -33
  7. package/android/src/main/java/com/lodev09/exify/ExifyPackage.kt +28 -10
  8. package/android/src/main/java/com/lodev09/exify/ExifyTags.kt +137 -135
  9. package/android/src/main/java/com/lodev09/exify/ExifyUtils.kt +31 -8
  10. package/ios/Exify.h +9 -0
  11. package/ios/Exify.mm +384 -25
  12. package/lib/module/index.js +12 -22
  13. package/lib/module/index.js.map +1 -1
  14. package/lib/module/package.json +1 -0
  15. package/lib/module/specs/NativeExifyModule.js +5 -0
  16. package/lib/module/specs/NativeExifyModule.js.map +1 -0
  17. package/lib/module/types.js +1 -1
  18. package/lib/typescript/package.json +1 -0
  19. package/lib/typescript/src/index.d.ts +7 -9
  20. package/lib/typescript/src/index.d.ts.map +1 -1
  21. package/lib/typescript/src/specs/NativeExifyModule.d.ts +8 -0
  22. package/lib/typescript/src/specs/NativeExifyModule.d.ts.map +1 -0
  23. package/lib/typescript/src/types.d.ts +2 -1
  24. package/lib/typescript/src/types.d.ts.map +1 -1
  25. package/package.json +82 -95
  26. package/src/index.ts +12 -31
  27. package/src/specs/NativeExifyModule.ts +8 -0
  28. package/src/types.ts +141 -140
  29. package/android/gradle.properties +0 -5
  30. package/android/src/main/AndroidManifestNew.xml +0 -2
  31. package/ios/Exify-Bridging-Header.h +0 -11
  32. package/ios/Exify.swift +0 -134
  33. package/ios/ExifyUtils.swift +0 -140
  34. package/lib/commonjs/index.js +0 -53
  35. package/lib/commonjs/index.js.map +0 -1
  36. package/lib/commonjs/types.js +0 -2
  37. package/lib/commonjs/types.js.map +0 -1
package/ios/Exify.swift DELETED
@@ -1,134 +0,0 @@
1
- /*
2
- *
3
- * Created by Jovanni Lo (@lodev09)
4
- * Copyright 2024
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- */
9
-
10
- import PhotosUI
11
-
12
- @objc(Exify)
13
- class Exify: NSObject {
14
- func readExif(uri: String, resolve: @escaping RCTPromiseResolveBlock) {
15
- guard let url = URL(string: uri) else {
16
- resolve(nil)
17
- return
18
- }
19
-
20
- readExifTags(from: url) { tags in
21
- resolve(tags)
22
- }
23
- }
24
-
25
- func readExif(assetId: String, resolve: @escaping RCTPromiseResolveBlock) {
26
- guard let asset = getAssetBy(id: assetId) else {
27
- resolve(nil)
28
- return
29
- }
30
-
31
- let imageOptions = PHContentEditingInputRequestOptions()
32
- imageOptions.isNetworkAccessAllowed = true
33
-
34
- asset.requestContentEditingInput(with: imageOptions) { contentInput, _ in
35
- guard let url = contentInput?.fullSizeImageURL else {
36
- resolve(nil)
37
- return
38
- }
39
-
40
- readExifTags(from: url) { tags in
41
- resolve(tags)
42
- }
43
- }
44
- }
45
-
46
- func writeExif(assetId: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
47
- guard let asset = getAssetBy(id: assetId) else {
48
- reject("Error", "Cannot retrieve asset.", nil)
49
- return
50
- }
51
-
52
- let imageOptions = PHContentEditingInputRequestOptions()
53
- imageOptions.isNetworkAccessAllowed = true
54
-
55
- asset.requestContentEditingInput(with: imageOptions) { contentInput, _ in
56
- guard let contentInput, let url = contentInput.fullSizeImageURL else {
57
- reject("Error", "Unable to read metadata from asset", nil)
58
- return
59
- }
60
-
61
- updateMetadata(url: url, with: tags) { metadata, data in
62
- guard let metadata, let data else {
63
- reject("Error", "Could not update metadata", nil)
64
- return
65
- }
66
-
67
- do {
68
- try PHPhotoLibrary.shared().performChangesAndWait {
69
- let request = PHAssetCreationRequest.forAsset()
70
- request.addResource(with: .photo, data: data, options: nil)
71
- request.creationDate = Date()
72
-
73
- let newAssetId = request.placeholderForCreatedAsset!.localIdentifier
74
- resolve([
75
- "uri": "ph://\(newAssetId)",
76
- "assetId": newAssetId,
77
- "tags": getExifTags(from: metadata),
78
- ])
79
- }
80
- } catch {
81
- reject("Error", "Could not save to image file", nil)
82
- print(error)
83
- }
84
- }
85
- }
86
- }
87
-
88
- func writeExif(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
89
- guard let url = URL(string: uri) else {
90
- reject("Error", "Invalid URL", nil)
91
- return
92
- }
93
-
94
- updateMetadata(url: url, with: tags) { metadata, data in
95
- guard let metadata, let data else {
96
- reject("Error", "Could not update metadata", nil)
97
- return
98
- }
99
-
100
- do {
101
- // Write to the current file
102
- try data.write(to: url, options: .atomic)
103
-
104
- resolve([
105
- "uri": uri,
106
- "tags": getExifTags(from: metadata),
107
- ])
108
- } catch {
109
- reject("Error", "Could not save to image file", nil)
110
- print(error)
111
- }
112
- }
113
- }
114
-
115
- @objc(readAsync:withResolver:withRejecter:)
116
- func readAsync(uri: String, resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
117
- if uri.starts(with: "ph://") {
118
- let assetId = String(uri[uri.index(uri.startIndex, offsetBy: 5)...])
119
- readExif(assetId: assetId, resolve: resolve)
120
- } else {
121
- readExif(uri: uri, resolve: resolve)
122
- }
123
- }
124
-
125
- @objc(writeAsync:withExif:withResolver:withRejecter:)
126
- func writeAsync(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
127
- if uri.starts(with: "ph://") {
128
- let assetId = String(uri[uri.index(uri.startIndex, offsetBy: 5)...])
129
- writeExif(assetId: assetId, tags: tags, resolve: resolve, reject: reject)
130
- } else {
131
- writeExif(uri: uri, tags: tags, resolve: resolve, reject: reject)
132
- }
133
- }
134
- }
@@ -1,140 +0,0 @@
1
- /*
2
- *
3
- * Created by Jovanni Lo (@lodev09)
4
- * Copyright 2024
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- */
9
-
10
- import Photos
11
-
12
- func getAssetBy(id: String?) -> PHAsset? {
13
- if let id {
14
- return getAssetsBy(assetIds: [id]).firstObject
15
- }
16
-
17
- return nil
18
- }
19
-
20
- func getAssetsBy(assetIds: [String]) -> PHFetchResult<PHAsset> {
21
- let options = PHFetchOptions()
22
-
23
- options.includeHiddenAssets = true
24
- options.includeAllBurstAssets = true
25
- options.fetchLimit = assetIds.count
26
-
27
- return PHAsset.fetchAssets(withLocalIdentifiers: assetIds, options: options)
28
- }
29
-
30
- func addTagEntries(from dictionary: CFString, metadata: NSDictionary, to tags: NSMutableDictionary) {
31
- if let entries = metadata[dictionary] as? [String: Any] {
32
- tags.addEntries(from: entries)
33
- }
34
- }
35
-
36
- func getExifTags(from metadata: NSDictionary) -> [String: Any] {
37
- let tags: NSMutableDictionary = [:]
38
-
39
- // Add root non-dictionary properties
40
- for (key, value) in metadata {
41
- if value as? [String: Any] == nil {
42
- tags[key] = value
43
- }
44
- }
45
-
46
- // Append all {Exif} properties
47
- addTagEntries(from: kCGImagePropertyExifDictionary, metadata: metadata, to: tags)
48
-
49
- // Prefix {GPS} dictionary with "GPS"
50
- if let gps = metadata[kCGImagePropertyGPSDictionary as String] as? [String: Any] {
51
- for (key, value) in gps {
52
- tags["GPS" + key] = value
53
- }
54
- }
55
-
56
- // Include tags from formats
57
- addTagEntries(from: kCGImagePropertyTIFFDictionary, metadata: metadata, to: tags)
58
- addTagEntries(from: kCGImagePropertyPNGDictionary, metadata: metadata, to: tags)
59
-
60
- if #available(iOS 13.0, *) {
61
- addTagEntries(from: kCGImagePropertyHEICSDictionary, metadata: metadata, to: tags)
62
- }
63
-
64
- return tags as? [String: Any] ?? [:]
65
- }
66
-
67
- func readExifTags(from url: URL?, completionHandler: ([String: Any]?) -> Void) {
68
- guard let url, let sourceImage = CGImageSourceCreateWithURL(url as CFURL, nil),
69
- let metadataDict = CGImageSourceCopyPropertiesAtIndex(sourceImage, 0, nil) else {
70
- completionHandler(nil)
71
- return
72
- }
73
-
74
- let tags = getExifTags(from: metadataDict)
75
- completionHandler(tags)
76
- }
77
-
78
- func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDictionary?, Data?) -> Void) {
79
- guard let cgImageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else {
80
- completionHanlder(nil, nil)
81
- return
82
- }
83
-
84
- let metadataDict = CGImageSourceCopyPropertiesAtIndex(cgImageSource, 0, nil) ?? [:] as CFDictionary
85
- let metadata = NSMutableDictionary(dictionary: metadataDict)
86
-
87
- // Append additional Exif data
88
- let exifDict = metadata[kCGImagePropertyExifDictionary as String] as? NSMutableDictionary
89
- exifDict?.addEntries(from: tags)
90
-
91
- // Handle GPS Tags
92
- var gpsDict = [String: Any]()
93
-
94
- if let latitude = tags["GPSLatitude"] as? Double {
95
- gpsDict[kCGImagePropertyGPSLatitude as String] = abs(latitude)
96
- gpsDict[kCGImagePropertyGPSLatitudeRef as String] = latitude >= 0 ? "N" : "S"
97
- }
98
-
99
- if let longitude = tags["GPSLongitude"] as? Double {
100
- gpsDict[kCGImagePropertyGPSLongitude as String] = abs(longitude)
101
- gpsDict[kCGImagePropertyGPSLongitudeRef as String] = longitude >= 0 ? "E" : "W"
102
- }
103
-
104
- if let altitude = tags["GPSAltitude"] as? Double {
105
- gpsDict[kCGImagePropertyGPSAltitude as String] = abs(altitude)
106
- gpsDict[kCGImagePropertyGPSAltitudeRef as String] = altitude >= 0 ? 0 : 1
107
- }
108
-
109
- if let gpsDate = tags["GPSDateStamp"] as? String {
110
- gpsDict[kCGImagePropertyGPSDateStamp as String] = gpsDate
111
- }
112
-
113
- if let gpsTime = tags["GPSTimeStamp"] as? String {
114
- gpsDict[kCGImagePropertyGPSTimeStamp as String] = gpsTime
115
- }
116
-
117
- if metadata[kCGImagePropertyGPSDictionary as String] == nil {
118
- metadata[kCGImagePropertyGPSDictionary as String] = gpsDict
119
- } else {
120
- if let metadataGpsDict = metadata[kCGImagePropertyGPSDictionary as String] as? NSMutableDictionary {
121
- metadataGpsDict.addEntries(from: gpsDict)
122
- }
123
- }
124
-
125
- metadata.setObject(NSNumber(value: 1), forKey: kCGImageDestinationLossyCompressionQuality as NSString)
126
-
127
- let destinationData = NSMutableData()
128
-
129
- guard let uiImage = UIImage(contentsOfFile: url.path),
130
- let sourceType = CGImageSourceGetType(cgImageSource),
131
- let destination = CGImageDestinationCreateWithData(destinationData, sourceType, 1, nil) else {
132
- completionHanlder(nil, nil)
133
- return
134
- }
135
-
136
- CGImageDestinationAddImage(destination, uiImage.cgImage!, metadata)
137
- CGImageDestinationFinalize(destination)
138
-
139
- completionHanlder(metadata, destinationData as Data)
140
- }
@@ -1,53 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- var _exportNames = {
7
- writeAsync: true,
8
- readAsync: true
9
- };
10
- exports.readAsync = readAsync;
11
- exports.writeAsync = writeAsync;
12
- var _reactNative = require("react-native");
13
- var _types = require("./types");
14
- Object.keys(_types).forEach(function (key) {
15
- if (key === "default" || key === "__esModule") return;
16
- if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
17
- if (key in exports && exports[key] === _types[key]) return;
18
- Object.defineProperty(exports, key, {
19
- enumerable: true,
20
- get: function () {
21
- return _types[key];
22
- }
23
- });
24
- });
25
- const LINKING_ERROR = `The package '@lodev09/react-native-exify' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
26
- ios: "- You have run 'pod install'\n",
27
- default: ''
28
- }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
29
- const Exify = _reactNative.NativeModules.Exify ? _reactNative.NativeModules.Exify : new Proxy({}, {
30
- get() {
31
- throw new Error(LINKING_ERROR);
32
- }
33
- });
34
-
35
- /**
36
- * Write Exif data into an image file.
37
- * @param {string} uri the image uri to write
38
- * @param {ExifTags} tags the exif tags to be written
39
- * @return {Promise<ExifyWriteResult>} the full exif tags of the image
40
- */
41
- function writeAsync(uri, tags) {
42
- return Exify.writeAsync(uri, tags);
43
- }
44
-
45
- /**
46
- * Read Exif data from an image file.
47
- * @param {string} uri the image uri to read
48
- * @return {Promise<ExifTags>} the raw exif tags of the image
49
- */
50
- function readAsync(uri) {
51
- return Exify.readAsync(uri);
52
- }
53
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_reactNative","require","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","LINKING_ERROR","Platform","select","ios","default","Exify","NativeModules","Proxy","Error","writeAsync","uri","tags","readAsync"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAuCA,IAAAC,MAAA,GAAAD,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAF,MAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,MAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,MAAA,CAAAI,GAAA;IAAA;EAAA;AAAA;AApCA,MAAMS,aAAa,GAChB,sFAAqF,GACtFC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,KAAK,GAAGC,0BAAa,CAACD,KAAK,GAC7BC,0BAAa,CAACD,KAAK,GACnB,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACER,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIS,KAAK,CAACR,aAAa,CAAC;EAChC;AACF,CACF,CAAC;;AAEL;AACA;AACA;AACA;AACA;AACA;AACO,SAASS,UAAUA,CAACC,GAAW,EAAEC,IAAc,EAAyC;EAC7F,OAAON,KAAK,CAACI,UAAU,CAACC,GAAG,EAAEC,IAAI,CAAC;AACpC;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASC,SAASA,CAACF,GAAW,EAAiC;EACpE,OAAOL,KAAK,CAACO,SAAS,CAACF,GAAG,CAAC;AAC7B","ignoreList":[]}
@@ -1,2 +0,0 @@
1
- "use strict";
2
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":"","ignoreList":[]}