spreadsheet 1.1.4 → 1.3.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 +5 -5
- data/Manifest.txt +1 -0
- data/bin/bundle +114 -0
- data/bin/bundler +17 -0
- data/bin/oletool +29 -0
- data/bin/rake +29 -0
- data/bin/sow +17 -0
- data/bin/xlsopcodes +12 -0
- data/lib/spreadsheet/excel/internals.rb +1 -1
- data/lib/spreadsheet/excel/reader/biff8.rb +1 -1
- data/lib/spreadsheet/excel/reader.rb +12 -10
- data/lib/spreadsheet/excel/workbook.rb +1 -2
- data/lib/spreadsheet/excel/writer/n_worksheet.rb +0 -75
- data/lib/spreadsheet/excel/writer/workbook.rb +5 -92
- data/lib/spreadsheet/excel/writer/worksheet.rb +3 -81
- data/lib/spreadsheet/excel.rb +0 -6
- data/lib/spreadsheet/format.rb +2 -2
- data/lib/spreadsheet/version.rb +7 -0
- data/lib/spreadsheet/workbook.rb +1 -1
- data/lib/spreadsheet/worksheet.rb +23 -13
- data/lib/spreadsheet.rb +2 -4
- data/test/data/test_compact_format_date.xls +0 -0
- data/test/data/test_compact_many_rows.xls +0 -0
- data/test/data/test_missing_format.xls +0 -0
- data/test/data/test_sizes.xls +0 -0
- data/test/excel/reader.rb +13 -0
- data/test/format.rb +2 -0
- data/test/integration.rb +36 -12
- data/test/suite.rb +2 -0
- data/test/worksheet.rb +26 -4
- metadata +48 -46
- data/.gitignore +0 -2
- data/.travis.yml +0 -35
- data/Excel97-2007BinaryFileFormatSpecification.pdf +0 -0
- data/GUIDE.md +0 -339
- data/Gemfile +0 -10
- data/Gemfile.lock +0 -23
- data/History.md +0 -746
- data/README.md +0 -113
- data/Rakefile +0 -24
- data/excelfileformat.pdf +0 -0
- data/spreadsheet.gemspec +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a831e5c252775858cbdba547a3ac3228028cb6c9a56a92a77ab8fdc393de0658
|
4
|
+
data.tar.gz: 1240cd6a3b8362fa9c807c26d36fd6f8df19872e195ce09e98583597f73fa8cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35dd4c09f32c793bf85f4290a497eb25a298b2f59e107eb4ecebf3c37e1e6a1ee505eaec3409057c0914754ca9036fb8a4c173c8d251e448ef932e38489d0c04
|
7
|
+
data.tar.gz: b20106050e1370ced689559af16e97efc4b19abde21165091d9f4a1ebea02758625713f09e20be0ccf7b8be2075fc59567c5bf581e583a2250ccc81ee04b2e5f
|
data/Manifest.txt
CHANGED
data/bin/bundle
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "rubygems"
|
12
|
+
|
13
|
+
m = Module.new do
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def invoked_as_script?
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def env_var_version
|
21
|
+
ENV["BUNDLER_VERSION"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def cli_arg_version
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
27
|
+
bundler_version = nil
|
28
|
+
update_index = nil
|
29
|
+
ARGV.each_with_index do |a, i|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
31
|
+
bundler_version = a
|
32
|
+
end
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
34
|
+
bundler_version = $1
|
35
|
+
update_index = i
|
36
|
+
end
|
37
|
+
bundler_version
|
38
|
+
end
|
39
|
+
|
40
|
+
def gemfile
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
43
|
+
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
45
|
+
end
|
46
|
+
|
47
|
+
def lockfile
|
48
|
+
lockfile =
|
49
|
+
case File.basename(gemfile)
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
51
|
+
else "#{gemfile}.lock"
|
52
|
+
end
|
53
|
+
File.expand_path(lockfile)
|
54
|
+
end
|
55
|
+
|
56
|
+
def lockfile_version
|
57
|
+
return unless File.file?(lockfile)
|
58
|
+
lockfile_contents = File.read(lockfile)
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
60
|
+
Regexp.last_match(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
def bundler_version
|
64
|
+
@bundler_version ||=
|
65
|
+
env_var_version || cli_arg_version ||
|
66
|
+
lockfile_version
|
67
|
+
end
|
68
|
+
|
69
|
+
def bundler_requirement
|
70
|
+
return "#{Gem::Requirement.default}.a" unless bundler_version
|
71
|
+
|
72
|
+
bundler_gem_version = Gem::Version.new(bundler_version)
|
73
|
+
|
74
|
+
requirement = bundler_gem_version.approximate_recommendation
|
75
|
+
|
76
|
+
return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
|
77
|
+
|
78
|
+
requirement += ".a" if bundler_gem_version.prerelease?
|
79
|
+
|
80
|
+
requirement
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_bundler!
|
84
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
85
|
+
|
86
|
+
activate_bundler
|
87
|
+
end
|
88
|
+
|
89
|
+
def activate_bundler
|
90
|
+
gem_error = activation_error_handling do
|
91
|
+
gem "bundler", bundler_requirement
|
92
|
+
end
|
93
|
+
return if gem_error.nil?
|
94
|
+
require_error = activation_error_handling do
|
95
|
+
require "bundler/version"
|
96
|
+
end
|
97
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
98
|
+
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
|
99
|
+
exit 42
|
100
|
+
end
|
101
|
+
|
102
|
+
def activation_error_handling
|
103
|
+
yield
|
104
|
+
nil
|
105
|
+
rescue StandardError, LoadError => e
|
106
|
+
e
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
m.load_bundler!
|
111
|
+
|
112
|
+
if m.invoked_as_script?
|
113
|
+
load Gem.bin_path("bundler", "bundle")
|
114
|
+
end
|
data/bin/bundler
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'bundler' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("bundler", "bundler")
|
data/bin/oletool
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'oletool' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("ruby-ole", "oletool")
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/sow
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'sow' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("hoe", "sow")
|
data/bin/xlsopcodes
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
|
+
|
3
4
|
#
|
4
5
|
# This file was generated by Bundler.
|
5
6
|
#
|
@@ -11,6 +12,17 @@ require "pathname"
|
|
11
12
|
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
13
|
Pathname.new(__FILE__).realpath)
|
13
14
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
14
26
|
require "rubygems"
|
15
27
|
require "bundler/setup"
|
16
28
|
|
@@ -54,7 +54,7 @@ module Internals
|
|
54
54
|
32768 => "MACROMAN",
|
55
55
|
32769 => "WINDOWS-1252", #(Latin I) (BIFF2-BIFF3)
|
56
56
|
}
|
57
|
-
SEGAPEDOC = CODEPAGES.invert
|
57
|
+
SEGAPEDOC = CODEPAGES.reject { |k, _v| k >= 21010 }.invert
|
58
58
|
# color_codes according to http://support.softartisans.com/kbview_1205.aspx
|
59
59
|
# synonyms are in comments when reverse lookup
|
60
60
|
COLOR_CODES = {
|
@@ -56,7 +56,7 @@ class Reader
|
|
56
56
|
## remove two bits
|
57
57
|
integer, = work.unpack 'V'
|
58
58
|
integer &= 0xfffffffc
|
59
|
-
value, = ("\0\0\0\0"
|
59
|
+
value, = ("\0\0\0\0" + [integer].pack('V')).unpack EIGHT_BYTE_DOUBLE
|
60
60
|
else
|
61
61
|
## I can't find a format for unpacking a little endian signed integer.
|
62
62
|
# 'V' works for packing, but not for unpacking. But the following works
|
@@ -125,11 +125,12 @@ class Reader
|
|
125
125
|
#So link the noteObject(text) to the note (with author, position)
|
126
126
|
#TODO
|
127
127
|
@noteList.each do |i|
|
128
|
-
|
129
|
-
if
|
128
|
+
matching_objs = @noteObjList.select { |j| j.objID == i.objID }
|
129
|
+
if matching_objs.length > 1
|
130
130
|
puts "ERROR - more than one matching object ID!"
|
131
131
|
end
|
132
|
-
|
132
|
+
matching_obj = matching_objs.first
|
133
|
+
i.text = matching_obj.nil? ? '' : matching_obj.text
|
133
134
|
worksheet.add_note i.row, i.col, i.text
|
134
135
|
end
|
135
136
|
end
|
@@ -398,10 +399,10 @@ class Reader
|
|
398
399
|
end
|
399
400
|
formula.value = value
|
400
401
|
elsif rtype == 0
|
401
|
-
pos, op,
|
402
|
+
pos, op, _len, work = get_next_chunk
|
402
403
|
if op == :sharedfmla
|
403
404
|
## TODO: formula-support in 0.8.0
|
404
|
-
pos, op,
|
405
|
+
pos, op, _len, work = get_next_chunk
|
405
406
|
end
|
406
407
|
if op == :string
|
407
408
|
formula.value = client read_string(work, 2), @workbook.encoding
|
@@ -851,6 +852,7 @@ class Reader
|
|
851
852
|
@detected_rows = {}
|
852
853
|
@noteObjList = []
|
853
854
|
@noteList = []
|
855
|
+
@noteObject = nil
|
854
856
|
previous = nil
|
855
857
|
while tuple = get_next_chunk
|
856
858
|
pos, op, len, work = tuple
|
@@ -1273,11 +1275,11 @@ class Reader
|
|
1273
1275
|
end
|
1274
1276
|
def setup io
|
1275
1277
|
## Reading from StringIO fails without forced encoding
|
1276
|
-
if
|
1277
|
-
|
1278
|
-
|
1278
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.3.0')
|
1279
|
+
io.set_encoding('ASCII-8BIT')
|
1280
|
+
elsif io.respond_to?(:string) && (str = io.string) && str.respond_to?(:force_encoding)
|
1281
|
+
str.force_encoding('ASCII-8BIT')
|
1279
1282
|
end
|
1280
|
-
##
|
1281
1283
|
io.rewind
|
1282
1284
|
@ole = Ole::Storage.open io
|
1283
1285
|
@workbook = Workbook.new io, {}
|
@@ -241,81 +241,6 @@ class Worksheet
|
|
241
241
|
end
|
242
242
|
write_multiples row, first_idx, multiples if multiples
|
243
243
|
end
|
244
|
-
def write_changes reader, endpos, sst_status
|
245
|
-
|
246
|
-
## FIXME this is not smart solution to update outline_level.
|
247
|
-
# without this process, outlines in row disappear in MS Excel.
|
248
|
-
@worksheet.row_count.times do |i|
|
249
|
-
if @worksheet.row(i).outline_level > 0
|
250
|
-
@worksheet.row(i).outline_level = @worksheet.row(i).outline_level
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
reader.seek @worksheet.offset
|
255
|
-
blocks = row_blocks
|
256
|
-
lastpos = reader.pos
|
257
|
-
offsets = {}
|
258
|
-
row_offsets = []
|
259
|
-
changes = @worksheet.changes
|
260
|
-
@worksheet.offsets.each do |key, pair|
|
261
|
-
if changes.include?(key) \
|
262
|
-
|| (sst_status == :complete_update && key.is_a?(Integer))
|
263
|
-
offsets.store pair, key
|
264
|
-
end
|
265
|
-
end
|
266
|
-
## FIXME it may be smarter to simply write all rowblocks, instead of doing a
|
267
|
-
# song-and-dance routine for every row...
|
268
|
-
work = offsets.invert
|
269
|
-
work.each do |key, (pos, len)|
|
270
|
-
case key
|
271
|
-
when Integer
|
272
|
-
row_offsets.push [key, [pos, len]]
|
273
|
-
when :dimensions
|
274
|
-
row_offsets.push [-1, [pos, len]]
|
275
|
-
end
|
276
|
-
end
|
277
|
-
row_offsets.sort!
|
278
|
-
row_offsets.reverse!
|
279
|
-
control = changes.size
|
280
|
-
@worksheet.each do |row|
|
281
|
-
key = row.idx
|
282
|
-
if changes.include?(key) && !work.include?(key)
|
283
|
-
row, pair = row_offsets.find do |idx, _| idx <= key end
|
284
|
-
work.store key, pair
|
285
|
-
end
|
286
|
-
end
|
287
|
-
if changes.size > control
|
288
|
-
warn <<-EOS
|
289
|
-
Your Worksheet was modified while it was being written. This should not happen.
|
290
|
-
Please contact the author (hannes dot wyss at gmail dot com) with a sample file
|
291
|
-
and minimal code that generates this warning. Thanks!
|
292
|
-
EOS
|
293
|
-
end
|
294
|
-
work = work.sort_by do |key, (pos, len)|
|
295
|
-
[pos, key.is_a?(Integer) ? key : -1]
|
296
|
-
end
|
297
|
-
work.each do |key, (pos, len)|
|
298
|
-
@io.write reader.read(pos - lastpos) if pos > lastpos
|
299
|
-
if key.is_a?(Integer)
|
300
|
-
if block = blocks.find do |rows| rows.any? do |row| row.idx == key end end
|
301
|
-
write_rowblock block
|
302
|
-
blocks.delete block
|
303
|
-
end
|
304
|
-
else
|
305
|
-
send "write_#{key}"
|
306
|
-
end
|
307
|
-
lastpos = pos + len
|
308
|
-
reader.seek lastpos
|
309
|
-
end
|
310
|
-
|
311
|
-
# Necessary for outline (grouping) and hiding functions
|
312
|
-
# but these below are not necessary to run
|
313
|
-
# if [Row|Column]#hidden? = false and [Row|Column]#outline_level == 0
|
314
|
-
write_colinfos
|
315
|
-
write_guts
|
316
|
-
|
317
|
-
@io.write reader.read(endpos - lastpos)
|
318
|
-
end
|
319
244
|
def write_colinfo bunch
|
320
245
|
col = bunch.first
|
321
246
|
width = col.width.to_f * 256
|
@@ -164,93 +164,6 @@ class Workbook < Spreadsheet::Writer
|
|
164
164
|
end
|
165
165
|
end
|
166
166
|
##
|
167
|
-
# Copy unchanged data verbatim, adjust offsets and write new records for
|
168
|
-
# changed data.
|
169
|
-
def write_changes workbook, io
|
170
|
-
sanitize_worksheets workbook.worksheets
|
171
|
-
collect_formats workbook, :existing_document => true
|
172
|
-
reader = workbook.ole
|
173
|
-
sheet_data = {}
|
174
|
-
sst_status, sst_total, sst_strings = complete_sst_update? workbook
|
175
|
-
sst = {}
|
176
|
-
sst_strings.each_with_index do |str, idx| sst.store str, idx end
|
177
|
-
sheets = worksheets(workbook)
|
178
|
-
positions = []
|
179
|
-
newsheets = []
|
180
|
-
sheets.each do |sheet|
|
181
|
-
@sst[sheet] = sst
|
182
|
-
pos, len = workbook.offsets[sheet.worksheet]
|
183
|
-
if pos
|
184
|
-
positions.push pos
|
185
|
-
sheet.write_changes reader, pos + len, sst_status
|
186
|
-
else
|
187
|
-
newsheets.push sheet
|
188
|
-
sheet.write_from_scratch
|
189
|
-
end
|
190
|
-
sheet_data[sheet.worksheet] = sheet.data
|
191
|
-
end
|
192
|
-
Ole::Storage.open io do |ole|
|
193
|
-
ole.file.open 'Workbook', 'w' do |writer|
|
194
|
-
reader.seek lastpos = 0
|
195
|
-
workbook.offsets.select do |key, pair|
|
196
|
-
workbook.changes.include? key
|
197
|
-
end.sort_by do |key, (pos, _)|
|
198
|
-
pos
|
199
|
-
end.each do |key, (pos, len)|
|
200
|
-
data = reader.read(pos - lastpos)
|
201
|
-
writer.write data
|
202
|
-
case key
|
203
|
-
when Spreadsheet::Worksheet
|
204
|
-
writer.write sheet_data[key]
|
205
|
-
when :boundsheets
|
206
|
-
## boundsheets are hard to calculate. The offset below is only
|
207
|
-
# correct if there are no more changes in the workbook globals
|
208
|
-
# string after this.
|
209
|
-
oldoffset = positions.min - len
|
210
|
-
lastpos = pos + len
|
211
|
-
bytechange = 0
|
212
|
-
buffer = StringIO.new ''
|
213
|
-
if tuple = workbook.offsets[:sst]
|
214
|
-
write_sst_changes workbook, buffer, writer.pos,
|
215
|
-
sst_total, sst_strings
|
216
|
-
pos, len = tuple
|
217
|
-
if offset = workbook.offsets[:extsst]
|
218
|
-
len += offset[1].to_i
|
219
|
-
end
|
220
|
-
bytechange = buffer.size - len
|
221
|
-
write_boundsheets workbook, writer, oldoffset + bytechange
|
222
|
-
reader.seek lastpos
|
223
|
-
writer.write reader.read(pos - lastpos)
|
224
|
-
buffer.rewind
|
225
|
-
writer.write buffer.read
|
226
|
-
elsif sst.empty? || workbook.biff_version < 8
|
227
|
-
write_boundsheets workbook, writer, oldoffset + bytechange
|
228
|
-
else
|
229
|
-
write_sst workbook, buffer, writer.pos
|
230
|
-
write_boundsheets workbook, writer, oldoffset + buffer.size
|
231
|
-
pos = lastpos
|
232
|
-
len = positions.min - lastpos
|
233
|
-
if len > OPCODE_SIZE
|
234
|
-
reader.seek pos
|
235
|
-
writer.write reader.read(len - OPCODE_SIZE)
|
236
|
-
end
|
237
|
-
buffer.rewind
|
238
|
-
writer.write buffer.read
|
239
|
-
write_eof workbook, writer
|
240
|
-
end
|
241
|
-
else
|
242
|
-
send "write_#{key}", workbook, writer
|
243
|
-
end
|
244
|
-
lastpos = [pos + len, reader.size - 1].min
|
245
|
-
reader.seek lastpos
|
246
|
-
end
|
247
|
-
writer.write reader.read
|
248
|
-
newsheets.each do |sheet|
|
249
|
-
writer.write sheet.data
|
250
|
-
end
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
254
167
|
def write_datemode workbook, writer
|
255
168
|
mode = @date_base.year == 1899 ? 0x00 : 0x01
|
256
169
|
data = [
|
@@ -396,7 +309,7 @@ class Workbook < Spreadsheet::Writer
|
|
396
309
|
sanitize_worksheets workbook.worksheets
|
397
310
|
collect_formats workbook
|
398
311
|
sheets = worksheets workbook
|
399
|
-
buffer1 = StringIO.new ''
|
312
|
+
buffer1 = StringIO.new ''.dup
|
400
313
|
# ● BOF Type = workbook globals (➜ 6.8)
|
401
314
|
write_bof workbook, buffer1, :globals
|
402
315
|
# ○ File Protection Block ➜ 4.19
|
@@ -441,7 +354,7 @@ class Workbook < Spreadsheet::Writer
|
|
441
354
|
# ○ USESELFS ➜ 5.106
|
442
355
|
buffer1.rewind
|
443
356
|
# ●● BOUNDSHEET ➜ 5.95
|
444
|
-
buffer2 = StringIO.new ''
|
357
|
+
buffer2 = StringIO.new ''.dup
|
445
358
|
# ○ COUNTRY ➜ 5.22
|
446
359
|
# ○ Link Table ➜ 4.10.3
|
447
360
|
# ○○ NAME ➜ 6.66
|
@@ -639,8 +552,7 @@ class Workbook < Spreadsheet::Writer
|
|
639
552
|
write_op writer, 0x003d, data.pack('v*')
|
640
553
|
end
|
641
554
|
##
|
642
|
-
# The main writer method. Calls #write_from_scratch
|
643
|
-
# depending on the class and state of _workbook_.
|
555
|
+
# The main writer method. Calls #write_from_scratch.
|
644
556
|
def write_workbook workbook, io
|
645
557
|
unless workbook.is_a?(Excel::Workbook) && workbook.io
|
646
558
|
@date_base = Date.new 1899, 12, 31
|
@@ -650,7 +562,8 @@ class Workbook < Spreadsheet::Writer
|
|
650
562
|
if workbook.changes.empty?
|
651
563
|
super
|
652
564
|
else
|
653
|
-
|
565
|
+
@date_base = Date.new 1899, 12, 31
|
566
|
+
write_from_scratch workbook, io
|
654
567
|
end
|
655
568
|
end
|
656
569
|
ensure
|
@@ -20,7 +20,7 @@ class Worksheet
|
|
20
20
|
def initialize workbook, worksheet
|
21
21
|
@workbook = workbook
|
22
22
|
@worksheet = worksheet
|
23
|
-
@io = StringIO.new ''
|
23
|
+
@io = StringIO.new ''.dup
|
24
24
|
@biff_version = 0x0600
|
25
25
|
@bof = 0x0809
|
26
26
|
@build_id = 3515
|
@@ -243,84 +243,6 @@ class Worksheet
|
|
243
243
|
end
|
244
244
|
write_multiples row, first_idx, multiples if multiples
|
245
245
|
end
|
246
|
-
def write_changes reader, endpos, sst_status
|
247
|
-
|
248
|
-
## FIXME this is not smart solution to update outline_level.
|
249
|
-
# without this process, outlines in row disappear in MS Excel.
|
250
|
-
@worksheet.row_count.times do |i|
|
251
|
-
if @worksheet.row(i).outline_level > 0
|
252
|
-
@worksheet.row(i).outline_level = @worksheet.row(i).outline_level
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
reader.seek @worksheet.offset
|
257
|
-
blocks = row_blocks
|
258
|
-
lastpos = reader.pos
|
259
|
-
offsets = {}
|
260
|
-
row_offsets = []
|
261
|
-
changes = @worksheet.changes
|
262
|
-
@worksheet.offsets.each do |key, pair|
|
263
|
-
if changes.include?(key) \
|
264
|
-
|| (sst_status == :complete_update && key.is_a?(Integer))
|
265
|
-
offsets.store pair, key
|
266
|
-
end
|
267
|
-
end
|
268
|
-
## FIXME it may be smarter to simply write all rowblocks, instead of doing a
|
269
|
-
# song-and-dance routine for every row...
|
270
|
-
work = offsets.invert
|
271
|
-
work.each do |key, (pos, len)|
|
272
|
-
case key
|
273
|
-
when Integer
|
274
|
-
row_offsets.push [key, [pos, len]]
|
275
|
-
when :dimensions
|
276
|
-
row_offsets.push [-1, [pos, len]]
|
277
|
-
end
|
278
|
-
end
|
279
|
-
row_offsets.sort!
|
280
|
-
row_offsets.reverse!
|
281
|
-
control = changes.size
|
282
|
-
@worksheet.each do |row|
|
283
|
-
key = row.idx
|
284
|
-
if changes.include?(key) && !work.include?(key)
|
285
|
-
row, pair = row_offsets.find do |idx, _| idx <= key end
|
286
|
-
work.store key, pair
|
287
|
-
end
|
288
|
-
end
|
289
|
-
if changes.size > control
|
290
|
-
warn <<-EOS
|
291
|
-
Your Worksheet was modified while it was being written. This should not happen.
|
292
|
-
Please contact the author (hannes dot wyss at gmail dot com) with a sample file
|
293
|
-
and minimal code that generates this warning. Thanks!
|
294
|
-
EOS
|
295
|
-
end
|
296
|
-
work = work.sort_by do |key, (pos, _)|
|
297
|
-
[pos, key.is_a?(Integer) ? key : -1]
|
298
|
-
end
|
299
|
-
work.each do |key, (pos, len)|
|
300
|
-
@io.write reader.read(pos - lastpos) if pos > lastpos
|
301
|
-
if key.is_a?(Integer)
|
302
|
-
if block = blocks.find do |rows| rows.any? do |row| row.idx == key end end
|
303
|
-
write_rowblock block
|
304
|
-
blocks.delete block
|
305
|
-
end
|
306
|
-
else
|
307
|
-
send "write_#{key}"
|
308
|
-
end
|
309
|
-
lastpos = pos + len
|
310
|
-
reader.seek lastpos
|
311
|
-
end
|
312
|
-
|
313
|
-
# Necessary for outline (grouping) and hiding functions
|
314
|
-
# but these below are not necessary to run
|
315
|
-
# if [Row|Column]#hidden? = false and [Row|Column]#outline_level == 0
|
316
|
-
write_merged_cells
|
317
|
-
write_pagesetup
|
318
|
-
write_margins
|
319
|
-
write_colinfos
|
320
|
-
write_guts
|
321
|
-
|
322
|
-
@io.write reader.read(endpos - lastpos)
|
323
|
-
end
|
324
246
|
def write_colinfo bunch
|
325
247
|
col = bunch.first
|
326
248
|
width = col.width.to_f * 256
|
@@ -642,7 +564,7 @@ and minimal code that generates this warning. Thanks!
|
|
642
564
|
##
|
643
565
|
# Write multiple consecutive cells with RK values (see #write_rk)
|
644
566
|
def write_mulrk row, idx, multiples
|
645
|
-
fmt = 'v2'
|
567
|
+
fmt = ['v2']
|
646
568
|
data = [
|
647
569
|
row.idx, # Index to row
|
648
570
|
idx, # Index to first column (fc)
|
@@ -655,7 +577,7 @@ and minimal code that generates this warning. Thanks!
|
|
655
577
|
end
|
656
578
|
# Index to last column (lc)
|
657
579
|
data.push idx + multiples.size - 1
|
658
|
-
write_op opcode(:mulrk), data.pack(fmt << 'v')
|
580
|
+
write_op opcode(:mulrk), data.pack((fmt << 'v').join)
|
659
581
|
end
|
660
582
|
def write_multiples row, idx, multiples
|
661
583
|
case multiples.last
|
data/lib/spreadsheet/excel.rb
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
require 'spreadsheet'
|
2
2
|
|
3
|
-
warn <<-EOS
|
4
|
-
[DEPRECATED] By requiring 'spreadsheet/excel' you are loading a Compatibility
|
5
|
-
layer which provides a drop-in replacement for Spreadsheet::Excel
|
6
|
-
versions <= 0.3.5.1. This code will be removed in Spreadsheet
|
7
|
-
version 1.0.0
|
8
|
-
EOS
|
9
3
|
##
|
10
4
|
# Spreadsheet::Excel Compatibility Layer.
|
11
5
|
# Drop-in replacement for Spreadsheet::Excel version <= 0.3.5.1
|