fast_xlsx 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8326db5fb0c181c2c47af1f6b0d5799aa0bb69ff2d3b63d5286dbd1cd32fa2b8
4
- data.tar.gz: 560335ade1b435b9084cb3d48e4e4b20d906140c4d2a6e243751d93d1eb01964
3
+ metadata.gz: c78ccbcfe82884370ef3e64e4613812a91c521f479495184bd99df1cb7f1d36c
4
+ data.tar.gz: 86702f59f3c33c490ef3879e0178a7bcc9650fdba612bd1cd17ba24662b0392d
5
5
  SHA512:
6
- metadata.gz: 360e7a3686eb4705527139fc7c50834046aa4d45d7e3e79e6f913475cb039de2e64119600a8c9a0e1147136be98a6776f8bbe8db267b67533e99c390fdaced4e
7
- data.tar.gz: c23307efb6c2c0ceb1faef4336be02512f98cda7bff60b01324e614d82374247187ee1225a04c04a1cf9208d8b4abce3852a943f166f57f53801c743c4df1af3
6
+ metadata.gz: 629352aa7bf14685eef2d940f78b23e142f66b946b6d8f2eb36db0bd004574aefb2e04fd29fe2fc42e9684ac5d125725cfea5384d7a439b8f5d361411f68149c
7
+ data.tar.gz: f8755a00230b81f405339f9d123344bf827c3010d80f159c0b5408e2fbcc984d3fd94758fd65e7fa5562d0e8b23825cb0d0b791bf97057db013259afd49c3c96
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.0] - 2026-10-02
4
+
5
+ ### Added
6
+
7
+ - `Worksheet#selection`, `#top_left_cell` (what is selected and scrolled to when the file opens), `#ignore_error` (turns off Excel's green-triangle warnings in a range) and `#unprotect_range` (a range users can edit on a protected sheet).
8
+ - `Worksheet#hide_rows`, `#hide_columns`, `#row_format` (default format for a row's cells) and `#default_row_height` (call it before `row_format`, `hide_rows` and `group_rows`, which copy the default into their rows; it raises otherwise).
9
+
10
+ ### Fixed
11
+
12
+ - `nil` written with a format (e.g. a border, or a row format in `append`) writes a blank cell with that format; the format was dropped.
13
+
3
14
  ## [0.5.0] - 2026-10-01
4
15
 
5
16
  ### Added
data/README.md CHANGED
@@ -101,6 +101,9 @@ ws.zoom(150) # 10..400 percent
101
101
  ws.tab_color("#C00000")
102
102
  ws.hide_gridlines
103
103
  ws.activate # Excel opens on this sheet (un-hides it if hidden)
104
+ ws.selection("B2:C3") # the cells selected when it opens
105
+ ws.top_left_cell("A50") # and the cell scrolled to the top left
106
+ ws.ignore_error("A2:A1000", :number_stored_as_text) # no green triangles (one kind per range; don't overlap ranges)
104
107
  other.hide # the sheet Excel opens on can't be hidden: activate another one first
105
108
 
106
109
  ws.page_setup(landscape: true, paper: :a4, # or :letter, :legal, :tabloid, :a3, :a5, Excel's paper number, 0 = printer default
@@ -130,6 +133,9 @@ Methods that take one cell (`write`, `write_comment`, `insert_image`, `insert_ch
130
133
  ```ruby
131
134
  ws.freeze_panes(1, 0) # keep the first row visible while scrolling
132
135
  ws.row_height(0, 30) # row 1, height in points
136
+ ws.default_row_height(18) # every other row; call it before row_format, hide_rows, group_rows
137
+ ws.row_format(0, header) # default for cells in row 1 written without a format
138
+ ws.hide_rows(10..20) # also hide_columns(3), an index or a Range
133
139
  ws.merge_range(0, 0, 0, 3, "Q3 report", title) # merge A1:D1; the value can be any cell type
134
140
  ws.page_breaks([50, 100]) # print a new page before rows 51 and 101
135
141
  ws.vertical_page_breaks([8]) # and before column I
@@ -164,6 +170,7 @@ input = FastXlsx::Format.new(locked: false)
164
170
  ws.write(1, 1, 0, input) # B2 stays editable
165
171
  ws.protect # lock everything else
166
172
  ws.protect(password: "secret", allow: %i[sort use_autofilter]) # or with a password and allowed actions
173
+ ws.unprotect_range("B2:D10", name: "Inputs") # a range users can still edit (optional password:)
167
174
  ```
168
175
 
169
176
  `allow:` takes `:format_cells`, `:format_columns`, `:format_rows`, `:insert_columns`, `:insert_rows`, `:insert_links`, `:delete_columns`, `:delete_rows`, `:sort`, `:use_autofilter`, `:use_pivot_tables`, `:edit_scenarios`, `:edit_objects`. The password only stops editing in Excel; it does not encrypt the file.
@@ -258,7 +265,7 @@ Values are mapped by type:
258
265
  | `FastXlsx::URL.new("https://…")`, `URL.new(url, text: "Title")` | hyperlink (optionally showing other text) |
259
266
  | `FastXlsx::RichString.new(["Total: ", bold], "1,234")` | text with a format per segment |
260
267
  | `true` / `false` | boolean |
261
- | `nil` | empty cell |
268
+ | `nil` | empty cell (a blank cell with the format, when one is given) |
262
269
  | anything else | `to_s` as string |
263
270
 
264
271
  `<<` and `concat` append after the last row written to that worksheet. In `:constant` / `:low` memory mode rows are written to disk as you go, so fill each worksheet top to bottom.
data/bench/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- fast_xlsx (0.5.0)
4
+ fast_xlsx (0.6.0)
5
5
  rb_sys (~> 0.9.130)
6
6
 
7
7
  GEM
@@ -77,7 +77,7 @@ DEPENDENCIES
77
77
  CHECKSUMS
78
78
  caxlsx (4.5.0) sha256=e3d98d859f148df05d5462086b5079b523f29c1766b569535f1d68629ce743ff
79
79
  fast_excel (0.5.0) sha256=59c418bdcf586a6030798d4e3a9f575742badaceaf7bb90f38eee5feb5bdc478
80
- fast_xlsx (0.5.0)
80
+ fast_xlsx (0.6.0)
81
81
  ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df
82
82
  ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39
83
83
  ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564
@@ -13,7 +13,7 @@ use rust_xlsxwriter::{
13
13
  ConditionalFormatCell, ConditionalFormatCellRule, ConditionalFormatDataBar,
14
14
  ConditionalFormatFormula, ConditionalFormatText, ConditionalFormatTextRule,
15
15
  ConditionalFormatValue, DataValidation, DataValidationRule, FormatAlign, FormatBorder,
16
- FormatScript, FormatUnderline, IntoDataValidationValue, IntoExcelData, XlsxError,
16
+ FormatScript, FormatUnderline, IgnoreError, IntoDataValidationValue, IntoExcelData, XlsxError,
17
17
  };
18
18
 
19
19
  use rust_xlsxwriter::{
@@ -63,6 +63,15 @@ fn check_positive(ruby: &Ruby, what: &str, size: f64) -> Result<f64, Error> {
63
63
  ))
64
64
  }
65
65
 
66
+ // A range of rows is checked before any row changes, so it is all or nothing.
67
+ fn check_last_row(last: u32) -> Result<(), Error> {
68
+ if last < MAX_ROWS {
69
+ Ok(())
70
+ } else {
71
+ Err(xerr(XlsxError::RowColumnLimitError))
72
+ }
73
+ }
74
+
66
75
  fn check_size(ruby: &Ruby, what: &str, size: f64, max: f64) -> Result<(), Error> {
67
76
  if (0.0..=max).contains(&size) {
68
77
  return Ok(());
@@ -149,6 +158,12 @@ struct Worksheet {
149
158
  merges: RefCell<Vec<(u32, u16, u32, u16)>>,
150
159
  // Formats given with column_format: (first, last, style).
151
160
  formatted_columns: RefCell<Vec<(u16, u16, Arc<Style>)>>,
161
+ // Formats given with row_format: (first, last, style).
162
+ formatted_rows: RefCell<Vec<(u32, u32, Arc<Style>)>>,
163
+ // Whether a row got options (format, hidden, outline level):
164
+ // rust_xlsxwriter copies the default row height into a row then, so
165
+ // default_row_height must come first.
166
+ row_options_set: Cell<bool>,
152
167
  }
153
168
 
154
169
  struct TableColumnFormat {
@@ -217,6 +232,8 @@ impl Workbook {
217
232
  table_formats: RefCell::new(Vec::new()),
218
233
  merges: RefCell::new(Vec::new()),
219
234
  formatted_columns: RefCell::new(Vec::new()),
235
+ formatted_rows: RefCell::new(Vec::new()),
236
+ row_options_set: Cell::new(false),
220
237
  })
221
238
  }
222
239
 
@@ -418,7 +435,11 @@ impl CellValue {
418
435
  format: Option<&rust_xlsxwriter::Format>,
419
436
  ) -> Result<(), Error> {
420
437
  match self {
421
- CellValue::Empty => Ok(()),
438
+ // nil writes nothing, unless it has a format (e.g. a border).
439
+ CellValue::Empty => match format {
440
+ Some(f) => ws.write_blank(row, col, f).map(|_| ()).map_err(xerr),
441
+ None => Ok(()),
442
+ },
422
443
  CellValue::Text(s) => emit(ws, row, col, s.as_str(), format),
423
444
  CellValue::Number(n) | CellValue::Date(n, _) => emit(ws, row, col, *n, format),
424
445
  CellValue::Bool(b) => emit(ws, row, col, *b, format),
@@ -786,6 +807,29 @@ const PROTECTION_ALLOW: &[(&str, ProtectionFlag)] = &[
786
807
  ("edit_objects", |o| &mut o.edit_objects),
787
808
  ];
788
809
 
810
+ // Not IgnoreError::TwoDigitTextYear: rust_xlsxwriter 0.99 writes it as
811
+ // "TwoDigitTextYear", not the schema's "twoDigitTextYear", which Excel would
812
+ // flag as damaged content. Add it once that is fixed upstream.
813
+ const IGNORE_ERRORS: &[(&str, IgnoreError)] = &[
814
+ ("number_stored_as_text", IgnoreError::NumberStoredAsText),
815
+ ("formula_error", IgnoreError::FormulaError),
816
+ ("formula_differs", IgnoreError::FormulaDiffers),
817
+ (
818
+ "formula_refers_to_empty_cells",
819
+ IgnoreError::FormulaRefersToEmptyCells,
820
+ ),
821
+ ("formula_omits_cells", IgnoreError::FormulaOmitsCells),
822
+ ("data_validation_error", IgnoreError::DataValidationError),
823
+ (
824
+ "unlocked_cells_with_formula",
825
+ IgnoreError::UnlockedCellsWithFormula,
826
+ ),
827
+ (
828
+ "inconsistent_column_formula",
829
+ IgnoreError::InconsistentColumnFormula,
830
+ ),
831
+ ];
832
+
789
833
  // Excel's paper size codes; page_setup also takes the number itself.
790
834
  const PAPER_SIZES: &[(&str, u8)] = &[
791
835
  ("letter", 1),
@@ -1030,11 +1074,13 @@ impl Worksheet {
1030
1074
 
1031
1075
  // The format a value is written with: its own, else its table column's.
1032
1076
  // A date cell takes that format's date variant (when it has no
1033
- // num_format); with neither, its column_format's date variant, or the
1034
- // default date format when the column has no format. Other cells without
1035
- // one get None, so rust_xlsxwriter applies any column format itself.
1077
+ // num_format); with neither, its row_format's, else its column_format's
1078
+ // date variant (rust_xlsxwriter's order), else the default date format.
1079
+ // Other cells without one get None, so rust_xlsxwriter applies any row or
1080
+ // column format itself.
1036
1081
  fn format_for<'a>(
1037
1082
  tables: &'a [TableColumnFormat],
1083
+ rows: &'a [(u32, u32, Arc<Style>)],
1038
1084
  columns: &'a [(u16, u16, Arc<Style>)],
1039
1085
  row: u32,
1040
1086
  col: u16,
@@ -1055,13 +1101,21 @@ impl Worksheet {
1055
1101
  return Some(style.pick(date));
1056
1102
  }
1057
1103
  let time = date?;
1058
- match columns
1104
+ let row_style = rows
1059
1105
  .iter()
1060
1106
  .rev()
1061
- .find(|(first, last, _)| (*first..=*last).contains(&col))
1062
- {
1107
+ .find(|(first, last, _)| (*first..=*last).contains(&row))
1108
+ .map(|(_, _, style)| style);
1109
+ let column_style = || {
1110
+ columns
1111
+ .iter()
1112
+ .rev()
1113
+ .find(|(first, last, _)| (*first..=*last).contains(&col))
1114
+ .map(|(_, _, style)| style)
1115
+ };
1116
+ match row_style.or_else(column_style) {
1063
1117
  // None when it has a num_format: rust_xlsxwriter applies it.
1064
- Some((_, _, style)) => style.for_date(time),
1118
+ Some(style) => style.for_date(time),
1065
1119
  None => DEFAULTS.for_date(time),
1066
1120
  }
1067
1121
  }
@@ -1088,13 +1142,21 @@ impl Worksheet {
1088
1142
  Ok(())
1089
1143
  })?;
1090
1144
  let tables = self.table_formats.borrow();
1145
+ let rows = self.formatted_rows.borrow();
1091
1146
  let columns = self.formatted_columns.borrow();
1092
1147
  self.with_ws(|ws| {
1093
1148
  // Values were checked when converted, so only a bad row number
1094
1149
  // fails here, and it fails on the first cell.
1095
1150
  for (col, value, format) in &values {
1096
- let format =
1097
- Self::format_for(&tables, &columns, row, *col, format.as_deref(), value);
1151
+ let format = Self::format_for(
1152
+ &tables,
1153
+ &rows,
1154
+ &columns,
1155
+ row,
1156
+ *col,
1157
+ format.as_deref(),
1158
+ value,
1159
+ );
1098
1160
  value.write(ws, row, *col, format)?;
1099
1161
  }
1100
1162
  Ok(())
@@ -1138,13 +1200,22 @@ impl Worksheet {
1138
1200
  rb_self.check_not_flushed(ruby, row)?;
1139
1201
  let value = CellValue::from_ruby(ruby, v)?;
1140
1202
  let tables = rb_self.table_formats.borrow();
1203
+ let rows = rb_self.formatted_rows.borrow();
1141
1204
  let columns = rb_self.formatted_columns.borrow();
1142
1205
  rb_self.with_ws(|ws| {
1143
1206
  value.write(
1144
1207
  ws,
1145
1208
  row,
1146
1209
  col,
1147
- Self::format_for(&tables, &columns, row, col, format.map(|f| &*f.0), &value),
1210
+ Self::format_for(
1211
+ &tables,
1212
+ &rows,
1213
+ &columns,
1214
+ row,
1215
+ col,
1216
+ format.map(|f| &*f.0),
1217
+ &value,
1218
+ ),
1148
1219
  )
1149
1220
  })?;
1150
1221
  rb_self.advance(row);
@@ -1191,6 +1262,143 @@ impl Worksheet {
1191
1262
  })
1192
1263
  }
1193
1264
 
1265
+ // Row options are written with the row, so rows on disk ignore them.
1266
+ fn hide_rows(
1267
+ ruby: &Ruby,
1268
+ rb_self: Obj<Self>,
1269
+ first: u32,
1270
+ last: u32,
1271
+ ) -> Result<Obj<Self>, Error> {
1272
+ rb_self.check_not_flushed(ruby, first)?;
1273
+ check_last_row(last)?;
1274
+ rb_self.with_ws(|ws| {
1275
+ for row in first..=last {
1276
+ ws.set_row_hidden(row).map_err(xerr)?;
1277
+ }
1278
+ Ok(())
1279
+ })?;
1280
+ rb_self.row_options_set.set(true);
1281
+ Ok(rb_self)
1282
+ }
1283
+
1284
+ fn selection(
1285
+ rb_self: Obj<Self>,
1286
+ fr: u32,
1287
+ fc: u16,
1288
+ lr: u32,
1289
+ lc: u16,
1290
+ ) -> Result<Obj<Self>, Error> {
1291
+ rb_self.with_ws(|ws| ws.set_selection(fr, fc, lr, lc).map(|_| ()).map_err(xerr))?;
1292
+ Ok(rb_self)
1293
+ }
1294
+
1295
+ fn top_left_cell(rb_self: Obj<Self>, row: u32, col: u16) -> Result<Obj<Self>, Error> {
1296
+ rb_self.with_ws(|ws| ws.set_top_left_cell(row, col).map(|_| ()).map_err(xerr))?;
1297
+ Ok(rb_self)
1298
+ }
1299
+
1300
+ #[allow(clippy::too_many_arguments)]
1301
+ fn ignore_error(
1302
+ ruby: &Ruby,
1303
+ rb_self: Obj<Self>,
1304
+ fr: u32,
1305
+ fc: u16,
1306
+ lr: u32,
1307
+ lc: u16,
1308
+ kind: Value,
1309
+ ) -> Result<Obj<Self>, Error> {
1310
+ let error = choice(ruby, "error to ignore", kind, IGNORE_ERRORS)?;
1311
+ rb_self.with_ws(|ws| {
1312
+ ws.ignore_error_range(fr, fc, lr, lc, error)
1313
+ .map(|_| ())
1314
+ .map_err(xerr)
1315
+ })?;
1316
+ Ok(rb_self)
1317
+ }
1318
+
1319
+ #[allow(clippy::too_many_arguments)]
1320
+ fn unprotect_range(
1321
+ rb_self: Obj<Self>,
1322
+ fr: u32,
1323
+ fc: u16,
1324
+ lr: u32,
1325
+ lc: u16,
1326
+ name: Option<String>,
1327
+ password: Option<String>,
1328
+ ) -> Result<Obj<Self>, Error> {
1329
+ rb_self.with_ws(|ws| {
1330
+ ws.unprotect_range_with_options(
1331
+ fr,
1332
+ fc,
1333
+ lr,
1334
+ lc,
1335
+ name.as_deref().unwrap_or(""),
1336
+ password.as_deref().unwrap_or(""),
1337
+ )
1338
+ .map(|_| ())
1339
+ .map_err(xerr)
1340
+ })?;
1341
+ Ok(rb_self)
1342
+ }
1343
+
1344
+ fn hide_columns(rb_self: Obj<Self>, first: u16, last: u16) -> Result<Obj<Self>, Error> {
1345
+ rb_self.with_ws(|ws| {
1346
+ ws.set_column_range_hidden(first, last)
1347
+ .map(|_| ())
1348
+ .map_err(xerr)
1349
+ })?;
1350
+ Ok(rb_self)
1351
+ }
1352
+
1353
+ fn set_row_format(
1354
+ ruby: &Ruby,
1355
+ rb_self: &Self,
1356
+ first: u32,
1357
+ last: u32,
1358
+ format: &Format,
1359
+ ) -> Result<(), Error> {
1360
+ rb_self.check_not_flushed(ruby, first)?;
1361
+ check_last_row(last)?;
1362
+ rb_self.with_ws(|ws| {
1363
+ for row in first..=last {
1364
+ ws.set_row_format(row, &format.0.format).map_err(xerr)?;
1365
+ }
1366
+ Ok(())
1367
+ })?;
1368
+ rb_self.row_options_set.set(true);
1369
+ rb_self
1370
+ .formatted_rows
1371
+ .borrow_mut()
1372
+ .push((first, last, format.0.clone()));
1373
+ Ok(())
1374
+ }
1375
+
1376
+ fn default_row_height(
1377
+ ruby: &Ruby,
1378
+ rb_self: Obj<Self>,
1379
+ height: f64,
1380
+ ) -> Result<Obj<Self>, Error> {
1381
+ // rust_xlsxwriter ignores 0.
1382
+ if height == 0.0 {
1383
+ return Err(Error::new(
1384
+ ruby.exception_arg_error(),
1385
+ "invalid default row height 0: use a number above 0",
1386
+ ));
1387
+ }
1388
+ check_size(ruby, "row height", height, 409.0)?;
1389
+ if rb_self.row_options_set.get() {
1390
+ return Err(Error::new(
1391
+ ruby.get_inner(&ERROR),
1392
+ "call default_row_height before row_format, hide_rows, group_rows or row_height(row, 0): rows given those keep the earlier default",
1393
+ ));
1394
+ }
1395
+ rb_self.with_ws(|ws| {
1396
+ ws.set_default_row_height(height);
1397
+ Ok(())
1398
+ })?;
1399
+ Ok(rb_self)
1400
+ }
1401
+
1194
1402
  fn set_column_format(&self, first: u16, last: u16, format: &Format) -> Result<(), Error> {
1195
1403
  self.with_ws(|ws| {
1196
1404
  ws.set_column_range_format(first, last, &format.0.format)
@@ -1239,6 +1447,10 @@ impl Worksheet {
1239
1447
  ) -> Result<Obj<Self>, Error> {
1240
1448
  check_size(ruby, "row height", height, 409.0)?;
1241
1449
  rb_self.with_ws(|ws| ws.set_row_height(row, height).map(|_| ()).map_err(xerr))?;
1450
+ // A height is kept as given; only 0 (hiding the row) copies the default.
1451
+ if height == 0.0 {
1452
+ rb_self.row_options_set.set(true);
1453
+ }
1242
1454
  Ok(rb_self)
1243
1455
  }
1244
1456
 
@@ -1344,9 +1556,11 @@ impl Worksheet {
1344
1556
  }
1345
1557
  let value = CellValue::from_ruby(ruby, v)?;
1346
1558
  let tables = rb_self.table_formats.borrow();
1559
+ let rows = rb_self.formatted_rows.borrow();
1347
1560
  let columns = rb_self.formatted_columns.borrow();
1348
1561
  let format = Self::format_for(
1349
1562
  &tables,
1563
+ &rows,
1350
1564
  &columns,
1351
1565
  first_row,
1352
1566
  first_col,
@@ -1922,6 +2136,7 @@ impl Worksheet {
1922
2136
  .map(|_| ())
1923
2137
  .map_err(xerr)
1924
2138
  })?;
2139
+ rb_self.row_options_set.set(true);
1925
2140
  Ok(rb_self)
1926
2141
  }
1927
2142
 
@@ -2012,6 +2227,17 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
2012
2227
  "vertical_page_breaks",
2013
2228
  method!(Worksheet::set_vertical_page_breaks, 1),
2014
2229
  )?;
2230
+ ws.define_method("_hide_rows", method!(Worksheet::hide_rows, 2))?;
2231
+ ws.define_method("_hide_columns", method!(Worksheet::hide_columns, 2))?;
2232
+ ws.define_method("_row_format", method!(Worksheet::set_row_format, 3))?;
2233
+ ws.define_method(
2234
+ "default_row_height",
2235
+ method!(Worksheet::default_row_height, 1),
2236
+ )?;
2237
+ ws.define_method("_selection", method!(Worksheet::selection, 4))?;
2238
+ ws.define_method("_top_left_cell", method!(Worksheet::top_left_cell, 2))?;
2239
+ ws.define_method("_ignore_error", method!(Worksheet::ignore_error, 5))?;
2240
+ ws.define_method("_unprotect_range", method!(Worksheet::unprotect_range, 6))?;
2015
2241
  ws.define_method("activate", method!(Worksheet::activate, 0))?;
2016
2242
  ws.define_method("hide", method!(Worksheet::hide, 0))?;
2017
2243
  ws.define_method("zoom", method!(Worksheet::zoom, 1))?;
@@ -2,5 +2,5 @@
2
2
 
3
3
  module FastXlsx
4
4
  # The gem version.
5
- VERSION = "0.5.0"
5
+ VERSION = "0.6.0"
6
6
  end
data/lib/fast_xlsx.rb CHANGED
@@ -226,6 +226,14 @@ module FastXlsx
226
226
  # @param color [String, Integer] "#RRGGBB" or 0xRRGGBB
227
227
  # @return [self]
228
228
  #
229
+ # @!method default_row_height(height)
230
+ # Height of rows not given one with {#row_height}. Call it before
231
+ # {#row_format}, {#hide_rows} and {#group_rows}: rows given those keep
232
+ # the earlier default.
233
+ # @param height [Numeric] points, above 0 and up to 409
234
+ # @return [self]
235
+ # @raise [FastXlsx::Error] when called after those
236
+ #
229
237
  # @!method hide_gridlines
230
238
  # Hides the gridlines on screen (see {#page_setup} for printing).
231
239
  # @return [self]
@@ -441,6 +449,71 @@ module FastXlsx
441
449
  _protect(password, Array(allow))
442
450
  end
443
451
 
452
+ # A range users can still edit on a protected sheet ({#protect}),
453
+ # optionally with its own password. Give each range its own name.
454
+ # @overload unprotect_range(*range, name: nil, password: nil)
455
+ # @param name [String, nil] shown in Excel's "Allow Edit Ranges"
456
+ # @return [self]
457
+ def unprotect_range(*range, name: nil, password: nil)
458
+ _unprotect_range(*CellRange.split(range).first, name, password)
459
+ end
460
+
461
+ # The cells selected when the file opens.
462
+ # @param range a cell range (see {Worksheet})
463
+ # @return [self]
464
+ def selection(*range)
465
+ _selection(*CellRange.split(range).first)
466
+ end
467
+
468
+ # The cell scrolled to the top left when the file opens.
469
+ # @param cell (row, col) or "B2"
470
+ # @return [self]
471
+ def top_left_cell(*cell)
472
+ _top_left_cell(*CellRange.cell(cell).first(2))
473
+ end
474
+
475
+ # Turns off one of Excel's warnings (green triangles) in the range, e.g.
476
+ # for codes stored as text. One kind per range (a rust_xlsxwriter limit);
477
+ # overlapping ranges are accepted, but Excel then uses only one of them.
478
+ # @overload ignore_error(*range, error)
479
+ # @param error [Symbol] :number_stored_as_text, :formula_error,
480
+ # :formula_differs, :formula_refers_to_empty_cells,
481
+ # :formula_omits_cells, :data_validation_error,
482
+ # :unlocked_cells_with_formula, :inconsistent_column_formula
483
+ # @return [self]
484
+ # @raise [FastXlsx::Error] when the range already has one
485
+ def ignore_error(*args)
486
+ range, (error, *) = CellRange.split(args, 1..1)
487
+ _ignore_error(*range, error)
488
+ end
489
+
490
+ # Each row in the range is kept (about 1 KB) until saving.
491
+ # @param rows [Integer, Range<Integer>]
492
+ # @return [self]
493
+ # @raise [FastXlsx::Error] in :constant / :low mode, for rows already on disk
494
+ def hide_rows(rows)
495
+ _hide_rows(*CellRange.bounds(rows))
496
+ end
497
+
498
+ # @param columns [Integer, Range<Integer>]
499
+ # @return [self]
500
+ def hide_columns(columns)
501
+ _hide_columns(*CellRange.bounds(columns))
502
+ end
503
+
504
+ # Default format for cells in these rows that are written without one.
505
+ # Use it or {#column_format} for a cell, not both: where both apply, Excel
506
+ # may combine them or show the row's. Each row is kept (about 1 KB) until
507
+ # saving, so style a whole sheet with {#column_format}.
508
+ # @param rows [Integer, Range<Integer>]
509
+ # @param format [Format]
510
+ # @return [self]
511
+ # @raise [FastXlsx::Error] in :constant / :low mode, for rows already on disk
512
+ def row_format(rows, format)
513
+ _row_format(*CellRange.bounds(rows), format)
514
+ self
515
+ end
516
+
444
517
  # Default format for cells in these columns that are written without one.
445
518
  # @param columns [Integer, Range<Integer>]
446
519
  # @param format [Format]
data/sig/fast_xlsx.rbs CHANGED
@@ -62,6 +62,18 @@ module FastXlsx
62
62
  def <<: (Array[cell] row) -> self
63
63
  def concat: (Array[Array[cell]] rows) -> self
64
64
  def column_width: (Integer | Range[Integer] columns, Numeric width) -> self
65
+ def hide_rows: (Integer | Range[Integer] rows) -> self
66
+ def hide_columns: (Integer | Range[Integer] columns) -> self
67
+ def row_format: (Integer | Range[Integer] rows, Format format) -> self
68
+ def default_row_height: (Numeric height) -> self
69
+ def selection: (*cell_range range) -> self
70
+ def top_left_cell: (Integer row, Integer col) -> self
71
+ | (String cell) -> self
72
+ type ignorable_error = :number_stored_as_text | :formula_error | :formula_differs | :formula_refers_to_empty_cells
73
+ | :formula_omits_cells | :data_validation_error | :unlocked_cells_with_formula
74
+ | :inconsistent_column_formula
75
+ def ignore_error: (*cell_range | ignorable_error args) -> self
76
+ def unprotect_range: (*cell_range range, ?name: String?, ?password: String?) -> self
65
77
  def column_format: (Integer | Range[Integer] columns, Format format) -> self
66
78
  def autofit: () -> self
67
79
  # A cell range: four 0-based numbers, an Excel reference ("A1:D10"), or
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fast_xlsx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zac