xlsxrb 0.1.11 → 0.1.13
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 +4 -4
- data/CHANGELOG.md +86 -34
- data/README.md +26 -21
- data/lib/xlsxrb/chart_builder.rb +18 -18
- data/lib/xlsxrb/dsl_helpers.rb +175 -0
- data/lib/xlsxrb/elements/cell.rb +56 -22
- data/lib/xlsxrb/elements/column.rb +27 -3
- data/lib/xlsxrb/elements/row.rb +27 -8
- data/lib/xlsxrb/elements/workbook.rb +27 -14
- data/lib/xlsxrb/elements/worksheet.rb +17 -1
- data/lib/xlsxrb/ooxml/cfb.rb +9 -6
- data/lib/xlsxrb/ooxml/crypto/agile.rb +5 -5
- data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +784 -0
- data/lib/xlsxrb/ooxml/reader/listeners/drawing_listeners.rb +3478 -0
- data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +1796 -0
- data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +428 -0
- data/lib/xlsxrb/ooxml/reader/listeners.rb +4 -6446
- data/lib/xlsxrb/ooxml/shared_strings_parser.rb +3 -5
- data/lib/xlsxrb/ooxml/utils.rb +5 -6
- data/lib/xlsxrb/ooxml/workbook_writer.rb +5 -7
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +172 -9
- data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +1602 -0
- data/lib/xlsxrb/ooxml/writer/features_xml.rb +731 -0
- data/lib/xlsxrb/ooxml/writer/styles_xml.rb +348 -0
- data/lib/xlsxrb/ooxml/writer.rb +9 -2646
- data/lib/xlsxrb/ooxml/xml_builder.rb +31 -2
- data/lib/xlsxrb/ooxml/zip_generator.rb +26 -5
- data/lib/xlsxrb/ooxml/zip_reader.rb +364 -78
- data/lib/xlsxrb/ooxml/zip_writer.rb +32 -5
- data/lib/xlsxrb/stream_sheet.rb +94 -6
- data/lib/xlsxrb/stream_writer.rb +48 -115
- data/lib/xlsxrb/style_builder.rb +10 -1
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +1 -1
- data/lib/xlsxrb/worksheet_builder.rb +19 -81
- data/lib/xlsxrb.rb +449 -171
- data/sig/generated/xlsxrb/chart_builder.rbs +36 -36
- data/sig/generated/xlsxrb/dsl_helpers.rbs +92 -0
- data/sig/generated/xlsxrb/elements/cell.rbs +29 -0
- data/sig/generated/xlsxrb/elements/column.rbs +32 -21
- data/sig/generated/xlsxrb/elements/row.rbs +94 -14
- data/sig/generated/xlsxrb/elements/workbook.rbs +102 -10
- data/sig/generated/xlsxrb/elements/worksheet.rbs +9 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/core_listeners.rbs +173 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/drawing_listeners.rbs +400 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/feature_listeners.rbs +442 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +80 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners.rbs +0 -1072
- data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +0 -2
- data/sig/generated/xlsxrb/ooxml/utils.rbs +2 -0
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +5 -4
- data/sig/generated/xlsxrb/ooxml/writer/drawing_xml.rbs +79 -0
- data/sig/generated/xlsxrb/ooxml/writer/features_xml.rbs +83 -0
- data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +53 -0
- data/sig/generated/xlsxrb/ooxml/writer.rbs +6 -185
- data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +18 -0
- data/sig/generated/xlsxrb/ooxml/zip_generator.rbs +15 -0
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +96 -8
- data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +5 -1
- data/sig/generated/xlsxrb/stream_sheet.rbs +43 -3
- data/sig/generated/xlsxrb/style_builder.rbs +7 -0
- data/sig/generated/xlsxrb.rbs +18 -11
- metadata +22 -144
- data/.devcontainer/Dockerfile +0 -65
- data/.devcontainer/devcontainer.json +0 -17
- data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/ast/2.4/ast.rbs +0 -73
- data/.gem_rbs_collection/concurrent-ruby/1.1/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/concurrent-ruby/1.1/array.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/atomic_reference.rbs +0 -16
- data/.gem_rbs_collection/concurrent-ruby/1.1/executor.rbs +0 -96
- data/.gem_rbs_collection/concurrent-ruby/1.1/hash.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/map.rbs +0 -68
- data/.gem_rbs_collection/concurrent-ruby/1.1/promises.rbs +0 -249
- data/.gem_rbs_collection/concurrent-ruby/1.1/set.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/timer_task.rbs +0 -47
- data/.gem_rbs_collection/concurrent-ruby/1.1/utility/processor_counter.rbs +0 -5
- data/.gem_rbs_collection/csv/3.3/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/csv/3.3/csv.rbs +0 -3871
- data/.gem_rbs_collection/csv/3.3/manifest.yaml +0 -3
- data/.gem_rbs_collection/lint_roller/1.1/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/lint_roller/1.1/lint_roller.rbs +0 -48
- data/.gem_rbs_collection/listen/3.9/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/listen/3.9/listen.rbs +0 -25
- data/.gem_rbs_collection/listen/3.9/listener.rbs +0 -24
- data/.gem_rbs_collection/logger/1.7/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/logger/1.7/formatter.rbs +0 -45
- data/.gem_rbs_collection/logger/1.7/log_device.rbs +0 -100
- data/.gem_rbs_collection/logger/1.7/logger.rbs +0 -796
- data/.gem_rbs_collection/logger/1.7/manifest.yaml +0 -2
- data/.gem_rbs_collection/logger/1.7/period.rbs +0 -17
- data/.gem_rbs_collection/logger/1.7/severity.rbs +0 -34
- data/.gem_rbs_collection/nokogiri/1.11/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/nokogiri/1.11/nokogiri.rbs +0 -2332
- data/.gem_rbs_collection/nokogiri/1.11/patch.rbs +0 -4
- data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parallel/1.20/parallel.rbs +0 -86
- data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parser/3.2/manifest.yaml +0 -7
- data/.gem_rbs_collection/parser/3.2/parser.rbs +0 -194
- data/.gem_rbs_collection/parser/3.2/polyfill.rbs +0 -4
- data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rainbow/3.0/global.rbs +0 -7
- data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +0 -209
- data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +0 -5
- data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rake/13.0/manifest.yaml +0 -2
- data/.gem_rbs_collection/rake/13.0/rake.rbs +0 -39
- data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +0 -17
- data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +0 -208
- data/.gem_rbs_collection/rubocop-ast/1.46/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop-ast/1.46/rubocop-ast.rbs +0 -903
- data/.gem_rbs_collection/rubyzip/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubyzip/3.2/manifest.yaml +0 -8
- data/.gem_rbs_collection/rubyzip/3.2/zip/central_directory.rbs +0 -42
- data/.gem_rbs_collection/rubyzip/3.2/zip/compressor.rbs +0 -5
- data/.gem_rbs_collection/rubyzip/3.2/zip/constants.rbs +0 -47
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/aes_encryption.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/decrypted_io.rbs +0 -9
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/encryption.rbs +0 -7
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/null_encryption.rbs +0 -19
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/traditional_encryption.rbs +0 -31
- data/.gem_rbs_collection/rubyzip/3.2/zip/decompressor.rbs +0 -18
- data/.gem_rbs_collection/rubyzip/3.2/zip/deflater.rbs +0 -12
- data/.gem_rbs_collection/rubyzip/3.2/zip/dirtyable.rbs +0 -11
- data/.gem_rbs_collection/rubyzip/3.2/zip/dos_time.rbs +0 -13
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry.rbs +0 -95
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry_set.rbs +0 -31
- data/.gem_rbs_collection/rubyzip/3.2/zip/errors.rbs +0 -58
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/aes.rbs +0 -24
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/generic.rbs +0 -17
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/ntfs.rbs +0 -23
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/old_unix.rbs +0 -22
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/universal_time.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unix.rbs +0 -20
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unknown.rbs +0 -15
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/zip64.rbs +0 -26
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field.rbs +0 -21
- data/.gem_rbs_collection/rubyzip/3.2/zip/file.rbs +0 -131
- data/.gem_rbs_collection/rubyzip/3.2/zip/file_split.rbs +0 -14
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/dir.rbs +0 -33
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/directory_iterator.rbs +0 -21
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file.rbs +0 -63
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file_stat.rbs +0 -55
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/zip_file_name_mapper.rbs +0 -35
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem.rbs +0 -7
- data/.gem_rbs_collection/rubyzip/3.2/zip/inflater.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/input_stream.rbs +0 -22
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_input_stream.rbs +0 -29
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_output_stream.rbs +0 -17
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras.rbs +0 -13
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_compressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_decompressor.rbs +0 -8
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_input_stream.rbs +0 -6
- data/.gem_rbs_collection/rubyzip/3.2/zip/output_stream.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_compressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_decompressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_directory.rbs +0 -5
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_stream.rbs +0 -15
- data/.gem_rbs_collection/rubyzip/3.2/zip/version.rbs +0 -3
- data/.gem_rbs_collection/rubyzip/3.2/zip.rbs +0 -40
- data/Rakefile +0 -567
- data/Steepfile +0 -18
- data/benchmark.rb +0 -406
- data/docs/ARCHITECTURE.md +0 -514
- data/docs/DEVELOPMENT.md +0 -133
- data/docs/PEER_LIBRARIES.md +0 -121
- data/docs/QUALITY_ASSURANCE.md +0 -25
- data/docs/SPEC_SOURCES.md +0 -48
- data/docs/assets/benchmark_results.svg +0 -144
- data/docs/assets/lsp_autocompletion.png +0 -0
- data/docs/assets/playground_preview.png +0 -0
- data/docs/coi-serviceworker.js +0 -82
- data/docs/office_thread.js +0 -77
- data/docs/preview.html +0 -719
- data/docs/visual/VisualGallery.md +0 -4672
- data/docs/wasm/ruby.wasm +0 -0
- data/docs/wasm/wasm_doc_helper.css +0 -351
- data/docs/wasm/wasm_doc_helper.js +0 -439
- data/docs/zeta.js +0 -1107
- data/rbs_collection.lock.yaml +0 -252
- data/rbs_collection.yaml +0 -19
- data/vendor/sdk_runner/Program.cs +0 -93
- data/vendor/sdk_runner/sdk_runner.csproj +0 -13
|
@@ -109,7 +109,33 @@ module Xlsxrb
|
|
|
109
109
|
compressed_size: @entry_compressed_size,
|
|
110
110
|
uncompressed_size: @entry_uncompressed_size,
|
|
111
111
|
offset: @entry_offset,
|
|
112
|
-
gp_flag: @entry_gp_flag
|
|
112
|
+
gp_flag: @entry_gp_flag,
|
|
113
|
+
method: 8
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Copies an entry from an existing archive IO without decompressing or recompressing.
|
|
118
|
+
#: (String path, untyped source_io, data_offset: Integer, compressed_size: Integer, uncompressed_size: Integer, crc32: Integer, ?method: Integer) -> void
|
|
119
|
+
def copy_raw_entry(path, source_io, data_offset:, compressed_size:, uncompressed_size:, crc32:, method: 8)
|
|
120
|
+
raise "ZipWriter is closed" if @closed
|
|
121
|
+
|
|
122
|
+
offset = @bytes_written
|
|
123
|
+
write_local_header(path, crc32, compressed_size, uncompressed_size, gp_flag: 0, method: method)
|
|
124
|
+
|
|
125
|
+
if compressed_size.positive?
|
|
126
|
+
source_io.seek(data_offset, IO::SEEK_SET)
|
|
127
|
+
IO.copy_stream(source_io, @io, compressed_size)
|
|
128
|
+
@bytes_written += compressed_size
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
@entries << {
|
|
132
|
+
path: path,
|
|
133
|
+
crc32: crc32,
|
|
134
|
+
compressed_size: compressed_size,
|
|
135
|
+
uncompressed_size: uncompressed_size,
|
|
136
|
+
offset: offset,
|
|
137
|
+
gp_flag: 0,
|
|
138
|
+
method: method
|
|
113
139
|
}
|
|
114
140
|
end
|
|
115
141
|
|
|
@@ -159,7 +185,8 @@ module Xlsxrb
|
|
|
159
185
|
compressed_size: compressed.bytesize,
|
|
160
186
|
uncompressed_size: content_bytes.bytesize,
|
|
161
187
|
offset: offset,
|
|
162
|
-
gp_flag: 0
|
|
188
|
+
gp_flag: 0,
|
|
189
|
+
method: 8
|
|
163
190
|
}
|
|
164
191
|
end
|
|
165
192
|
|
|
@@ -172,13 +199,13 @@ module Xlsxrb
|
|
|
172
199
|
end
|
|
173
200
|
end
|
|
174
201
|
|
|
175
|
-
def write_local_header(path, crc, compressed_size, uncompressed_size, gp_flag: 0, count_bytes: true)
|
|
202
|
+
def write_local_header(path, crc, compressed_size, uncompressed_size, gp_flag: 0, count_bytes: true, method: 8)
|
|
176
203
|
name_bytes = path.encode("UTF-8").b
|
|
177
204
|
header = [
|
|
178
205
|
0x04034B50, # local file header signature
|
|
179
206
|
20, # version needed (2.0)
|
|
180
207
|
gp_flag, # general purpose bit flag (0x0008 if data descriptor follows)
|
|
181
|
-
|
|
208
|
+
method, # compression method (0 = store, 8 = deflate)
|
|
182
209
|
0, # last mod file time
|
|
183
210
|
33, # last mod file date
|
|
184
211
|
crc,
|
|
@@ -206,7 +233,7 @@ module Xlsxrb
|
|
|
206
233
|
20, # version made by
|
|
207
234
|
20, # version needed
|
|
208
235
|
entry[:gp_flag] || 0, # general purpose bit flag
|
|
209
|
-
8,
|
|
236
|
+
entry[:method] || 8, # compression method
|
|
210
237
|
0, # last mod file time
|
|
211
238
|
33, # last mod file date
|
|
212
239
|
entry[:crc32],
|
data/lib/xlsxrb/stream_sheet.rb
CHANGED
|
@@ -36,15 +36,20 @@ module Xlsxrb
|
|
|
36
36
|
# Initializes a streaming worksheet context.
|
|
37
37
|
#
|
|
38
38
|
# @param name [String] The sheet name.
|
|
39
|
-
# @param
|
|
39
|
+
# @param sheet_source [String, Proc, IO, nil] Raw XML content or chunk supplier/stream.
|
|
40
40
|
# @param shared_strings [Array<String>] Shared strings table.
|
|
41
41
|
# @param styles [Hash, nil] Optional parsed styles hash.
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
# @param zip_reader [Ooxml::ZipReader, nil] Optional ZipReader context.
|
|
43
|
+
# @param entry_name [String, nil] Archive entry name for this sheet.
|
|
44
|
+
#: (String name, untyped sheet_source, Array[String] shared_strings, ?Hash[untyped, untyped]? styles, ?zip_reader: Ooxml::ZipReader?, ?entry_name: String?) -> void
|
|
45
|
+
def initialize(name, sheet_source, shared_strings, styles = nil, zip_reader: nil, entry_name: nil)
|
|
44
46
|
@name = name
|
|
45
|
-
@
|
|
47
|
+
@sheet_source = sheet_source
|
|
46
48
|
@shared_strings = shared_strings
|
|
47
49
|
@styles = styles
|
|
50
|
+
@zip_reader = zip_reader
|
|
51
|
+
@entry_name = entry_name
|
|
52
|
+
@sheet_xml = sheet_source if sheet_source.is_a?(String)
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
# Iterates over rows in this streaming worksheet with O(1) memory.
|
|
@@ -63,7 +68,13 @@ module Xlsxrb
|
|
|
63
68
|
def each_row(&)
|
|
64
69
|
return enum_for(:each_row) unless block_given?
|
|
65
70
|
|
|
66
|
-
|
|
71
|
+
source = if @sheet_source
|
|
72
|
+
@sheet_source
|
|
73
|
+
elsif @zip_reader && @entry_name
|
|
74
|
+
->(&blk) { @zip_reader.each_entry_chunk(@entry_name, &blk) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
Ooxml::WorksheetParser.each_row(source, shared_strings: @shared_strings, &)
|
|
67
78
|
end
|
|
68
79
|
|
|
69
80
|
# Iterates over all cells across all rows continuously with O(1) memory.
|
|
@@ -112,8 +123,85 @@ module Xlsxrb
|
|
|
112
123
|
# @api public
|
|
113
124
|
#: () -> Elements::Worksheet
|
|
114
125
|
def load
|
|
115
|
-
Xlsxrb.send(:build_worksheet, @name,
|
|
126
|
+
Xlsxrb.send(:build_worksheet, @name, raw_sheet_xml, @shared_strings, @styles)
|
|
116
127
|
end
|
|
117
128
|
alias to_worksheet load
|
|
129
|
+
|
|
130
|
+
# Returns merged cell ranges (e.g. ["A1:B2"]) for this worksheet.
|
|
131
|
+
#
|
|
132
|
+
# @return [Array<String>]
|
|
133
|
+
# @api public
|
|
134
|
+
#: () -> Array[String]
|
|
135
|
+
def merged_cells
|
|
136
|
+
@merged_cells ||= begin
|
|
137
|
+
listener = Ooxml::Reader::MergeCellsListener.new
|
|
138
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
139
|
+
listener.ranges
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns the auto-filter range (e.g. "A1:E100") for this worksheet, or nil if none.
|
|
144
|
+
#
|
|
145
|
+
# @return [String, nil]
|
|
146
|
+
# @api public
|
|
147
|
+
#: () -> String?
|
|
148
|
+
def auto_filter
|
|
149
|
+
@auto_filter ||= begin
|
|
150
|
+
listener = Ooxml::Reader::AutoFilterListener.new
|
|
151
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
152
|
+
listener.ref
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Returns data validation rules configured for this worksheet.
|
|
157
|
+
#
|
|
158
|
+
# @return [Array<Hash[Symbol, untyped]>]
|
|
159
|
+
# @api public
|
|
160
|
+
#: () -> Array[Hash[Symbol, untyped]]
|
|
161
|
+
def data_validations
|
|
162
|
+
@data_validations ||= begin
|
|
163
|
+
listener = Ooxml::Reader::DataValidationsListener.new
|
|
164
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
165
|
+
listener.validations
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Returns conditional formatting rules configured for this worksheet.
|
|
170
|
+
#
|
|
171
|
+
# @return [Array<Hash[Symbol, untyped]>]
|
|
172
|
+
# @api public
|
|
173
|
+
#: () -> Array[Hash[Symbol, untyped]]
|
|
174
|
+
def conditional_formats
|
|
175
|
+
@conditional_formats ||= begin
|
|
176
|
+
listener = Ooxml::Reader::ConditionalFormattingListener.new
|
|
177
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
178
|
+
listener.rules
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Closes any underlying reader resources.
|
|
183
|
+
#: () -> void
|
|
184
|
+
def close
|
|
185
|
+
@zip_reader&.close
|
|
186
|
+
nil
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
private
|
|
190
|
+
|
|
191
|
+
# Returns the full raw XML string for this worksheet, loaded on demand.
|
|
192
|
+
#: () -> String
|
|
193
|
+
def raw_sheet_xml
|
|
194
|
+
@raw_sheet_xml ||= if @sheet_source.is_a?(String)
|
|
195
|
+
@sheet_source
|
|
196
|
+
elsif @zip_reader && @entry_name
|
|
197
|
+
@zip_reader.read_entry(@entry_name) || ""
|
|
198
|
+
elsif @sheet_source.respond_to?(:call)
|
|
199
|
+
buf = +""
|
|
200
|
+
@sheet_source.call { |c| buf << c }
|
|
201
|
+
buf
|
|
202
|
+
else
|
|
203
|
+
""
|
|
204
|
+
end
|
|
205
|
+
end
|
|
118
206
|
end
|
|
119
207
|
end
|
data/lib/xlsxrb/stream_writer.rb
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
require "tempfile"
|
|
6
6
|
require_relative "style_builder"
|
|
7
7
|
require_relative "chart_builder"
|
|
8
|
+
require_relative "dsl_helpers"
|
|
8
9
|
require_relative "ooxml/writer"
|
|
9
10
|
require_relative "ooxml/worksheet_writer"
|
|
10
11
|
require_relative "ooxml/workbook_writer"
|
|
@@ -910,39 +911,11 @@ module Xlsxrb
|
|
|
910
911
|
sheet if @current_sheet.nil?
|
|
911
912
|
|
|
912
913
|
row_index = @current_row_index
|
|
913
|
-
|
|
914
|
-
if @strict_excel_mode
|
|
915
|
-
raise ArgumentError, "Row index #{row_index} exceeds Excel limit of 1,048,576 rows" if row_index >= 1_048_576
|
|
916
|
-
raise ArgumentError, "Row height #{height} must be between 0 and 409 points (Excel limitation)" if height && (height.negative? || height > 409)
|
|
917
|
-
end
|
|
914
|
+
DslHelpers.validate_row_bounds!(row_index, height, strict_excel_mode: @strict_excel_mode)
|
|
918
915
|
@current_row_index += 1
|
|
919
916
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
cells_array = Array.new(max_col + 1)
|
|
923
|
-
values.each do |k, v|
|
|
924
|
-
idx = Elements::Cell.column_index(k)
|
|
925
|
-
cells_array[idx] = v
|
|
926
|
-
end
|
|
927
|
-
values = cells_array
|
|
928
|
-
end
|
|
929
|
-
|
|
930
|
-
if styles.is_a?(Hash)
|
|
931
|
-
expanded_styles = {}
|
|
932
|
-
styles.each do |k, v|
|
|
933
|
-
if k.is_a?(Range) || k.is_a?(Array)
|
|
934
|
-
k.each { |idx| expanded_styles[Elements::Cell.column_index(idx)] = v }
|
|
935
|
-
else
|
|
936
|
-
expanded_styles[Elements::Cell.column_index(k)] = v
|
|
937
|
-
end
|
|
938
|
-
end
|
|
939
|
-
max_col_style = expanded_styles.keys.max || -1
|
|
940
|
-
styles_array = Array.new(max_col_style + 1)
|
|
941
|
-
expanded_styles.each do |idx, v|
|
|
942
|
-
styles_array[idx] = v
|
|
943
|
-
end
|
|
944
|
-
styles = styles_array
|
|
945
|
-
end
|
|
917
|
+
values = DslHelpers.normalize_row_values(values)
|
|
918
|
+
styles = DslHelpers.normalize_row_styles(styles)
|
|
946
919
|
|
|
947
920
|
if @current_charts && !@current_charts.empty?
|
|
948
921
|
@current_cells ||= {}
|
|
@@ -984,16 +957,9 @@ module Xlsxrb
|
|
|
984
957
|
def column(index, width: nil, hidden: false, custom_width: false, outline_level: nil)
|
|
985
958
|
raise ArgumentError, "Column width #{width} must be between 0 and 255 characters (Excel limitation)" if @strict_excel_mode && width && (width.negative? || width > 255)
|
|
986
959
|
|
|
987
|
-
indices = case index
|
|
988
|
-
when Range, Array
|
|
989
|
-
index.map { |i| Elements::Cell.column_index(i) }
|
|
990
|
-
else
|
|
991
|
-
[Elements::Cell.column_index(index)]
|
|
992
|
-
end
|
|
993
|
-
|
|
994
960
|
sheet if @current_sheet.nil?
|
|
995
961
|
|
|
996
|
-
|
|
962
|
+
DslHelpers.normalize_column_indices(index).each do |idx|
|
|
997
963
|
@current_columns << { index: idx, width: width, hidden: hidden, custom_width: custom_width || !width.nil?, outline_level: outline_level }
|
|
998
964
|
end
|
|
999
965
|
end
|
|
@@ -1109,14 +1075,9 @@ module Xlsxrb
|
|
|
1109
1075
|
# @return [void]
|
|
1110
1076
|
# @api public
|
|
1111
1077
|
#: (untyped ref, columns: untyped, ?name: untyped, ?display_name: untyped, ?style: untyped, **untyped opts) -> void
|
|
1112
|
-
def table(ref, columns:, name: nil, display_name: nil, style: nil, **
|
|
1078
|
+
def table(ref, columns:, name: nil, display_name: nil, style: nil, **)
|
|
1113
1079
|
sheet if @current_sheet.nil?
|
|
1114
|
-
|
|
1115
|
-
tbl[:name] = name if name
|
|
1116
|
-
tbl[:display_name] = display_name if display_name
|
|
1117
|
-
tbl[:style] = style if style
|
|
1118
|
-
tbl.merge!(opts)
|
|
1119
|
-
@current_tables << tbl
|
|
1080
|
+
@current_tables << DslHelpers.normalize_table_options(ref, columns: columns, name: name, display_name: display_name, style: style, **)
|
|
1120
1081
|
end
|
|
1121
1082
|
|
|
1122
1083
|
# Adds a Pivot Table.
|
|
@@ -1186,28 +1147,13 @@ module Xlsxrb
|
|
|
1186
1147
|
#: (?(String | Hash[Symbol, Integer | String])? range, ?row: Integer?, ?col_start: (Integer | String)?, ?col_end: (Integer | String)?, ?row_start: Integer?, ?row_end: Integer?) -> void
|
|
1187
1148
|
def merge(range = nil, row: nil, col_start: nil, col_end: nil, row_start: nil, row_end: nil)
|
|
1188
1149
|
sheet if @current_sheet.nil?
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
end
|
|
1197
|
-
|
|
1198
|
-
if range
|
|
1199
|
-
raise ArgumentError, "Invalid merge range format: '#{range}'. Expected format like 'A1:B2'." if @strict_excel_mode && !range.match?(/^[A-Za-z]{1,3}\d+(:[A-Za-z]{1,3}\d+)?$/)
|
|
1200
|
-
|
|
1201
|
-
@current_merge_cells << range
|
|
1202
|
-
else
|
|
1203
|
-
r_start = row || row_start || 0
|
|
1204
|
-
r_end = row || row_end || 0
|
|
1205
|
-
c_start = Elements::Cell.column_index(col_start || 0)
|
|
1206
|
-
c_end = Elements::Cell.column_index(col_end || 0)
|
|
1207
|
-
start_ref = "#{Xlsxrb::Elements::Cell.column_letter(c_start)}#{r_start + 1}"
|
|
1208
|
-
end_ref = "#{Xlsxrb::Elements::Cell.column_letter(c_end)}#{r_end + 1}"
|
|
1209
|
-
@current_merge_cells << "#{start_ref}:#{end_ref}"
|
|
1210
|
-
end
|
|
1150
|
+
canonical = DslHelpers.normalize_merge_range(
|
|
1151
|
+
range,
|
|
1152
|
+
row: row, col_start: col_start, col_end: col_end,
|
|
1153
|
+
row_start: row_start, row_end: row_end,
|
|
1154
|
+
strict_excel_mode: @strict_excel_mode
|
|
1155
|
+
)
|
|
1156
|
+
@current_merge_cells << canonical unless @current_merge_cells.include?(canonical)
|
|
1211
1157
|
end
|
|
1212
1158
|
|
|
1213
1159
|
# Freezes window panes at the given row and column.
|
|
@@ -1263,7 +1209,9 @@ module Xlsxrb
|
|
|
1263
1209
|
#: (?left: Float?, ?right: Float?, ?top: Float?, ?bottom: Float?, ?header: Float?, ?footer: Float?) -> void
|
|
1264
1210
|
def page_margins(left: nil, right: nil, top: nil, bottom: nil, header: nil, footer: nil)
|
|
1265
1211
|
sheet if @current_sheet.nil?
|
|
1266
|
-
@current_page_margins =
|
|
1212
|
+
@current_page_margins = DslHelpers.normalize_page_margins(
|
|
1213
|
+
left: left, right: right, top: top, bottom: bottom, header: header, footer: footer
|
|
1214
|
+
)
|
|
1267
1215
|
end
|
|
1268
1216
|
|
|
1269
1217
|
# Sets page setup configuration.
|
|
@@ -1308,17 +1256,7 @@ module Xlsxrb
|
|
|
1308
1256
|
#: (**untyped opts) -> void
|
|
1309
1257
|
def protect_sheet(**opts)
|
|
1310
1258
|
sheet if @current_sheet.nil?
|
|
1311
|
-
|
|
1312
|
-
plain_password = normalized[:password]
|
|
1313
|
-
needs_hash = plain_password.is_a?(String) && !plain_password.empty? &&
|
|
1314
|
-
normalized[:algorithm_name].nil? && normalized[:hash_value].nil? &&
|
|
1315
|
-
normalized[:salt_value].nil? && normalized[:spin_count].nil? &&
|
|
1316
|
-
!plain_password.match?(/\A[0-9A-Fa-f]{4}\z/)
|
|
1317
|
-
if needs_hash
|
|
1318
|
-
normalized.delete(:password)
|
|
1319
|
-
normalized.merge!(Xlsxrb::Ooxml::Utils.hash_password(plain_password))
|
|
1320
|
-
end
|
|
1321
|
-
@current_sheet_protection = normalized
|
|
1259
|
+
@current_sheet_protection = DslHelpers.normalize_protection_options(opts)
|
|
1322
1260
|
end
|
|
1323
1261
|
|
|
1324
1262
|
# Inserts an embedded image into the active sheet.
|
|
@@ -1543,37 +1481,35 @@ module Xlsxrb
|
|
|
1543
1481
|
def close
|
|
1544
1482
|
raise ArgumentError, "Workbook must contain at least one sheet (Excel limitation)" if @strict_excel_mode && @sheets.empty? && @current_sheet.nil?
|
|
1545
1483
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
@io.close if @owns_io && !@io.closed?
|
|
1576
|
-
end
|
|
1484
|
+
flush_current_sheet
|
|
1485
|
+
|
|
1486
|
+
styles_definition = {
|
|
1487
|
+
fonts: @style_writer.fonts.dup,
|
|
1488
|
+
fills: @style_writer.fills.dup,
|
|
1489
|
+
borders: @style_writer.borders.dup,
|
|
1490
|
+
xf_entries: @style_writer.xf_entries.dup,
|
|
1491
|
+
num_fmts: @style_writer.num_fmts.dup,
|
|
1492
|
+
dxfs: @dxfs || []
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
resolved_names = resolve_defined_names(@defined_names, @sheets)
|
|
1496
|
+
|
|
1497
|
+
wb_writer = Ooxml::WorkbookWriter.new(
|
|
1498
|
+
sheets: @sheets,
|
|
1499
|
+
shared_strings: @sst,
|
|
1500
|
+
shared_strings_index: @sst_index,
|
|
1501
|
+
styles: styles_definition,
|
|
1502
|
+
defined_names: resolved_names.empty? ? nil : resolved_names,
|
|
1503
|
+
core_properties: @core_properties.empty? ? nil : @core_properties,
|
|
1504
|
+
app_properties: @app_properties.empty? ? nil : @app_properties,
|
|
1505
|
+
custom_properties: @custom_properties.empty? ? nil : @custom_properties,
|
|
1506
|
+
workbook_protection: @workbook_protection,
|
|
1507
|
+
workbook_properties: @workbook_properties
|
|
1508
|
+
)
|
|
1509
|
+
wb_writer.write_package_parts(@zip)
|
|
1510
|
+
|
|
1511
|
+
@zip.close
|
|
1512
|
+
@io.close if @owns_io && !@io.closed?
|
|
1577
1513
|
ensure
|
|
1578
1514
|
cleanup!
|
|
1579
1515
|
end
|
|
@@ -1604,10 +1540,7 @@ module Xlsxrb
|
|
|
1604
1540
|
|
|
1605
1541
|
#: (untyped range) -> untyped
|
|
1606
1542
|
def absolute_range(range)
|
|
1607
|
-
|
|
1608
|
-
# Edge case / untested delegation block
|
|
1609
|
-
range.gsub(/([A-Z]+)(\d+)/, '$\1$\2')
|
|
1610
|
-
# simplecov:enable
|
|
1543
|
+
DslHelpers.absolute_range(range)
|
|
1611
1544
|
end
|
|
1612
1545
|
|
|
1613
1546
|
#: (untyped names, untyped sheets) -> untyped
|
data/lib/xlsxrb/style_builder.rb
CHANGED
|
@@ -20,7 +20,12 @@ module Xlsxrb
|
|
|
20
20
|
grey: "FF808080"
|
|
21
21
|
}.freeze
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
# Resolves a color symbol, string, or prefix to an aRGB hex string.
|
|
24
|
+
#
|
|
25
|
+
# @param color [Symbol, String, Object, nil]
|
|
26
|
+
# @return [String, nil]
|
|
27
|
+
#: (untyped color) -> String?
|
|
28
|
+
def self.resolve_color(color)
|
|
24
29
|
return nil unless color
|
|
25
30
|
|
|
26
31
|
if color.is_a?(Symbol) || (color.is_a?(String) && color.start_with?(":") && color.length > 1)
|
|
@@ -30,6 +35,10 @@ module Xlsxrb
|
|
|
30
35
|
color.to_s
|
|
31
36
|
end
|
|
32
37
|
|
|
38
|
+
def resolve_color(color)
|
|
39
|
+
self.class.resolve_color(color)
|
|
40
|
+
end
|
|
41
|
+
|
|
33
42
|
#: (?String? name) -> void
|
|
34
43
|
def initialize(name = nil)
|
|
35
44
|
@name = name
|
data/lib/xlsxrb/version.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# rbs_inline: enabled
|
|
4
4
|
|
|
5
|
+
require_relative "dsl_helpers"
|
|
6
|
+
|
|
5
7
|
module Xlsxrb
|
|
6
8
|
# DSL context for building a single in-memory worksheet in {Xlsxrb.build}.
|
|
7
9
|
#
|
|
@@ -85,38 +87,10 @@ module Xlsxrb
|
|
|
85
87
|
#: (Array[untyped] | Hash[untyped, untyped] values, ?styles: untyped, ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) -> void
|
|
86
88
|
def row(values, styles: nil, height: nil, hidden: false, custom_height: false, outline_level: nil)
|
|
87
89
|
row_index = @rows.size
|
|
88
|
-
|
|
89
|
-
if @strict_excel_mode
|
|
90
|
-
raise ArgumentError, "Row index #{row_index} exceeds Excel limit of 1,048,576 rows" if row_index >= 1_048_576
|
|
91
|
-
raise ArgumentError, "Row height #{height} must be between 0 and 409 points (Excel limitation)" if height && (height.negative? || height > 409)
|
|
92
|
-
end
|
|
90
|
+
DslHelpers.validate_row_bounds!(row_index, height, strict_excel_mode: @strict_excel_mode)
|
|
93
91
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
cells_array = Array.new(max_col + 1)
|
|
97
|
-
values.each do |k, v|
|
|
98
|
-
idx = Elements::Cell.column_index(k)
|
|
99
|
-
cells_array[idx] = v
|
|
100
|
-
end
|
|
101
|
-
values = cells_array
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
if styles.is_a?(Hash)
|
|
105
|
-
expanded_styles = {}
|
|
106
|
-
styles.each do |k, v|
|
|
107
|
-
if k.is_a?(Range) || k.is_a?(Array)
|
|
108
|
-
k.each { |idx| expanded_styles[Elements::Cell.column_index(idx)] = v }
|
|
109
|
-
else
|
|
110
|
-
expanded_styles[Elements::Cell.column_index(k)] = v
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
max_col_style = expanded_styles.keys.max || -1
|
|
114
|
-
styles_array = Array.new(max_col_style + 1)
|
|
115
|
-
expanded_styles.each do |idx, v|
|
|
116
|
-
styles_array[idx] = v
|
|
117
|
-
end
|
|
118
|
-
styles = styles_array
|
|
119
|
-
end
|
|
92
|
+
values = DslHelpers.normalize_row_values(values)
|
|
93
|
+
styles = DslHelpers.normalize_row_styles(styles)
|
|
120
94
|
|
|
121
95
|
# Auto-detect Date / Time for built-in styles
|
|
122
96
|
values.each_with_index do |val, idx|
|
|
@@ -239,14 +213,7 @@ module Xlsxrb
|
|
|
239
213
|
def column(index, width: nil, hidden: false, custom_width: false, outline_level: nil)
|
|
240
214
|
raise ArgumentError, "Column width #{width} must be between 0 and 255 characters (Excel limitation)" if @strict_excel_mode && width && (width.negative? || width > 255)
|
|
241
215
|
|
|
242
|
-
|
|
243
|
-
when Range, Array
|
|
244
|
-
index.map { |i| Elements::Cell.column_index(i) }
|
|
245
|
-
else
|
|
246
|
-
[Elements::Cell.column_index(index)]
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
indices.each do |idx|
|
|
216
|
+
DslHelpers.normalize_column_indices(index).each do |idx|
|
|
250
217
|
@columns << Elements::Column.new(
|
|
251
218
|
index: idx,
|
|
252
219
|
width: width,
|
|
@@ -359,13 +326,8 @@ module Xlsxrb
|
|
|
359
326
|
# @return [void]
|
|
360
327
|
# @api public
|
|
361
328
|
#: (String ref, columns: Array[String | Hash[Symbol, untyped]], ?name: String?, ?display_name: String?, ?style: String?, **untyped opts) -> void
|
|
362
|
-
def table(ref, columns:, name: nil, display_name: nil, style: nil, **
|
|
363
|
-
|
|
364
|
-
tbl[:name] = name if name
|
|
365
|
-
tbl[:display_name] = display_name if display_name
|
|
366
|
-
tbl[:style] = style if style
|
|
367
|
-
tbl.merge!(opts)
|
|
368
|
-
@tables << tbl
|
|
329
|
+
def table(ref, columns:, name: nil, display_name: nil, style: nil, **)
|
|
330
|
+
@tables << DslHelpers.normalize_table_options(ref, columns: columns, name: name, display_name: display_name, style: style, **)
|
|
369
331
|
end
|
|
370
332
|
|
|
371
333
|
# Adds a Pivot Table to the sheet.
|
|
@@ -436,29 +398,13 @@ module Xlsxrb
|
|
|
436
398
|
# @api public
|
|
437
399
|
#: (?(String | Hash[Symbol, Integer | String])? range, ?row: Integer?, ?col_start: (Integer | String)?, ?col_end: (Integer | String)?, ?row_start: Integer?, ?row_end: Integer?) -> void
|
|
438
400
|
def merge(range = nil, row: nil, col_start: nil, col_end: nil, row_start: nil, row_end: nil)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
end
|
|
447
|
-
|
|
448
|
-
if range
|
|
449
|
-
raise ArgumentError, "Invalid merge range format: '#{range}'. Expected format like 'A1:B2'." if @strict_excel_mode && !range.match?(/^[A-Za-z]{1,3}\d+(:[A-Za-z]{1,3}\d+)?$/)
|
|
450
|
-
return if @merge_cells_ranges.include?(range)
|
|
451
|
-
|
|
452
|
-
@merge_cells_ranges << range
|
|
453
|
-
else
|
|
454
|
-
r_start = row || row_start || 0
|
|
455
|
-
r_end = row || row_end || 0
|
|
456
|
-
c_start = Elements::Cell.column_index(col_start || 0)
|
|
457
|
-
c_end = Elements::Cell.column_index(col_end || 0)
|
|
458
|
-
start_ref = "#{Xlsxrb::Elements::Cell.column_letter(c_start)}#{r_start + 1}"
|
|
459
|
-
end_ref = "#{Xlsxrb::Elements::Cell.column_letter(c_end)}#{r_end + 1}"
|
|
460
|
-
@merge_cells_ranges << "#{start_ref}:#{end_ref}"
|
|
461
|
-
end
|
|
401
|
+
canonical = DslHelpers.normalize_merge_range(
|
|
402
|
+
range,
|
|
403
|
+
row: row, col_start: col_start, col_end: col_end,
|
|
404
|
+
row_start: row_start, row_end: row_end,
|
|
405
|
+
strict_excel_mode: @strict_excel_mode
|
|
406
|
+
)
|
|
407
|
+
@merge_cells_ranges << canonical unless @merge_cells_ranges.include?(canonical)
|
|
462
408
|
end
|
|
463
409
|
|
|
464
410
|
# Freezes window panes at the specified row and column.
|
|
@@ -510,7 +456,9 @@ module Xlsxrb
|
|
|
510
456
|
# @api public
|
|
511
457
|
#: (?left: Float?, ?right: Float?, ?top: Float?, ?bottom: Float?, ?header: Float?, ?footer: Float?) -> void
|
|
512
458
|
def page_margins(left: nil, right: nil, top: nil, bottom: nil, header: nil, footer: nil)
|
|
513
|
-
@page_margins =
|
|
459
|
+
@page_margins = DslHelpers.normalize_page_margins(
|
|
460
|
+
left: left, right: right, top: top, bottom: bottom, header: header, footer: footer
|
|
461
|
+
)
|
|
514
462
|
end
|
|
515
463
|
|
|
516
464
|
# Sets page setup configuration (orientation, paper size, fit to page, scaling).
|
|
@@ -551,17 +499,7 @@ module Xlsxrb
|
|
|
551
499
|
# @api public
|
|
552
500
|
#: (**untyped opts) -> void
|
|
553
501
|
def protect_sheet(**opts)
|
|
554
|
-
|
|
555
|
-
plain_password = normalized[:password]
|
|
556
|
-
needs_hash = plain_password.is_a?(String) && !plain_password.empty? &&
|
|
557
|
-
normalized[:algorithm_name].nil? && normalized[:hash_value].nil? &&
|
|
558
|
-
normalized[:salt_value].nil? && normalized[:spin_count].nil? &&
|
|
559
|
-
!plain_password.match?(/\A[0-9A-Fa-f]{4}\z/)
|
|
560
|
-
if needs_hash
|
|
561
|
-
normalized.delete(:password)
|
|
562
|
-
normalized.merge!(Xlsxrb::Ooxml::Utils.hash_password(plain_password))
|
|
563
|
-
end
|
|
564
|
-
@sheet_protection = normalized
|
|
502
|
+
@sheet_protection = DslHelpers.normalize_protection_options(opts)
|
|
565
503
|
end
|
|
566
504
|
|
|
567
505
|
# Inserts an embedded image from raw binary data.
|