react-native-nitro-xlsx 0.1.2 → 0.2.5

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 (42) hide show
  1. package/NOTICE +8 -35
  2. package/NitroXlsx.podspec +21 -57
  3. package/Package.swift +35 -0
  4. package/README.md +243 -36
  5. package/android/CMakeLists.txt +17 -23
  6. package/android/build.gradle +6 -0
  7. package/android/cpp-adapter.cpp +12 -0
  8. package/android/src/main/java/com/margelo/nitro/xlsx/NitroXlsxPackage.kt +9 -0
  9. package/cpp/HybridNitroXlsx.cpp +32 -9
  10. package/cpp/HybridNitroXlsx.hpp +2 -1
  11. package/cpp/HybridXlsxConditionalFormat.cpp +252 -0
  12. package/cpp/HybridXlsxConditionalFormat.hpp +62 -0
  13. package/cpp/HybridXlsxConditionalFormats.cpp +46 -0
  14. package/cpp/HybridXlsxConditionalFormats.hpp +28 -0
  15. package/cpp/HybridXlsxWorkbook.cpp +162 -87
  16. package/cpp/HybridXlsxWorkbook.hpp +7 -1
  17. package/cpp/HybridXlsxWorksheet.cpp +456 -61
  18. package/cpp/HybridXlsxWorksheet.hpp +73 -26
  19. package/cpp/XlsxCacheDir.cpp +76 -0
  20. package/cpp/XlsxCacheDir.hpp +27 -0
  21. package/cpp/XlsxError.hpp +68 -0
  22. package/cpp/XlsxJson.cpp +185 -0
  23. package/cpp/XlsxJson.hpp +36 -0
  24. package/ios/XlsxCacheDir.mm +60 -0
  25. package/nitrogen/generated/android/NitroXlsx+autolinking.cmake +2 -0
  26. package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.cpp +1 -0
  27. package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.hpp +5 -2
  28. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.cpp +54 -0
  29. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.hpp +95 -0
  30. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.cpp +24 -0
  31. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.hpp +68 -0
  32. package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.cpp +4 -0
  33. package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.hpp +7 -0
  34. package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.cpp +47 -19
  35. package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.hpp +52 -20
  36. package/package.json +5 -2
  37. package/scripts/prepare-openxlsx.js +126 -0
  38. package/scripts/run-cpp-tests.sh +14 -0
  39. package/src/__mocks__/react-native-nitro-modules.ts +1 -0
  40. package/src/__tests__/index.test.ts +76 -7
  41. package/src/index.ts +110 -9
  42. package/src/specs/NitroXlsx.nitro.ts +141 -42
@@ -3,9 +3,11 @@
3
3
  #include "HybridXlsxWorksheetSpec.hpp"
4
4
  #include <OpenXLSX/XLDocument.hpp>
5
5
  #include <OpenXLSX/XLSheet.hpp>
6
+ #include <OpenXLSX/XLComments.hpp>
6
7
  #include <string>
7
8
  #include <memory>
8
9
  #include "HybridXlsxCellFormat.hpp"
10
+ #include "HybridXlsxConditionalFormats.hpp"
9
11
 
10
12
  namespace margelo::nitro::xlsx {
11
13
 
@@ -30,57 +32,102 @@ public:
30
32
 
31
33
  void setColumn(double firstCol, double lastCol, double width, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) override;
32
34
  void setRow(double row, double height, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) override;
33
-
35
+ bool deleteRow(double row) override;
36
+ bool deleteColumn(double col) override;
37
+
34
38
  void mergeRange(double firstRow, double firstCol, double lastRow, double lastCol, const std::string& value, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) override;
35
39
  void mergeRangeNum(double firstRow, double firstCol, double lastRow, double lastCol, double value, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) override;
36
-
37
- void insertImage(double row, double col, const std::string& path, std::optional<double> xOffset, std::optional<double> yOffset, std::optional<double> xScale, std::optional<double> yScale) override;
38
- void insertImageBuffer(double row, double col, const std::shared_ptr<ArrayBuffer>& buffer, const std::string& extension, std::optional<double> xOffset, std::optional<double> yOffset, std::optional<double> xScale, std::optional<double> yScale) override;
39
-
40
- void autofilter(double firstRow, double firstCol, double lastRow, double lastCol) override;
41
-
42
- void freezePanes(double row, double col) override;
43
- void splitPanes(double row, double col) override;
44
-
45
- void setTabColor(double color) override;
40
+ void unmergeCells(double firstRow, double firstCol, double lastRow, double lastCol) override;
41
+
46
42
  void hide() override;
47
43
  void activate() override;
48
- void setFirstSheet() override;
44
+
45
+ // Sheet protection
49
46
  void protect(const std::optional<std::string>& password) override;
50
-
51
- void setPortrait() override;
52
- void setLandscape() override;
53
- void setPaper(double paperType) override;
54
- void setHeader(const std::string& header) override;
55
- void setFooter(const std::string& footer) override;
56
- void setPrintArea(double firstRow, double firstCol, double lastRow, double lastCol) override;
57
- void fitToPages(double width, double height) override;
58
- void setZoom(double scale) override;
59
- void setGridlines(double option) override;
60
- void centerHorizontally() override;
61
- void centerVertically() override;
62
-
63
- void setOutline(double level) override;
47
+ void protectSheet(std::optional<bool> set) override;
48
+ void protectObjects(std::optional<bool> set) override;
49
+ void protectScenarios(std::optional<bool> set) override;
50
+
51
+ // Password
52
+ void setPassword(const std::string& password) override;
53
+ void setPasswordHash(const std::string& hash) override;
54
+ void clearPassword() override;
55
+ void clearSheetProtection() override;
56
+
57
+ // Fine-grained permissions
58
+ void allowInsertColumns(std::optional<bool> set) override;
59
+ void allowInsertRows(std::optional<bool> set) override;
60
+ void allowDeleteColumns(std::optional<bool> set) override;
61
+ void allowDeleteRows(std::optional<bool> set) override;
62
+ void allowSelectLockedCells(std::optional<bool> set) override;
63
+ void allowSelectUnlockedCells(std::optional<bool> set) override;
64
+
65
+ void denyInsertColumns() override;
66
+ void denyInsertRows() override;
67
+ void denyDeleteColumns() override;
68
+ void denyDeleteRows() override;
69
+ void denySelectLockedCells() override;
70
+ void denySelectUnlockedCells() override;
71
+
72
+ // Protection state
73
+ bool sheetProtected() override;
74
+ bool objectsProtected() override;
75
+ bool scenariosProtected() override;
76
+ bool insertColumnsAllowed() override;
77
+ bool insertRowsAllowed() override;
78
+ bool deleteColumnsAllowed() override;
79
+ bool deleteRowsAllowed() override;
80
+ bool selectLockedCellsAllowed() override;
81
+ bool selectUnlockedCellsAllowed() override;
82
+ bool passwordIsSet() override;
83
+ std::string passwordHash() override;
84
+ std::string sheetProtectionSummary() override;
85
+
64
86
  void setColumnHidden(double firstCol, double lastCol, bool hidden) override;
65
87
  void setRowHidden(double row, bool hidden) override;
66
88
 
89
+ // Comments (XLComments)
90
+ void setComment(double row, double col, const std::string& text, std::optional<double> authorId) override;
91
+ std::string getComment(double row, double col) override;
92
+ bool hasComment(double row, double col) override;
93
+ bool deleteComment(double row, double col) override;
94
+ double getCommentCount() override;
95
+ double addCommentAuthor(const std::string& author) override;
96
+ std::string getCommentAuthor(double authorId) override;
97
+
98
+ // Conditional formatting
99
+ std::shared_ptr<HybridXlsxConditionalFormatsSpec> getConditionalFormats() override;
100
+
67
101
  // Read methods
68
102
  std::variant<bool, nitro::NullType, std::string, double> getCellValue(double row, double col) override;
69
103
  std::string getCellString(double row, double col) override;
70
104
  std::string getCellRawValue(double row, double col) override;
71
105
  CellType getCellType(double row, double col) override;
72
106
  std::shared_ptr<HybridXlsxCellFormatSpec> getCellFormat(double row, double col) override;
107
+ bool hasFormula(double row, double col) override;
108
+ std::string formula(double row, double col) override;
109
+ bool findCell(double row, double col) override;
73
110
  double getRowCount() override;
74
111
  double getColumnCount() override;
75
112
  double getLastRow() override;
76
113
  double getLastColumn() override;
77
114
  std::string getName() override;
78
115
 
116
+ // JSON import / export for this worksheet
117
+ std::vector<std::shared_ptr<AnyMap>> toJSON(const std::optional<std::vector<std::string>>& keys) override;
118
+ void fromJSON(const std::vector<std::shared_ptr<AnyMap>>& data) override;
119
+
120
+ // Internal: rename this sheet and rewrite formula references
121
+ void applyRename(const std::string& oldName, const std::string& newName);
122
+ // Internal: rewrite formula references to a renamed sheet (no actual rename)
123
+ void rewriteSheetNameRef(const std::string& oldName, const std::string& newName);
124
+
79
125
  private:
80
126
  OpenXLSX::XLDocument& _doc;
81
127
  OpenXLSX::XLWorksheet _worksheet;
82
128
 
83
129
  void applyFormat(OpenXLSX::XLCell& cell, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format);
130
+ std::string cellAddress(double row, double col);
84
131
  };
85
132
 
86
133
  }
@@ -0,0 +1,76 @@
1
+ // Android / desktop implementation of XlsxCacheDir.hpp.
2
+ // (Apple is handled by ios/XlsxCacheDir.mm.)
3
+
4
+ #ifndef __APPLE__
5
+
6
+ #include "XlsxCacheDir.hpp"
7
+
8
+ #include <cstdlib>
9
+ #include <sys/stat.h>
10
+
11
+ #ifdef __ANDROID__
12
+ #include <jni.h>
13
+ #include <fbjni/fbjni.h>
14
+ #endif
15
+
16
+ namespace margelo::nitro::xlsx {
17
+
18
+ namespace {
19
+ std::string g_cacheDir;
20
+
21
+ bool isUsableDir(const std::string& path) {
22
+ if (path.empty()) return false;
23
+ struct stat st {};
24
+ return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
25
+ }
26
+ }
27
+
28
+ void setCacheDir(const std::string& dir) {
29
+ g_cacheDir = dir;
30
+ }
31
+
32
+ std::string getCacheDir() {
33
+ if (!g_cacheDir.empty()) return g_cacheDir;
34
+ return detail::resolvePlatformCacheDir();
35
+ }
36
+
37
+ namespace detail {
38
+
39
+ std::string resolvePlatformCacheDir() {
40
+ #ifdef __ANDROID__
41
+ // Ask the Android runtime for Context.getCacheDir()
42
+ using namespace facebook::jni;
43
+ try {
44
+ auto activityThread = findClassStatic("android/app/ActivityThread");
45
+ auto currentApplication = activityThread->getStaticMethod<JObject()>("currentApplication");
46
+ // JStaticMethod::operator() takes (alias_ref<jclass>, args...)
47
+ auto application = currentApplication(activityThread);
48
+ if (application) {
49
+ auto contextClass = findClassStatic("android/content/Context");
50
+ auto getCacheDir = contextClass->getMethod<JObject()>("getCacheDir");
51
+ // JMethod::operator() takes (alias_ref<jobject>, args...)
52
+ auto cacheDir = getCacheDir(application);
53
+ if (cacheDir) {
54
+ auto fileClass = findClassStatic("java/io/File");
55
+ auto getAbsolutePath = fileClass->getMethod<JString()>("getAbsolutePath");
56
+ auto path = getAbsolutePath(cacheDir);
57
+ std::string result = path->toStdString();
58
+ if (isUsableDir(result)) return result;
59
+ }
60
+ }
61
+ } catch (...) {
62
+ // fall through to env / default
63
+ }
64
+ #endif
65
+
66
+ const char* tmp = std::getenv("TMPDIR");
67
+ if (tmp != nullptr && isUsableDir(tmp)) {
68
+ return std::string(tmp);
69
+ }
70
+ return "/tmp";
71
+ }
72
+
73
+ } // namespace detail
74
+ } // namespace margelo::nitro::xlsx
75
+
76
+ #endif // !__APPLE__
@@ -0,0 +1,27 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+
5
+ namespace margelo::nitro::xlsx {
6
+
7
+ /**
8
+ * Override the cache directory used for temporary XLSX files.
9
+ * Normally called once from the Android native side at startup;
10
+ * if never called, getCacheDir() falls back to the platform resolver.
11
+ */
12
+ void setCacheDir(const std::string& dir);
13
+
14
+ /**
15
+ * Directory used for temporary XLSX files.
16
+ * Priority: explicit setCacheDir() > platform cache dir > TMPDIR > /tmp
17
+ *
18
+ * Implemented in ios/XlsxCacheDir.mm (Apple) and cpp/XlsxCacheDir.cpp (other).
19
+ */
20
+ std::string getCacheDir();
21
+
22
+ namespace detail {
23
+ /** Platform-specific cache directory (NSCachesDirectory / Context.getCacheDir). */
24
+ std::string resolvePlatformCacheDir();
25
+ }
26
+
27
+ } // namespace margelo::nitro::xlsx
@@ -0,0 +1,68 @@
1
+ #pragma once
2
+
3
+ #include <OpenXLSX/XLException.hpp>
4
+ #include <stdexcept>
5
+ #include <string>
6
+
7
+ namespace margelo::nitro::xlsx {
8
+
9
+ /**
10
+ * Structured error with a stable code that the JS side can switch on.
11
+ * Message format written to JS: "CODE: message" (Nitro prefixes the method name).
12
+ */
13
+ class XlsxError : public std::runtime_error {
14
+ public:
15
+ XlsxError(std::string code, const std::string& message)
16
+ : std::runtime_error(code + ": " + message), _code(std::move(code)) {}
17
+
18
+ const std::string& code() const noexcept { return _code; }
19
+
20
+ private:
21
+ std::string _code;
22
+ };
23
+
24
+ namespace xlsx_error {
25
+ inline constexpr const char* INVALID_ARGUMENT = "INVALID_ARGUMENT";
26
+ inline constexpr const char* SHEET_NOT_FOUND = "SHEET_NOT_FOUND";
27
+ inline constexpr const char* SHEET_EXISTS = "SHEET_EXISTS";
28
+ inline constexpr const char* INDEX_OUT_OF_RANGE = "INDEX_OUT_OF_RANGE";
29
+ inline constexpr const char* CELL_NOT_FOUND = "CELL_NOT_FOUND";
30
+ inline constexpr const char* WORKBOOK_CLOSED = "WORKBOOK_CLOSED";
31
+ inline constexpr const char* IO_ERROR = "IO_ERROR";
32
+ inline constexpr const char* UNSUPPORTED = "UNSUPPORTED";
33
+ inline constexpr const char* XLSX_ERROR = "XLSX_ERROR";
34
+ inline constexpr const char* INTERNAL_ERROR = "INTERNAL_ERROR";
35
+ } // namespace xlsx_error
36
+
37
+ /**
38
+ * Map OpenXLSX / std exceptions to XlsxError so the JS side always sees "CODE: message".
39
+ * Anything already an XlsxError is rethrown unchanged.
40
+ */
41
+ template <typename Fn>
42
+ auto withXlsxError(const char* defaultCode, Fn&& fn) -> decltype(fn()) {
43
+ try {
44
+ return fn();
45
+ } catch (const XlsxError&) {
46
+ throw;
47
+ } catch (const OpenXLSX::XLInputError& e) {
48
+ throw XlsxError(xlsx_error::INVALID_ARGUMENT, e.what());
49
+ } catch (const OpenXLSX::XLCellAddressError& e) {
50
+ throw XlsxError(xlsx_error::INVALID_ARGUMENT, e.what());
51
+ } catch (const OpenXLSX::XLSheetError& e) {
52
+ throw XlsxError(xlsx_error::SHEET_NOT_FOUND, e.what());
53
+ } catch (const OpenXLSX::XLOverflowError& e) {
54
+ throw XlsxError(xlsx_error::INDEX_OUT_OF_RANGE, e.what());
55
+ } catch (const OpenXLSX::XLValueTypeError& e) {
56
+ throw XlsxError(xlsx_error::INVALID_ARGUMENT, e.what());
57
+ } catch (const OpenXLSX::XLFormulaError& e) {
58
+ throw XlsxError(xlsx_error::INVALID_ARGUMENT, e.what());
59
+ } catch (const OpenXLSX::XLException& e) {
60
+ throw XlsxError(defaultCode, e.what());
61
+ } catch (const std::exception& e) {
62
+ throw XlsxError(defaultCode, e.what());
63
+ } catch (...) {
64
+ throw XlsxError(xlsx_error::INTERNAL_ERROR, "Unknown native error");
65
+ }
66
+ }
67
+
68
+ } // namespace margelo::nitro::xlsx
@@ -0,0 +1,185 @@
1
+ #include "XlsxJson.hpp"
2
+ #include <algorithm>
3
+ #include <cmath>
4
+ #include <limits>
5
+ #include <stdexcept>
6
+
7
+ namespace margelo::nitro::xlsx {
8
+
9
+ namespace {
10
+
11
+ bool isCellEmpty(OpenXLSX::XLWorksheet& ws, uint32_t row, uint16_t col) {
12
+ return ws.findCell(row, col).empty() || ws.findCell(row, col).value().type() == OpenXLSX::XLValueType::Empty;
13
+ }
14
+
15
+ std::string keyFromCellValue(OpenXLSX::XLWorksheet& ws, uint32_t row, uint16_t col) {
16
+ auto cell = ws.findCell(row, col);
17
+ if (cell.empty()) return "col" + std::to_string(col);
18
+ switch (cell.value().type()) {
19
+ case OpenXLSX::XLValueType::String:
20
+ return cell.value().get<std::string>();
21
+ case OpenXLSX::XLValueType::Integer:
22
+ return std::to_string(static_cast<long long>(cell.value().get<int64_t>()));
23
+ case OpenXLSX::XLValueType::Float:
24
+ return std::to_string(static_cast<int>(cell.value().get<double>()));
25
+ default:
26
+ return "col" + std::to_string(col);
27
+ }
28
+ }
29
+
30
+ void recordFromRow(OpenXLSX::XLWorksheet& ws,
31
+ uint32_t row,
32
+ const std::vector<std::string>& keys,
33
+ uint16_t lastCol,
34
+ std::shared_ptr<AnyMap> record) {
35
+ uint16_t keyCount = std::min(static_cast<uint16_t>(keys.size()), lastCol);
36
+ for (uint16_t c = 0; c < keyCount; ++c) {
37
+ const std::string& key = keys[c];
38
+ auto cell = ws.findCell(row, c + 1);
39
+ if (cell.empty()) {
40
+ record->setNull(key);
41
+ continue;
42
+ }
43
+ switch (cell.value().type()) {
44
+ case OpenXLSX::XLValueType::String:
45
+ record->setString(key, cell.value().get<std::string>());
46
+ break;
47
+ case OpenXLSX::XLValueType::Integer:
48
+ record->setDouble(key, static_cast<double>(cell.value().get<int64_t>()));
49
+ break;
50
+ case OpenXLSX::XLValueType::Float: {
51
+ double d = cell.value().get<double>();
52
+ if (!std::isnan(d) && !std::isinf(d)) {
53
+ record->setDouble(key, d);
54
+ } else {
55
+ record->setNull(key);
56
+ }
57
+ break;
58
+ }
59
+ case OpenXLSX::XLValueType::Boolean:
60
+ record->setBoolean(key, cell.value().get<bool>());
61
+ break;
62
+ default:
63
+ record->setNull(key);
64
+ break;
65
+ }
66
+ }
67
+ }
68
+
69
+ void writeValue(OpenXLSX::XLWorksheet& ws,
70
+ uint32_t row,
71
+ uint16_t col,
72
+ const std::shared_ptr<AnyMap>& map,
73
+ const std::string& key) {
74
+ if (!map->contains(key) || map->isNull(key)) return;
75
+ auto cell = ws.cell(row, col);
76
+ if (map->isString(key)) {
77
+ cell.value() = map->getString(key);
78
+ } else if (map->isDouble(key)) {
79
+ cell.value() = map->getDouble(key);
80
+ } else if (map->isInt64(key)) {
81
+ // store as number so get<double> round-trips
82
+ cell.value() = static_cast<double>(map->getInt64(key));
83
+ } else if (map->isBoolean(key)) {
84
+ cell.value() = map->getBoolean(key);
85
+ }
86
+ // arrays / objects are not representable as a single cell — skipped
87
+ }
88
+
89
+ // AnyMap is backed by std::unordered_map — sort each row's keys so column
90
+ // order is deterministic, then keep first-seen order across rows.
91
+ std::vector<std::string> collectKeys(const std::vector<std::shared_ptr<AnyMap>>& data) {
92
+ std::vector<std::string> keys;
93
+ for (const auto& row : data) {
94
+ if (!row) continue;
95
+ auto rowKeys = row->getAllKeys();
96
+ std::sort(rowKeys.begin(), rowKeys.end());
97
+ for (const auto& key : rowKeys) {
98
+ bool known = false;
99
+ for (const auto& existing : keys) {
100
+ if (existing == key) { known = true; break; }
101
+ }
102
+ if (!known) keys.push_back(key);
103
+ }
104
+ }
105
+ return keys;
106
+ }
107
+
108
+ } // anonymous namespace
109
+
110
+ std::vector<std::shared_ptr<AnyMap>> worksheetToRecords(
111
+ OpenXLSX::XLWorksheet& worksheet,
112
+ const std::optional<std::vector<std::string>>& keys) {
113
+ std::vector<std::shared_ptr<AnyMap>> result;
114
+ uint32_t lastRow = worksheet.rowCount();
115
+ uint16_t lastCol = worksheet.columnCount();
116
+ if (lastRow == 0 || lastCol == 0) return result;
117
+
118
+ uint32_t dataStartRow = 1;
119
+ std::vector<std::string> resolvedKeys;
120
+
121
+ if (keys.has_value()) {
122
+ resolvedKeys = *keys;
123
+ } else {
124
+ dataStartRow = 2;
125
+ for (uint16_t c = 1; c <= lastCol; ++c) {
126
+ resolvedKeys.push_back(keyFromCellValue(worksheet, 1, c));
127
+ }
128
+ }
129
+ if (resolvedKeys.empty()) return result;
130
+
131
+ uint16_t keyCount = std::min(static_cast<uint16_t>(resolvedKeys.size()), lastCol);
132
+ for (uint32_t r = dataStartRow; r <= lastRow; ++r) {
133
+ bool hasData = false;
134
+ for (uint16_t c = 1; c <= keyCount; ++c) {
135
+ if (!isCellEmpty(worksheet, r, c)) {
136
+ hasData = true;
137
+ break;
138
+ }
139
+ }
140
+ if (!hasData) continue;
141
+
142
+ auto record = AnyMap::make();
143
+ recordFromRow(worksheet, r, resolvedKeys, lastCol, record);
144
+ result.push_back(record);
145
+ }
146
+ return result;
147
+ }
148
+
149
+ void recordsToWorksheet(
150
+ OpenXLSX::XLWorksheet& worksheet,
151
+ const std::vector<std::shared_ptr<AnyMap>>& data) {
152
+ if (data.empty()) return;
153
+
154
+ // Column order: first-seen keys across rows; each row's keys sorted (AnyMap
155
+ // is unordered, so sorting keeps the layout deterministic).
156
+ std::vector<std::string> keys = collectKeys(data);
157
+
158
+ // Header row
159
+ for (size_t c = 0; c < keys.size(); ++c) {
160
+ worksheet.cell(1, static_cast<uint16_t>(c + 1)).value() = keys[c];
161
+ }
162
+
163
+ // Data rows
164
+ for (size_t r = 0; r < data.size(); ++r) {
165
+ const auto& row = data[r];
166
+ if (!row) continue;
167
+ uint32_t rowNum = static_cast<uint32_t>(r + 2);
168
+ for (size_t c = 0; c < keys.size(); ++c) {
169
+ writeValue(worksheet, rowNum, static_cast<uint16_t>(c + 1), row, keys[c]);
170
+ }
171
+ }
172
+ }
173
+
174
+ std::unordered_map<std::string, std::vector<std::shared_ptr<AnyMap>>> workbookToRecords(
175
+ OpenXLSX::XLWorkbook& workbook) {
176
+ std::unordered_map<std::string, std::vector<std::shared_ptr<AnyMap>>> result;
177
+ uint16_t count = static_cast<uint16_t>(workbook.worksheetCount());
178
+ for (uint16_t i = 1; i <= count; ++i) {
179
+ auto ws = workbook.worksheet(i);
180
+ result[ws.name()] = worksheetToRecords(ws);
181
+ }
182
+ return result;
183
+ }
184
+
185
+ } // namespace margelo::nitro::xlsx
@@ -0,0 +1,36 @@
1
+ #pragma once
2
+
3
+ #include <NitroModules/AnyMap.hpp>
4
+ #include <OpenXLSX/XLSheet.hpp>
5
+ #include <OpenXLSX/XLWorkbook.hpp>
6
+ #include <memory>
7
+ #include <optional>
8
+ #include <string>
9
+ #include <unordered_map>
10
+ #include <vector>
11
+
12
+ namespace margelo::nitro::xlsx {
13
+
14
+ /**
15
+ * Convert one worksheet to an array of records.
16
+ * @param keys if provided, used as record keys; otherwise row 1 is treated as the header
17
+ */
18
+ std::vector<std::shared_ptr<AnyMap>> worksheetToRecords(
19
+ OpenXLSX::XLWorksheet& worksheet,
20
+ const std::optional<std::vector<std::string>>& keys = std::nullopt);
21
+
22
+ /**
23
+ * Write an array of records into a worksheet.
24
+ * The first record's keys become the header row; later-only keys extend the columns.
25
+ */
26
+ void recordsToWorksheet(
27
+ OpenXLSX::XLWorksheet& worksheet,
28
+ const std::vector<std::shared_ptr<AnyMap>>& data);
29
+
30
+ /**
31
+ * Export every worksheet as a Record keyed by sheet name.
32
+ */
33
+ std::unordered_map<std::string, std::vector<std::shared_ptr<AnyMap>>> workbookToRecords(
34
+ OpenXLSX::XLWorkbook& workbook);
35
+
36
+ } // namespace margelo::nitro::xlsx
@@ -0,0 +1,60 @@
1
+ // Apple (iOS / macOS) cache directory via Foundation.
2
+ // Full implementation of XlsxCacheDir.hpp for __APPLE__.
3
+
4
+ #ifdef __APPLE__
5
+
6
+ #include "XlsxCacheDir.hpp"
7
+
8
+ #include <cstdlib>
9
+ #include <sys/stat.h>
10
+
11
+ #import <Foundation/Foundation.h>
12
+
13
+ namespace margelo::nitro::xlsx {
14
+
15
+ namespace {
16
+ std::string g_cacheDir;
17
+
18
+ bool isUsableDir(const std::string& path) {
19
+ if (path.empty()) return false;
20
+ struct stat st {};
21
+ return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
22
+ }
23
+ }
24
+
25
+ void setCacheDir(const std::string& dir) {
26
+ g_cacheDir = dir;
27
+ }
28
+
29
+ std::string getCacheDir() {
30
+ if (!g_cacheDir.empty()) return g_cacheDir;
31
+ return detail::resolvePlatformCacheDir();
32
+ }
33
+
34
+ namespace detail {
35
+
36
+ std::string resolvePlatformCacheDir() {
37
+ @autoreleasepool {
38
+ NSArray<NSString*>* caches =
39
+ NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
40
+ if (caches.count > 0) {
41
+ std::string result([caches.firstObject UTF8String]);
42
+ if (isUsableDir(result)) return result;
43
+ }
44
+ NSString* tmp = NSTemporaryDirectory();
45
+ if (tmp != nil) {
46
+ std::string result([tmp UTF8String]);
47
+ if (isUsableDir(result)) return result;
48
+ }
49
+ }
50
+ const char* env = std::getenv("TMPDIR");
51
+ if (env != nullptr && isUsableDir(env)) {
52
+ return std::string(env);
53
+ }
54
+ return "/tmp";
55
+ }
56
+
57
+ } // namespace detail
58
+ } // namespace margelo::nitro::xlsx
59
+
60
+ #endif // __APPLE__
@@ -35,6 +35,8 @@ target_sources(
35
35
  # Shared Nitrogen C++ sources
36
36
  ../nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.cpp
37
37
  ../nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.cpp
38
+ ../nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.cpp
39
+ ../nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.cpp
38
40
  ../nitrogen/generated/shared/c++/HybridXlsxCellFormatSpec.cpp
39
41
  ../nitrogen/generated/shared/c++/HybridNitroXlsxSpec.cpp
40
42
  # Android-specific Nitrogen C++ sources
@@ -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
 
@@ -19,8 +19,10 @@ namespace margelo::nitro::xlsx { class HybridXlsxWorkbookSpec; }
19
19
  #include <memory>
20
20
  #include "HybridXlsxWorkbookSpec.hpp"
21
21
  #include <string>
22
- #include <optional>
23
22
  #include <NitroModules/ArrayBuffer.hpp>
23
+ #include <NitroModules/AnyMap.hpp>
24
+ #include <vector>
25
+ #include <unordered_map>
24
26
 
25
27
  namespace margelo::nitro::xlsx {
26
28
 
@@ -53,9 +55,10 @@ namespace margelo::nitro::xlsx {
53
55
 
54
56
  public:
55
57
  // Methods
56
- virtual std::shared_ptr<HybridXlsxWorkbookSpec> createWorkbook(const std::optional<std::string>& tempDir) = 0;
58
+ virtual std::shared_ptr<HybridXlsxWorkbookSpec> createWorkbook() = 0;
57
59
  virtual std::shared_ptr<HybridXlsxWorkbookSpec> openWorkbook(const std::string& path) = 0;
58
60
  virtual std::shared_ptr<HybridXlsxWorkbookSpec> openWorkbookFromBuffer(const std::shared_ptr<ArrayBuffer>& buffer) = 0;
61
+ virtual std::shared_ptr<HybridXlsxWorkbookSpec> fromJSON(const std::unordered_map<std::string, std::vector<std::shared_ptr<AnyMap>>>& data) = 0;
59
62
 
60
63
  protected:
61
64
  // Hybrid Setup