ruby-spreadsheet 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.document +5 -0
  2. data/GUIDE.txt +267 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +20 -0
  5. data/History.txt +307 -0
  6. data/LICENSE.txt +619 -0
  7. data/README.txt +91 -0
  8. data/Rakefile +53 -0
  9. data/VERSION +1 -0
  10. data/bin/xlsopcodes +18 -0
  11. data/lib/parseexcel.rb +27 -0
  12. data/lib/parseexcel/parseexcel.rb +75 -0
  13. data/lib/parseexcel/parser.rb +11 -0
  14. data/lib/spreadsheet.rb +79 -0
  15. data/lib/spreadsheet/column.rb +71 -0
  16. data/lib/spreadsheet/compatibility.rb +23 -0
  17. data/lib/spreadsheet/datatypes.rb +110 -0
  18. data/lib/spreadsheet/encodings.rb +46 -0
  19. data/lib/spreadsheet/excel.rb +88 -0
  20. data/lib/spreadsheet/excel/error.rb +26 -0
  21. data/lib/spreadsheet/excel/internals.rb +386 -0
  22. data/lib/spreadsheet/excel/internals/biff5.rb +17 -0
  23. data/lib/spreadsheet/excel/internals/biff8.rb +19 -0
  24. data/lib/spreadsheet/excel/offset.rb +41 -0
  25. data/lib/spreadsheet/excel/reader.rb +1173 -0
  26. data/lib/spreadsheet/excel/reader/biff5.rb +22 -0
  27. data/lib/spreadsheet/excel/reader/biff8.rb +193 -0
  28. data/lib/spreadsheet/excel/row.rb +92 -0
  29. data/lib/spreadsheet/excel/sst_entry.rb +46 -0
  30. data/lib/spreadsheet/excel/workbook.rb +80 -0
  31. data/lib/spreadsheet/excel/worksheet.rb +100 -0
  32. data/lib/spreadsheet/excel/writer.rb +1 -0
  33. data/lib/spreadsheet/excel/writer/biff8.rb +75 -0
  34. data/lib/spreadsheet/excel/writer/format.rb +253 -0
  35. data/lib/spreadsheet/excel/writer/workbook.rb +652 -0
  36. data/lib/spreadsheet/excel/writer/worksheet.rb +948 -0
  37. data/lib/spreadsheet/font.rb +92 -0
  38. data/lib/spreadsheet/format.rb +177 -0
  39. data/lib/spreadsheet/formula.rb +9 -0
  40. data/lib/spreadsheet/helpers.rb +11 -0
  41. data/lib/spreadsheet/link.rb +43 -0
  42. data/lib/spreadsheet/row.rb +132 -0
  43. data/lib/spreadsheet/workbook.rb +120 -0
  44. data/lib/spreadsheet/worksheet.rb +279 -0
  45. data/lib/spreadsheet/writer.rb +30 -0
  46. data/ruby-spreadsheet.gemspec +126 -0
  47. data/test/data/test_changes.xls +0 -0
  48. data/test/data/test_copy.xls +0 -0
  49. data/test/data/test_datetime.xls +0 -0
  50. data/test/data/test_empty.xls +0 -0
  51. data/test/data/test_formula.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/worksheet.rb +23 -0
  58. data/test/font.rb +163 -0
  59. data/test/integration.rb +1281 -0
  60. data/test/row.rb +33 -0
  61. data/test/suite.rb +14 -0
  62. data/test/workbook.rb +21 -0
  63. data/test/worksheet.rb +80 -0
  64. metadata +203 -0
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+ require 'spreadsheet/datatypes'
3
+ require 'spreadsheet/encodings'
4
+
5
+ module Spreadsheet
6
+ ##
7
+ # Font formatting data
8
+ class Font
9
+ include Spreadsheet::Datatypes
10
+ include Spreadsheet::Encodings
11
+ attr_accessor :name
12
+ ##
13
+ # You can set the following boolean Font attributes
14
+ # * #italic
15
+ # * #strikeout
16
+ # * #outline
17
+ # * #shadow
18
+ boolean :italic, :strikeout, :outline, :shadow
19
+ ##
20
+ # Font color
21
+ colors :color
22
+ ##
23
+ # Font weight
24
+ # Valid values: :normal, :bold or any positive Integer.
25
+ # In Excel:
26
+ # 100 <= weight <= 1000
27
+ # :bold => 700
28
+ # :normal => 400
29
+ # Default: :normal
30
+ enum :weight, :normal, :bold, Integer, :bold => :b
31
+ ##
32
+ # Escapement
33
+ # Valid values: :normal, :superscript or :subscript.
34
+ # Default: :normal
35
+ enum :escapement, :normal, :superscript, :subscript,
36
+ :subscript => :sub,
37
+ :superscript => :super
38
+ # Font size
39
+ # Valid values: Any positive Integer.
40
+ # Default: 10
41
+ enum :size, 10, Numeric
42
+ # Underline type
43
+ # Valid values: :none, :single, :double, :single_accounting and
44
+ # :double_accounting.
45
+ # Default: :none
46
+ enum :underline, :none, :single, :double,
47
+ :single_accounting, :double_accounting,
48
+ :single => true
49
+ # Font Family
50
+ # Valid values: :none, :roman, :swiss, :modern, :script, :decorative
51
+ # Default: :none
52
+ enum :family, :none, :roman, :swiss, :modern, :script, :decorative
53
+ # Font Family
54
+ # Valid values: :default, :iso_latin1, :symbol, :apple_roman, :shift_jis,
55
+ # :korean_hangul, :korean_johab, :chinese_simplified,
56
+ # :chinese_traditional, :greek, :turkish, :vietnamese,
57
+ # :hebrew, :arabic, :cyrillic, :thai, :iso_latin2, :oem_latin1
58
+ # Default: :default
59
+ enum :encoding, :default, :iso_latin1, :symbol, :apple_roman, :shift_jis,
60
+ :korean_hangul, :korean_johab, :chinese_simplified,
61
+ :chinese_traditional, :greek, :turkish, :vietnamese,
62
+ :hebrew, :arabic, :baltic, :cyrillic, :thai, :iso_latin2,
63
+ :oem_latin1
64
+ def initialize name, opts={}
65
+ self.name = name
66
+ @color = :text
67
+ opts.each do |key, val|
68
+ self.send "#{key}=", val
69
+ end
70
+ end
71
+ ##
72
+ # Sets #weight to :bold if(_bool_), :normal otherwise.
73
+ def bold= bool
74
+ self.weight = bool ? :bold : nil
75
+ end
76
+ def key # :nodoc:
77
+ key = @name.dup
78
+ underscore = client('_', 'UTF-8')
79
+ key << underscore << client(size.to_s, 'US-ASCII')
80
+ key << client('_', 'UTF-8') << client(weight.to_s, 'US-ASCII')
81
+ key << client('_italic', 'UTF-8') if italic?
82
+ key << client('_strikeout', 'UTF-8') if strikeout?
83
+ key << client('_outline', 'UTF-8') if outline?
84
+ key << client('_shadow', 'UTF-8') if shadow?
85
+ key << underscore << client(escapement.to_s, 'US-ASCII')
86
+ key << underscore << client(underline.to_s, 'US-ASCII')
87
+ key << underscore << client(color.to_s, 'US-ASCII')
88
+ key << underscore << client(family.to_s, 'US-ASCII')
89
+ key << underscore << client(encoding.to_s, 'US-ASCII')
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,177 @@
1
+ # encoding: utf-8
2
+ require 'spreadsheet/datatypes'
3
+ require 'spreadsheet/encodings'
4
+ require 'spreadsheet/font'
5
+
6
+ module Spreadsheet
7
+ ##
8
+ # Formatting data
9
+ class Format
10
+ include Spreadsheet::Datatypes
11
+ include Spreadsheet::Encodings
12
+ ##
13
+ # You can set the following boolean attributes:
14
+ # #cross_down:: Draws a Line from the top-left to the bottom-right
15
+ # corner of a cell.
16
+ # #cross_up:: Draws a Line from the bottom-left to the top-right
17
+ # corner of a cell.
18
+ # #hidden:: The cell is hidden.
19
+ # #locked:: The cell is locked.
20
+ # #merge_range:: The cell is in a merged range.
21
+ # #shrink:: Shrink the contents to fit the cell.
22
+ # #text_justlast:: Force the last line of a cell to be justified. This
23
+ # probably makes sense if horizontal_align = :justify
24
+ # #left:: Draw a border to the left of the cell.
25
+ # #right:: Draw a border to the right of the cell.
26
+ # #top:: Draw a border at the top of the cell.
27
+ # #bottom:: Draw a border at the bottom of the cell.
28
+ # #rotation_stacked:: Characters in the cell are stacked on top of each
29
+ # other. Excel will ignore other rotation values if
30
+ # this is set.
31
+ boolean :cross_down, :cross_up, :hidden, :locked,
32
+ :merge_range, :shrink, :text_justlast, :text_wrap, :left, :right,
33
+ :top, :bottom, :rotation_stacked
34
+ ##
35
+ # Color attributes
36
+ colors :bottom_color, :top_color, :left_color, :right_color,
37
+ :pattern_fg_color, :pattern_bg_color,
38
+ :diagonal_color
39
+ ##
40
+ # Text direction
41
+ # Valid values: :context, :left_to_right, :right_to_left
42
+ # Default: :context
43
+ enum :text_direction, :context, :left_to_right, :right_to_left,
44
+ :left_to_right => [:ltr, :l2r],
45
+ :right_to_left => [:rtl, :r2l]
46
+ alias :reading_order :text_direction
47
+ alias :reading_order= :text_direction=
48
+ ##
49
+ # Indentation level
50
+ enum :indent_level, 0, Integer
51
+ alias :indent :indent_level
52
+ alias :indent= :indent_level=
53
+ ##
54
+ # Horizontal alignment
55
+ # Valid values: :default, :left, :center, :right, :fill, :justify, :merge,
56
+ # :distributed
57
+ # Default: :default
58
+ enum :horizontal_align, :default, :left, :center, :right, :fill, :justify,
59
+ :merge, :distributed,
60
+ :center => :centre,
61
+ :merge => [ :center_across, :centre_across ],
62
+ :distributed => :equal_space
63
+ ##
64
+ # Vertical alignment
65
+ # Valid values: :bottom, :top, :middle, :justify, :distributed
66
+ # Default: :bottom
67
+ enum :vertical_align, :bottom, :top, :middle, :justify, :distributed,
68
+ :distributed => [:vdistributed, :vequal_space, :equal_space],
69
+ :justify => :vjustify,
70
+ :middle => [:vcenter, :vcentre, :center, :centre]
71
+ attr_accessor :font, :number_format, :name, :pattern, :used_merge
72
+ ##
73
+ # Text rotation
74
+ attr_reader :rotation
75
+ def initialize opts={}
76
+ @font = Font.new client("Arial", 'UTF-8'), :family => :swiss
77
+ @number_format = client 'GENERAL', 'UTF-8'
78
+ @rotation = 0
79
+ @pattern = 0
80
+ @bottom_color = :builtin_black
81
+ @top_color = :builtin_black
82
+ @left_color = :builtin_black
83
+ @right_color = :builtin_black
84
+ @diagonal_color = :builtin_black
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
+ !!Regexp.new(client("[YMD]", 'UTF-8')).match(@number_format.to_s)
160
+ end
161
+ ##
162
+ # Is the cell formatted as a Date or Time?
163
+ def date_or_time?
164
+ !!Regexp.new(client("[hmsYMD]", 'UTF-8')).match(@number_format.to_s)
165
+ end
166
+ ##
167
+ # Is the cell formatted as a DateTime?
168
+ def datetime?
169
+ !!Regexp.new(client("([YMD].*[HS])|([HS].*[YMD])", 'UTF-8')).match(@number_format.to_s)
170
+ end
171
+ ##
172
+ # Is the cell formatted as a Time?
173
+ def time?
174
+ !!Regexp.new(client("[hms]", 'UTF-8')).match(@number_format.to_s)
175
+ end
176
+ end
177
+ 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,11 @@
1
+ class Array
2
+ def rcompact
3
+ dup.rcompact!
4
+ end
5
+ def rcompact!
6
+ while !empty? && last.nil?
7
+ pop
8
+ end
9
+ self
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ require 'uri'
2
+ require 'spreadsheet/encodings'
3
+
4
+ module Spreadsheet
5
+ ##
6
+ # The Link class. Is a Subclass of String, which lets you treat a Cell that
7
+ # contains a Link just as if it was a String (containing the link's description
8
+ # if there is one or the url with fragment otherwise), but gives you access
9
+ # to the url, fragment and target_frame if you need it.
10
+ #
11
+ #
12
+ # Interesting Attributes
13
+ # #url :: The Uniform Resource Location this Link points to.
14
+ # #fragment :: Also called text mark: http://example.com/page.html#fragment
15
+ # #target_frame :: Which frame a Link should be opened in, should also support
16
+ # the special frames _blank, _parent, _self and _top.
17
+ # #dos :: Excel may store a DOS-Filename together with the long
18
+ # Filename introduced in VFAT. You probably will not need this,
19
+ # but if you do, here is where you can find it.
20
+ class Link < String
21
+ include Encodings
22
+ attr_accessor :target_frame, :url, :dos, :fragment
23
+ def initialize url='', description=url, fragment=nil
24
+ super description
25
+ @url = url
26
+ @fragment = fragment
27
+ end
28
+ ##
29
+ # The Url with the fragment appended if present.
30
+ def href
31
+ href = (@url || @dos).to_s.dup
32
+ if @fragment
33
+ href << client('#', 'UTF-8') << @fragment
34
+ end
35
+ href
36
+ end
37
+ ##
38
+ # Attempts to parse the output of href. May raise a URI::InvalidURIError
39
+ def to_uri
40
+ URI.parse href
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,132 @@
1
+ require 'spreadsheet/helpers'
2
+
3
+ module Spreadsheet
4
+ ##
5
+ # The Row class. Encapsulates Cell data and formatting.
6
+ # Since Row is a subclass of Array, you may use all the standard Array methods
7
+ # to manipulate a Row.
8
+ # By convention, Row#at will give you raw values, while Row#[] may be
9
+ # overridden to return enriched data if necessary (see also the Date- and
10
+ # DateTime-handling in Excel::Row#[]
11
+ #
12
+ # Useful Attributes are:
13
+ # #idx:: The 0-based index of this Row in its Worksheet.
14
+ # #formats:: A parallel array containing Formatting information for
15
+ # all cells stored in a Row.
16
+ # #default_format:: The default Format used when writing a Cell if no explicit
17
+ # Format is stored in #formats for the cell.
18
+ # #height:: The height of this Row in points (defaults to 12).
19
+ class Row < Array
20
+ include Datatypes
21
+ class << self
22
+ def format_updater *keys
23
+ keys.each do |key|
24
+ unless instance_methods.include? "unupdated_#{key}="
25
+ alias_method :"unupdated_#{key}=", :"#{key}="
26
+ define_method "#{key}=" do |value|
27
+ send "unupdated_#{key}=", value
28
+ @worksheet.row_updated @idx, self if @worksheet
29
+ value
30
+ end
31
+ end
32
+ end
33
+ end
34
+ def updater *keys
35
+ keys.each do |key|
36
+ ## Passing blocks to methods defined with define_method is not possible
37
+ # in Ruby 1.8:
38
+ # http://groups.google.com/group/ruby-talk-google/msg/778184912b769e5f
39
+ # use class_eval as suggested by someone else in
40
+ # http://rubyforge.org/tracker/index.php?func=detail&aid=25732&group_id=678&atid=2677
41
+ class_eval <<-SRC, __FILE__, __LINE__
42
+ def #{key}(*args)
43
+ res = super(*args)
44
+ @worksheet.row_updated @idx, self if @worksheet
45
+ res
46
+ end
47
+ SRC
48
+ end
49
+ end
50
+ end
51
+ attr_reader :formats, :default_format
52
+ attr_accessor :idx, :height, :worksheet
53
+ boolean :hidden, :collapsed
54
+ enum :outline_level, 0, Integer
55
+ updater :[]=, :clear, :concat, :delete, :delete_if, :fill, :insert, :map!,
56
+ :pop, :push, :reject!, :replace, :reverse!, :shift, :slice!,
57
+ :sort!, :uniq!, :unshift
58
+ format_updater :collapsed, :height, :hidden, :outline_level
59
+ def initialize worksheet, idx, cells=[]
60
+ @default_format = nil
61
+ @worksheet = worksheet
62
+ @idx = idx
63
+ super cells
64
+ @formats = []
65
+ @height = 12.1
66
+ end
67
+ ##
68
+ # Set the default Format used when writing a Cell if no explicit Format is
69
+ # stored for the cell.
70
+ def default_format= format
71
+ @worksheet.add_format format if @worksheet
72
+ @default_format = format
73
+ end
74
+ format_updater :default_format
75
+ ##
76
+ # #first_used the 0-based index of the first non-blank Cell.
77
+ def first_used
78
+ [ index_of_first(self), index_of_first(@formats) ].compact.min
79
+ end
80
+ ##
81
+ # The Format for the Cell at _idx_ (0-based), or the first valid Format in
82
+ # Row#default_format, Column#default_format and Worksheet#default_format.
83
+ def format idx
84
+ @formats[idx] || @default_format \
85
+ || @worksheet.column(idx).default_format if @worksheet
86
+ end
87
+ ##
88
+ # Returns a copy of self with nil-values appended for empty cells that have
89
+ # an associated Format.
90
+ # This is primarily a helper-function for the writer classes.
91
+ def formatted
92
+ copy = dup
93
+ @formats.rcompact!
94
+ if copy.length < @formats.size
95
+ copy.concat Array.new(@formats.size - copy.length)
96
+ end
97
+ copy
98
+ end
99
+ ##
100
+ # Same as Row#size, but takes into account formatted empty cells
101
+ def formatted_size
102
+ @formats.rcompact!
103
+ sz = size
104
+ fs = @formats.size
105
+ fs > sz ? fs : sz
106
+ end
107
+ ##
108
+ # #first_unused (really last used + 1) - the 0-based index of the first of
109
+ # all remaining contiguous blank Cells.
110
+ alias :first_unused :formatted_size
111
+ def inspect
112
+ variables = instance_variables.collect do |name|
113
+ "%s=%s" % [name, instance_variable_get(name)]
114
+ end.join(' ')
115
+ sprintf "#<%s:0x%014x %s %s>", self.class, object_id, variables, super
116
+ end
117
+ ##
118
+ # Set the Format for the Cell at _idx_ (0-based).
119
+ def set_format idx, fmt
120
+ @formats[idx] = fmt
121
+ @worksheet.add_format fmt
122
+ @worksheet.row_updated @idx, self if @worksheet
123
+ fmt
124
+ end
125
+ private
126
+ def index_of_first ary # :nodoc:
127
+ if first = ary.find do |elm| !elm.nil? end
128
+ ary.index first
129
+ end
130
+ end
131
+ end
132
+ end