xlsxrb 0.1.10 → 0.1.12

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +8 -0
  3. data/CHANGELOG.md +54 -0
  4. data/README.md +1 -0
  5. data/Rakefile +66 -0
  6. data/benchmark.rb +3 -1
  7. data/docs/ARCHITECTURE.md +1 -1
  8. data/docs/DEVELOPMENT.md +6 -2
  9. data/docs/MUTATION_TESTING.md +52 -0
  10. data/docs/PEER_LIBRARIES.md +14 -14
  11. data/docs/QUALITY_ASSURANCE.md +1 -0
  12. data/docs/assets/benchmark_results.svg +49 -49
  13. data/docs/visual/VisualGallery.md +38 -27
  14. data/docs/wasm/ruby.wasm +0 -0
  15. data/lib/xlsxrb/chart_builder.rb +18 -18
  16. data/lib/xlsxrb/dsl_helpers.rb +116 -0
  17. data/lib/xlsxrb/elements/cell.rb +122 -34
  18. data/lib/xlsxrb/elements/column.rb +27 -3
  19. data/lib/xlsxrb/elements/row.rb +27 -8
  20. data/lib/xlsxrb/elements/workbook.rb +16 -14
  21. data/lib/xlsxrb/elements/worksheet.rb +17 -1
  22. data/lib/xlsxrb/ooxml/cfb.rb +9 -6
  23. data/lib/xlsxrb/ooxml/crypto/agile.rb +5 -5
  24. data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +784 -0
  25. data/lib/xlsxrb/ooxml/reader/listeners/drawing_listeners.rb +3478 -0
  26. data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +1796 -0
  27. data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +428 -0
  28. data/lib/xlsxrb/ooxml/reader/listeners.rb +4 -6446
  29. data/lib/xlsxrb/ooxml/utils.rb +5 -6
  30. data/lib/xlsxrb/ooxml/workbook_writer.rb +287 -226
  31. data/lib/xlsxrb/ooxml/worksheet_parser.rb +115 -208
  32. data/lib/xlsxrb/ooxml/worksheet_writer.rb +122 -9
  33. data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +1602 -0
  34. data/lib/xlsxrb/ooxml/writer/features_xml.rb +731 -0
  35. data/lib/xlsxrb/ooxml/writer/styles_xml.rb +348 -0
  36. data/lib/xlsxrb/ooxml/writer.rb +7 -2645
  37. data/lib/xlsxrb/ooxml/xml_builder.rb +11 -2
  38. data/lib/xlsxrb/ooxml/zip_generator.rb +26 -5
  39. data/lib/xlsxrb/ooxml/zip_writer.rb +31 -38
  40. data/lib/xlsxrb/stream_row.rb +30 -6
  41. data/lib/xlsxrb/stream_sheet.rb +2 -8
  42. data/lib/xlsxrb/stream_writer.rb +186 -133
  43. data/lib/xlsxrb/style_builder.rb +10 -1
  44. data/lib/xlsxrb/version.rb +1 -1
  45. data/lib/xlsxrb/worksheet_builder.rb +16 -50
  46. data/lib/xlsxrb.rb +1 -0
  47. data/sig/generated/xlsxrb/chart_builder.rbs +36 -36
  48. data/sig/generated/xlsxrb/dsl_helpers.rbs +60 -0
  49. data/sig/generated/xlsxrb/elements/cell.rbs +166 -12
  50. data/sig/generated/xlsxrb/elements/column.rbs +32 -21
  51. data/sig/generated/xlsxrb/elements/row.rbs +94 -14
  52. data/sig/generated/xlsxrb/elements/workbook.rbs +95 -10
  53. data/sig/generated/xlsxrb/elements/worksheet.rbs +9 -0
  54. data/sig/generated/xlsxrb/ooxml/reader/listeners/core_listeners.rbs +173 -0
  55. data/sig/generated/xlsxrb/ooxml/reader/listeners/drawing_listeners.rbs +400 -0
  56. data/sig/generated/xlsxrb/ooxml/reader/listeners/feature_listeners.rbs +442 -0
  57. data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +80 -0
  58. data/sig/generated/xlsxrb/ooxml/reader/listeners.rbs +0 -1072
  59. data/sig/generated/xlsxrb/ooxml/utils.rbs +2 -0
  60. data/sig/generated/xlsxrb/ooxml/workbook_writer.rbs +17 -1
  61. data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +16 -2
  62. data/sig/generated/xlsxrb/ooxml/worksheet_writer.rbs +4 -0
  63. data/sig/generated/xlsxrb/ooxml/writer/drawing_xml.rbs +79 -0
  64. data/sig/generated/xlsxrb/ooxml/writer/features_xml.rbs +83 -0
  65. data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +53 -0
  66. data/sig/generated/xlsxrb/ooxml/writer.rbs +6 -185
  67. data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +7 -0
  68. data/sig/generated/xlsxrb/ooxml/zip_generator.rbs +15 -0
  69. data/sig/generated/xlsxrb/stream_row.rbs +3 -0
  70. data/sig/generated/xlsxrb/stream_writer.rbs +7 -1
  71. data/sig/generated/xlsxrb/style_builder.rbs +7 -0
  72. metadata +19 -1
@@ -1136,8 +1136,8 @@ end
1136
1136
  ```text
1137
1137
  === Read Validation ===
1138
1138
  Row 0: A1: "Format" (Format ID: , Code: nil), B1: "Value" (Format ID: , Code: nil)
1139
- Row 1: A2: "Positive Yen" (Format ID: , Code: nil), B2: 12500 (Format ID: 164, Code: "¥#,##0;[Red]¥-#,##0")
1140
- Row 2: A3: "Negative Yen" (Format ID: , Code: nil), B3: -8000 (Format ID: 164, Code: "¥#,##0;[Red]¥-#,##0")
1139
+ Row 1: A2: "Positive Yen" (Format ID: , Code: nil), B2: 12500 (Format ID: 166, Code: "¥#,##0;[Red]¥-#,##0")
1140
+ Row 2: A3: "Negative Yen" (Format ID: , Code: nil), B3: -8000 (Format ID: 166, Code: "¥#,##0;[Red]¥-#,##0")
1141
1141
  ```
1142
1142
 
1143
1143
  <hr/>
@@ -1187,8 +1187,8 @@ end
1187
1187
  ```text
1188
1188
  === Read Validation ===
1189
1189
  Row 0: A1: "Format" (Format ID: , Code: nil), B1: "Value" (Format ID: , Code: nil)
1190
- Row 1: A2: "Positive (Green)" (Format ID: , Code: nil), B2: 5000 (Format ID: 164, Code: "[Green]#,##0;[Red]-#,##0")
1191
- Row 2: A3: "Negative (Red)" (Format ID: , Code: nil), B3: -2500 (Format ID: 164, Code: "[Green]#,##0;[Red]-#,##0")
1190
+ Row 1: A2: "Positive (Green)" (Format ID: , Code: nil), B2: 5000 (Format ID: 166, Code: "[Green]#,##0;[Red]-#,##0")
1191
+ Row 2: A3: "Negative (Red)" (Format ID: , Code: nil), B3: -2500 (Format ID: 166, Code: "[Green]#,##0;[Red]-#,##0")
1192
1192
  ```
1193
1193
 
1194
1194
  <hr/>
@@ -1239,9 +1239,9 @@ end
1239
1239
  ```text
1240
1240
  === Read Validation ===
1241
1241
  Row 0: A1: "Format" (Format ID: , Code: nil), B1: "Value" (Format ID: , Code: nil)
1242
- Row 1: A2: "Half" (Format ID: , Code: nil), B2: 0.5 (Format ID: 164, Code: "# ?/?")
1243
- Row 2: A3: "Third" (Format ID: , Code: nil), B3: 0.3333 (Format ID: 164, Code: "# ?/?")
1244
- Row 3: A4: "Quarter" (Format ID: , Code: nil), B4: 0.75 (Format ID: 164, Code: "# ?/?")
1242
+ Row 1: A2: "Half" (Format ID: , Code: nil), B2: 0.5 (Format ID: 166, Code: "# ?/?")
1243
+ Row 2: A3: "Third" (Format ID: , Code: nil), B3: 0.3333 (Format ID: 166, Code: "# ?/?")
1244
+ Row 3: A4: "Quarter" (Format ID: , Code: nil), B4: 0.75 (Format ID: 166, Code: "# ?/?")
1245
1245
  ```
1246
1246
 
1247
1247
  <hr/>
@@ -1290,7 +1290,7 @@ end
1290
1290
  ```text
1291
1291
  === Read Validation ===
1292
1292
  Row 0: A1: "Format" (Format ID: , Code: nil), B1: "Value" (Format ID: , Code: nil)
1293
- Row 1: A2: "Percent with 2 Decimals" (Format ID: , Code: nil), B2: 0.12345 (Format ID: 164, Code: "0.00%")
1293
+ Row 1: A2: "Percent with 2 Decimals" (Format ID: , Code: nil), B2: 0.12345 (Format ID: 166, Code: "0.00%")
1294
1294
  ```
1295
1295
 
1296
1296
  <hr/>
@@ -1340,8 +1340,8 @@ end
1340
1340
  ```text
1341
1341
  === Read Validation ===
1342
1342
  Row 0: A1: "Format" (Format ID: , Code: nil), B1: "Value" (Format ID: , Code: nil)
1343
- Row 1: A2: "Scientific" (Format ID: , Code: nil), B2: 123456789.0 (Format ID: 164, Code: "0.00E+00")
1344
- Row 2: A3: "Small Scientific" (Format ID: , Code: nil), B3: 1.234e-05 (Format ID: 164, Code: "0.00E+00")
1343
+ Row 1: A2: "Scientific" (Format ID: , Code: nil), B2: 123456789.0 (Format ID: 166, Code: "0.00E+00")
1344
+ Row 2: A3: "Small Scientific" (Format ID: , Code: nil), B3: 1.234e-05 (Format ID: 166, Code: "0.00E+00")
1345
1345
  ```
1346
1346
 
1347
1347
  <hr/>
@@ -2867,8 +2867,6 @@ Visual demonstration for Drawing Shapes.
2867
2867
 
2868
2868
  <div><img src="../../test/visual/baselines/drawing_shapes/page-1.png" width="100%" alt="Preview"/></div>
2869
2869
 
2870
- <div><img src="../../test/visual/baselines/drawing_shapes/page-2.png" width="100%" alt="Preview"/></div>
2871
-
2872
2870
  ### DSL Code
2873
2871
 
2874
2872
  ```ruby
@@ -2880,12 +2878,12 @@ output_path = ARGV[0] || "drawing_shapes.xlsx"
2880
2878
 
2881
2879
  Xlsxrb.write(output_path) do |wb|
2882
2880
  wb.sheet("Shapes") do |sheet|
2883
- sheet.column(0..7, width: 15)
2881
+ sheet.column(0..4, width: 12)
2884
2882
  sheet.row(["Diagram with shapes and annotations"])
2885
2883
  sheet.row([])
2886
- sheet.shape(preset: "rect", text: "Process Start", from_col: 1, from_row: 2, to_col: 3, to_row: 4)
2887
- sheet.shape(preset: "rightArrow", text: "Next", from_col: 4, from_row: 3, to_col: 5, to_row: 4)
2888
- sheet.shape(preset: "roundRect", text: "Processing", from_col: 6, from_row: 2, to_col: 8, to_row: 4)
2884
+ sheet.shape(preset: "rect", text: "Start", from_col: 0, from_row: 2, to_col: 1, to_row: 4)
2885
+ sheet.shape(preset: "rightArrow", text: "Next", from_col: 1, from_row: 3, to_col: 2, to_row: 4)
2886
+ sheet.shape(preset: "roundRect", text: "Processing", from_col: 2, from_row: 2, to_col: 4, to_row: 4)
2889
2887
  end
2890
2888
  end
2891
2889
 
@@ -4082,8 +4080,6 @@ Visual demonstration for Pivot Table.
4082
4080
 
4083
4081
  <div><img src="../../test/visual/baselines/pivot_table/page-1.png" width="100%" alt="Preview"/></div>
4084
4082
 
4085
- <div><img src="../../test/visual/baselines/pivot_table/page-2.png" width="100%" alt="Preview"/></div>
4086
-
4087
4083
  ### DSL Code
4088
4084
 
4089
4085
  ```ruby
@@ -4095,7 +4091,7 @@ output_path = ARGV[0] || "pivot_table.xlsx"
4095
4091
 
4096
4092
  Xlsxrb.write(output_path) do |wb|
4097
4093
  wb.sheet("SalesData") do |sheet|
4098
- sheet.column(0..3, width: 18)
4094
+ sheet.column(0..3, width: 14)
4099
4095
  sheet.row(%w[Region Quarter Sales Rep])
4100
4096
  sheet.row(["East", "Q1", 1000, "Alice"])
4101
4097
  sheet.row(["West", "Q1", 1500, "Bob"])
@@ -4104,12 +4100,22 @@ Xlsxrb.write(output_path) do |wb|
4104
4100
  sheet.row(["North", "Q1", 800, "Charlie"])
4105
4101
  sheet.row(["North", "Q2", 950, "Charlie"])
4106
4102
 
4103
+ sheet.row([])
4104
+ sheet.row([])
4105
+ # Pre-populate summary cells in the pivot table target region (A10:D15) for headless renderers
4106
+ sheet.row(["Sum of Sales", "Quarter", nil, nil])
4107
+ sheet.row(["Region", "Q1", "Q2", "Grand Total"])
4108
+ sheet.row(["East", 1000, 1200, 2200])
4109
+ sheet.row(["North", 800, 950, 1750])
4110
+ sheet.row(["West", 1500, 1800, 3300])
4111
+ sheet.row(["Grand Total", 3300, 3950, 7250])
4112
+
4107
4113
  sheet.pivot_table(
4108
4114
  "SalesData!A1:D7",
4109
4115
  row_fields: ["Region"],
4110
4116
  data_fields: [{ name: "Sales", subtotal: "sum" }],
4111
4117
  col_fields: ["Quarter"],
4112
- dest_ref: "F1",
4118
+ dest_ref: "A10",
4113
4119
  name: "RegionalSalesSummary"
4114
4120
  )
4115
4121
  end
@@ -4135,6 +4141,14 @@ Row 3: A4: "East", B4: "Q2", C4: 1200, D4: "Alice"
4135
4141
  Row 4: A5: "West", B5: "Q2", C5: 1800, D5: "Bob"
4136
4142
  Row 5: A6: "North", B6: "Q1", C6: 800, D6: "Charlie"
4137
4143
  Row 6: A7: "North", B7: "Q2", C7: 950, D7: "Charlie"
4144
+ Row 7:
4145
+ Row 8:
4146
+ Row 9: A10: "Sum of Sales", B10: "Quarter"
4147
+ Row 10: A11: "Region", B11: "Q1", C11: "Q2", D11: "Grand Total"
4148
+ Row 11: A12: "East", B12: 1000, C12: 1200, D12: 2200
4149
+ Row 12: A13: "North", B13: 800, C13: 950, D13: 1750
4150
+ Row 13: A14: "West", B14: 1500, C14: 1800, D14: 3300
4151
+ Row 14: A15: "Grand Total", B15: 3300, C15: 3950, D15: 7250
4138
4152
  ```
4139
4153
 
4140
4154
  <hr/>
@@ -4494,8 +4508,6 @@ Visual demonstration for Table Styles.
4494
4508
 
4495
4509
  <div><img src="../../test/visual/baselines/table_styles/page-1.png" width="100%" alt="Preview"/></div>
4496
4510
 
4497
- <div><img src="../../test/visual/baselines/table_styles/page-2.png" width="100%" alt="Preview"/></div>
4498
-
4499
4511
  ### DSL Code
4500
4512
 
4501
4513
  ```ruby
@@ -4507,7 +4519,10 @@ output_path = ARGV[0] || "table_styles.xlsx"
4507
4519
 
4508
4520
  Xlsxrb.write(output_path) do |wb|
4509
4521
  wb.sheet("Table Example") do |sheet|
4510
- sheet.column(0..3, width: 20)
4522
+ sheet.column(0, width: 8)
4523
+ sheet.column(1, width: 16)
4524
+ sheet.column(2, width: 16)
4525
+ sheet.column(3, width: 14)
4511
4526
  sheet.row(%w[ID Name Department Salary])
4512
4527
  sheet.row([101, "Alice Smith", "Engineering", 120_000])
4513
4528
  sheet.row([102, "Bob Jones", "Marketing", 95_000])
@@ -4548,8 +4563,6 @@ Demonstrates disabling visible grid lines in spreadsheet view.
4548
4563
 
4549
4564
  <div><img src="../../test/visual/baselines/view_show_grid_lines/page-1.png" width="100%" alt="Preview"/></div>
4550
4565
 
4551
- <div><img src="../../test/visual/support/illustrations/view_show_grid_lines_page-2.png" width="100%" alt="Preview"/></div>
4552
-
4553
4566
  ### DSL Code
4554
4567
 
4555
4568
  ```ruby
@@ -4593,8 +4606,6 @@ Demonstrates setting custom zoom scale in sheet view (e.g. 150%).
4593
4606
 
4594
4607
  <div><img src="../../test/visual/baselines/view_zoom_scale/page-1.png" width="100%" alt="Preview"/></div>
4595
4608
 
4596
- <div><img src="../../test/visual/support/illustrations/view_zoom_scale_page-2.png" width="100%" alt="Preview"/></div>
4597
-
4598
4609
  ### DSL Code
4599
4610
 
4600
4611
  ```ruby
data/docs/wasm/ruby.wasm CHANGED
Binary file
@@ -80,7 +80,7 @@ module Xlsxrb
80
80
  # @param kwargs [Hash] Keyword arguments for legend styling/layout.
81
81
  # @return [Object] The configured legend property.
82
82
  # @api public
83
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
83
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
84
84
  def legend(*args, **kwargs)
85
85
  @options[:legend] = kwargs.empty? ? args.first : kwargs
86
86
  end
@@ -91,7 +91,7 @@ module Xlsxrb
91
91
  # @param kwargs [Hash] Keyword arguments for plot area configuration.
92
92
  # @return [Object] The configured plot_area property.
93
93
  # @api public
94
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
94
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
95
95
  def plot_area(*args, **kwargs)
96
96
  @options[:plot_area] = kwargs.empty? ? args.first : kwargs
97
97
  end
@@ -102,7 +102,7 @@ module Xlsxrb
102
102
  # @param kwargs [Hash] Keyword arguments.
103
103
  # @return [Object] The configured chart_space property.
104
104
  # @api public
105
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
105
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
106
106
  def chart_space(*args, **kwargs)
107
107
  @options[:chart_space] = kwargs.empty? ? args.first : kwargs
108
108
  end
@@ -113,7 +113,7 @@ module Xlsxrb
113
113
  # @param kwargs [Hash] Keyword arguments.
114
114
  # @return [Object] The configured style property.
115
115
  # @api public
116
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String | Integer) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String | Integer)
116
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String | Integer) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String | Integer)
117
117
  def style(*args, **kwargs)
118
118
  @options[:style] = kwargs.empty? ? args.first : kwargs
119
119
  end
@@ -124,7 +124,7 @@ module Xlsxrb
124
124
  # @param kwargs [Hash] Keyword arguments.
125
125
  # @return [Object] The configured data_labels property.
126
126
  # @api public
127
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
127
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
128
128
  def data_labels(*args, **kwargs)
129
129
  @options[:data_labels] = kwargs.empty? ? args.first : kwargs
130
130
  end
@@ -135,7 +135,7 @@ module Xlsxrb
135
135
  # @param kwargs [Hash] Keyword arguments.
136
136
  # @return [Boolean, String]
137
137
  # @api public
138
- #: (*(bool | String) args, **String | Integer | bool | nil kwargs) -> (bool | String)
138
+ #: (*(bool | String) args, **String | Numeric | bool | nil kwargs) -> (bool | String)
139
139
  def plot_visible_only(*args, **kwargs)
140
140
  @options[:plot_visible_only] = kwargs.empty? ? args.first : kwargs
141
141
  end
@@ -146,7 +146,7 @@ module Xlsxrb
146
146
  # @param kwargs [Hash] Keyword arguments.
147
147
  # @return [String]
148
148
  # @api public
149
- #: (*(String) args, **String | Integer | bool | nil kwargs) -> String
149
+ #: (*(String) args, **String | Numeric | bool | nil kwargs) -> String
150
150
  def display_blanks_as(*args, **kwargs)
151
151
  @options[:display_blanks_as] = kwargs.empty? ? args.first : kwargs
152
152
  end
@@ -157,7 +157,7 @@ module Xlsxrb
157
157
  # @param kwargs [Hash] Keyword arguments.
158
158
  # @return [Object]
159
159
  # @api public
160
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
160
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
161
161
  def view3d(*args, **kwargs)
162
162
  @options[:view3d] = kwargs.empty? ? args.first : kwargs
163
163
  end
@@ -168,7 +168,7 @@ module Xlsxrb
168
168
  # @param kwargs [Hash] Keyword arguments.
169
169
  # @return [Object]
170
170
  # @api public
171
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
171
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
172
172
  def category_axis(*args, **kwargs)
173
173
  @options[:category_axis] = kwargs.empty? ? args.first : kwargs
174
174
  end
@@ -179,7 +179,7 @@ module Xlsxrb
179
179
  # @param kwargs [Hash] Keyword arguments.
180
180
  # @return [Object]
181
181
  # @api public
182
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
182
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
183
183
  def value_axis(*args, **kwargs)
184
184
  @options[:value_axis] = kwargs.empty? ? args.first : kwargs
185
185
  end
@@ -190,7 +190,7 @@ module Xlsxrb
190
190
  # @param kwargs [Hash] Keyword arguments.
191
191
  # @return [Boolean, String]
192
192
  # @api public
193
- #: (*(bool | String) args, **String | Integer | bool | nil kwargs) -> (bool | String)
193
+ #: (*(bool | String) args, **String | Numeric | bool | nil kwargs) -> (bool | String)
194
194
  def show_legend_key(*args, **kwargs)
195
195
  @options[:show_legend_key] = kwargs.empty? ? args.first : kwargs
196
196
  end
@@ -258,7 +258,7 @@ module Xlsxrb
258
258
  # @param kwargs [Hash] Keyword arguments.
259
259
  # @return [Object]
260
260
  # @api public
261
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
261
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
262
262
  def fill(*args, **kwargs)
263
263
  @options[:fill] = kwargs.empty? ? args.first : kwargs
264
264
  end
@@ -269,7 +269,7 @@ module Xlsxrb
269
269
  # @param kwargs [Hash] Keyword arguments.
270
270
  # @return [Object]
271
271
  # @api public
272
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
272
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
273
273
  def line(*args, **kwargs)
274
274
  @options[:line] = kwargs.empty? ? args.first : kwargs
275
275
  end
@@ -280,7 +280,7 @@ module Xlsxrb
280
280
  # @param kwargs [Hash] Keyword arguments.
281
281
  # @return [Object]
282
282
  # @api public
283
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
283
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
284
284
  def trendline(*args, **kwargs)
285
285
  @options[:trendline] = kwargs.empty? ? args.first : kwargs
286
286
  end
@@ -291,7 +291,7 @@ module Xlsxrb
291
291
  # @param kwargs [Hash] Keyword arguments.
292
292
  # @return [Object]
293
293
  # @api public
294
- #: (*(Hash[Symbol, String | Integer | bool | nil] | String) args, **String | Integer | bool | nil kwargs) -> (Hash[Symbol, String | Integer | bool | nil] | String)
294
+ #: (*(Hash[Symbol, String | Numeric | bool | nil] | String) args, **String | Numeric | bool | nil kwargs) -> (Hash[Symbol, String | Numeric | bool | nil] | String)
295
295
  def data_labels(*args, **kwargs)
296
296
  @options[:data_labels] = kwargs.empty? ? args.first : kwargs
297
297
  end
@@ -302,7 +302,7 @@ module Xlsxrb
302
302
  # @param kwargs [Hash] Keyword arguments.
303
303
  # @return [Boolean, String]
304
304
  # @api public
305
- #: (*(bool | String) args, **String | Integer | bool | nil kwargs) -> (bool | String)
305
+ #: (*(bool | String) args, **String | Numeric | bool | nil kwargs) -> (bool | String)
306
306
  def smooth(*args, **kwargs)
307
307
  @options[:smooth] = kwargs.empty? ? args.first : kwargs
308
308
  end
@@ -313,7 +313,7 @@ module Xlsxrb
313
313
  # @param kwargs [Hash] Keyword arguments.
314
314
  # @return [String]
315
315
  # @api public
316
- #: (*(String) args, **String | Integer | bool | nil kwargs) -> String
316
+ #: (*(String) args, **String | Numeric | bool | nil kwargs) -> String
317
317
  def shape(*args, **kwargs)
318
318
  @options[:shape] = kwargs.empty? ? args.first : kwargs
319
319
  end
@@ -324,7 +324,7 @@ module Xlsxrb
324
324
  # @param kwargs [Hash] Keyword arguments.
325
325
  # @return [String]
326
326
  # @api public
327
- #: (*(String) args, **String | Integer | bool | nil kwargs) -> String
327
+ #: (*(String) args, **String | Numeric | bool | nil kwargs) -> String
328
328
  def type(*args, **kwargs)
329
329
  @options[:type] = kwargs.empty? ? args.first : kwargs
330
330
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ require_relative "elements/cell"
6
+ require_relative "ooxml/utils"
7
+
8
+ module Xlsxrb
9
+ # Internal helper module for normalizing and validating DSL arguments
10
+ # shared between {WorksheetBuilder} (in-memory) and {StreamWriter} (streaming).
11
+ #
12
+ # @api private
13
+ module DslHelpers
14
+ # Normalizes merge cell range string or coordinate components into a canonical range reference (e.g. "A1:C3").
15
+ #
16
+ # @param range [String, Hash, nil]
17
+ # @param row [Integer, nil]
18
+ # @param col_start [Integer, String, nil]
19
+ # @param col_end [Integer, String, nil]
20
+ # @param row_start [Integer, nil]
21
+ # @param row_end [Integer, nil]
22
+ # @param strict_excel_mode [Boolean]
23
+ # @return [String]
24
+ #: (untyped range, ?row: Integer?, ?col_start: (Integer | String)?, ?col_end: (Integer | String)?, ?row_start: Integer?, ?row_end: Integer?, ?strict_excel_mode: bool) -> String
25
+ def self.normalize_merge_range(range = nil, row: nil, col_start: nil, col_end: nil, row_start: nil, row_end: nil, strict_excel_mode: true)
26
+ if range.is_a?(Hash)
27
+ row = range[:row]
28
+ row_start = range[:row_start]
29
+ row_end = range[:row_end]
30
+ col_start = range[:col_start]
31
+ col_end = range[:col_end]
32
+ range = nil
33
+ end
34
+
35
+ if range
36
+ raise ArgumentError, "Invalid merge range format: '#{range}'. Expected format like 'A1:B2'." if strict_excel_mode && !range.match?(/\A[A-Za-z]{1,3}\d+(:[A-Za-z]{1,3}\d+)?\z/)
37
+
38
+ range
39
+ else
40
+ r_start = row || row_start || 0
41
+ r_end = row || row_end || 0
42
+ c_start = Elements::Cell.column_index(col_start || 0)
43
+ c_end = Elements::Cell.column_index(col_end || 0)
44
+ start_ref = "#{Elements::Cell.column_letter(c_start)}#{r_start + 1}"
45
+ end_ref = "#{Elements::Cell.column_letter(c_end)}#{r_end + 1}"
46
+ "#{start_ref}:#{end_ref}"
47
+ end
48
+ end
49
+
50
+ # Normalizes sheet protection options and hashes plain-text password if supplied.
51
+ #
52
+ # @param opts [Hash]
53
+ # @return [Hash]
54
+ #: (Hash[Symbol, untyped] opts) -> Hash[Symbol, untyped]
55
+ def self.normalize_protection_options(opts)
56
+ normalized = opts.dup
57
+ plain_password = normalized[:password]
58
+ needs_hash = plain_password.is_a?(String) && !plain_password.empty? &&
59
+ normalized[:algorithm_name].nil? && normalized[:hash_value].nil? &&
60
+ normalized[:salt_value].nil? && normalized[:spin_count].nil? &&
61
+ !plain_password.match?(/\A[0-9A-Fa-f]{4}\z/)
62
+ if needs_hash
63
+ normalized.delete(:password)
64
+ normalized.merge!(Ooxml::Utils.hash_password(plain_password))
65
+ end
66
+ normalized
67
+ end
68
+
69
+ # Normalizes column index or letter or Range to an array of 0-based column integers.
70
+ #
71
+ # @param index [Integer, String, Symbol, Range, Array]
72
+ # @return [Array<Integer>]
73
+ #: (untyped index) -> Array[Integer]
74
+ def self.normalize_column_indices(index)
75
+ case index
76
+ when Range, Array
77
+ index.map { |i| Elements::Cell.column_index(i) }
78
+ else
79
+ [Elements::Cell.column_index(index)]
80
+ end
81
+ end
82
+
83
+ # Normalizes page margins into a compact hash.
84
+ #
85
+ # @param left [Float, nil]
86
+ # @param right [Float, nil]
87
+ # @param top [Float, nil]
88
+ # @param bottom [Float, nil]
89
+ # @param header [Float, nil]
90
+ # @param footer [Float, nil]
91
+ # @return [Hash{Symbol => Float}]
92
+ #: (?left: Float?, ?right: Float?, ?top: Float?, ?bottom: Float?, ?header: Float?, ?footer: Float?) -> Hash[Symbol, Float]
93
+ def self.normalize_page_margins(left: nil, right: nil, top: nil, bottom: nil, header: nil, footer: nil)
94
+ { left: left, right: right, top: top, bottom: bottom, header: header, footer: footer }.compact
95
+ end
96
+
97
+ # Normalizes table options hash.
98
+ #
99
+ # @param ref [String]
100
+ # @param columns [Array<String>, Array<Hash>]
101
+ # @param name [String, nil]
102
+ # @param display_name [String, nil]
103
+ # @param style [String, nil]
104
+ # @param opts [Hash]
105
+ # @return [Hash]
106
+ #: (String ref, columns: untyped, ?name: String?, ?display_name: String?, ?style: String?, **untyped opts) -> Hash[Symbol, untyped]
107
+ def self.normalize_table_options(ref, columns:, name: nil, display_name: nil, style: nil, **opts)
108
+ tbl = { ref: ref, columns: columns }
109
+ tbl[:name] = name if name
110
+ tbl[:display_name] = display_name if display_name
111
+ tbl[:style] = style if style
112
+ tbl.merge!(opts)
113
+ tbl
114
+ end
115
+ end
116
+ end