react-native-nitro-xlsx 0.1.2 → 0.1.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/NOTICE +8 -35
- package/NitroXlsx.podspec +20 -57
- package/Package.swift +31 -0
- package/README.md +45 -0
- package/android/CMakeLists.txt +13 -23
- package/android/build.gradle +6 -0
- package/cpp/HybridNitroXlsx.cpp +177 -0
- package/cpp/HybridNitroXlsx.hpp +1 -0
- package/cpp/HybridXlsxWorkbook.cpp +112 -0
- package/cpp/HybridXlsxWorkbook.hpp +1 -0
- package/cpp/HybridXlsxWorksheet.cpp +24 -0
- package/ios/dummy.swift +2 -0
- package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.hpp +1 -0
- package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.hpp +2 -0
- package/package.json +4 -1
- package/scripts/prepare-openxlsx.js +109 -0
- package/src/index.ts +14 -2
- package/src/specs/NitroXlsx.nitro.ts +2 -0
package/NOTICE
CHANGED
|
@@ -4,7 +4,8 @@ This software uses third-party open source components listed below, with full li
|
|
|
4
4
|
================================================================================
|
|
5
5
|
1. OpenXLSX
|
|
6
6
|
Repository: https://github.com/troldal/OpenXLSX
|
|
7
|
-
|
|
7
|
+
Version: 0.5.1
|
|
8
|
+
License: BSD 3-Clause License (SPDX: BSD-3-Clause)
|
|
8
9
|
Copyright (c) 2020 Kenneth Troldal Balslev
|
|
9
10
|
|
|
10
11
|
Full BSD 3-Clause License text:
|
|
@@ -27,7 +28,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
27
28
|
2. react-native-nitro-modules
|
|
28
29
|
Repository: https://github.com/mrousavy/nitro
|
|
29
30
|
Version: 0.58.3
|
|
30
|
-
License: MIT License
|
|
31
|
+
License: MIT License (SPDX: MIT)
|
|
31
32
|
Copyright (c) 2024 Marc Rousavy
|
|
32
33
|
|
|
33
34
|
Full MIT License text:
|
|
@@ -56,7 +57,8 @@ SOFTWARE.
|
|
|
56
57
|
================================================================================
|
|
57
58
|
3. pugixml (OpenXLSX dependency)
|
|
58
59
|
Repository: https://github.com/zeux/pugixml
|
|
59
|
-
|
|
60
|
+
Version: 1.16
|
|
61
|
+
License: MIT License (SPDX: MIT)
|
|
60
62
|
Copyright (c) 2006-2024 Arseny Kapoulkine
|
|
61
63
|
|
|
62
64
|
Full MIT License text:
|
|
@@ -72,39 +74,10 @@ The above copyright notice and this permission notice shall be included in all c
|
|
|
72
74
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
73
75
|
|
|
74
76
|
================================================================================
|
|
75
|
-
4.
|
|
76
|
-
Repository: https://github.com/troldal/Zippy
|
|
77
|
-
License: MIT License
|
|
78
|
-
Copyright (c) 2019 Kenneth Troldal Balslev
|
|
79
|
-
|
|
80
|
-
Full MIT License text:
|
|
81
|
-
|
|
82
|
-
MIT License
|
|
83
|
-
|
|
84
|
-
Copyright (c) 2019 Kenneth Troldal Balslev
|
|
85
|
-
|
|
86
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
87
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
88
|
-
in the Software without restriction, including without limitation the rights
|
|
89
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
90
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
91
|
-
furnished to do so, subject to the following conditions:
|
|
92
|
-
|
|
93
|
-
The above copyright notice and this permission notice shall be included in all
|
|
94
|
-
copies or substantial portions of the Software.
|
|
95
|
-
|
|
96
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
97
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
98
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
99
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
100
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
101
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
102
|
-
SOFTWARE.
|
|
103
|
-
|
|
104
|
-
================================================================================
|
|
105
|
-
5. miniz (OpenXLSX dependency)
|
|
77
|
+
4. miniz (OpenXLSX dependency)
|
|
106
78
|
Repository: https://github.com/richgel999/miniz
|
|
107
|
-
|
|
79
|
+
Version: 3.1.2
|
|
80
|
+
License: MIT License (SPDX: MIT)
|
|
108
81
|
Copyright (c) 2013-2014 RAD Game Tools and Valve Software
|
|
109
82
|
Copyright (c) 2010-2014 Rich Geldreich and Tenacious Software LLC
|
|
110
83
|
|
package/NitroXlsx.podspec
CHANGED
|
@@ -11,9 +11,13 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
13
|
s.platforms = { :ios => "15.1" }
|
|
14
|
-
s.source = { :git => "", :tag => "#{s.version}" }
|
|
14
|
+
s.source = { :git => "https://github.com/zydbkqf/react-native-nitro-xlsx.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
|
-
s.exclude_files =
|
|
16
|
+
s.exclude_files = [
|
|
17
|
+
"cpp/tests/**/*",
|
|
18
|
+
"OpenXLSX/ThirdParty/miniz/tests/**/*",
|
|
19
|
+
"OpenXLSX/ThirdParty/miniz/examples/**/*",
|
|
20
|
+
]
|
|
17
21
|
|
|
18
22
|
s.source_files = [
|
|
19
23
|
"cpp/**/*.{h,hpp,cpp}",
|
|
@@ -49,61 +53,20 @@ Pod::Spec.new do |s|
|
|
|
49
53
|
"HEADER_SEARCH_PATHS" => "$(inherited) ${PODS_TARGET_SRCROOT}/OpenXLSX ${PODS_TARGET_SRCROOT}/OpenXLSX/OpenXLSX ${PODS_TARGET_SRCROOT}/OpenXLSX/OpenXLSX/headers ${PODS_TARGET_SRCROOT}/OpenXLSX/ThirdParty/pugixml/src ${PODS_TARGET_SRCROOT}/OpenXLSX/ThirdParty/miniz",
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
#define MINIZ_EXPORT_H
|
|
67
|
-
|
|
68
|
-
#ifndef MINIZ_EXPORT
|
|
69
|
-
#define MINIZ_EXPORT
|
|
70
|
-
#endif
|
|
71
|
-
|
|
72
|
-
#ifndef MINIZ_DEPRECATED
|
|
73
|
-
#define MINIZ_DEPRECATED
|
|
74
|
-
#endif
|
|
75
|
-
|
|
76
|
-
#ifndef MINIZ_DEPRECATED_EXPORT
|
|
77
|
-
#define MINIZ_DEPRECATED_EXPORT MINIZ_EXPORT MINIZ_DEPRECATED
|
|
78
|
-
#endif
|
|
79
|
-
|
|
80
|
-
#ifndef MINIZ_NO_EXPORT
|
|
81
|
-
#define MINIZ_NO_EXPORT
|
|
82
|
-
#endif
|
|
83
|
-
|
|
84
|
-
#endif
|
|
85
|
-
EOF
|
|
86
|
-
fi
|
|
87
|
-
if [ ! -f "OpenXLSX/OpenXLSX/headers/OpenXLSX-Exports.hpp" ]; then
|
|
88
|
-
cat > OpenXLSX/OpenXLSX/headers/OpenXLSX-Exports.hpp << 'EOF'
|
|
89
|
-
#ifndef OPENXLSX_EXPORTS_HPP
|
|
90
|
-
#define OPENXLSX_EXPORTS_HPP
|
|
91
|
-
|
|
92
|
-
#ifndef OPENXLSX_EXPORT
|
|
93
|
-
#define OPENXLSX_EXPORT
|
|
94
|
-
#endif
|
|
95
|
-
|
|
96
|
-
#ifndef OPENXLSX_HIDDEN
|
|
97
|
-
#define OPENXLSX_HIDDEN
|
|
98
|
-
#endif
|
|
99
|
-
|
|
100
|
-
#endif
|
|
101
|
-
EOF
|
|
102
|
-
fi
|
|
103
|
-
if [ ! -d "OpenXLSX/OpenXLSX/headers/OpenXLSX" ]; then
|
|
104
|
-
cd OpenXLSX/OpenXLSX/headers && ln -sf . OpenXLSX && cd -
|
|
105
|
-
fi
|
|
106
|
-
CMD
|
|
56
|
+
# Prepare OpenXLSX and its dependencies (pugixml, miniz) via a Node script
|
|
57
|
+
# so the same logic can be reused from both CocoaPods and Android CMake.
|
|
58
|
+
s.prepare_command = "node #{__dir__}/scripts/prepare-openxlsx.js"
|
|
59
|
+
|
|
60
|
+
# Swift Package Manager declaration for React Native 0.87+.
|
|
61
|
+
# This exposes NitroXlsx as a local Swift package; the heavy lifting for
|
|
62
|
+
# OpenXLSX is still done by the prepare command above.
|
|
63
|
+
if ENV['NITRO_XLSX_USE_SPM'] == '1' && defined?(SPM) && SPM.respond_to?(:dependency)
|
|
64
|
+
SPM.dependency(s,
|
|
65
|
+
url: File.join(__dir__, "."),
|
|
66
|
+
requirement: { kind: 'upToNextMajorVersion', minimumVersion: s.version.to_s },
|
|
67
|
+
products: ['NitroXlsx']
|
|
68
|
+
)
|
|
69
|
+
end
|
|
107
70
|
|
|
108
71
|
load 'nitrogen/generated/ios/NitroXlsx+autolinking.rb'
|
|
109
72
|
add_nitrogen_files(s)
|
package/Package.swift
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
// NOTE: This Package.swift is a Swift Package Manager manifest stub for
|
|
5
|
+
// React Native 0.87+ compatibility. The library is still primarily built
|
|
6
|
+
// through CocoaPods (NitroXlsx.podspec), where OpenXLSX, pugixml and miniz
|
|
7
|
+
// are cloned and compiled from source by scripts/prepare-openxlsx.js.
|
|
8
|
+
//
|
|
9
|
+
// The local package reference below lets RN 0.87's spm_dependency helper
|
|
10
|
+
// register NitroXlsx in the Pods project when SwiftPM mode is enabled.
|
|
11
|
+
// Full SPM-based compilation of the C++ sources is not yet supported.
|
|
12
|
+
|
|
13
|
+
let package = Package(
|
|
14
|
+
name: "NitroXlsx",
|
|
15
|
+
platforms: [
|
|
16
|
+
.iOS(.v15)
|
|
17
|
+
],
|
|
18
|
+
products: [
|
|
19
|
+
.library(
|
|
20
|
+
name: "NitroXlsx",
|
|
21
|
+
targets: ["NitroXlsx"]
|
|
22
|
+
)
|
|
23
|
+
],
|
|
24
|
+
targets: [
|
|
25
|
+
.target(
|
|
26
|
+
name: "NitroXlsx",
|
|
27
|
+
path: "ios",
|
|
28
|
+
sources: ["dummy.swift"]
|
|
29
|
+
)
|
|
30
|
+
]
|
|
31
|
+
)
|
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ npm install react-native-nitro-xlsx
|
|
|
22
22
|
yarn add react-native-nitro-xlsx
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
> **Note**: Native dependencies (OpenXlsx) are downloaded from GitHub during the first build. Please make sure you have a stable network connection.
|
|
26
|
+
|
|
25
27
|
### iOS
|
|
26
28
|
|
|
27
29
|
```sh
|
|
@@ -122,6 +124,47 @@ const fontName = format.getFontName();
|
|
|
122
124
|
const fontSize = format.getFontSize();
|
|
123
125
|
```
|
|
124
126
|
|
|
127
|
+
### Working with JSON
|
|
128
|
+
|
|
129
|
+
#### Export to JSON (`toJSON`)
|
|
130
|
+
|
|
131
|
+
Convert worksheet data to a JSON array. By default, the first row is used as object keys.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { NitroXlsx, toJSON } from 'react-native-nitro-xlsx';
|
|
135
|
+
|
|
136
|
+
const workbook = NitroXlsx.openWorkbook('/path/to/data.xlsx');
|
|
137
|
+
|
|
138
|
+
// Use the first row as keys (default)
|
|
139
|
+
const data = toJSON(workbook);
|
|
140
|
+
// Result: [{ "Name": "Alice", "Age": 30 }, { "Name": "Bob", "Age": 25 }]
|
|
141
|
+
|
|
142
|
+
// Provide custom keys
|
|
143
|
+
const data = toJSON(workbook, ['firstName', 'yearsOld']);
|
|
144
|
+
// Result: [{ "firstName": "Alice", "yearsOld": 30 }, { "firstName": "Bob", "yearsOld": 25 }]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Import from JSON (`fromJSON`)
|
|
148
|
+
|
|
149
|
+
Create a workbook from a JSON array. The first object's keys become the header row.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { NitroXlsx } from 'react-native-nitro-xlsx';
|
|
153
|
+
|
|
154
|
+
const data = [
|
|
155
|
+
{ Name: 'Alice', Age: 30, Active: true },
|
|
156
|
+
{ Name: 'Bob', Age: 25, Active: false },
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const workbook = NitroXlsx.fromJSON(data);
|
|
160
|
+
// The workbook now has a worksheet with:
|
|
161
|
+
// Row 1 (header): Name | Age | Active
|
|
162
|
+
// Row 2: Alice | 30 | true
|
|
163
|
+
// Row 3: Bob | 25 | false
|
|
164
|
+
|
|
165
|
+
const buffer = await workbook.getBuffer();
|
|
166
|
+
```
|
|
167
|
+
|
|
125
168
|
## API
|
|
126
169
|
|
|
127
170
|
### NitroXlsx
|
|
@@ -129,6 +172,7 @@ const fontSize = format.getFontSize();
|
|
|
129
172
|
- `createWorkbook(tempDir?: string): XlsxWorkbook` - Create a new workbook. On Android, pass the app's cache directory (e.g., `RNFS.CachesDirectoryPath`) to avoid sandbox write permission issues.
|
|
130
173
|
- `openWorkbook(path: string): XlsxWorkbook` - Open workbook from file path
|
|
131
174
|
- `openWorkbookFromBuffer(buffer: ArrayBuffer): XlsxWorkbook` - Open workbook from buffer
|
|
175
|
+
- `fromJSON(data: Array<Record<string, string | number>>): XlsxWorkbook` - Create a workbook from a JSON array (static method)
|
|
132
176
|
|
|
133
177
|
### XlsxWorkbook
|
|
134
178
|
|
|
@@ -139,6 +183,7 @@ const fontSize = format.getFontSize();
|
|
|
139
183
|
- `getWorksheetCount(): number` - Get number of worksheets
|
|
140
184
|
- `addCellFormat(): XlsxCellFormat` - Add a new cell format
|
|
141
185
|
- `getBuffer(): Promise<ArrayBuffer>` - Generate and return the XLSX file as a buffer
|
|
186
|
+
- `toJSON(keys?: string[]): Array<Record<string, string | number>>` - Convert worksheet data to a JSON array
|
|
142
187
|
|
|
143
188
|
### XlsxWorksheet
|
|
144
189
|
|
package/android/CMakeLists.txt
CHANGED
|
@@ -5,22 +5,21 @@ set(CMAKE_CXX_STANDARD 20)
|
|
|
5
5
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
6
|
|
|
7
7
|
set(OPENXLSX_DIR ${CMAKE_SOURCE_DIR}/../OpenXLSX)
|
|
8
|
-
if(NOT EXISTS ${OPENXLSX_DIR}/CMakeLists.txt)
|
|
9
|
-
# Clean up incomplete directory to ensure fresh clone
|
|
10
|
-
if(EXISTS ${OPENXLSX_DIR})
|
|
11
|
-
file(REMOVE_RECURSE ${OPENXLSX_DIR})
|
|
12
|
-
endif()
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
9
|
+
# Prepare OpenXLSX and its dependencies (pugixml, miniz) using the same
|
|
10
|
+
# Node script that CocoaPods' prepare_command runs on iOS.
|
|
11
|
+
find_program(NODE_EXECUTABLE node REQUIRED)
|
|
12
|
+
execute_process(
|
|
13
|
+
COMMAND ${NODE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/../scripts/prepare-openxlsx.js
|
|
14
|
+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/..
|
|
15
|
+
RESULT_VARIABLE PREPARE_RESULT
|
|
16
|
+
)
|
|
17
|
+
if(NOT PREPARE_RESULT EQUAL 0)
|
|
18
|
+
message(FATAL_ERROR
|
|
19
|
+
"Failed to prepare OpenXLSX dependencies.\n"
|
|
20
|
+
"Please ensure Node.js is installed and run:\n"
|
|
21
|
+
" node scripts/prepare-openxlsx.js"
|
|
22
22
|
)
|
|
23
|
-
FetchContent_Populate(OpenXLSX)
|
|
24
23
|
endif()
|
|
25
24
|
|
|
26
25
|
# Verify OpenXLSX is available
|
|
@@ -33,15 +32,6 @@ if(NOT EXISTS ${OPENXLSX_DIR}/CMakeLists.txt)
|
|
|
33
32
|
)
|
|
34
33
|
endif()
|
|
35
34
|
|
|
36
|
-
# Create symlink for #include <OpenXLSX/XXX.hpp> resolution
|
|
37
|
-
# Same as iOS podspec: cd OpenXLSX/OpenXLSX/headers && ln -sf . OpenXLSX
|
|
38
|
-
if(NOT EXISTS ${OPENXLSX_DIR}/OpenXLSX/headers/OpenXLSX)
|
|
39
|
-
execute_process(
|
|
40
|
-
COMMAND ${CMAKE_COMMAND} -E create_symlink . OpenXLSX
|
|
41
|
-
WORKING_DIRECTORY ${OPENXLSX_DIR}/OpenXLSX/headers
|
|
42
|
-
)
|
|
43
|
-
endif()
|
|
44
|
-
|
|
45
35
|
set(OPENXLSX_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
|
|
46
36
|
set(OPENXLSX_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|
47
37
|
set(OPENXLSX_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
|
package/android/build.gradle
CHANGED
|
@@ -4,6 +4,11 @@ buildscript {
|
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
def reactNativeArchitectures() {
|
|
8
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
9
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
apply plugin: 'com.android.library'
|
|
8
13
|
apply plugin: 'kotlin-android'
|
|
9
14
|
|
|
@@ -16,6 +21,7 @@ android {
|
|
|
16
21
|
externalNativeBuild {
|
|
17
22
|
cmake {
|
|
18
23
|
arguments "-DANDROID_STL=c++_shared"
|
|
24
|
+
abiFilters (*reactNativeArchitectures())
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
27
|
}
|
package/cpp/HybridNitroXlsx.cpp
CHANGED
|
@@ -1,5 +1,98 @@
|
|
|
1
1
|
#include "HybridNitroXlsx.hpp"
|
|
2
2
|
#include <fstream>
|
|
3
|
+
#include <stdexcept>
|
|
4
|
+
#include <cstring>
|
|
5
|
+
#include <cstdlib>
|
|
6
|
+
|
|
7
|
+
namespace {
|
|
8
|
+
|
|
9
|
+
struct JsonParser {
|
|
10
|
+
const char* p;
|
|
11
|
+
explicit JsonParser(const char* s) : p(s) {}
|
|
12
|
+
|
|
13
|
+
void skipWs() { while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') ++p; }
|
|
14
|
+
|
|
15
|
+
char peek() { skipWs(); return *p; }
|
|
16
|
+
|
|
17
|
+
void expect(char c) {
|
|
18
|
+
skipWs();
|
|
19
|
+
if (*p != c) throw std::runtime_error(std::string("Expected '") + c + "' but got '" + *p + "'");
|
|
20
|
+
++p;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
std::string parseString() {
|
|
24
|
+
expect('"');
|
|
25
|
+
std::string s;
|
|
26
|
+
while (*p && *p != '"') {
|
|
27
|
+
if (*p == '\\') {
|
|
28
|
+
++p;
|
|
29
|
+
switch (*p) {
|
|
30
|
+
case '"': s += '"'; break;
|
|
31
|
+
case '\\': s += '\\'; break;
|
|
32
|
+
case '/': s += '/'; break;
|
|
33
|
+
case 'b': s += '\b'; break;
|
|
34
|
+
case 'f': s += '\f'; break;
|
|
35
|
+
case 'n': s += '\n'; break;
|
|
36
|
+
case 'r': s += '\r'; break;
|
|
37
|
+
case 't': s += '\t'; break;
|
|
38
|
+
case 'u': {
|
|
39
|
+
++p;
|
|
40
|
+
char hex[5] = {};
|
|
41
|
+
std::memcpy(hex, p, 4);
|
|
42
|
+
p += 4;
|
|
43
|
+
unsigned long cp = std::strtoul(hex, nullptr, 16);
|
|
44
|
+
if (cp < 0x80) {
|
|
45
|
+
s += static_cast<char>(cp);
|
|
46
|
+
} else if (cp < 0x800) {
|
|
47
|
+
s += static_cast<char>(0xC0 | (cp >> 6));
|
|
48
|
+
s += static_cast<char>(0x80 | (cp & 0x3F));
|
|
49
|
+
} else {
|
|
50
|
+
s += static_cast<char>(0xE0 | (cp >> 12));
|
|
51
|
+
s += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
|
52
|
+
s += static_cast<char>(0x80 | (cp & 0x3F));
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
default: s += '\\'; s += *p; break;
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
s += *p;
|
|
60
|
+
}
|
|
61
|
+
++p;
|
|
62
|
+
}
|
|
63
|
+
if (*p == '"') ++p;
|
|
64
|
+
return s;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
enum ValueType { T_STRING, T_NUMBER, T_BOOL, T_NULL };
|
|
68
|
+
|
|
69
|
+
struct Value {
|
|
70
|
+
ValueType type;
|
|
71
|
+
std::string str;
|
|
72
|
+
double num = 0;
|
|
73
|
+
bool boolean = false;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
Value parseValue() {
|
|
77
|
+
skipWs();
|
|
78
|
+
if (*p == '"') return { T_STRING, parseString(), 0, false };
|
|
79
|
+
if (*p == 't') { p += 4; return { T_BOOL, "", 0, true }; }
|
|
80
|
+
if (*p == 'f') { p += 5; return { T_BOOL, "", 0, false }; }
|
|
81
|
+
if (*p == 'n') { p += 4; return { T_NULL, "", 0, false }; }
|
|
82
|
+
|
|
83
|
+
// number
|
|
84
|
+
Value v{ T_NUMBER, "", 0, false };
|
|
85
|
+
const char* start = p;
|
|
86
|
+
if (*p == '-') ++p;
|
|
87
|
+
while (*p >= '0' && *p <= '9') ++p;
|
|
88
|
+
if (*p == '.') { ++p; while (*p >= '0' && *p <= '9') ++p; }
|
|
89
|
+
if (*p == 'e' || *p == 'E') { ++p; if (*p == '+' || *p == '-') ++p; while (*p >= '0' && *p <= '9') ++p; }
|
|
90
|
+
v.num = std::strtod(start, nullptr);
|
|
91
|
+
return v;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
} // anonymous namespace
|
|
3
96
|
|
|
4
97
|
namespace margelo::nitro::xlsx {
|
|
5
98
|
|
|
@@ -26,4 +119,88 @@ std::shared_ptr<HybridXlsxWorkbookSpec> HybridNitroXlsx::openWorkbookFromBuffer(
|
|
|
26
119
|
return workbook;
|
|
27
120
|
}
|
|
28
121
|
|
|
122
|
+
std::shared_ptr<HybridXlsxWorkbookSpec> HybridNitroXlsx::fromJSON(const std::string& data) {
|
|
123
|
+
auto workbook = std::make_shared<HybridXlsxWorkbook>();
|
|
124
|
+
auto sheet = workbook->addWorksheet("Sheet1");
|
|
125
|
+
|
|
126
|
+
JsonParser parser(data.c_str());
|
|
127
|
+
parser.skipWs();
|
|
128
|
+
parser.expect('[');
|
|
129
|
+
|
|
130
|
+
if (parser.peek() == ']') return workbook;
|
|
131
|
+
|
|
132
|
+
// Parse first object to extract keys
|
|
133
|
+
parser.expect('{');
|
|
134
|
+
|
|
135
|
+
std::vector<std::string> keys;
|
|
136
|
+
unsigned int col = 1;
|
|
137
|
+
|
|
138
|
+
while (parser.peek() != '}') {
|
|
139
|
+
std::string key = parser.parseString();
|
|
140
|
+
keys.push_back(key);
|
|
141
|
+
parser.expect(':');
|
|
142
|
+
auto val = parser.parseValue();
|
|
143
|
+
|
|
144
|
+
switch (val.type) {
|
|
145
|
+
case JsonParser::T_STRING:
|
|
146
|
+
sheet->writeString(1, col, val.str, std::nullopt);
|
|
147
|
+
break;
|
|
148
|
+
case JsonParser::T_NUMBER:
|
|
149
|
+
sheet->writeNumber(1, col, val.num, std::nullopt);
|
|
150
|
+
break;
|
|
151
|
+
case JsonParser::T_BOOL:
|
|
152
|
+
sheet->writeBoolean(1, col, val.boolean, std::nullopt);
|
|
153
|
+
break;
|
|
154
|
+
default:
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
++col;
|
|
158
|
+
if (parser.peek() == ',') ++parser.p;
|
|
159
|
+
}
|
|
160
|
+
parser.expect('}');
|
|
161
|
+
|
|
162
|
+
unsigned int row = 2;
|
|
163
|
+
|
|
164
|
+
while (parser.peek() == ',') {
|
|
165
|
+
++parser.p;
|
|
166
|
+
parser.expect('{');
|
|
167
|
+
|
|
168
|
+
while (parser.peek() != '}') {
|
|
169
|
+
std::string key = parser.parseString();
|
|
170
|
+
parser.expect(':');
|
|
171
|
+
auto val = parser.parseValue();
|
|
172
|
+
|
|
173
|
+
unsigned int targetCol = 0;
|
|
174
|
+
for (size_t k = 0; k < keys.size(); ++k) {
|
|
175
|
+
if (keys[k] == key) { targetCol = static_cast<unsigned int>(k + 1); break; }
|
|
176
|
+
}
|
|
177
|
+
if (targetCol == 0) {
|
|
178
|
+
keys.push_back(key);
|
|
179
|
+
targetCol = static_cast<unsigned int>(keys.size());
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
switch (val.type) {
|
|
183
|
+
case JsonParser::T_STRING:
|
|
184
|
+
sheet->writeString(row, targetCol, val.str, std::nullopt);
|
|
185
|
+
break;
|
|
186
|
+
case JsonParser::T_NUMBER:
|
|
187
|
+
sheet->writeNumber(row, targetCol, val.num, std::nullopt);
|
|
188
|
+
break;
|
|
189
|
+
case JsonParser::T_BOOL:
|
|
190
|
+
sheet->writeBoolean(row, targetCol, val.boolean, std::nullopt);
|
|
191
|
+
break;
|
|
192
|
+
default:
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (parser.peek() == ',') ++parser.p;
|
|
197
|
+
}
|
|
198
|
+
parser.expect('}');
|
|
199
|
+
++row;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
parser.expect(']');
|
|
203
|
+
return workbook;
|
|
204
|
+
}
|
|
205
|
+
|
|
29
206
|
}
|
package/cpp/HybridNitroXlsx.hpp
CHANGED
|
@@ -14,6 +14,7 @@ public:
|
|
|
14
14
|
std::shared_ptr<HybridXlsxWorkbookSpec> createWorkbook(const std::optional<std::string>& tempDir = std::nullopt) override;
|
|
15
15
|
std::shared_ptr<HybridXlsxWorkbookSpec> openWorkbook(const std::string& path) override;
|
|
16
16
|
std::shared_ptr<HybridXlsxWorkbookSpec> openWorkbookFromBuffer(const std::shared_ptr<ArrayBuffer>& buffer) override;
|
|
17
|
+
std::shared_ptr<HybridXlsxWorkbookSpec> fromJSON(const std::string& data) override;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
}
|
|
@@ -4,12 +4,43 @@
|
|
|
4
4
|
#include <sstream>
|
|
5
5
|
#include <cstdlib>
|
|
6
6
|
#include <unistd.h>
|
|
7
|
+
#include <cmath>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
#include <iomanip>
|
|
7
10
|
|
|
8
11
|
#ifdef __ANDROID__
|
|
9
12
|
#include <android/api-level.h>
|
|
10
13
|
#include <sys/stat.h>
|
|
11
14
|
#endif
|
|
12
15
|
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
void writeJsonString(std::ostringstream& out, const std::string& s) {
|
|
19
|
+
out << '"';
|
|
20
|
+
for (char c : s) {
|
|
21
|
+
switch (c) {
|
|
22
|
+
case '"': out << "\\\""; break;
|
|
23
|
+
case '\\': out << "\\\\"; break;
|
|
24
|
+
case '\b': out << "\\b"; break;
|
|
25
|
+
case '\f': out << "\\f"; break;
|
|
26
|
+
case '\n': out << "\\n"; break;
|
|
27
|
+
case '\r': out << "\\r"; break;
|
|
28
|
+
case '\t': out << "\\t"; break;
|
|
29
|
+
default:
|
|
30
|
+
if (static_cast<unsigned char>(c) < 0x20) {
|
|
31
|
+
out << "\\u" << std::hex << std::setw(4) << std::setfill('0')
|
|
32
|
+
<< static_cast<int>(static_cast<unsigned char>(c))
|
|
33
|
+
<< std::dec;
|
|
34
|
+
} else {
|
|
35
|
+
out << c;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
out << '"';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
} // anonymous namespace
|
|
43
|
+
|
|
13
44
|
namespace margelo::nitro::xlsx {
|
|
14
45
|
|
|
15
46
|
namespace {
|
|
@@ -186,4 +217,85 @@ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> HybridXlsxWorkbook::getBu
|
|
|
186
217
|
return promise;
|
|
187
218
|
}
|
|
188
219
|
|
|
220
|
+
std::string HybridXlsxWorkbook::toJSON(const std::optional<std::vector<std::string>>& keys) {
|
|
221
|
+
if (_worksheets.empty()) {
|
|
222
|
+
return "[]";
|
|
223
|
+
}
|
|
224
|
+
auto& ws = _worksheets[0];
|
|
225
|
+
auto lastRow = static_cast<unsigned int>(ws->getLastRow());
|
|
226
|
+
auto lastCol = static_cast<unsigned int>(ws->getLastColumn());
|
|
227
|
+
if (lastRow == 0 || lastCol == 0) {
|
|
228
|
+
return "[]";
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
unsigned int dataStartRow = 1;
|
|
232
|
+
std::vector<std::string> resolvedKeys;
|
|
233
|
+
|
|
234
|
+
if (keys.has_value()) {
|
|
235
|
+
resolvedKeys = *keys;
|
|
236
|
+
} else {
|
|
237
|
+
dataStartRow = 2;
|
|
238
|
+
for (unsigned int c = 1; c <= lastCol; ++c) {
|
|
239
|
+
auto val = ws->getCellValue(1, c);
|
|
240
|
+
if (auto* s = std::get_if<std::string>(&val)) {
|
|
241
|
+
resolvedKeys.push_back(*s);
|
|
242
|
+
} else if (auto* d = std::get_if<double>(&val)) {
|
|
243
|
+
resolvedKeys.push_back(std::to_string(static_cast<int>(*d)));
|
|
244
|
+
} else {
|
|
245
|
+
resolvedKeys.push_back("col" + std::to_string(c));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (resolvedKeys.empty()) {
|
|
251
|
+
return "[]";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
std::ostringstream out;
|
|
255
|
+
out << "[";
|
|
256
|
+
|
|
257
|
+
bool firstRow = true;
|
|
258
|
+
|
|
259
|
+
for (unsigned int r = dataStartRow; r <= lastRow; ++r) {
|
|
260
|
+
bool hasData = false;
|
|
261
|
+
for (unsigned int c = 1; c <= std::min(static_cast<unsigned int>(resolvedKeys.size()), lastCol); ++c) {
|
|
262
|
+
auto cellType = ws->getCellType(r, c);
|
|
263
|
+
if (cellType != CellType::EMPTY) {
|
|
264
|
+
hasData = true;
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (!hasData) continue;
|
|
269
|
+
|
|
270
|
+
if (!firstRow) out << ",";
|
|
271
|
+
firstRow = false;
|
|
272
|
+
|
|
273
|
+
out << "{";
|
|
274
|
+
unsigned int keyCount = std::min(static_cast<unsigned int>(resolvedKeys.size()), lastCol);
|
|
275
|
+
for (unsigned int c = 0; c < keyCount; ++c) {
|
|
276
|
+
if (c > 0) out << ",";
|
|
277
|
+
writeJsonString(out, resolvedKeys[c]);
|
|
278
|
+
out << ":";
|
|
279
|
+
auto val = ws->getCellValue(r, c + 1);
|
|
280
|
+
if (auto* s = std::get_if<std::string>(&val)) {
|
|
281
|
+
writeJsonString(out, *s);
|
|
282
|
+
} else if (auto* d = std::get_if<double>(&val)) {
|
|
283
|
+
if (std::isnan(*d) || std::isinf(*d)) {
|
|
284
|
+
out << "null";
|
|
285
|
+
} else {
|
|
286
|
+
out << *d;
|
|
287
|
+
}
|
|
288
|
+
} else if (auto* b = std::get_if<bool>(&val)) {
|
|
289
|
+
out << (*b ? "true" : "false");
|
|
290
|
+
} else {
|
|
291
|
+
out << "null";
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
out << "}";
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
out << "]";
|
|
298
|
+
return out.str();
|
|
299
|
+
}
|
|
300
|
+
|
|
189
301
|
}
|
|
@@ -26,6 +26,7 @@ public:
|
|
|
26
26
|
double getWorksheetCount() override;
|
|
27
27
|
std::shared_ptr<HybridXlsxCellFormatSpec> addCellFormat() override;
|
|
28
28
|
std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> getBuffer() override;
|
|
29
|
+
std::string toJSON(const std::optional<std::vector<std::string>>& keys) override;
|
|
29
30
|
|
|
30
31
|
private:
|
|
31
32
|
std::unique_ptr<OpenXLSX::XLDocument> _workbook;
|
|
@@ -75,6 +75,7 @@ void HybridXlsxWorksheet::writeFormulaBoolean(double row, double col, const std:
|
|
|
75
75
|
void HybridXlsxWorksheet::writeArrayFormula(double firstRow, double firstCol, double lastRow, double lastCol, const std::string& formula, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
|
|
76
76
|
OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(firstRow), static_cast<unsigned int>(firstCol));
|
|
77
77
|
cell.formula() = formula;
|
|
78
|
+
applyFormat(cell, format);
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
void HybridXlsxWorksheet::writeDatetime(double row, double col, double datetime, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
|
|
@@ -93,10 +94,33 @@ void HybridXlsxWorksheet::setColumn(double firstCol, double lastCol, double widt
|
|
|
93
94
|
for (unsigned int c = static_cast<unsigned int>(firstCol); c <= static_cast<unsigned int>(lastCol); ++c) {
|
|
94
95
|
_worksheet.column(c).setWidth(static_cast<float>(width));
|
|
95
96
|
}
|
|
97
|
+
if (format.has_value()) {
|
|
98
|
+
// Apply format to entire column range by setting it on cells in the first row
|
|
99
|
+
// (OpenXLSX does not support column-level style; apply to first row cells as a hint)
|
|
100
|
+
auto hybridFormat = std::dynamic_pointer_cast<HybridXlsxCellFormat>(*format);
|
|
101
|
+
if (hybridFormat) {
|
|
102
|
+
auto styleIndex = hybridFormat->applyToDocument(_doc.styles());
|
|
103
|
+
for (unsigned int c = static_cast<unsigned int>(firstCol); c <= static_cast<unsigned int>(lastCol); ++c) {
|
|
104
|
+
OpenXLSX::XLCell cell = _worksheet.cell(1, c);
|
|
105
|
+
hybridFormat->applyToCell(cell, styleIndex);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
void HybridXlsxWorksheet::setRow(double row, double height, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
|
|
99
112
|
_worksheet.row(static_cast<unsigned int>(row)).setHeight(static_cast<float>(height));
|
|
113
|
+
if (format.has_value()) {
|
|
114
|
+
auto hybridFormat = std::dynamic_pointer_cast<HybridXlsxCellFormat>(*format);
|
|
115
|
+
if (hybridFormat) {
|
|
116
|
+
auto styleIndex = hybridFormat->applyToDocument(_doc.styles());
|
|
117
|
+
unsigned int colCount = _worksheet.columnCount();
|
|
118
|
+
for (unsigned int c = 1; c <= colCount; ++c) {
|
|
119
|
+
OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), c);
|
|
120
|
+
hybridFormat->applyToCell(cell, styleIndex);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
100
124
|
}
|
|
101
125
|
|
|
102
126
|
void HybridXlsxWorksheet::mergeRange(double firstRow, double firstCol, double lastRow, double lastCol, const std::string& value, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
|
package/ios/dummy.swift
ADDED
|
@@ -17,6 +17,7 @@ namespace margelo::nitro::xlsx {
|
|
|
17
17
|
prototype.registerHybridMethod("createWorkbook", &HybridNitroXlsxSpec::createWorkbook);
|
|
18
18
|
prototype.registerHybridMethod("openWorkbook", &HybridNitroXlsxSpec::openWorkbook);
|
|
19
19
|
prototype.registerHybridMethod("openWorkbookFromBuffer", &HybridNitroXlsxSpec::openWorkbookFromBuffer);
|
|
20
|
+
prototype.registerHybridMethod("fromJSON", &HybridNitroXlsxSpec::fromJSON);
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -56,6 +56,7 @@ namespace margelo::nitro::xlsx {
|
|
|
56
56
|
virtual std::shared_ptr<HybridXlsxWorkbookSpec> createWorkbook(const std::optional<std::string>& tempDir) = 0;
|
|
57
57
|
virtual std::shared_ptr<HybridXlsxWorkbookSpec> openWorkbook(const std::string& path) = 0;
|
|
58
58
|
virtual std::shared_ptr<HybridXlsxWorkbookSpec> openWorkbookFromBuffer(const std::shared_ptr<ArrayBuffer>& buffer) = 0;
|
|
59
|
+
virtual std::shared_ptr<HybridXlsxWorkbookSpec> fromJSON(const std::string& data) = 0;
|
|
59
60
|
|
|
60
61
|
protected:
|
|
61
62
|
// Hybrid Setup
|
|
@@ -21,6 +21,7 @@ namespace margelo::nitro::xlsx {
|
|
|
21
21
|
prototype.registerHybridMethod("getWorksheetCount", &HybridXlsxWorkbookSpec::getWorksheetCount);
|
|
22
22
|
prototype.registerHybridMethod("addCellFormat", &HybridXlsxWorkbookSpec::addCellFormat);
|
|
23
23
|
prototype.registerHybridMethod("getBuffer", &HybridXlsxWorkbookSpec::getBuffer);
|
|
24
|
+
prototype.registerHybridMethod("toJSON", &HybridXlsxWorkbookSpec::toJSON);
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -25,6 +25,7 @@ namespace margelo::nitro::xlsx { class HybridXlsxCellFormatSpec; }
|
|
|
25
25
|
#include "HybridXlsxCellFormatSpec.hpp"
|
|
26
26
|
#include <NitroModules/ArrayBuffer.hpp>
|
|
27
27
|
#include <NitroModules/Promise.hpp>
|
|
28
|
+
#include <vector>
|
|
28
29
|
|
|
29
30
|
namespace margelo::nitro::xlsx {
|
|
30
31
|
|
|
@@ -64,6 +65,7 @@ namespace margelo::nitro::xlsx {
|
|
|
64
65
|
virtual double getWorksheetCount() = 0;
|
|
65
66
|
virtual std::shared_ptr<HybridXlsxCellFormatSpec> addCellFormat() = 0;
|
|
66
67
|
virtual std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> getBuffer() = 0;
|
|
68
|
+
virtual std::string toJSON(const std::optional<std::vector<std::string>>& keys) = 0;
|
|
67
69
|
|
|
68
70
|
protected:
|
|
69
71
|
// Hybrid Setup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-xlsx",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Fast XLSX file reading and writing for React Native using OpenXLSX and Nitro Modules",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/zydbkqf/react-native-nitro-xlsx",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"types": "src/index.ts",
|
|
11
11
|
"source": "src/index.ts",
|
|
12
12
|
"scripts": {
|
|
13
|
+
"postinstall": "node scripts/prepare-openxlsx.js",
|
|
13
14
|
"specs": "nitrogen",
|
|
14
15
|
"test": "jest",
|
|
15
16
|
"test:cpp": "cd cpp/tests && [ -n \"$OPENXLSX_PATH\" ] || OPENXLSX_PATH=$HOME/OpenXLSX; cmake -B build -DOPENXLSX_PATH=$OPENXLSX_PATH && cmake --build build && cd build && ctest --output-on-failure",
|
|
@@ -36,6 +37,8 @@
|
|
|
36
37
|
"!cpp/tests",
|
|
37
38
|
"android",
|
|
38
39
|
"ios",
|
|
40
|
+
"scripts",
|
|
41
|
+
"Package.swift",
|
|
39
42
|
"nitro.json",
|
|
40
43
|
"nitrogen",
|
|
41
44
|
"NitroXlsx.podspec",
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, symlinkSync, readFileSync } from 'fs';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
const rootDir = dirname(__dirname);
|
|
9
|
+
|
|
10
|
+
function run(command, options = {}) {
|
|
11
|
+
console.log(`> ${command}`);
|
|
12
|
+
execSync(command, { stdio: 'inherit', ...options });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function ensureDir(dir) {
|
|
16
|
+
if (!existsSync(dir)) {
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function cloneRepo(url, tag, dest) {
|
|
22
|
+
if (existsSync(dest)) {
|
|
23
|
+
console.log(`Already exists: ${dest}`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
run(`git clone --depth 1 --branch ${tag} ${url} ${dest}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeFileIfNotExists(path, content) {
|
|
30
|
+
if (existsSync(path)) {
|
|
31
|
+
console.log(`Already exists: ${path}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
writeFileSync(path, content);
|
|
35
|
+
console.log(`Created: ${path}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createSymlinkIfNotExists(target, link) {
|
|
39
|
+
if (existsSync(link)) {
|
|
40
|
+
console.log(`Already exists: ${link}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
symlinkSync(target, link);
|
|
44
|
+
console.log(`Created symlink: ${link} -> ${target}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getPackageVersion() {
|
|
48
|
+
const packageJsonPath = join(rootDir, 'package.json');
|
|
49
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
50
|
+
return packageJson.version || '0.1.0';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const openxlsxDir = join(rootDir, 'OpenXLSX');
|
|
54
|
+
const thirdPartyDir = join(openxlsxDir, 'ThirdParty');
|
|
55
|
+
const pugixmlDir = join(thirdPartyDir, 'pugixml');
|
|
56
|
+
const minizDir = join(thirdPartyDir, 'miniz');
|
|
57
|
+
|
|
58
|
+
// Clone OpenXLSX and dependencies
|
|
59
|
+
cloneRepo('https://github.com/troldal/OpenXLSX.git', 'v0.5.1', openxlsxDir);
|
|
60
|
+
ensureDir(thirdPartyDir);
|
|
61
|
+
cloneRepo('https://github.com/zeux/pugixml.git', 'v1.16', pugixmlDir);
|
|
62
|
+
cloneRepo('https://github.com/richgel999/miniz.git', '3.1.2', minizDir);
|
|
63
|
+
|
|
64
|
+
// Generate miniz_export.h
|
|
65
|
+
const minizExportHeader = `#ifndef MINIZ_EXPORT_H
|
|
66
|
+
#define MINIZ_EXPORT_H
|
|
67
|
+
|
|
68
|
+
#ifndef MINIZ_EXPORT
|
|
69
|
+
#define MINIZ_EXPORT
|
|
70
|
+
#endif
|
|
71
|
+
|
|
72
|
+
#ifndef MINIZ_DEPRECATED
|
|
73
|
+
#define MINIZ_DEPRECATED
|
|
74
|
+
#endif
|
|
75
|
+
|
|
76
|
+
#ifndef MINIZ_DEPRECATED_EXPORT
|
|
77
|
+
#define MINIZ_DEPRECATED_EXPORT MINIZ_EXPORT MINIZ_DEPRECATED
|
|
78
|
+
#endif
|
|
79
|
+
|
|
80
|
+
#ifndef MINIZ_NO_EXPORT
|
|
81
|
+
#define MINIZ_NO_EXPORT
|
|
82
|
+
#endif
|
|
83
|
+
|
|
84
|
+
#endif
|
|
85
|
+
`;
|
|
86
|
+
writeFileIfNotExists(join(minizDir, 'miniz_export.h'), minizExportHeader);
|
|
87
|
+
|
|
88
|
+
// Generate OpenXLSX-Exports.hpp
|
|
89
|
+
const openxlsxExportsHeader = `#ifndef OPENXLSX_EXPORTS_HPP
|
|
90
|
+
#define OPENXLSX_EXPORTS_HPP
|
|
91
|
+
|
|
92
|
+
#ifndef OPENXLSX_EXPORT
|
|
93
|
+
#define OPENXLSX_EXPORT
|
|
94
|
+
#endif
|
|
95
|
+
|
|
96
|
+
#ifndef OPENXLSX_HIDDEN
|
|
97
|
+
#define OPENXLSX_HIDDEN
|
|
98
|
+
#endif
|
|
99
|
+
|
|
100
|
+
#endif
|
|
101
|
+
`;
|
|
102
|
+
const headersDir = join(openxlsxDir, 'OpenXLSX', 'headers');
|
|
103
|
+
ensureDir(headersDir);
|
|
104
|
+
writeFileIfNotExists(join(headersDir, 'OpenXLSX-Exports.hpp'), openxlsxExportsHeader);
|
|
105
|
+
|
|
106
|
+
// Create symlink for #include <OpenXLSX/XXX.hpp> resolution
|
|
107
|
+
createSymlinkIfNotExists('.', join(headersDir, 'OpenXLSX'));
|
|
108
|
+
|
|
109
|
+
console.log(`OpenXLSX dependencies prepared successfully for react-native-nitro-xlsx v${getPackageVersion()}.`);
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { NitroModules } from 'react-native-nitro-modules'
|
|
2
|
-
import type { NitroXlsx as NitroXlsxSpec } from './specs/NitroXlsx.nitro'
|
|
2
|
+
import type { NitroXlsx as NitroXlsxSpec, XlsxWorkbook as XlsxWorkbookSpec } from './specs/NitroXlsx.nitro'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const NitroXlsxBridge = NitroModules.createHybridObject<NitroXlsxSpec>('NitroXlsx')
|
|
5
|
+
|
|
6
|
+
type JsonRecord = Record<string, string | number>
|
|
7
|
+
|
|
8
|
+
export const NitroXlsx = Object.assign(NitroXlsxBridge, {
|
|
9
|
+
fromJSON(data: JsonRecord[]): XlsxWorkbookSpec {
|
|
10
|
+
return NitroXlsxBridge.fromJSON(JSON.stringify(data))
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export function toJSON(workbook: XlsxWorkbookSpec, keys?: string[]): JsonRecord[] {
|
|
15
|
+
return JSON.parse(workbook.toJSON(keys))
|
|
16
|
+
}
|
|
5
17
|
|
|
6
18
|
export type { XlsxWorkbook, XlsxWorksheet, XlsxCellFormat } from './specs/NitroXlsx.nitro'
|
|
7
19
|
|
|
@@ -10,6 +10,7 @@ export interface XlsxWorkbook extends HybridObject<{ ios: 'c++'; android: 'c++'
|
|
|
10
10
|
getWorksheetCount(): number
|
|
11
11
|
addCellFormat(): XlsxCellFormat
|
|
12
12
|
getBuffer(): Promise<ArrayBuffer>
|
|
13
|
+
toJSON(keys?: string[]): string
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export interface XlsxWorksheet extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
|
|
@@ -151,4 +152,5 @@ export interface NitroXlsx extends HybridObject<{ ios: 'c++'; android: 'c++' }>
|
|
|
151
152
|
createWorkbook(tempDir?: string): XlsxWorkbook
|
|
152
153
|
openWorkbook(path: string): XlsxWorkbook
|
|
153
154
|
openWorkbookFromBuffer(buffer: ArrayBuffer): XlsxWorkbook
|
|
155
|
+
fromJSON(data: string): XlsxWorkbook
|
|
154
156
|
}
|