ttb-spreadsheet 0.6.5.8

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 (65) hide show
  1. data/GUIDE.txt +267 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +18 -0
  4. data/History.txt +365 -0
  5. data/LICENSE.txt +619 -0
  6. data/Manifest.txt +62 -0
  7. data/README.txt +107 -0
  8. data/Rakefile +0 -0
  9. data/bin/xlsopcodes +18 -0
  10. data/lib/parseexcel.rb +27 -0
  11. data/lib/parseexcel/parseexcel.rb +75 -0
  12. data/lib/parseexcel/parser.rb +11 -0
  13. data/lib/spreadsheet.rb +79 -0
  14. data/lib/spreadsheet/column.rb +71 -0
  15. data/lib/spreadsheet/compatibility.rb +23 -0
  16. data/lib/spreadsheet/datatypes.rb +106 -0
  17. data/lib/spreadsheet/encodings.rb +57 -0
  18. data/lib/spreadsheet/excel.rb +88 -0
  19. data/lib/spreadsheet/excel/error.rb +26 -0
  20. data/lib/spreadsheet/excel/internals.rb +365 -0
  21. data/lib/spreadsheet/excel/internals/biff5.rb +17 -0
  22. data/lib/spreadsheet/excel/internals/biff8.rb +19 -0
  23. data/lib/spreadsheet/excel/offset.rb +41 -0
  24. data/lib/spreadsheet/excel/reader.rb +1173 -0
  25. data/lib/spreadsheet/excel/reader/biff5.rb +22 -0
  26. data/lib/spreadsheet/excel/reader/biff8.rb +199 -0
  27. data/lib/spreadsheet/excel/row.rb +92 -0
  28. data/lib/spreadsheet/excel/sst_entry.rb +46 -0
  29. data/lib/spreadsheet/excel/workbook.rb +80 -0
  30. data/lib/spreadsheet/excel/worksheet.rb +100 -0
  31. data/lib/spreadsheet/excel/writer.rb +1 -0
  32. data/lib/spreadsheet/excel/writer/biff8.rb +75 -0
  33. data/lib/spreadsheet/excel/writer/format.rb +253 -0
  34. data/lib/spreadsheet/excel/writer/workbook.rb +690 -0
  35. data/lib/spreadsheet/excel/writer/worksheet.rb +891 -0
  36. data/lib/spreadsheet/font.rb +92 -0
  37. data/lib/spreadsheet/format.rb +177 -0
  38. data/lib/spreadsheet/formula.rb +9 -0
  39. data/lib/spreadsheet/helpers.rb +11 -0
  40. data/lib/spreadsheet/link.rb +43 -0
  41. data/lib/spreadsheet/row.rb +132 -0
  42. data/lib/spreadsheet/workbook.rb +126 -0
  43. data/lib/spreadsheet/worksheet.rb +287 -0
  44. data/lib/spreadsheet/writer.rb +30 -0
  45. data/spreadsheet.gemspec +20 -0
  46. data/test/data/test_changes.xls +0 -0
  47. data/test/data/test_copy.xls +0 -0
  48. data/test/data/test_datetime.xls +0 -0
  49. data/test/data/test_empty.xls +0 -0
  50. data/test/data/test_formula.xls +0 -0
  51. data/test/data/test_long_sst_record.xls +0 -0
  52. data/test/data/test_missing_row.xls +0 -0
  53. data/test/data/test_version_excel5.xls +0 -0
  54. data/test/data/test_version_excel95.xls +0 -0
  55. data/test/data/test_version_excel97.xls +0 -0
  56. data/test/excel/row.rb +35 -0
  57. data/test/excel/writer/workbook.rb +23 -0
  58. data/test/excel/writer/worksheet.rb +24 -0
  59. data/test/font.rb +163 -0
  60. data/test/integration.rb +1311 -0
  61. data/test/row.rb +33 -0
  62. data/test/suite.rb +17 -0
  63. data/test/workbook.rb +29 -0
  64. data/test/worksheet.rb +80 -0
  65. metadata +151 -0
@@ -0,0 +1,126 @@
1
+ require 'spreadsheet/format'
2
+ require 'spreadsheet/encodings'
3
+
4
+ module Spreadsheet
5
+ ##
6
+ # The Workbook class represents a Spreadsheet-Document and is the entry point
7
+ # for all Spreadsheet manipulation.
8
+ #
9
+ # Interesting Attributes:
10
+ # #default_format:: The default format used for all cells in this Workbook.
11
+ # that have no format set explicitly or in
12
+ # Row#default_format or Worksheet#default_format.
13
+ class Workbook
14
+ include Spreadsheet::Encodings
15
+ attr_reader :io, :worksheets, :formats, :fonts
16
+ attr_accessor :active_worksheet, :encoding, :default_format, :version
17
+ def initialize io = nil, opts={:default_format => Format.new}
18
+ @worksheets = []
19
+ @io = io
20
+ @fonts = []
21
+ @formats = []
22
+ if @default_format = opts[:default_format]
23
+ @formats.push @default_format
24
+ end
25
+ end
26
+ ##
27
+ # Add a Font to the Workbook. Used by the parser. You should not need to
28
+ # use this Method.
29
+ def add_font font
30
+ @fonts.push(font).uniq! if font
31
+ font
32
+ end
33
+ ##
34
+ # Add a Format to the Workbook. If you use Row#set_format, you should not
35
+ # need to use this Method.
36
+ def add_format format
37
+ @formats.push(format) if format && !@formats.include?(format)
38
+ format
39
+ end
40
+ ##
41
+ # Add a Worksheet to the Workbook.
42
+ def add_worksheet worksheet
43
+ worksheet.workbook = self
44
+ @worksheets.push worksheet
45
+ worksheet
46
+ end
47
+ ##
48
+ # Create a new Worksheet in this Workbook.
49
+ # Used without options this creates a Worksheet with the name 'WorksheetN'
50
+ # where the new Worksheet is the Nth Worksheet in this Workbook.
51
+ #
52
+ # Use the option <em>:name => 'My pretty Name'</em> to override this
53
+ # behavior.
54
+ def create_worksheet opts = {}
55
+ opts[:name] ||= client("Worksheet#{@worksheets.size.next}", 'UTF-8')
56
+ add_worksheet Worksheet.new(opts)
57
+ end
58
+ ##
59
+ # Returns the count of total worksheets present.
60
+ # Takes no arguments. Just returns the length of @worksheets array.
61
+ def sheet_count
62
+ @worksheets.length
63
+ end
64
+ ##
65
+ # The Font at _idx_
66
+ def font idx
67
+ @fonts[idx]
68
+ end
69
+ ##
70
+ # The Format at _idx_, or - if _idx_ is a String -
71
+ # the Format with name == _idx_
72
+ def format idx
73
+ case idx
74
+ when Integer
75
+ @formats[idx] || @default_format
76
+ when String
77
+ @formats.find do |fmt| fmt.name == idx end
78
+ end
79
+ end
80
+ def inspect
81
+ variables = (instance_variables - uninspect_variables).collect do |name|
82
+ "%s=%s" % [name, instance_variable_get(name)]
83
+ end.join(' ')
84
+ uninspect = uninspect_variables.collect do |name|
85
+ var = instance_variable_get name
86
+ "%s=%s[%i]" % [name, var.class, var.size]
87
+ end.join(' ')
88
+ sprintf "#<%s:0x%014x %s %s>", self.class, object_id,
89
+ variables, uninspect
90
+ end
91
+ def uninspect_variables # :nodoc:
92
+ %w{@formats @fonts @worksheets}
93
+ end
94
+ ##
95
+ # The Worksheet at _idx_, or - if _idx_ is a String -
96
+ # the Worksheet with name == _idx_
97
+ def worksheet idx
98
+ case idx
99
+ when Integer
100
+ @worksheets[idx]
101
+ when String
102
+ @worksheets.find do |sheet| sheet.name == idx end
103
+ end
104
+ end
105
+ ##
106
+ # Write this Workbook to a File, IO Stream or Writer Object. The latter will
107
+ # make more sense once there are more than just an Excel-Writer available.
108
+ def write io_path_or_writer
109
+ if io_path_or_writer.is_a? Writer
110
+ io_path_or_writer.write self
111
+ else
112
+ writer(io_path_or_writer).write(self)
113
+ end
114
+ end
115
+ ##
116
+ # Returns a new instance of the default Writer class for this Workbook (can
117
+ # only be an Excel::Writer::Workbook at this time)
118
+ def writer io_or_path, type=Excel, version=self.version
119
+ if type == Excel
120
+ Excel::Writer::Workbook.new io_or_path
121
+ else
122
+ raise NotImplementedError, "No Writer defined for #{type}"
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,287 @@
1
+ require 'date'
2
+ require 'spreadsheet/column'
3
+ require 'spreadsheet/encodings'
4
+ require 'spreadsheet/row'
5
+
6
+ module Spreadsheet
7
+ ##
8
+ # The Worksheet class. Contains most of the Spreadsheet data in Rows.
9
+ #
10
+ # Interesting Attributes
11
+ # #name :: The Name of this Worksheet.
12
+ # #default_format:: The default format used for all cells in this Workhseet
13
+ # that have no format set explicitly or in
14
+ # Row#default_format.
15
+ # #rows :: The Rows in this Worksheet. It is not recommended to
16
+ # Manipulate this Array directly. If you do, call
17
+ # #updated_from with the smallest modified index.
18
+ # #columns :: The Column formatting in this Worksheet. Column
19
+ # instances may appear at more than one position in #columns.
20
+ # If you modify a Column directly, your changes will be
21
+ # reflected in all those positions.
22
+ # #selected :: When a user chooses to print a Workbook, Excel will include
23
+ # all selected Worksheets. If no Worksheet is selected at
24
+ # Workbook#write, then the first Worksheet is selected by
25
+ # default.
26
+ class Worksheet
27
+ include Spreadsheet::Encodings
28
+ include Enumerable
29
+ attr_accessor :name, :selected, :workbook
30
+ attr_reader :autofilter_enabled, :autofilter_left_column_index, :autofilter_right_column_index
31
+ attr_reader :rows, :columns
32
+ def initialize opts={}
33
+ @default_format = nil
34
+ @selected = opts[:selected]
35
+ @dimensions = [0,0,0,0]
36
+ @name = opts[:name] || 'Worksheet'
37
+ @workbook = opts[:workbook]
38
+ @rows = []
39
+ @columns = []
40
+ @links = {}
41
+ end
42
+ def active # :nodoc:
43
+ warn "Worksheet#active is deprecated. Please use Worksheet#selected instead."
44
+ selected
45
+ end
46
+ def active= selected # :nodoc:
47
+ warn "Worksheet#active= is deprecated. Please use Worksheet#selected= instead."
48
+ self.selected = selected
49
+ end
50
+ ##
51
+ # Add a Format to the Workbook. If you use Row#set_format, you should not
52
+ # need to use this Method.
53
+ def add_format fmt
54
+ @workbook.add_format fmt if fmt
55
+ end
56
+ ##
57
+ # Get the enriched value of the Cell at _row_, _column_.
58
+ # See also Worksheet#[], Row#[].
59
+ def cell row, column
60
+ row(row)[column]
61
+ end
62
+ ##
63
+ # Returns the Column at _idx_.
64
+ def column idx
65
+ @columns[idx] || Column.new(idx, default_format, :worksheet => self)
66
+ end
67
+ ##
68
+ # The number of columns in this Worksheet which contain data.
69
+ def column_count
70
+ dimensions[3] - dimensions[2]
71
+ end
72
+ def column_updated idx, column
73
+ @columns[idx] = column
74
+ end
75
+ ##
76
+ # Delete the Row at _idx_ (0-based) from this Worksheet.
77
+ def delete_row idx
78
+ res = @rows.delete_at idx
79
+ updated_from idx
80
+ res
81
+ end
82
+ ##
83
+ # The default Format of this Worksheet, if you have set one.
84
+ # Returns the Workbook's default Format otherwise.
85
+ def default_format
86
+ @default_format || @workbook.default_format
87
+ end
88
+ ##
89
+ # Set the default Format of this Worksheet.
90
+ def default_format= format
91
+ @default_format = format
92
+ add_format format
93
+ format
94
+ end
95
+ ##
96
+ # Dimensions:: [ first used row, first unused row,
97
+ # first used column, first unused column ]
98
+ # ( First used means that all rows or columns before that are
99
+ # empty. First unused means that this and all following rows
100
+ # or columns are empty. )
101
+ def dimensions
102
+ @dimensions || recalculate_dimensions
103
+ end
104
+ ##
105
+ # If no argument is given, #each iterates over all used Rows (from the first
106
+ # used Row until but omitting the first unused Row, see also #dimensions).
107
+ #
108
+ # If the argument skip is given, #each iterates from that row until but
109
+ # omitting the first unused Row, effectively skipping the first _skip_ Rows
110
+ # from the top of the Worksheet.
111
+ def each skip=dimensions[0], &block
112
+ skip.upto(dimensions[1] - 1) do |idx|
113
+ block.call row(idx)
114
+ end
115
+ end
116
+ def encoding # :nodoc:
117
+ @workbook.encoding
118
+ end
119
+ ##
120
+ # Sets the default Format of the column at _idx_.
121
+ #
122
+ # _idx_ may be an Integer, or an Enumerable that iterates over a number of
123
+ # Integers.
124
+ #
125
+ # _format_ is a Format, or nil if you want to remove the Formatting at _idx_
126
+ #
127
+ # Returns an instance of Column if _idx_ is an Integer, an Array of Columns
128
+ # otherwise.
129
+ def format_column idx, format=nil, opts={}
130
+ opts[:worksheet] = self
131
+ res = case idx
132
+ when Integer
133
+ column = nil
134
+ if format
135
+ column = Column.new(idx, format, opts)
136
+ end
137
+ @columns[idx] = column
138
+ else
139
+ idx.collect do |col| format_column col, format, opts end
140
+ end
141
+ shorten @columns
142
+ res
143
+ end
144
+ ##
145
+ # Formats all Date, DateTime and Time cells with _format_ or the default
146
+ # formats:
147
+ # - 'DD.MM.YYYY' for Date
148
+ # - 'DD.MM.YYYY hh:mm:ss' for DateTime and Time
149
+ def format_dates! format=nil
150
+ each do |row|
151
+ row.each_with_index do |value, idx|
152
+ unless row.formats[idx] || row.format(idx).date_or_time?
153
+ numfmt = case value
154
+ when DateTime, Time
155
+ format || client('DD.MM.YYYY hh:mm:ss', 'UTF-8')
156
+ when Date
157
+ format || client('DD.MM.YYYY', 'UTF-8')
158
+ end
159
+ case numfmt
160
+ when Format
161
+ row.set_format idx, numfmt
162
+ when String
163
+ fmt = row.format(idx).dup
164
+ fmt.number_format = numfmt
165
+ row.set_format idx, fmt
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ ##
172
+ # Insert a Row at _idx_ (0-based) containing _cells_
173
+ def insert_row idx, cells=[]
174
+ res = @rows.insert idx, Row.new(self, idx, cells)
175
+ updated_from idx
176
+ res
177
+ end
178
+ def inspect
179
+ names = instance_variables
180
+ names.delete '@rows'
181
+ variables = names.collect do |name|
182
+ "%s=%s" % [name, instance_variable_get(name)]
183
+ end.join(' ')
184
+ sprintf "#<%s:0x%014x %s @rows[%i]>", self.class, object_id,
185
+ variables, row_count
186
+ end
187
+ ## The last Row containing any data
188
+ def last_row
189
+ row(last_row_index)
190
+ end
191
+ ## The index of the last Row containing any data
192
+ def last_row_index
193
+ [dimensions[1] - 1, 0].max
194
+ end
195
+ ##
196
+ # Replace the Row at _idx_ with the following arguments. Like #update_row,
197
+ # but truncates the Row if there are fewer arguments than Cells in the Row.
198
+ def replace_row idx, *cells
199
+ if(row = @rows[idx]) && cells.size < row.size
200
+ cells.concat Array.new(row.size - cells.size)
201
+ end
202
+ update_row idx, *cells
203
+ end
204
+ ##
205
+ # The Row at _idx_ or a new Row.
206
+ def row idx
207
+ @rows[idx] || Row.new(self, idx)
208
+ end
209
+ ##
210
+ # The number of Rows in this Worksheet which contain data.
211
+ def row_count
212
+ dimensions[1] - dimensions[0]
213
+ end
214
+ ##
215
+ # Tell Worksheet that the Row at _idx_ has been updated and the #dimensions
216
+ # need to be recalculated. You should not need to call this directly.
217
+ def row_updated idx, row
218
+ @dimensions = nil
219
+ @rows[idx] = row
220
+ end
221
+ ##
222
+ # Updates the Row at _idx_ with the following arguments.
223
+ def update_row idx, *cells
224
+ res = if row = @rows[idx]
225
+ row[0, cells.size] = cells
226
+ row
227
+ else
228
+ Row.new self, idx, cells
229
+ end
230
+ row_updated idx, res
231
+ res
232
+ end
233
+ ##
234
+ # Renumbers all Rows starting at _idx_ and calls #row_updated for each of
235
+ # them.
236
+ def updated_from index
237
+ index.upto(@rows.size - 1) do |idx|
238
+ row = row(idx)
239
+ row.idx = idx
240
+ row_updated idx, row
241
+ end
242
+ end
243
+ ##
244
+ # Get the enriched value of the Cell at _row_, _column_.
245
+ # See also Worksheet#cell, Row#[].
246
+ def [] row, column
247
+ row(row)[column]
248
+ end
249
+ ##
250
+ # Set the value of the Cell at _row_, _column_ to _value_.
251
+ # See also Row#[]=.
252
+ def []= row, column, value
253
+ row(row)[column] = value
254
+ end
255
+ ##
256
+ # enable autofiltering on this sheet, NOTE autofiltering can (for now) only
257
+ # be done on 1 sheet and on the first row starting from column on
258
+ # left_column_index (0 based) up to right column_index (0 based)
259
+ def enable_autofilter(number_of_autofilter_columns)
260
+ @autofilter_enabled = true
261
+ @autofilter_left_column_index = 0
262
+ @autofilter_right_column_index = number_of_autofilter_columns - 1
263
+ end
264
+ private
265
+ def index_of_first ary # :nodoc:
266
+ return unless ary
267
+ ary.index(ary.find do |elm| elm end)
268
+ end
269
+ def recalculate_dimensions # :nodoc:
270
+ shorten @rows
271
+ @dimensions = []
272
+ @dimensions[0] = index_of_first(@rows) || 0
273
+ @dimensions[1] = @rows.size
274
+ compact = @rows.compact
275
+ @dimensions[2] = compact.collect do |row| row.first_used end.compact.min || 0
276
+ @dimensions[3] = compact.collect do |row| row.first_unused end.max || 0
277
+ @dimensions
278
+ end
279
+ def shorten ary # :nodoc:
280
+ return unless ary
281
+ while !ary.empty? && !ary.last
282
+ ary.pop
283
+ end
284
+ ary unless ary.empty?
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,30 @@
1
+ module Spreadsheet
2
+ ##
3
+ # Parent Class for all Writers. Implements the copying of unmodified
4
+ # Spreadsheet documents.
5
+ class Writer
6
+ def initialize io_or_path
7
+ @io_or_path = io_or_path
8
+ end
9
+ def write workbook
10
+ if @io_or_path.respond_to? :seek
11
+ @io_or_path.binmode
12
+ write_workbook workbook, @io_or_path
13
+ else
14
+ File.open(@io_or_path, "wb+") do |fh|
15
+ write_workbook workbook, fh
16
+ end
17
+ end
18
+ end
19
+ private
20
+ def write_workbook workbook, io
21
+ reader = workbook.io
22
+ unless io == reader
23
+ reader.rewind
24
+ data = reader.read
25
+ io.rewind
26
+ io.write data
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |s|
3
+ s.name = "ttb-spreadsheet"
4
+ s.version = "0.6.5.8"
5
+ s.platform = Gem::Platform::RUBY
6
+ s.authors = "Masaomi Hatakeyama, Zeno R.R. Davatz"
7
+ s.email = "mhatakeyama@ywesee.com, zdavatz@ywesee.com"
8
+ s.homepage = "https://github.com/TalentBox/spreadsheet"
9
+ s.summary = "The Spreadsheet Library is designed to read and write Spreadsheet Documents"
10
+ s.description = "As of version 0.6.0, only Microsoft Excel compatible spreadsheets are supported"
11
+
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_file = "test/suite.rb"
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_runtime_dependency("ruby-ole")
19
+ s.add_development_dependency("rake", ["~> 0.8.7"])
20
+ end