keeguon-spreadsheet 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
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