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
@@ -1,5 +1,9 @@
1
1
  #include "HybridXlsxWorksheet.hpp"
2
+ #include "XlsxError.hpp"
3
+ #include "XlsxJson.hpp"
4
+ #include <OpenXLSX/XLCellReference.hpp>
2
5
  #include <cstring>
6
+ #include <cmath>
3
7
  #include <fstream>
4
8
  #include <chrono>
5
9
 
@@ -23,80 +27,174 @@ void HybridXlsxWorksheet::applyFormat(OpenXLSX::XLCell& cell, const std::optiona
23
27
  }
24
28
 
25
29
  void HybridXlsxWorksheet::writeString(double row, double col, const std::string& value, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
26
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
27
- cell.value() = value;
28
- applyFormat(cell, format);
30
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
31
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
32
+ cell.value() = value;
33
+ applyFormat(cell, format);
34
+ });
29
35
  }
30
36
 
31
37
  void HybridXlsxWorksheet::writeNumber(double row, double col, double value, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
32
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
33
- cell.value() = value;
34
- applyFormat(cell, format);
38
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
39
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
40
+ cell.value() = value;
41
+ applyFormat(cell, format);
42
+ });
35
43
  }
36
44
 
37
45
  void HybridXlsxWorksheet::writeBoolean(double row, double col, bool value, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
38
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
39
- cell.value() = value;
40
- applyFormat(cell, format);
46
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
47
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
48
+ cell.value() = value;
49
+ applyFormat(cell, format);
50
+ });
41
51
  }
42
52
 
43
53
  void HybridXlsxWorksheet::writeBlank(double row, double col, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
44
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
45
- applyFormat(cell, format);
54
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
55
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
56
+ applyFormat(cell, format);
57
+ });
46
58
  }
47
59
 
48
60
  void HybridXlsxWorksheet::writeFormula(double row, double col, const std::string& formula, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
49
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
50
- cell.formula() = formula;
51
- applyFormat(cell, format);
61
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
62
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
63
+ cell.formula() = formula;
64
+ applyFormat(cell, format);
65
+ });
52
66
  }
53
67
 
54
68
  void HybridXlsxWorksheet::writeFormulaNum(double row, double col, const std::string& formula, double number, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
55
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
56
- cell.formula() = formula;
57
- cell.value() = number;
58
- applyFormat(cell, format);
69
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
70
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
71
+ cell.formula() = formula;
72
+ cell.value() = number;
73
+ applyFormat(cell, format);
74
+ });
59
75
  }
60
76
 
61
77
  void HybridXlsxWorksheet::writeFormulaString(double row, double col, const std::string& formula, const std::string& str, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
62
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
63
- cell.formula() = formula;
64
- cell.value() = str;
65
- applyFormat(cell, format);
78
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
79
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
80
+ cell.formula() = formula;
81
+ cell.value() = str;
82
+ applyFormat(cell, format);
83
+ });
66
84
  }
67
85
 
68
86
  void HybridXlsxWorksheet::writeFormulaBoolean(double row, double col, const std::string& formula, bool boolVal, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
69
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
70
- cell.formula() = formula;
71
- cell.value() = boolVal;
72
- applyFormat(cell, format);
87
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
88
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
89
+ cell.formula() = formula;
90
+ cell.value() = boolVal;
91
+ applyFormat(cell, format);
92
+ });
73
93
  }
74
94
 
75
95
  void HybridXlsxWorksheet::writeArrayFormula(double firstRow, double firstCol, double lastRow, double lastCol, const std::string& formula, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
76
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(firstRow), static_cast<unsigned int>(firstCol));
77
- cell.formula() = formula;
96
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
97
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(firstRow), static_cast<unsigned int>(firstCol));
98
+ cell.formula() = formula;
99
+ applyFormat(cell, format);
100
+ });
78
101
  }
79
102
 
80
103
  void HybridXlsxWorksheet::writeDatetime(double row, double col, double datetime, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
81
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
82
- cell.value() = datetime;
83
- applyFormat(cell, format);
104
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
105
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
106
+ cell.value() = datetime;
107
+ applyFormat(cell, format);
108
+ });
84
109
  }
85
110
 
86
111
  void HybridXlsxWorksheet::writeURL(double row, double col, const std::string& url, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
87
- OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
88
- cell.value() = url;
89
- applyFormat(cell, format);
112
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
113
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), static_cast<unsigned int>(col));
114
+ cell.value() = url;
115
+ applyFormat(cell, format);
116
+ });
90
117
  }
91
118
 
92
119
  void HybridXlsxWorksheet::setColumn(double firstCol, double lastCol, double width, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
93
120
  for (unsigned int c = static_cast<unsigned int>(firstCol); c <= static_cast<unsigned int>(lastCol); ++c) {
94
121
  _worksheet.column(c).setWidth(static_cast<float>(width));
95
122
  }
123
+ if (format.has_value()) {
124
+ // Apply format to entire column range by setting it on cells in the first row
125
+ // (OpenXLSX does not support column-level style; apply to first row cells as a hint)
126
+ auto hybridFormat = std::dynamic_pointer_cast<HybridXlsxCellFormat>(*format);
127
+ if (hybridFormat) {
128
+ auto styleIndex = hybridFormat->applyToDocument(_doc.styles());
129
+ for (unsigned int c = static_cast<unsigned int>(firstCol); c <= static_cast<unsigned int>(lastCol); ++c) {
130
+ OpenXLSX::XLCell cell = _worksheet.cell(1, c);
131
+ hybridFormat->applyToCell(cell, styleIndex);
132
+ }
133
+ }
134
+ }
96
135
  }
97
136
 
98
137
  void HybridXlsxWorksheet::setRow(double row, double height, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
99
138
  _worksheet.row(static_cast<unsigned int>(row)).setHeight(static_cast<float>(height));
139
+ if (format.has_value()) {
140
+ auto hybridFormat = std::dynamic_pointer_cast<HybridXlsxCellFormat>(*format);
141
+ if (hybridFormat) {
142
+ auto styleIndex = hybridFormat->applyToDocument(_doc.styles());
143
+ unsigned int colCount = _worksheet.columnCount();
144
+ for (unsigned int c = 1; c <= colCount; ++c) {
145
+ OpenXLSX::XLCell cell = _worksheet.cell(static_cast<unsigned int>(row), c);
146
+ hybridFormat->applyToCell(cell, styleIndex);
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ bool HybridXlsxWorksheet::deleteRow(double row) {
153
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
154
+ return _worksheet.deleteRow(static_cast<uint32_t>(row));
155
+ });
156
+ }
157
+
158
+ bool HybridXlsxWorksheet::deleteColumn(double col) {
159
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
160
+ unsigned int targetCol = static_cast<unsigned int>(col);
161
+ if (targetCol < 1) {
162
+ throw XlsxError(xlsx_error::INVALID_ARGUMENT, "Column index must be >= 1");
163
+ }
164
+
165
+ unsigned int lastCol = _worksheet.columnCount();
166
+ unsigned int lastRow = _worksheet.rowCount();
167
+ if (targetCol > lastCol) {
168
+ return false;
169
+ }
170
+
171
+ // Shift cell contents left for every row
172
+ for (unsigned int r = 1; r <= lastRow; ++r) {
173
+ for (unsigned int c = targetCol + 1; c <= lastCol; ++c) {
174
+ OpenXLSX::XLCell src = _worksheet.cell(r, c);
175
+ OpenXLSX::XLCell dst = _worksheet.cell(r, c - 1);
176
+ if (!src.empty()) {
177
+ dst.copyFrom(src);
178
+ dst.setCellFormat(src.cellFormat());
179
+ } else {
180
+ dst.clear(0);
181
+ }
182
+ }
183
+ // Clear the trailing column
184
+ OpenXLSX::XLCell trailing = _worksheet.cell(r, lastCol);
185
+ trailing.clear(0);
186
+ }
187
+
188
+ // Shift column widths / hidden state left
189
+ for (unsigned int c = targetCol + 1; c <= lastCol; ++c) {
190
+ OpenXLSX::XLColumn srcCol = _worksheet.column(c);
191
+ OpenXLSX::XLColumn dstCol = _worksheet.column(c - 1);
192
+ dstCol.setWidth(srcCol.width());
193
+ dstCol.setHidden(srcCol.isHidden());
194
+ }
195
+
196
+ return true;
197
+ });
100
198
  }
101
199
 
102
200
  void HybridXlsxWorksheet::mergeRange(double firstRow, double firstCol, double lastRow, double lastCol, const std::string& value, const std::optional<std::shared_ptr<HybridXlsxCellFormatSpec>>& format) {
@@ -119,75 +217,251 @@ void HybridXlsxWorksheet::mergeRangeNum(double firstRow, double firstCol, double
119
217
  applyFormat(cell, format);
120
218
  }
121
219
 
122
- void HybridXlsxWorksheet::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) {
220
+ void HybridXlsxWorksheet::unmergeCells(double firstRow, double firstCol, double lastRow, double lastCol) {
221
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
222
+ OpenXLSX::XLCellReference topLeft(static_cast<unsigned int>(firstRow), static_cast<unsigned int>(firstCol));
223
+ OpenXLSX::XLCellReference bottomRight(static_cast<unsigned int>(lastRow), static_cast<unsigned int>(lastCol));
224
+ std::string rangeRef = topLeft.address() + ":" + bottomRight.address();
225
+ _worksheet.unmergeCells(rangeRef);
226
+ });
123
227
  }
124
228
 
125
- void HybridXlsxWorksheet::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) {
229
+ void HybridXlsxWorksheet::hide() {
230
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
231
+ _worksheet.setVisibility(OpenXLSX::XLSheetState::Hidden);
232
+ });
126
233
  }
127
234
 
128
- void HybridXlsxWorksheet::autofilter(double firstRow, double firstCol, double lastRow, double lastCol) {
235
+ void HybridXlsxWorksheet::activate() {
236
+ _worksheet.setActive();
129
237
  }
130
238
 
131
- void HybridXlsxWorksheet::freezePanes(double row, double col) {
239
+ // ========== Sheet protection ==========
240
+
241
+ namespace {
242
+ // OpenXLSX defaults `set` to true when the argument is omitted
243
+ bool resolveSet(const std::optional<bool>& set) {
244
+ return set.value_or(true);
245
+ }
132
246
  }
133
247
 
134
- void HybridXlsxWorksheet::splitPanes(double row, double col) {
248
+ void HybridXlsxWorksheet::protect(const std::optional<std::string>& password) {
249
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
250
+ if (password.has_value() && !password->empty()) {
251
+ if (!_worksheet.setPassword(*password)) {
252
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set sheet password");
253
+ }
254
+ }
255
+ if (!_worksheet.protectSheet()) {
256
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to protect sheet");
257
+ }
258
+ });
135
259
  }
136
260
 
137
- void HybridXlsxWorksheet::setTabColor(double color) {
261
+ void HybridXlsxWorksheet::protectSheet(std::optional<bool> set) {
262
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
263
+ if (!_worksheet.protectSheet(resolveSet(set))) {
264
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set protectSheet");
265
+ }
266
+ });
138
267
  }
139
268
 
140
- void HybridXlsxWorksheet::hide() {
269
+ void HybridXlsxWorksheet::protectObjects(std::optional<bool> set) {
270
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
271
+ if (!_worksheet.protectObjects(resolveSet(set))) {
272
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set protectObjects");
273
+ }
274
+ });
141
275
  }
142
276
 
143
- void HybridXlsxWorksheet::activate() {
144
- _worksheet.setActive();
277
+ void HybridXlsxWorksheet::protectScenarios(std::optional<bool> set) {
278
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
279
+ if (!_worksheet.protectScenarios(resolveSet(set))) {
280
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set protectScenarios");
281
+ }
282
+ });
145
283
  }
146
284
 
147
- void HybridXlsxWorksheet::setFirstSheet() {
285
+ // ========== Password ==========
286
+
287
+ void HybridXlsxWorksheet::setPassword(const std::string& password) {
288
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
289
+ if (!_worksheet.setPassword(password)) {
290
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set password");
291
+ }
292
+ });
148
293
  }
149
294
 
150
- void HybridXlsxWorksheet::protect(const std::optional<std::string>& password) {
151
- if (password.has_value() && !password->empty()) {
152
- _worksheet.setPassword(*password);
153
- }
154
- _worksheet.protectSheet();
295
+ void HybridXlsxWorksheet::setPasswordHash(const std::string& hash) {
296
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
297
+ if (!_worksheet.setPasswordHash(hash)) {
298
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set password hash");
299
+ }
300
+ });
301
+ }
302
+
303
+ void HybridXlsxWorksheet::clearPassword() {
304
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
305
+ if (!_worksheet.clearPassword()) {
306
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to clear password");
307
+ }
308
+ });
309
+ }
310
+
311
+ void HybridXlsxWorksheet::clearSheetProtection() {
312
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
313
+ if (!_worksheet.clearSheetProtection()) {
314
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to clear sheet protection");
315
+ }
316
+ });
317
+ }
318
+
319
+ // ========== Fine-grained permissions ==========
320
+
321
+ void HybridXlsxWorksheet::allowInsertColumns(std::optional<bool> set) {
322
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
323
+ if (!_worksheet.allowInsertColumns(resolveSet(set))) {
324
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set allowInsertColumns");
325
+ }
326
+ });
155
327
  }
156
328
 
157
- void HybridXlsxWorksheet::setPortrait() {
329
+ void HybridXlsxWorksheet::allowInsertRows(std::optional<bool> set) {
330
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
331
+ if (!_worksheet.allowInsertRows(resolveSet(set))) {
332
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set allowInsertRows");
333
+ }
334
+ });
158
335
  }
159
336
 
160
- void HybridXlsxWorksheet::setLandscape() {
337
+ void HybridXlsxWorksheet::allowDeleteColumns(std::optional<bool> set) {
338
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
339
+ if (!_worksheet.allowDeleteColumns(resolveSet(set))) {
340
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set allowDeleteColumns");
341
+ }
342
+ });
161
343
  }
162
344
 
163
- void HybridXlsxWorksheet::setPaper(double paperType) {
345
+ void HybridXlsxWorksheet::allowDeleteRows(std::optional<bool> set) {
346
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
347
+ if (!_worksheet.allowDeleteRows(resolveSet(set))) {
348
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set allowDeleteRows");
349
+ }
350
+ });
164
351
  }
165
352
 
166
- void HybridXlsxWorksheet::setHeader(const std::string& header) {
353
+ void HybridXlsxWorksheet::allowSelectLockedCells(std::optional<bool> set) {
354
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
355
+ if (!_worksheet.allowSelectLockedCells(resolveSet(set))) {
356
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set allowSelectLockedCells");
357
+ }
358
+ });
167
359
  }
168
360
 
169
- void HybridXlsxWorksheet::setFooter(const std::string& footer) {
361
+ void HybridXlsxWorksheet::allowSelectUnlockedCells(std::optional<bool> set) {
362
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
363
+ if (!_worksheet.allowSelectUnlockedCells(resolveSet(set))) {
364
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set allowSelectUnlockedCells");
365
+ }
366
+ });
170
367
  }
171
368
 
172
- void HybridXlsxWorksheet::setPrintArea(double firstRow, double firstCol, double lastRow, double lastCol) {
369
+ void HybridXlsxWorksheet::denyInsertColumns() {
370
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
371
+ if (!_worksheet.denyInsertColumns()) {
372
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to denyInsertColumns");
373
+ }
374
+ });
173
375
  }
174
376
 
175
- void HybridXlsxWorksheet::fitToPages(double width, double height) {
377
+ void HybridXlsxWorksheet::denyInsertRows() {
378
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
379
+ if (!_worksheet.denyInsertRows()) {
380
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to denyInsertRows");
381
+ }
382
+ });
176
383
  }
177
384
 
178
- void HybridXlsxWorksheet::setZoom(double scale) {
385
+ void HybridXlsxWorksheet::denyDeleteColumns() {
386
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
387
+ if (!_worksheet.denyDeleteColumns()) {
388
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to denyDeleteColumns");
389
+ }
390
+ });
179
391
  }
180
392
 
181
- void HybridXlsxWorksheet::setGridlines(double option) {
393
+ void HybridXlsxWorksheet::denyDeleteRows() {
394
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
395
+ if (!_worksheet.denyDeleteRows()) {
396
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to denyDeleteRows");
397
+ }
398
+ });
182
399
  }
183
400
 
184
- void HybridXlsxWorksheet::centerHorizontally() {
401
+ void HybridXlsxWorksheet::denySelectLockedCells() {
402
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
403
+ if (!_worksheet.denySelectLockedCells()) {
404
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to denySelectLockedCells");
405
+ }
406
+ });
185
407
  }
186
408
 
187
- void HybridXlsxWorksheet::centerVertically() {
409
+ void HybridXlsxWorksheet::denySelectUnlockedCells() {
410
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
411
+ if (!_worksheet.denySelectUnlockedCells()) {
412
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to denySelectUnlockedCells");
413
+ }
414
+ });
188
415
  }
189
416
 
190
- void HybridXlsxWorksheet::setOutline(double level) {
417
+ // ========== Protection state ==========
418
+
419
+ bool HybridXlsxWorksheet::sheetProtected() {
420
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.sheetProtected(); });
421
+ }
422
+
423
+ bool HybridXlsxWorksheet::objectsProtected() {
424
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.objectsProtected(); });
425
+ }
426
+
427
+ bool HybridXlsxWorksheet::scenariosProtected() {
428
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.scenariosProtected(); });
429
+ }
430
+
431
+ bool HybridXlsxWorksheet::insertColumnsAllowed() {
432
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.insertColumnsAllowed(); });
433
+ }
434
+
435
+ bool HybridXlsxWorksheet::insertRowsAllowed() {
436
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.insertRowsAllowed(); });
437
+ }
438
+
439
+ bool HybridXlsxWorksheet::deleteColumnsAllowed() {
440
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.deleteColumnsAllowed(); });
441
+ }
442
+
443
+ bool HybridXlsxWorksheet::deleteRowsAllowed() {
444
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.deleteRowsAllowed(); });
445
+ }
446
+
447
+ bool HybridXlsxWorksheet::selectLockedCellsAllowed() {
448
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.selectLockedCellsAllowed(); });
449
+ }
450
+
451
+ bool HybridXlsxWorksheet::selectUnlockedCellsAllowed() {
452
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.selectUnlockedCellsAllowed(); });
453
+ }
454
+
455
+ bool HybridXlsxWorksheet::passwordIsSet() {
456
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.passwordIsSet(); });
457
+ }
458
+
459
+ std::string HybridXlsxWorksheet::passwordHash() {
460
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.passwordHash(); });
461
+ }
462
+
463
+ std::string HybridXlsxWorksheet::sheetProtectionSummary() {
464
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] { return _worksheet.sheetProtectionSummary(); });
191
465
  }
192
466
 
193
467
  void HybridXlsxWorksheet::setColumnHidden(double firstCol, double lastCol, bool hidden) {
@@ -317,4 +591,125 @@ std::string HybridXlsxWorksheet::getName() {
317
591
  return _worksheet.name();
318
592
  }
319
593
 
594
+ void HybridXlsxWorksheet::applyRename(const std::string& oldName, const std::string& newName) {
595
+ _worksheet.setName(newName);
596
+ _worksheet.updateSheetName(oldName, newName);
597
+ }
598
+
599
+ void HybridXlsxWorksheet::rewriteSheetNameRef(const std::string& oldName, const std::string& newName) {
600
+ _worksheet.updateSheetName(oldName, newName);
601
+ }
602
+
603
+ // ========== JSON import / export (this worksheet) ==========
604
+
605
+ std::vector<std::shared_ptr<AnyMap>> HybridXlsxWorksheet::toJSON(const std::optional<std::vector<std::string>>& keys) {
606
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
607
+ return worksheetToRecords(_worksheet, keys);
608
+ });
609
+ }
610
+
611
+ void HybridXlsxWorksheet::fromJSON(const std::vector<std::shared_ptr<AnyMap>>& data) {
612
+ withXlsxError(xlsx_error::INVALID_ARGUMENT, [&] {
613
+ recordsToWorksheet(_worksheet, data);
614
+ });
615
+ }
616
+
617
+ std::string HybridXlsxWorksheet::cellAddress(double row, double col) {
618
+ return OpenXLSX::XLCellReference(static_cast<uint32_t>(row), static_cast<uint16_t>(col)).address();
619
+ }
620
+
621
+ // ========== Comments ==========
622
+
623
+ void HybridXlsxWorksheet::setComment(double row, double col, const std::string& text, std::optional<double> authorId) {
624
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
625
+ std::string ref = cellAddress(row, col);
626
+ auto& comments = _worksheet.comments();
627
+ // Excel requires every comment's authorId to reference a real <author> entry.
628
+ // OpenXLSX does not auto-register authors, so ensure at least one exists here;
629
+ // otherwise the generated comments.xml has an empty <authors> + a dangling authorId,
630
+ // which Excel flags as "We found a problem with some content".
631
+ if (comments.authorCount() == 0) {
632
+ comments.addAuthor("Author"); // becomes index 0
633
+ }
634
+ uint16_t author = authorId.has_value() ? static_cast<uint16_t>(*authorId) : 0;
635
+ if (author >= comments.authorCount()) {
636
+ author = 0; // fall back to a valid author
637
+ }
638
+ if (!comments.set(ref, text, author)) {
639
+ throw XlsxError(xlsx_error::XLSX_ERROR, "Failed to set comment for cell " + ref);
640
+ }
641
+ });
642
+ }
643
+
644
+ std::string HybridXlsxWorksheet::getComment(double row, double col) {
645
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
646
+ return _worksheet.comments().get(cellAddress(row, col));
647
+ });
648
+ }
649
+
650
+ bool HybridXlsxWorksheet::hasComment(double row, double col) {
651
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
652
+ std::string text = _worksheet.comments().get(cellAddress(row, col));
653
+ return !text.empty();
654
+ });
655
+ }
656
+
657
+ bool HybridXlsxWorksheet::deleteComment(double row, double col) {
658
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
659
+ return _worksheet.comments().deleteComment(cellAddress(row, col));
660
+ });
661
+ }
662
+
663
+ double HybridXlsxWorksheet::getCommentCount() {
664
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
665
+ return static_cast<double>(_worksheet.comments().count());
666
+ });
667
+ }
668
+
669
+ double HybridXlsxWorksheet::addCommentAuthor(const std::string& author) {
670
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
671
+ return static_cast<double>(_worksheet.comments().addAuthor(author));
672
+ });
673
+ }
674
+
675
+ std::string HybridXlsxWorksheet::getCommentAuthor(double authorId) {
676
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
677
+ return _worksheet.comments().author(static_cast<uint16_t>(authorId));
678
+ });
679
+ }
680
+
681
+ // ========== Conditional formatting ==========
682
+
683
+ std::shared_ptr<HybridXlsxConditionalFormatsSpec> HybridXlsxWorksheet::getConditionalFormats() {
684
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
685
+ return std::static_pointer_cast<HybridXlsxConditionalFormatsSpec>(
686
+ std::make_shared<HybridXlsxConditionalFormats>(_worksheet.conditionalFormats()));
687
+ });
688
+ }
689
+
690
+ // ========== Formula / findCell ==========
691
+
692
+ bool HybridXlsxWorksheet::hasFormula(double row, double col) {
693
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
694
+ OpenXLSX::XLCellAssignable cell = _worksheet.findCell(static_cast<uint32_t>(row), static_cast<uint16_t>(col));
695
+ if (cell.empty()) return false;
696
+ return cell.hasFormula();
697
+ });
698
+ }
699
+
700
+ std::string HybridXlsxWorksheet::formula(double row, double col) {
701
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
702
+ OpenXLSX::XLCellAssignable cell = _worksheet.findCell(static_cast<uint32_t>(row), static_cast<uint16_t>(col));
703
+ if (cell.empty() || !cell.hasFormula()) return std::string();
704
+ return cell.formula().get();
705
+ });
706
+ }
707
+
708
+ bool HybridXlsxWorksheet::findCell(double row, double col) {
709
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
710
+ OpenXLSX::XLCellAssignable cell = _worksheet.findCell(static_cast<uint32_t>(row), static_cast<uint16_t>(col));
711
+ return !cell.empty();
712
+ });
713
+ }
714
+
320
715
  }