@lodev09/react-native-exify 0.2.7 → 1.0.0
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/Exify.podspec +3 -23
- package/LICENSE +1 -1
- package/README.md +44 -18
- package/android/build.gradle +26 -53
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/com/lodev09/exify/ExifyModule.kt +146 -32
- package/android/src/main/java/com/lodev09/exify/ExifyPackage.kt +28 -10
- package/android/src/main/java/com/lodev09/exify/ExifyTags.kt +137 -135
- package/android/src/main/java/com/lodev09/exify/ExifyUtils.kt +31 -8
- package/ios/Exify.h +9 -0
- package/ios/Exify.mm +384 -25
- package/lib/module/index.js +12 -22
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/module/specs/NativeExifyModule.js +5 -0
- package/lib/module/specs/NativeExifyModule.js.map +1 -0
- package/lib/module/types.js +1 -1
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/index.d.ts +7 -9
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativeExifyModule.d.ts +8 -0
- package/lib/typescript/src/specs/NativeExifyModule.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +2 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +82 -95
- package/src/index.ts +12 -31
- package/src/specs/NativeExifyModule.ts +8 -0
- package/src/types.ts +141 -140
- package/android/gradle.properties +0 -5
- package/android/src/main/AndroidManifestNew.xml +0 -2
- package/ios/Exify-Bridging-Header.h +0 -11
- package/ios/Exify.swift +0 -134
- package/ios/ExifyUtils.swift +0 -140
- package/lib/commonjs/index.js +0 -53
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/types.js +0 -2
- 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
|
-
}
|
package/ios/ExifyUtils.swift
DELETED
|
@@ -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
|
-
}
|
package/lib/commonjs/index.js
DELETED
|
@@ -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":[]}
|
package/lib/commonjs/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":"","ignoreList":[]}
|