spreadsheet 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/GUIDE.txt +209 -0
  2. data/History.txt +8 -0
  3. data/LICENSE.txt +619 -0
  4. data/Manifest.txt +46 -0
  5. data/README.txt +54 -0
  6. data/Rakefile +15 -0
  7. data/lib/parseexcel.rb +27 -0
  8. data/lib/parseexcel/parseexcel.rb +75 -0
  9. data/lib/parseexcel/parser.rb +11 -0
  10. data/lib/spreadsheet.rb +79 -0
  11. data/lib/spreadsheet/datatypes.rb +99 -0
  12. data/lib/spreadsheet/encodings.rb +49 -0
  13. data/lib/spreadsheet/excel.rb +75 -0
  14. data/lib/spreadsheet/excel/error.rb +26 -0
  15. data/lib/spreadsheet/excel/internals.rb +322 -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 +37 -0
  19. data/lib/spreadsheet/excel/reader.rb +798 -0
  20. data/lib/spreadsheet/excel/reader/biff5.rb +22 -0
  21. data/lib/spreadsheet/excel/reader/biff8.rb +168 -0
  22. data/lib/spreadsheet/excel/row.rb +67 -0
  23. data/lib/spreadsheet/excel/sst_entry.rb +45 -0
  24. data/lib/spreadsheet/excel/workbook.rb +76 -0
  25. data/lib/spreadsheet/excel/worksheet.rb +85 -0
  26. data/lib/spreadsheet/excel/writer.rb +1 -0
  27. data/lib/spreadsheet/excel/writer/biff8.rb +66 -0
  28. data/lib/spreadsheet/excel/writer/format.rb +270 -0
  29. data/lib/spreadsheet/excel/writer/workbook.rb +586 -0
  30. data/lib/spreadsheet/excel/writer/worksheet.rb +556 -0
  31. data/lib/spreadsheet/font.rb +86 -0
  32. data/lib/spreadsheet/format.rb +172 -0
  33. data/lib/spreadsheet/formula.rb +9 -0
  34. data/lib/spreadsheet/row.rb +87 -0
  35. data/lib/spreadsheet/workbook.rb +120 -0
  36. data/lib/spreadsheet/worksheet.rb +215 -0
  37. data/lib/spreadsheet/writer.rb +29 -0
  38. data/test/data/test_copy.xls +0 -0
  39. data/test/data/test_version_excel5.xls +0 -0
  40. data/test/data/test_version_excel95.xls +0 -0
  41. data/test/data/test_version_excel97.xls +0 -0
  42. data/test/excel/row.rb +29 -0
  43. data/test/font.rb +163 -0
  44. data/test/integration.rb +1021 -0
  45. data/test/workbook.rb +21 -0
  46. data/test/worksheet.rb +62 -0
  47. metadata +113 -0
@@ -0,0 +1,86 @@
1
+ require 'spreadsheet/datatypes'
2
+ require 'spreadsheet/encodings'
3
+
4
+ module Spreadsheet
5
+ ##
6
+ # Font formatting data
7
+ class Font
8
+ include Datatypes
9
+ include Encodings
10
+ attr_accessor :name
11
+ ##
12
+ # You can set the following boolean Font attributes
13
+ # * #italic
14
+ # * #strikeout
15
+ # * #outline
16
+ # * #shadow
17
+ boolean :italic, :strikeout, :outline, :shadow
18
+ ##
19
+ # Font color
20
+ colors :color
21
+ ##
22
+ # Font weight
23
+ # Valid values: :normal, :bold or any positive Integer.
24
+ # In Excel:
25
+ # 100 <= weight <= 1000
26
+ # :bold => 700
27
+ # :normal => 400
28
+ # Default: :normal
29
+ enum :weight, :normal, :bold, Integer, :bold => :b
30
+ ##
31
+ # Escapement
32
+ # Valid values: :normal, :superscript or :subscript.
33
+ # Default: :normal
34
+ enum :escapement, :normal, :superscript, :subscript,
35
+ :subscript => :sub,
36
+ :superscript => :super
37
+ # Font size
38
+ # Valid values: Any positive Integer.
39
+ # Default: 10
40
+ enum :size, 10, Numeric
41
+ # Underline type
42
+ # Valid values: :none, :single, :double, :single_accounting and
43
+ # :double_accounting.
44
+ # Default: :none
45
+ enum :underline, :none, :single, :double,
46
+ :single_accounting, :double_accounting,
47
+ :single => true
48
+ # Font Family
49
+ # Valid values: :none, :roman, :swiss, :modern, :script, :decorative
50
+ # Default: :none
51
+ enum :family, :none, :roman, :swiss, :modern, :script, :decorative
52
+ # Font Family
53
+ # Valid values: :default, :iso_latin1, :symbol, :apple_roman, :shift_jis,
54
+ # :korean_hangul, :korean_johab, :chinese_simplified,
55
+ # :chinese_traditional, :greek, :turkish, :vietnamese,
56
+ # :hebrew, :arabic, :cyrillic, :thai, :iso_latin2, :oem_latin1
57
+ # Default: :default
58
+ enum :encoding, :default, :iso_latin1, :symbol, :apple_roman, :shift_jis,
59
+ :korean_hangul, :korean_johab, :chinese_simplified,
60
+ :chinese_traditional, :greek, :turkish, :vietnamese,
61
+ :hebrew, :arabic, :cyrillic, :thai, :iso_latin2, :oem_latin1
62
+ def initialize name
63
+ self.name = name
64
+ @color = :text
65
+ end
66
+ ##
67
+ # Sets #weight to :bold if(_bool_), :normal otherwise.
68
+ def bold= bool
69
+ self.weight = bool ? :bold : nil
70
+ end
71
+ def key # :nodoc:
72
+ key = @name.dup
73
+ key << '_' << size.to_s
74
+ key << '_' << weight.to_s
75
+ key << '_italic' if italic?
76
+ key << '_strikeout' if strikeout?
77
+ key << '_outline' if outline?
78
+ key << '_shadow' if shadow?
79
+ key << '_' << escapement.to_s
80
+ key << '_' << underline.to_s
81
+ key << '_' << color.to_s
82
+ key << '_' << family.to_s
83
+ key << '_' << encoding.to_s
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,172 @@
1
+ require 'spreadsheet/datatypes'
2
+ require 'spreadsheet/encodings'
3
+ require 'spreadsheet/font'
4
+
5
+ module Spreadsheet
6
+ ##
7
+ # Formatting data
8
+ class Format
9
+ include Encodings
10
+ include Datatypes
11
+ ##
12
+ # You can set the following boolean attributes:
13
+ # #cross_down:: Draws a Line from the top-left to the bottom-right
14
+ # corner of a cell.
15
+ # #cross_up:: Draws a Line from the bottom-left to the top-right
16
+ # corner of a cell.
17
+ # #hidden:: The cell is hidden.
18
+ # #locked:: The cell is locked.
19
+ # #merge_range:: The cell is in a merged range.
20
+ # #shrink:: Shrink the contents to fit the cell.
21
+ # #text_justlast:: Force the last line of a cell to be justified. This
22
+ # probably makes sense if horizontal_align = :justify
23
+ # #left:: Draw a border to the left of the cell.
24
+ # #right:: Draw a border to the right of the cell.
25
+ # #top:: Draw a border at the top of the cell.
26
+ # #bottom:: Draw a border at the bottom of the cell.
27
+ # #rotation_stacked:: Characters in the cell are stacked on top of each
28
+ # other. Excel will ignore other rotation values if
29
+ # this is set.
30
+ boolean :cross_down, :cross_up, :hidden, :locked,
31
+ :merge_range, :shrink, :text_justlast, :text_wrap, :left, :right,
32
+ :top, :bottom, :rotation_stacked
33
+ ##
34
+ # Color attributes
35
+ colors :bottom_color, :top_color, :left_color, :right_color,
36
+ :bg_color, :pattern_fg_color, :pattern_bg_color,
37
+ :diagonal_color
38
+ ##
39
+ # Text direction
40
+ # Valid values: :context, :left_to_right, :right_to_left
41
+ # Default: :context
42
+ enum :text_direction, :context, :left_to_right, :right_to_left,
43
+ :left_to_right => [:ltr, :l2r],
44
+ :right_to_left => [:rtl, :r2l]
45
+ alias :reading_order :text_direction
46
+ alias :reading_order= :text_direction=
47
+ ##
48
+ # Indentation level
49
+ enum :indent_level, 0, Integer
50
+ alias :indent :indent_level
51
+ alias :indent= :indent_level=
52
+ ##
53
+ # Horizontal alignment
54
+ # Valid values: :default, :left, :center, :right, :fill, :justify, :merge,
55
+ # :distributed
56
+ # Default: :default
57
+ enum :horizontal_align, :default, :left, :center, :right, :fill, :justify,
58
+ :merge, :distributed,
59
+ :center => :centre,
60
+ :merge => [ :center_across, :centre_across ],
61
+ :distributed => :equal_space
62
+ ##
63
+ # Vertical alignment
64
+ # Valid values: :bottom, :top, :middle, :justify, :distributed
65
+ # Default: :bottom
66
+ enum :vertical_align, :bottom, :top, :middle, :justify, :distributed,
67
+ :distributed => [:vdistributed, :vequal_space, :equal_space],
68
+ :justify => :vjustify,
69
+ :middle => [:vcenter, :vcentre, :center, :centre]
70
+ attr_accessor :font, :number_format, :name, :pattern, :used_merge
71
+ ##
72
+ # Text rotation
73
+ attr_reader :rotation
74
+ def initialize opts={}
75
+ @font = Font.new client("Arial", 'UTF8')
76
+ @number_format = client 'General', 'UTF8'
77
+ @rotation = 0
78
+ @bg_color = :pattern_bg
79
+ @pattern = 0
80
+ @bottom_color = :border
81
+ @top_color = :border
82
+ @left_color = :border
83
+ @right_color = :border
84
+ @diagonal_color = :border
85
+ @pattern_fg_color = :border
86
+ @pattern_bg_color = :pattern_bg
87
+ # Temp code to prevent merged formats in non-merged cells.
88
+ @used_merge = 0
89
+ opts.each do |key, val|
90
+ writer = "#{key}="
91
+ if @font.respond_to? writer
92
+ @font.send writer, val
93
+ else
94
+ self.send writer, val
95
+ end
96
+ end
97
+ yield self if block_given?
98
+ end
99
+ ##
100
+ # Combined method for both horizontal and vertical alignment. Sets the
101
+ # first valid value (e.g. Format#align = :justify only sets the horizontal
102
+ # alignment. Use one of the aliases prefixed with :v if you need to
103
+ # disambiguate.)
104
+ #
105
+ # This is essentially a backward-compatibility method and may be removed at
106
+ # some point in the future.
107
+ def align= location
108
+ self.horizontal_align = location
109
+ rescue ArgumentError
110
+ self.vertical_align = location rescue ArgumentError
111
+ end
112
+ ##
113
+ # Returns an Array containing the status of the four borders:
114
+ # bottom, top, right, left
115
+ def border
116
+ [bottom,top,right,left]
117
+ end
118
+ ##
119
+ # Activate or deactivate all four borders (left, right, top, bottom)
120
+ def border=(boolean)
121
+ [:bottom=, :top=, :right=, :left=].each do |writer| send writer, boolean end
122
+ end
123
+ ##
124
+ # Returns an Array containing the colors of the four borders:
125
+ # bottom, top, right, left
126
+ def border_color
127
+ [@bottom_color,@top_color,@left_color,@right_color]
128
+ end
129
+ ##
130
+ # Set all four border colors to _color_ (left, right, top, bottom)
131
+ def border_color=(color)
132
+ [:bottom_color=, :top_color=, :right_color=, :left_color=].each do |writer|
133
+ send writer, color end
134
+ end
135
+ ##
136
+ # Set the Text rotation
137
+ # Valid values: Integers from -90 to 90,
138
+ # or :stacked (sets #rotation_stacked to true)
139
+ def rotation=(rot)
140
+ if rot.to_s.downcase == 'stacked'
141
+ @rotation_stacked = true
142
+ @rotation = 0
143
+ elsif rot.kind_of?(Integer)
144
+ @rotation_stacked = false
145
+ @rotation = rot % 360
146
+ else
147
+ raise TypeError, "rotation value must be an Integer or the String 'stacked'"
148
+ end
149
+ end
150
+ ##
151
+ # Backward compatibility method. May disappear at some point in the future.
152
+ def center_across!
153
+ self.horizontal_align = :merge
154
+ end
155
+ alias :merge! :center_across!
156
+ ##
157
+ # Is the cell formatted as a Date?
158
+ def date?
159
+ !!/[YMD]/.match(@number_format.to_s)
160
+ end
161
+ ##
162
+ # Is the cell formatted as a DateTime?
163
+ def datetime?
164
+ !!/([YMD].*[hms])|([hms].*[YMD])/.match(@number_format.to_s)
165
+ end
166
+ ##
167
+ # Is the cell formatted as a Time?
168
+ def time?
169
+ !!/[hms]/.match(@number_format.to_s)
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,9 @@
1
+ module Spreadsheet
2
+ ##
3
+ # Formula implementation. At the moment this is just a placeholder.
4
+ # You may access the last calculated #value, other attributes are needed for
5
+ # writing the Formula back into modified Excel Files.
6
+ class Formula
7
+ attr_accessor :data, :value, :shared
8
+ end
9
+ end
@@ -0,0 +1,87 @@
1
+ module Spreadsheet
2
+ ##
3
+ # The Row class. Encapsulates Cell data and formatting.
4
+ # Since Row is a subclass of Array, you may use all the standard Array methods
5
+ # to manipulate a Row.
6
+ # By convention, Row#at will give you raw values, while Row#[] may be
7
+ # overridden to return enriched data if necessary (see also the Date- and
8
+ # DateTime-handling in Excel::Row#[]
9
+ #
10
+ # Useful Attributes are:
11
+ # #idx:: The 0-based index of this Row in its Worksheet.
12
+ # #formats:: A parallel array containing Formatting information for
13
+ # all cells stored in a Row.
14
+ # #default_format:: The default Format used when writing a Cell if no explicit
15
+ # Format is stored in #formats for the cell.
16
+ # #height:: The height of this Row in points (defaults to 12).
17
+ class Row < Array
18
+ class << self
19
+ def updater *keys
20
+ keys.each do |key|
21
+ define_method key do |*args|
22
+ res = super
23
+ @worksheet.row_updated @idx, self if @worksheet
24
+ res
25
+ end
26
+ end
27
+ end
28
+ end
29
+ attr_reader :formats, :default_format
30
+ attr_accessor :idx, :height, :worksheet
31
+ updater :[]=, :clear, :concat, :delete, :delete_if, :fill, :insert, :map!,
32
+ :pop, :push, :reject!, :replace, :reverse!, :shift, :slice!,
33
+ :sort!, :uniq!, :unshift
34
+ def initialize worksheet, idx, cells=[]
35
+ @worksheet = worksheet
36
+ @idx = idx
37
+ while !cells.empty? && !cells.last
38
+ cells.pop
39
+ end
40
+ super cells
41
+ @first_used ||= index_of_first self
42
+ @first_unused ||= size
43
+ @formats = []
44
+ @height = 12
45
+ end
46
+ ##
47
+ # Set the default Format used when writing a Cell if no explicit Format is
48
+ # stored for the cell.
49
+ def default_format= format
50
+ @worksheet.add_format format
51
+ @default_format = format
52
+ end
53
+ ##
54
+ # #first_unused (really last used + 1) - the 0-based index of the first of
55
+ # all remaining contiguous blank Cells.
56
+ alias :first_unused :size
57
+ ##
58
+ # #first_used the 0-based index of the first non-blank Cell.
59
+ def first_used
60
+ index_of_first self
61
+ end
62
+ ##
63
+ # The Format for the Cell at _idx_ (0-based), or default_format
64
+ # if no Format is set.
65
+ def format idx
66
+ @formats[idx] || @default_format
67
+ end
68
+ ##
69
+ # Set the Format for the Cell at _idx_ (0-based).
70
+ def set_format idx, fmt
71
+ @formats[idx] = fmt
72
+ @worksheet.add_format fmt
73
+ @worksheet.row_updated @idx, self if @worksheet
74
+ fmt
75
+ end
76
+ def inspect
77
+ variables = instance_variables.collect do |name|
78
+ "%s=%s" % [name, instance_variable_get(name)]
79
+ end.join(' ')
80
+ sprintf "#<%s:0x%014x %s %s>", self.class, object_id, variables, super
81
+ end
82
+ private
83
+ def index_of_first ary # :nodoc:
84
+ ary.index(ary.find do |elm| elm end)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,120 @@
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 Encodings
15
+ attr_reader :io, :worksheets, :formats, :fonts
16
+ attr_accessor :encoding, :version, :default_format
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
31
+ end
32
+ ##
33
+ # Add a Format to the Workbook. If you use Row#set_format, you should not
34
+ # need to use this Method.
35
+ def add_format format
36
+ @formats.push(format).uniq!
37
+ format
38
+ end
39
+ ##
40
+ # Add a Worksheet to the Workbook.
41
+ def add_worksheet worksheet
42
+ worksheet.workbook = self
43
+ @worksheets.push worksheet
44
+ worksheet
45
+ end
46
+ ##
47
+ # Create a new Worksheet in this Workbook.
48
+ # Used without options this creates a Worksheet with the name 'WorksheetN'
49
+ # where the new Worksheet is the Nth Worksheet in this Workbook.
50
+ #
51
+ # Use the option <em>:name => 'My pretty Name'</em> to override this
52
+ # behavior.
53
+ def create_worksheet opts = {}
54
+ opts[:name] ||= client("Worksheet#{@worksheets.size.next}", 'UTF8')
55
+ add_worksheet Worksheet.new(opts)
56
+ end
57
+ ##
58
+ # The Font at _idx_
59
+ def font idx
60
+ @fonts[idx]
61
+ end
62
+ ##
63
+ # The Format at _idx_, or - if _idx_ is a String -
64
+ # the Format with name == _idx_
65
+ def format idx
66
+ case idx
67
+ when Integer
68
+ @formats[idx] || @default_format
69
+ when String
70
+ @formats.find do |fmt| fmt.name == idx end
71
+ end
72
+ end
73
+ def inspect
74
+ "#<#{self.class}:#{object_id} "
75
+ variables = (instance_variables - uninspect_variables).collect do |name|
76
+ "%s=%s" % [name, instance_variable_get(name)]
77
+ end.join(' ')
78
+ uninspect = uninspect_variables.collect do |name|
79
+ var = instance_variable_get name
80
+ "%s=%s[%i]" % [name, var.class, var.size]
81
+ end.join(' ')
82
+ sprintf "#<%s:0x%014x %s %s>", self.class, object_id,
83
+ variables, uninspect
84
+ end
85
+ def uninspect_variables # :nodoc:
86
+ %w{@formats @fonts @worksheets}
87
+ end
88
+ ##
89
+ # The Worksheet at _idx_, or - if _idx_ is a String -
90
+ # the Worksheet with name == _idx_
91
+ def worksheet idx
92
+ case idx
93
+ when Integer
94
+ @worksheets[idx]
95
+ when String
96
+ @worksheets.find do |sheet| sheet.name == idx end
97
+ end
98
+ end
99
+ ##
100
+ # Write this Workbook to a File, IO Stream or Writer Object. The latter will
101
+ # make more sense once there are more than just an Excel-Writer available.
102
+ def write io_path_or_writer
103
+ if io_path_or_writer.is_a? Writer
104
+ io_path_or_writer.write self
105
+ else
106
+ writer(io_path_or_writer).write(self)
107
+ end
108
+ end
109
+ ##
110
+ # Returns a new instance of the default Writer class for this Workbook (can
111
+ # only be an Excel::Writer::Workbook at this time)
112
+ def writer io_or_path, type=Excel, version=self.version
113
+ if type == Excel
114
+ Excel::Writer::Workbook.new io_or_path
115
+ else
116
+ raise NotImplementedError, "No Writer defined for #{type}"
117
+ end
118
+ end
119
+ end
120
+ end