keeguon-spreadsheet 0.9.3

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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +619 -0
  3. data/Manifest.txt +85 -0
  4. data/bin/xlsopcodes +18 -0
  5. data/lib/parseexcel.rb +27 -0
  6. data/lib/parseexcel/parseexcel.rb +75 -0
  7. data/lib/parseexcel/parser.rb +11 -0
  8. data/lib/spreadsheet.rb +80 -0
  9. data/lib/spreadsheet/column.rb +71 -0
  10. data/lib/spreadsheet/compatibility.rb +23 -0
  11. data/lib/spreadsheet/datatypes.rb +161 -0
  12. data/lib/spreadsheet/encodings.rb +57 -0
  13. data/lib/spreadsheet/excel.rb +88 -0
  14. data/lib/spreadsheet/excel/error.rb +26 -0
  15. data/lib/spreadsheet/excel/internals.rb +458 -0
  16. data/lib/spreadsheet/excel/internals/biff5.rb +17 -0
  17. data/lib/spreadsheet/excel/internals/biff8.rb +19 -0
  18. data/lib/spreadsheet/excel/offset.rb +41 -0
  19. data/lib/spreadsheet/excel/password_hash.rb +24 -0
  20. data/lib/spreadsheet/excel/reader.rb +1302 -0
  21. data/lib/spreadsheet/excel/reader/biff5.rb +42 -0
  22. data/lib/spreadsheet/excel/reader/biff8.rb +231 -0
  23. data/lib/spreadsheet/excel/rgb.rb +122 -0
  24. data/lib/spreadsheet/excel/row.rb +98 -0
  25. data/lib/spreadsheet/excel/sst_entry.rb +46 -0
  26. data/lib/spreadsheet/excel/workbook.rb +80 -0
  27. data/lib/spreadsheet/excel/worksheet.rb +115 -0
  28. data/lib/spreadsheet/excel/writer.rb +1 -0
  29. data/lib/spreadsheet/excel/writer/biff8.rb +75 -0
  30. data/lib/spreadsheet/excel/writer/format.rb +264 -0
  31. data/lib/spreadsheet/excel/writer/n_worksheet.rb +888 -0
  32. data/lib/spreadsheet/excel/writer/workbook.rb +735 -0
  33. data/lib/spreadsheet/excel/writer/worksheet.rb +940 -0
  34. data/lib/spreadsheet/font.rb +115 -0
  35. data/lib/spreadsheet/format.rb +209 -0
  36. data/lib/spreadsheet/formula.rb +9 -0
  37. data/lib/spreadsheet/helpers.rb +11 -0
  38. data/lib/spreadsheet/link.rb +43 -0
  39. data/lib/spreadsheet/note.rb +23 -0
  40. data/lib/spreadsheet/noteObject.rb +17 -0
  41. data/lib/spreadsheet/row.rb +151 -0
  42. data/lib/spreadsheet/workbook.rb +143 -0
  43. data/lib/spreadsheet/worksheet.rb +326 -0
  44. data/lib/spreadsheet/writer.rb +30 -0
  45. data/test/data/test_adding_data_to_existing_file.xls +0 -0
  46. data/test/data/test_borders.xls +0 -0
  47. data/test/data/test_changes.xls +0 -0
  48. data/test/data/test_comment.xls +0 -0
  49. data/test/data/test_copy.xls +0 -0
  50. data/test/data/test_datetime.xls +0 -0
  51. data/test/data/test_empty.xls +0 -0
  52. data/test/data/test_formula.xls +0 -0
  53. data/test/data/test_long_sst_record.xls +0 -0
  54. data/test/data/test_margin.xls +0 -0
  55. data/test/data/test_merged_and_protected.xls +0 -0
  56. data/test/data/test_merged_cells.xls +0 -0
  57. data/test/data/test_missing_row.xls +0 -0
  58. data/test/data/test_pagesetup.xls +0 -0
  59. data/test/data/test_version_excel5.xls +0 -0
  60. data/test/data/test_version_excel95.xls +0 -0
  61. data/test/data/test_version_excel97.xls +0 -0
  62. data/test/data/test_version_excel97_2010.xls +0 -0
  63. data/test/data/test_worksheet_visibility.xls +0 -0
  64. data/test/excel/reader.rb +30 -0
  65. data/test/excel/row.rb +40 -0
  66. data/test/excel/writer/workbook.rb +95 -0
  67. data/test/excel/writer/worksheet.rb +81 -0
  68. data/test/font.rb +163 -0
  69. data/test/format.rb +95 -0
  70. data/test/integration.rb +1390 -0
  71. data/test/row.rb +33 -0
  72. data/test/suite.rb +18 -0
  73. data/test/workbook.rb +55 -0
  74. data/test/workbook_protection.rb +19 -0
  75. data/test/worksheet.rb +112 -0
  76. metadata +148 -0
@@ -0,0 +1,143 @@
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, :palette
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
+ @palette = {}
22
+ @formats = []
23
+ @formats_set = {}
24
+ if @default_format = opts[:default_format]
25
+ add_format @default_format
26
+ end
27
+ end
28
+ ##
29
+ # Add a Font to the Workbook. Used by the parser. You should not need to
30
+ # use this Method.
31
+ def add_font font
32
+ @fonts.push(font).uniq! if font
33
+ font
34
+ end
35
+ ##
36
+ # Add a Format to the Workbook. If you use Row#set_format, you should not
37
+ # need to use this Method.
38
+ def add_format format
39
+ if format && !@formats_set[format]
40
+ @formats_set[format] = true
41
+ @formats.push(format)
42
+ end
43
+ format
44
+ end
45
+ ##
46
+ # Add a Worksheet to the Workbook.
47
+ def add_worksheet worksheet
48
+ worksheet.workbook = self
49
+ @worksheets.push worksheet
50
+ worksheet
51
+ end
52
+ ##
53
+ # Delete a Worksheet from Workbook by it's index
54
+ def delete_worksheet worksheet_index
55
+ @worksheets.delete_at worksheet_index
56
+ end
57
+ ##
58
+ # Change the RGB components of the elements in the colour palette.
59
+ def set_custom_color idx, red, green, blue
60
+ raise 'Invalid format' if [red, green, blue].find { |c| ! (0..255).include?(c) }
61
+
62
+ @palette[idx] = [red, green, blue]
63
+ end
64
+ ##
65
+ # Create a new Worksheet in this Workbook.
66
+ # Used without options this creates a Worksheet with the name 'WorksheetN'
67
+ # where the new Worksheet is the Nth Worksheet in this Workbook.
68
+ #
69
+ # Use the option <em>:name => 'My pretty Name'</em> to override this
70
+ # behavior.
71
+ def create_worksheet opts = {}
72
+ opts[:name] ||= client("Worksheet#{@worksheets.size.next}", 'UTF-8')
73
+ add_worksheet Worksheet.new(opts)
74
+ end
75
+ ##
76
+ # Returns the count of total worksheets present.
77
+ # Takes no arguments. Just returns the length of @worksheets array.
78
+ def sheet_count
79
+ @worksheets.length
80
+ end
81
+ ##
82
+ # The Font at _idx_
83
+ def font idx
84
+ @fonts[idx]
85
+ end
86
+ ##
87
+ # The Format at _idx_, or - if _idx_ is a String -
88
+ # the Format with name == _idx_
89
+ def format idx
90
+ case idx
91
+ when Integer
92
+ @formats[idx] || @default_format
93
+ when String
94
+ @formats.find do |fmt| fmt.name == idx end
95
+ end
96
+ end
97
+ def inspect
98
+ variables = (instance_variables - uninspect_variables).collect do |name|
99
+ "%s=%s" % [name, instance_variable_get(name)]
100
+ end.join(' ')
101
+ uninspect = uninspect_variables.collect do |name|
102
+ var = instance_variable_get name
103
+ "%s=%s[%i]" % [name, var.class, var.size]
104
+ end.join(' ')
105
+ sprintf "#<%s:0x%014x %s %s>", self.class, object_id,
106
+ variables, uninspect
107
+ end
108
+ def uninspect_variables # :nodoc:
109
+ %w{@formats @fonts @worksheets}
110
+ end
111
+ ##
112
+ # The Worksheet at _idx_, or - if _idx_ is a String -
113
+ # the Worksheet with name == _idx_
114
+ def worksheet idx
115
+ case idx
116
+ when Integer
117
+ @worksheets[idx]
118
+ when String
119
+ @worksheets.find do |sheet| sheet.name == idx end
120
+ end
121
+ end
122
+ ##
123
+ # Write this Workbook to a File, IO Stream or Writer Object. The latter will
124
+ # make more sense once there are more than just an Excel-Writer available.
125
+ def write io_path_or_writer
126
+ if io_path_or_writer.is_a? Writer
127
+ io_path_or_writer.write self
128
+ else
129
+ writer(io_path_or_writer).write(self)
130
+ end
131
+ end
132
+ ##
133
+ # Returns a new instance of the default Writer class for this Workbook (can
134
+ # only be an Excel::Writer::Workbook at this time)
135
+ def writer io_or_path, type=Excel, version=self.version
136
+ if type == Excel
137
+ Excel::Writer::Workbook.new io_or_path
138
+ else
139
+ raise NotImplementedError, "No Writer defined for #{type}"
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,326 @@
1
+ require 'date'
2
+ require 'spreadsheet/column'
3
+ require 'spreadsheet/encodings'
4
+ require 'spreadsheet/row'
5
+ require 'spreadsheet/excel/password_hash'
6
+
7
+ module Spreadsheet
8
+ ##
9
+ # The Worksheet class. Contains most of the Spreadsheet data in Rows.
10
+ #
11
+ # Interesting Attributes
12
+ # #name :: The Name of this Worksheet.
13
+ # #default_format:: The default format used for all cells in this Workhseet
14
+ # that have no format set explicitly or in
15
+ # Row#default_format.
16
+ # #rows :: The Rows in this Worksheet. It is not recommended to
17
+ # Manipulate this Array directly. If you do, call
18
+ # #updated_from with the smallest modified index.
19
+ # #columns :: The Column formatting in this Worksheet. Column
20
+ # instances may appear at more than one position in #columns.
21
+ # If you modify a Column directly, your changes will be
22
+ # reflected in all those positions.
23
+ # #selected :: When a user chooses to print a Workbook, Excel will include
24
+ # all selected Worksheets. If no Worksheet is selected at
25
+ # Workbook#write, then the first Worksheet is selected by
26
+ # default.
27
+ class Worksheet
28
+ include Spreadsheet::Encodings
29
+ include Spreadsheet::Datatypes
30
+ include Enumerable
31
+ attr_accessor :name, :selected, :workbook, :password_hash
32
+ attr_reader :rows, :columns, :merged_cells, :margins, :pagesetup
33
+ enum :visibility, :visible, :hidden, :strong_hidden
34
+ def initialize opts={}
35
+ @default_format = nil
36
+ @selected = opts[:selected]
37
+ @dimensions = [0,0,0,0]
38
+ @pagesetup = {
39
+ :orig_data => [9, 100, 1, 1, 1, 0, 300, 300, 0.5, 0.5, 1],
40
+ :orientation => :portrait,
41
+ :adjust_to => 100
42
+ }
43
+ @margins = {
44
+ :top => 1,
45
+ :left => 0.75,
46
+ :right => 0.75,
47
+ :bottom => 1
48
+ }
49
+ @name = opts[:name] || 'Worksheet'
50
+ @workbook = opts[:workbook]
51
+ @rows = []
52
+ @columns = []
53
+ @links = {}
54
+ @merged_cells = []
55
+ @protected = false
56
+ @password_hash = 0
57
+ @visibility = opts[:visibility]
58
+ end
59
+ def active # :nodoc:
60
+ warn "Worksheet#active is deprecated. Please use Worksheet#selected instead."
61
+ selected
62
+ end
63
+ def active= selected # :nodoc:
64
+ warn "Worksheet#active= is deprecated. Please use Worksheet#selected= instead."
65
+ self.selected = selected
66
+ end
67
+ ##
68
+ # Add a Format to the Workbook. If you use Row#set_format, you should not
69
+ # need to use this Method.
70
+ def add_format fmt
71
+ @workbook.add_format fmt if fmt
72
+ end
73
+ ##
74
+ # Get the enriched value of the Cell at _row_, _column_.
75
+ # See also Worksheet#[], Row#[].
76
+ def cell row, column
77
+ row(row)[column]
78
+ end
79
+ ##
80
+ # Returns the Column at _idx_.
81
+ def column idx
82
+ @columns[idx] || Column.new(idx, default_format, :worksheet => self)
83
+ end
84
+ ##
85
+ # The number of columns in this Worksheet which contain data.
86
+ def column_count
87
+ dimensions[3] - dimensions[2]
88
+ end
89
+ def column_updated idx, column
90
+ @columns[idx] = column
91
+ end
92
+ ##
93
+ # Delete the Row at _idx_ (0-based) from this Worksheet.
94
+ def delete_row idx
95
+ res = @rows.delete_at idx
96
+ updated_from idx
97
+ res
98
+ end
99
+ ##
100
+ # The default Format of this Worksheet, if you have set one.
101
+ # Returns the Workbook's default Format otherwise.
102
+ def default_format
103
+ @default_format || @workbook.default_format
104
+ end
105
+ ##
106
+ # Set the default Format of this Worksheet.
107
+ def default_format= format
108
+ @default_format = format
109
+ add_format format
110
+ format
111
+ end
112
+ ##
113
+ # Is the worksheet protected?
114
+ def protected?
115
+ @protected
116
+ end
117
+ ##
118
+ # Set worklist protection
119
+ def protect! password = ''
120
+ @protected = true
121
+ password = password.to_s
122
+ if password.size == 0
123
+ @password_hash = 0
124
+ else
125
+ @password_hash = Excel::Password.password_hash password
126
+ end
127
+ end
128
+
129
+ ##
130
+ # Dimensions:: [ first used row, first unused row,
131
+ # first used column, first unused column ]
132
+ # ( First used means that all rows or columns before that are
133
+ # empty. First unused means that this and all following rows
134
+ # or columns are empty. )
135
+ def dimensions
136
+ @dimensions || recalculate_dimensions
137
+ end
138
+ ##
139
+ # If no argument is given, #each iterates over all used Rows (from the first
140
+ # used Row until but omitting the first unused Row, see also #dimensions).
141
+ #
142
+ # If the argument skip is given, #each iterates from that row until but
143
+ # omitting the first unused Row, effectively skipping the first _skip_ Rows
144
+ # from the top of the Worksheet.
145
+ def each skip=dimensions[0]
146
+ skip.upto(dimensions[1] - 1) do |idx|
147
+ yield row(idx)
148
+ end
149
+ end
150
+ def encoding # :nodoc:
151
+ @workbook.encoding
152
+ end
153
+ ##
154
+ # Sets the default Format of the column at _idx_.
155
+ #
156
+ # _idx_ may be an Integer, or an Enumerable that iterates over a number of
157
+ # Integers.
158
+ #
159
+ # _format_ is a Format, or nil if you want to remove the Formatting at _idx_
160
+ #
161
+ # Returns an instance of Column if _idx_ is an Integer, an Array of Columns
162
+ # otherwise.
163
+ def format_column idx, format=nil, opts={}
164
+ opts[:worksheet] = self
165
+ res = case idx
166
+ when Integer
167
+ column = nil
168
+ if format
169
+ column = Column.new(idx, format, opts)
170
+ end
171
+ @columns[idx] = column
172
+ else
173
+ idx.collect do |col| format_column col, format, opts end
174
+ end
175
+ shorten @columns
176
+ res
177
+ end
178
+ ##
179
+ # Formats all Date, DateTime and Time cells with _format_ or the default
180
+ # formats:
181
+ # - 'DD.MM.YYYY' for Date
182
+ # - 'DD.MM.YYYY hh:mm:ss' for DateTime and Time
183
+ def format_dates! format=nil
184
+ new_formats = {}
185
+ fmt_str_time = client('DD.MM.YYYY hh:mm:ss', 'UTF-8')
186
+ fmt_str_date = client('DD.MM.YYYY', 'UTF-8')
187
+ each do |row|
188
+ row.each_with_index do |value, idx|
189
+ unless row.formats[idx] || row.format(idx).date_or_time?
190
+ numfmt = case value
191
+ when DateTime, Time
192
+ format || fmt_str_time
193
+ when Date
194
+ format || fmt_str_date
195
+ end
196
+ case numfmt
197
+ when Format
198
+ row.set_format idx, numfmt
199
+ when String
200
+ existing_format = row.format(idx)
201
+ new_formats[existing_format] ||= {}
202
+ new_format = new_formats[existing_format][numfmt]
203
+ if !new_format
204
+ new_format = new_formats[existing_format][numfmt] = existing_format.dup
205
+ new_format.number_format = numfmt
206
+ end
207
+ row.set_format idx, new_format
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
213
+ ##
214
+ # Insert a Row at _idx_ (0-based) containing _cells_
215
+ def insert_row idx, cells=[]
216
+ res = @rows.insert idx, Row.new(self, idx, cells)
217
+ updated_from idx
218
+ res
219
+ end
220
+ def inspect
221
+ names = instance_variables
222
+ names.delete '@rows'
223
+ variables = names.collect do |name|
224
+ "%s=%s" % [name, instance_variable_get(name)]
225
+ end.join(' ')
226
+ sprintf "#<%s:0x%014x %s @rows[%i]>", self.class, object_id,
227
+ variables, row_count
228
+ end
229
+ ## The last Row containing any data
230
+ def last_row
231
+ row(last_row_index)
232
+ end
233
+ ## The index of the last Row containing any data
234
+ def last_row_index
235
+ [dimensions[1] - 1, 0].max
236
+ end
237
+ ##
238
+ # Replace the Row at _idx_ with the following arguments. Like #update_row,
239
+ # but truncates the Row if there are fewer arguments than Cells in the Row.
240
+ def replace_row idx, *cells
241
+ if(row = @rows[idx]) && cells.size < row.size
242
+ cells.concat Array.new(row.size - cells.size)
243
+ end
244
+ update_row idx, *cells
245
+ end
246
+ ##
247
+ # The Row at _idx_ or a new Row.
248
+ def row idx
249
+ @rows[idx] || Row.new(self, idx)
250
+ end
251
+ ##
252
+ # The number of Rows in this Worksheet which contain data.
253
+ def row_count
254
+ dimensions[1] - dimensions[0]
255
+ end
256
+ ##
257
+ # Tell Worksheet that the Row at _idx_ has been updated and the #dimensions
258
+ # need to be recalculated. You should not need to call this directly.
259
+ def row_updated idx, row
260
+ @dimensions = nil
261
+ @rows[idx] = row
262
+ end
263
+ ##
264
+ # Updates the Row at _idx_ with the following arguments.
265
+ def update_row idx, *cells
266
+ res = if row = @rows[idx]
267
+ row[0, cells.size] = cells
268
+ row
269
+ else
270
+ Row.new self, idx, cells
271
+ end
272
+ row_updated idx, res
273
+ res
274
+ end
275
+ ##
276
+ # Renumbers all Rows starting at _idx_ and calls #row_updated for each of
277
+ # them.
278
+ def updated_from index
279
+ index.upto(@rows.size - 1) do |idx|
280
+ row = row(idx)
281
+ row.idx = idx
282
+ row_updated idx, row
283
+ end
284
+ end
285
+ ##
286
+ # Get the enriched value of the Cell at _row_, _column_.
287
+ # See also Worksheet#cell, Row#[].
288
+ def [] row, column
289
+ row(row)[column]
290
+ end
291
+ ##
292
+ # Set the value of the Cell at _row_, _column_ to _value_.
293
+ # See also Row#[]=.
294
+ def []= row, column, value
295
+ row(row)[column] = value
296
+ end
297
+ ##
298
+ # Merges multiple cells into one.
299
+ def merge_cells start_row, start_col, end_row, end_col
300
+ # FIXME enlarge or dup check
301
+ @merged_cells.push [start_row, end_row, start_col, end_col]
302
+ end
303
+ private
304
+ def index_of_first ary # :nodoc:
305
+ return unless ary
306
+ ary.index(ary.find do |elm| elm end)
307
+ end
308
+ def recalculate_dimensions # :nodoc:
309
+ shorten @rows
310
+ @dimensions = []
311
+ @dimensions[0] = index_of_first(@rows) || 0
312
+ @dimensions[1] = @rows.size
313
+ compact = @rows.compact
314
+ @dimensions[2] = compact.collect do |row| row.first_used end.compact.min || 0
315
+ @dimensions[3] = compact.collect do |row| row.first_unused end.max || 0
316
+ @dimensions
317
+ end
318
+ def shorten ary # :nodoc:
319
+ return unless ary
320
+ while !ary.empty? && !ary.last
321
+ ary.pop
322
+ end
323
+ ary unless ary.empty?
324
+ end
325
+ end
326
+ end