roo 1.13.2 → 2.10.1

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 (236) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +17 -0
  3. data/.github/issue_template.md +16 -0
  4. data/.github/pull_request_template.md +14 -0
  5. data/.github/workflows/pull-request.yml +15 -0
  6. data/.github/workflows/ruby.yml +34 -0
  7. data/.gitignore +11 -0
  8. data/.rubocop.yml +186 -0
  9. data/.simplecov +4 -0
  10. data/CHANGELOG.md +702 -0
  11. data/Gemfile +18 -12
  12. data/Guardfile +23 -0
  13. data/LICENSE +5 -1
  14. data/README.md +328 -0
  15. data/Rakefile +23 -23
  16. data/examples/roo_soap_client.rb +28 -31
  17. data/examples/roo_soap_server.rb +4 -6
  18. data/examples/write_me.rb +9 -10
  19. data/lib/roo/base.rb +317 -504
  20. data/lib/roo/constants.rb +7 -0
  21. data/lib/roo/csv.rb +141 -113
  22. data/lib/roo/errors.rb +11 -0
  23. data/lib/roo/excelx/cell/base.rb +108 -0
  24. data/lib/roo/excelx/cell/boolean.rb +30 -0
  25. data/lib/roo/excelx/cell/date.rb +28 -0
  26. data/lib/roo/excelx/cell/datetime.rb +107 -0
  27. data/lib/roo/excelx/cell/empty.rb +20 -0
  28. data/lib/roo/excelx/cell/number.rb +99 -0
  29. data/lib/roo/excelx/cell/string.rb +19 -0
  30. data/lib/roo/excelx/cell/time.rb +44 -0
  31. data/lib/roo/excelx/cell.rb +110 -0
  32. data/lib/roo/excelx/comments.rb +55 -0
  33. data/lib/roo/excelx/coordinate.rb +19 -0
  34. data/lib/roo/excelx/extractor.rb +39 -0
  35. data/lib/roo/excelx/format.rb +71 -0
  36. data/lib/roo/excelx/images.rb +26 -0
  37. data/lib/roo/excelx/relationships.rb +33 -0
  38. data/lib/roo/excelx/shared.rb +39 -0
  39. data/lib/roo/excelx/shared_strings.rb +151 -0
  40. data/lib/roo/excelx/sheet.rb +151 -0
  41. data/lib/roo/excelx/sheet_doc.rb +257 -0
  42. data/lib/roo/excelx/styles.rb +64 -0
  43. data/lib/roo/excelx/workbook.rb +64 -0
  44. data/lib/roo/excelx.rb +407 -601
  45. data/lib/roo/font.rb +17 -0
  46. data/lib/roo/formatters/base.rb +15 -0
  47. data/lib/roo/formatters/csv.rb +84 -0
  48. data/lib/roo/formatters/matrix.rb +23 -0
  49. data/lib/roo/formatters/xml.rb +31 -0
  50. data/lib/roo/formatters/yaml.rb +40 -0
  51. data/lib/roo/helpers/default_attr_reader.rb +20 -0
  52. data/lib/roo/helpers/weak_instance_cache.rb +41 -0
  53. data/lib/roo/libre_office.rb +4 -0
  54. data/lib/roo/link.rb +34 -0
  55. data/lib/roo/open_office.rb +631 -0
  56. data/lib/roo/spreadsheet.rb +28 -23
  57. data/lib/roo/tempdir.rb +24 -0
  58. data/lib/roo/utils.rb +128 -0
  59. data/lib/roo/version.rb +3 -0
  60. data/lib/roo.rb +26 -24
  61. data/roo.gemspec +29 -203
  62. data/spec/helpers.rb +5 -0
  63. data/spec/lib/roo/base_spec.rb +291 -3
  64. data/spec/lib/roo/csv_spec.rb +38 -11
  65. data/spec/lib/roo/excelx/cell/time_spec.rb +15 -0
  66. data/spec/lib/roo/excelx/format_spec.rb +7 -6
  67. data/spec/lib/roo/excelx/relationships_spec.rb +43 -0
  68. data/spec/lib/roo/excelx/sheet_doc_spec.rb +11 -0
  69. data/spec/lib/roo/excelx_spec.rb +672 -11
  70. data/spec/lib/roo/libreoffice_spec.rb +16 -6
  71. data/spec/lib/roo/openoffice_spec.rb +30 -8
  72. data/spec/lib/roo/spreadsheet_spec.rb +60 -12
  73. data/spec/lib/roo/strict_spec.rb +43 -0
  74. data/spec/lib/roo/utils_spec.rb +119 -0
  75. data/spec/lib/roo/weak_instance_cache_spec.rb +92 -0
  76. data/spec/lib/roo_spec.rb +0 -0
  77. data/spec/spec_helper.rb +7 -6
  78. data/test/all_ss.rb +12 -11
  79. data/test/excelx/cell/test_attr_reader_default.rb +72 -0
  80. data/test/excelx/cell/test_base.rb +68 -0
  81. data/test/excelx/cell/test_boolean.rb +36 -0
  82. data/test/excelx/cell/test_date.rb +38 -0
  83. data/test/excelx/cell/test_datetime.rb +45 -0
  84. data/test/excelx/cell/test_empty.rb +18 -0
  85. data/test/excelx/cell/test_number.rb +90 -0
  86. data/test/excelx/cell/test_string.rb +48 -0
  87. data/test/excelx/cell/test_time.rb +30 -0
  88. data/test/excelx/test_coordinate.rb +51 -0
  89. data/test/formatters/test_csv.rb +136 -0
  90. data/test/formatters/test_matrix.rb +76 -0
  91. data/test/formatters/test_xml.rb +78 -0
  92. data/test/formatters/test_yaml.rb +20 -0
  93. data/test/helpers/test_accessing_files.rb +81 -0
  94. data/test/helpers/test_comments.rb +43 -0
  95. data/test/helpers/test_formulas.rb +9 -0
  96. data/test/helpers/test_labels.rb +103 -0
  97. data/test/helpers/test_sheets.rb +55 -0
  98. data/test/helpers/test_styles.rb +62 -0
  99. data/test/roo/test_base.rb +182 -0
  100. data/test/roo/test_csv.rb +88 -0
  101. data/test/roo/test_excelx.rb +360 -0
  102. data/test/roo/test_libre_office.rb +9 -0
  103. data/test/roo/test_open_office.rb +289 -0
  104. data/test/test_helper.rb +123 -59
  105. data/test/test_roo.rb +392 -2292
  106. metadata +153 -298
  107. data/CHANGELOG +0 -417
  108. data/Gemfile.lock +0 -78
  109. data/README.markdown +0 -126
  110. data/VERSION +0 -1
  111. data/lib/roo/excel.rb +0 -355
  112. data/lib/roo/excel2003xml.rb +0 -300
  113. data/lib/roo/google.rb +0 -292
  114. data/lib/roo/openoffice.rb +0 -496
  115. data/lib/roo/roo_rails_helper.rb +0 -83
  116. data/lib/roo/worksheet.rb +0 -18
  117. data/scripts/txt2html +0 -67
  118. data/spec/lib/roo/excel2003xml_spec.rb +0 -15
  119. data/spec/lib/roo/excel_spec.rb +0 -17
  120. data/spec/lib/roo/google_spec.rb +0 -64
  121. data/test/files/1900_base.xls +0 -0
  122. data/test/files/1900_base.xlsx +0 -0
  123. data/test/files/1904_base.xls +0 -0
  124. data/test/files/1904_base.xlsx +0 -0
  125. data/test/files/Bibelbund.csv +0 -3741
  126. data/test/files/Bibelbund.ods +0 -0
  127. data/test/files/Bibelbund.xls +0 -0
  128. data/test/files/Bibelbund.xlsx +0 -0
  129. data/test/files/Bibelbund.xml +0 -62518
  130. data/test/files/Bibelbund1.ods +0 -0
  131. data/test/files/Pfand_from_windows_phone.xlsx +0 -0
  132. data/test/files/bad_excel_date.xls +0 -0
  133. data/test/files/bbu.ods +0 -0
  134. data/test/files/bbu.xls +0 -0
  135. data/test/files/bbu.xlsx +0 -0
  136. data/test/files/bbu.xml +0 -152
  137. data/test/files/bode-v1.ods.zip +0 -0
  138. data/test/files/bode-v1.xls.zip +0 -0
  139. data/test/files/boolean.csv +0 -2
  140. data/test/files/boolean.ods +0 -0
  141. data/test/files/boolean.xls +0 -0
  142. data/test/files/boolean.xlsx +0 -0
  143. data/test/files/boolean.xml +0 -112
  144. data/test/files/borders.ods +0 -0
  145. data/test/files/borders.xls +0 -0
  146. data/test/files/borders.xlsx +0 -0
  147. data/test/files/borders.xml +0 -144
  148. data/test/files/bug-numbered-sheet-names.xlsx +0 -0
  149. data/test/files/bug-row-column-fixnum-float.xls +0 -0
  150. data/test/files/bug-row-column-fixnum-float.xml +0 -127
  151. data/test/files/comments.ods +0 -0
  152. data/test/files/comments.xls +0 -0
  153. data/test/files/comments.xlsx +0 -0
  154. data/test/files/csvtypes.csv +0 -1
  155. data/test/files/datetime.ods +0 -0
  156. data/test/files/datetime.xls +0 -0
  157. data/test/files/datetime.xlsx +0 -0
  158. data/test/files/datetime.xml +0 -142
  159. data/test/files/datetime_floatconv.xls +0 -0
  160. data/test/files/datetime_floatconv.xml +0 -148
  161. data/test/files/dreimalvier.ods +0 -0
  162. data/test/files/emptysheets.ods +0 -0
  163. data/test/files/emptysheets.xls +0 -0
  164. data/test/files/emptysheets.xlsx +0 -0
  165. data/test/files/emptysheets.xml +0 -105
  166. data/test/files/excel2003.xml +0 -21140
  167. data/test/files/false_encoding.xls +0 -0
  168. data/test/files/false_encoding.xml +0 -132
  169. data/test/files/file_item_error.xlsx +0 -0
  170. data/test/files/formula.ods +0 -0
  171. data/test/files/formula.xls +0 -0
  172. data/test/files/formula.xlsx +0 -0
  173. data/test/files/formula.xml +0 -134
  174. data/test/files/formula_parse_error.xls +0 -0
  175. data/test/files/formula_parse_error.xml +0 -1833
  176. data/test/files/formula_string_error.xlsx +0 -0
  177. data/test/files/html-escape.ods +0 -0
  178. data/test/files/link.xls +0 -0
  179. data/test/files/link.xlsx +0 -0
  180. data/test/files/matrix.ods +0 -0
  181. data/test/files/matrix.xls +0 -0
  182. data/test/files/named_cells.ods +0 -0
  183. data/test/files/named_cells.xls +0 -0
  184. data/test/files/named_cells.xlsx +0 -0
  185. data/test/files/no_spreadsheet_file.txt +0 -1
  186. data/test/files/numbers1.csv +0 -18
  187. data/test/files/numbers1.ods +0 -0
  188. data/test/files/numbers1.xls +0 -0
  189. data/test/files/numbers1.xlsx +0 -0
  190. data/test/files/numbers1.xml +0 -312
  191. data/test/files/numeric-link.xlsx +0 -0
  192. data/test/files/only_one_sheet.ods +0 -0
  193. data/test/files/only_one_sheet.xls +0 -0
  194. data/test/files/only_one_sheet.xlsx +0 -0
  195. data/test/files/only_one_sheet.xml +0 -67
  196. data/test/files/paragraph.ods +0 -0
  197. data/test/files/paragraph.xls +0 -0
  198. data/test/files/paragraph.xlsx +0 -0
  199. data/test/files/paragraph.xml +0 -127
  200. data/test/files/prova.xls +0 -0
  201. data/test/files/ric.ods +0 -0
  202. data/test/files/simple_spreadsheet.ods +0 -0
  203. data/test/files/simple_spreadsheet.xls +0 -0
  204. data/test/files/simple_spreadsheet.xlsx +0 -0
  205. data/test/files/simple_spreadsheet.xml +0 -225
  206. data/test/files/simple_spreadsheet_from_italo.ods +0 -0
  207. data/test/files/simple_spreadsheet_from_italo.xls +0 -0
  208. data/test/files/simple_spreadsheet_from_italo.xml +0 -242
  209. data/test/files/so_datetime.csv +0 -7
  210. data/test/files/style.ods +0 -0
  211. data/test/files/style.xls +0 -0
  212. data/test/files/style.xlsx +0 -0
  213. data/test/files/style.xml +0 -154
  214. data/test/files/time-test.csv +0 -2
  215. data/test/files/time-test.ods +0 -0
  216. data/test/files/time-test.xls +0 -0
  217. data/test/files/time-test.xlsx +0 -0
  218. data/test/files/time-test.xml +0 -131
  219. data/test/files/type_excel.ods +0 -0
  220. data/test/files/type_excel.xlsx +0 -0
  221. data/test/files/type_excelx.ods +0 -0
  222. data/test/files/type_excelx.xls +0 -0
  223. data/test/files/type_openoffice.xls +0 -0
  224. data/test/files/type_openoffice.xlsx +0 -0
  225. data/test/files/whitespace.ods +0 -0
  226. data/test/files/whitespace.xls +0 -0
  227. data/test/files/whitespace.xlsx +0 -0
  228. data/test/files/whitespace.xml +0 -184
  229. data/test/rm_sub_test.rb +0 -12
  230. data/test/rm_test.rb +0 -7
  231. data/test/test_generic_spreadsheet.rb +0 -259
  232. data/website/index.html +0 -385
  233. data/website/index.txt +0 -423
  234. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  235. data/website/stylesheets/screen.css +0 -130
  236. data/website/template.rhtml +0 -48
data/lib/roo/excel.rb DELETED
@@ -1,355 +0,0 @@
1
- require 'spreadsheet'
2
-
3
- # Class for handling Excel-Spreadsheets
4
- class Roo::Excel < Roo::Base
5
- FORMULAS_MESSAGE = 'the spreadsheet gem does not support forumulas, so roo can not.'
6
- CHARGUESS =
7
- begin
8
- require 'charguess'
9
- true
10
- rescue LoadError
11
- false
12
- end
13
-
14
- attr_reader :workbook
15
-
16
- # Creates a new Excel spreadsheet object.
17
- # Parameter packed: :zip - File is a zip-file
18
- def initialize(filename, options = {}, deprecated_file_warning = :error)
19
- if Hash === options
20
- packed = options[:packed]
21
- file_warning = options[:file_warning] || :error
22
- mode = options[:mode] || "rb+"
23
- else
24
- warn 'Supplying `packed` or `file_warning` as separate arguments to `Roo::Excel.new` is deprecated. Use an options hash instead.'
25
- packed = options
26
- mode = "rb+"
27
- file_warning = deprecated_file_warning
28
- end
29
-
30
- file_type_check(filename,'.xls','an Excel', file_warning, packed)
31
- make_tmpdir do |tmpdir|
32
- filename = download_uri(filename, tmpdir) if uri?(filename)
33
- filename = open_from_stream(filename[7..-1], tmpdir) if filename[0,7] == "stream:"
34
- filename = unzip(filename, tmpdir) if packed == :zip
35
-
36
- @filename = filename
37
- unless File.file?(@filename)
38
- raise IOError, "file #{@filename} does not exist"
39
- end
40
- @workbook = Spreadsheet.open(filename, mode)
41
- end
42
- super(filename, options)
43
- @formula = Hash.new
44
- @fonts = Hash.new
45
- end
46
-
47
- def encoding=(codepage)
48
- @workbook.encoding = codepage
49
- end
50
-
51
- # returns an array of sheet names in the spreadsheet
52
- def sheets
53
- @workbook.worksheets.collect {|worksheet| normalize_string(worksheet.name)}
54
- end
55
-
56
- # this method lets you find the worksheet with the most data
57
- def longest_sheet
58
- sheet(@workbook.worksheets.inject {|m,o|
59
- o.row_count > m.row_count ? o : m
60
- }.name)
61
- end
62
-
63
- # returns the content of a cell. The upper left corner is (1,1) or ('A',1)
64
- def cell(row,col,sheet=nil)
65
- sheet ||= @default_sheet
66
- validate_sheet!(sheet)
67
-
68
- read_cells(sheet)
69
- raise "should be read" unless @cells_read[sheet]
70
- row,col = normalize(row,col)
71
- if celltype(row,col,sheet) == :date
72
- yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
73
- return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
74
- end
75
- if celltype(row,col,sheet) == :string
76
- return platform_specific_encoding(@cell[sheet][[row,col]])
77
- else
78
- if @cell[sheet] and @cell[sheet][[row,col]]
79
- return @cell[sheet][[row,col]]
80
- else
81
- return nil
82
- end
83
- end
84
- end
85
-
86
- # returns the type of a cell:
87
- # * :float
88
- # * :string,
89
- # * :date
90
- # * :percentage
91
- # * :formula
92
- # * :time
93
- # * :datetime
94
- def celltype(row,col,sheet=nil)
95
- sheet ||= @default_sheet
96
- read_cells(sheet)
97
- row,col = normalize(row,col)
98
- begin
99
- if @formula[sheet] and @formula[sheet][[row,col]]
100
- :formula
101
- elsif @cell_type[sheet]
102
- @cell_type[sheet][[row,col]]
103
- end
104
- rescue
105
- puts "Error in sheet #{sheet}, row #{row}, col #{col}"
106
- raise
107
- end
108
- end
109
-
110
- # returns NO formula in excel spreadsheets
111
- def formula(row,col,sheet=nil)
112
- raise NotImplementedError, FORMULAS_MESSAGE
113
- end
114
- alias_method :formula?, :formula
115
-
116
- # returns NO formulas in excel spreadsheets
117
- def formulas(sheet=nil)
118
- raise NotImplementedError, FORMULAS_MESSAGE
119
- end
120
-
121
- # Given a cell, return the cell's font
122
- def font(row, col, sheet=nil)
123
- sheet ||= @default_sheet
124
- read_cells(sheet)
125
- row,col = normalize(row,col)
126
- @fonts[sheet][[row,col]]
127
- end
128
-
129
- # shows the internal representation of all cells
130
- # mainly for debugging purposes
131
- def to_s(sheet=nil)
132
- sheet ||= @default_sheet
133
- read_cells(sheet)
134
- @cell[sheet].inspect
135
- end
136
-
137
- private
138
-
139
- # converts name of a sheet to index (0,1,2,..)
140
- def sheet_no(name)
141
- return name-1 if name.kind_of?(Fixnum)
142
- i = 0
143
- @workbook.worksheets.each do |worksheet|
144
- return i if name == normalize_string(worksheet.name)
145
- i += 1
146
- end
147
- raise StandardError, "sheet '#{name}' not found"
148
- end
149
-
150
- def normalize_string(value)
151
- value = every_second_null?(value) ? remove_every_second_null(value) : value
152
- if CHARGUESS && encoding = CharGuess::guess(value)
153
- encoding.encode Encoding::UTF_8
154
- else
155
- platform_specific_encoding(value)
156
- end
157
- end
158
-
159
- def platform_specific_encoding(value)
160
- result =
161
- case RUBY_PLATFORM.downcase
162
- when /darwin|solaris/
163
- value.encode Encoding::UTF_8
164
- when /mswin32/
165
- value.encode Encoding::ISO_8859_1
166
- else
167
- value
168
- end
169
- if every_second_null?(result)
170
- result = remove_every_second_null(result)
171
- end
172
- result
173
- end
174
-
175
- def every_second_null?(str)
176
- result = true
177
- return false if str.length < 2
178
- 0.upto(str.length/2-1) do |i|
179
- if str[i*2+1,1] != "\000"
180
- result = false
181
- break
182
- end
183
- end
184
- result
185
- end
186
-
187
- def remove_every_second_null(str)
188
- result = ''
189
- 0.upto(str.length/2-1) do |i|
190
- c = str[i*2,1]
191
- result += c
192
- end
193
- result
194
- end
195
-
196
- # helper function to set the internal representation of cells
197
- def set_cell_values(sheet,row,col,i,v,value_type,formula,tr,font)
198
- #key = "#{y},#{x+i}"
199
- key = [row,col+i]
200
- @cell_type[sheet] = {} unless @cell_type[sheet]
201
- @cell_type[sheet][key] = value_type
202
- @formula[sheet] = {} unless @formula[sheet]
203
- @formula[sheet][key] = formula if formula
204
- @cell[sheet] = {} unless @cell[sheet]
205
- @fonts[sheet] = {} unless @fonts[sheet]
206
- @fonts[sheet][key] = font
207
-
208
- @cell[sheet][key] =
209
- case value_type
210
- when :float
211
- v.to_f
212
- when :string
213
- v
214
- when :date
215
- v
216
- when :datetime
217
- @cell[sheet][key] = DateTime.new(v.year,v.month,v.day,v.hour,v.min,v.sec)
218
- when :percentage
219
- v.to_f
220
- when :time
221
- v
222
- else
223
- v
224
- end
225
- end
226
-
227
- # ruby-spreadsheet has a font object so we're extending it
228
- # with our own functionality but still providing full access
229
- # to the user for other font information
230
- module ExcelFontExtensions
231
- def bold?(*args)
232
- #From ruby-spreadsheet doc: 100 <= weight <= 1000, bold => 700, normal => 400
233
- weight == 700
234
- end
235
-
236
- def italic?
237
- italic
238
- end
239
-
240
- def underline?
241
- underline != :none
242
- end
243
- end
244
-
245
- # read all cells in the selected sheet
246
- def read_cells(sheet=nil)
247
- sheet ||= @default_sheet
248
- validate_sheet!(sheet)
249
- return if @cells_read[sheet]
250
-
251
- worksheet = @workbook.worksheet(sheet_no(sheet))
252
- row_index=1
253
- worksheet.each(0) do |row|
254
- (0..row.size).each do |cell_index|
255
- cell = row.at(cell_index)
256
- next if cell.nil? #skip empty cells
257
- next if cell.class == Spreadsheet::Formula && cell.value.nil? # skip empty formula cells
258
- value_type, v =
259
- if date_or_time?(row, cell_index)
260
- read_cell_date_or_time(row, cell_index)
261
- else
262
- read_cell(row, cell_index)
263
- end
264
- formula = tr = nil #TODO:???
265
- col_index = cell_index + 1
266
- font = row.format(cell_index).font
267
- font.extend(ExcelFontExtensions)
268
- set_cell_values(sheet,row_index,col_index,0,v,value_type,formula,tr,font)
269
- end #row
270
- row_index += 1
271
- end # worksheet
272
- @cells_read[sheet] = true
273
- end
274
-
275
- # Get the contents of a cell, accounting for the
276
- # way formula stores the value
277
- def read_cell_content(row, idx)
278
- cell = row.at(idx)
279
- cell = row[idx] if row[idx].class == Spreadsheet::Link
280
- cell = cell.value if cell.class == Spreadsheet::Formula
281
- cell
282
- end
283
-
284
- # Test the cell to see if it's a valid date/time.
285
- def date_or_time?(row, idx)
286
- format = row.format(idx)
287
- if format.date_or_time?
288
- cell = read_cell_content(row, idx)
289
- true if Float(cell) > 0 rescue false
290
- else
291
- false
292
- end
293
- end
294
-
295
- # Read the date-time cell and convert to,
296
- # the date-time values for Roo
297
- def read_cell_date_or_time(row, idx)
298
- cell = read_cell_content(row, idx)
299
- cell = cell.to_s.to_f
300
- if cell < 1.0
301
- value_type = :time
302
- f = cell*24.0*60.0*60.0
303
- secs = f.round
304
- h = (secs / 3600.0).floor
305
- secs = secs - 3600*h
306
- m = (secs / 60.0).floor
307
- secs = secs - 60*m
308
- s = secs
309
- value = h*3600+m*60+s
310
- else
311
- if row.at(idx).class == Spreadsheet::Formula
312
- datetime = row.send(:_datetime, cell)
313
- else
314
- datetime = row.datetime(idx)
315
- end
316
- if datetime.hour != 0 or
317
- datetime.min != 0 or
318
- datetime.sec != 0
319
- value_type = :datetime
320
- value = datetime
321
- else
322
- value_type = :date
323
- if row.at(idx).class == Spreadsheet::Formula
324
- value = row.send(:_date, cell)
325
- else
326
- value = row.date(idx)
327
- end
328
- value = sprintf("%04d-%02d-%02d",value.year,value.month,value.day)
329
- end
330
- end
331
- return value_type, value
332
- end
333
-
334
- # Read the cell and based on the class,
335
- # return the values for Roo
336
- def read_cell(row, idx)
337
- cell = read_cell_content(row, idx)
338
- case cell
339
- when Float, Integer, Fixnum, Bignum
340
- value_type = :float
341
- value = cell.to_f
342
- when Spreadsheet::Link
343
- value_type = :link
344
- value = cell
345
- when String, TrueClass, FalseClass
346
- value_type = :string
347
- value = cell.to_s
348
- else
349
- value_type = cell.class.to_s.downcase.to_sym
350
- value = nil
351
- end # case
352
- return value_type, value
353
- end
354
-
355
- end
@@ -1,300 +0,0 @@
1
- require 'date'
2
- require 'base64'
3
- require 'nokogiri'
4
-
5
- class Roo::Excel2003XML < Roo::Base
6
-
7
- # initialization and opening of a spreadsheet file
8
- # values for packed: :zip
9
- def initialize(filename, options={}, deprecated_file_warning=:error)
10
- if Hash === options
11
- packed = options[:packed]
12
- file_warning = options[:file_warning] || :error
13
- else
14
- warn 'Supplying `packed` or `file_warning` as separate arguments to `Roo::Excel2003XML.new` is deprecated. Use an options hash instead.'
15
- packed = options
16
- file_warning = deprecated_file_warning
17
- end
18
-
19
- make_tmpdir do |tmpdir|
20
- filename = download_uri(filename, tmpdir) if uri?(filename)
21
- filename = unzip(filename, tmpdir) if packed == :zip
22
-
23
- file_type_check(filename,'.xml','an Excel 2003 XML', file_warning)
24
- @filename = filename
25
- unless File.file?(@filename)
26
- raise IOError, "file #{@filename} does not exist"
27
- end
28
- @doc = load_xml(@filename)
29
- end
30
- super(filename, options)
31
- @formula = Hash.new
32
- @style = Hash.new
33
- @style_defaults = Hash.new { |h,k| h[k] = [] }
34
- @style_definitions = Hash.new
35
- read_styles
36
- end
37
-
38
- # Returns the content of a spreadsheet-cell.
39
- # (1,1) is the upper left corner.
40
- # (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
41
- # cell at the first line and first row.
42
- def cell(row, col, sheet=nil)
43
- sheet ||= @default_sheet
44
- read_cells(sheet)
45
- row,col = normalize(row,col)
46
- if celltype(row,col,sheet) == :date
47
- yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
48
- return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
49
- end
50
- @cell[sheet][[row,col]]
51
- end
52
-
53
- # Returns the formula at (row,col).
54
- # Returns nil if there is no formula.
55
- # The method #formula? checks if there is a formula.
56
- def formula(row,col,sheet=nil)
57
- sheet ||= @default_sheet
58
- read_cells(sheet)
59
- row,col = normalize(row,col)
60
- @formula[sheet][[row,col]] && @formula[sheet][[row,col]]["oooc:".length..-1]
61
- end
62
- alias_method :formula?, :formula
63
-
64
- class Font
65
- attr_accessor :bold, :italic, :underline
66
-
67
- def bold?
68
- @bold == '1'
69
- end
70
-
71
- def italic?
72
- @italic == '1'
73
- end
74
-
75
- def underline?
76
- @underline != nil
77
- end
78
- end
79
-
80
- # Given a cell, return the cell's style
81
- def font(row, col, sheet=nil)
82
- sheet ||= @default_sheet
83
- read_cells(sheet)
84
- row,col = normalize(row,col)
85
- style_name = @style[sheet][[row,col]] || @style_defaults[sheet][col - 1] || 'Default'
86
- @style_definitions[style_name]
87
- end
88
-
89
- # returns the type of a cell:
90
- # * :float
91
- # * :string
92
- # * :date
93
- # * :percentage
94
- # * :formula
95
- # * :time
96
- # * :datetime
97
- def celltype(row,col,sheet=nil)
98
- sheet ||= @default_sheet
99
- read_cells(sheet)
100
- row,col = normalize(row,col)
101
- if @formula[sheet][[row,col]]
102
- return :formula
103
- else
104
- @cell_type[sheet][[row,col]]
105
- end
106
- end
107
-
108
- def sheets
109
- @doc.xpath("/ss:Workbook/ss:Worksheet").map do |sheet|
110
- sheet['ss:Name']
111
- end
112
- end
113
-
114
- # version of the openoffice document
115
- # at 2007 this is always "1.0"
116
- def officeversion
117
- oo_version
118
- @officeversion
119
- end
120
-
121
- # shows the internal representation of all cells
122
- # mainly for debugging purposes
123
- def to_s(sheet=nil)
124
- sheet ||= @default_sheet
125
- read_cells(sheet)
126
- @cell[sheet].inspect
127
- end
128
-
129
- # save spreadsheet
130
- def save #:nodoc:
131
- 42
132
- end
133
-
134
- # returns each formula in the selected sheet as an array of elements
135
- # [row, col, formula]
136
- def formulas(sheet=nil)
137
- theformulas = Array.new
138
- sheet ||= @default_sheet
139
- read_cells(sheet)
140
- first_row(sheet).upto(last_row(sheet)) {|row|
141
- first_column(sheet).upto(last_column(sheet)) {|col|
142
- if formula?(row,col,sheet)
143
- f = [row, col, formula(row,col,sheet)]
144
- theformulas << f
145
- end
146
- }
147
- }
148
- theformulas
149
- end
150
-
151
- private
152
-
153
- # read the version of the OO-Version
154
- def oo_version
155
- @doc.find("//*[local-name()='document-content']").each do |office|
156
- @officeversion = office['version']
157
- end
158
- end
159
-
160
- # helper function to set the internal representation of cells
161
- def set_cell_values(sheet,x,y,i,v,value_type,formula,table_cell,str_v,style_name)
162
- key = [y,x+i]
163
- @cell_type[sheet] = {} unless @cell_type[sheet]
164
- @cell_type[sheet][key] = value_type
165
- @formula[sheet] = {} unless @formula[sheet]
166
- @formula[sheet][key] = formula if formula
167
- @cell[sheet] = {} unless @cell[sheet]
168
- @style[sheet] = {} unless @style[sheet]
169
- @style[sheet][key] = style_name
170
- @cell[sheet][key] =
171
- case @cell_type[sheet][key]
172
- when :float
173
- v.to_f
174
- when :string
175
- str_v
176
- when :datetime
177
- DateTime.parse(v)
178
- when :percentage
179
- v.to_f
180
- # when :time
181
- # hms = v.split(':')
182
- # hms[0].to_i*3600 + hms[1].to_i*60 + hms[2].to_i
183
- else
184
- v
185
- end
186
- end
187
-
188
- # read all cells in the selected sheet
189
- #--
190
- # the following construct means '4 blanks'
191
- # some content <text:s text:c="3"/>
192
- #++
193
- def read_cells(sheet=nil)
194
- sheet ||= @default_sheet
195
- validate_sheet!(sheet)
196
- return if @cells_read[sheet]
197
- sheet_found = false
198
- @doc.xpath("/ss:Workbook/ss:Worksheet[@ss:Name='#{sheet}']").each do |ws|
199
- sheet_found = true
200
- row = 1
201
- col = 1
202
- column_attributes = {}
203
- idx = 0
204
- ws.xpath('./ss:Table/ss:Column').each do |c|
205
- column_attributes[(idx += 1).to_s] = c['StyleID']
206
- end
207
- ws.xpath('./ss:Table/ss:Row').each do |r|
208
- skip_to_row = r['Index'].to_i
209
- row = skip_to_row if skip_to_row > 0
210
- style_name = r['StyleID'] if r['StyleID']
211
- r.xpath('./ss:Cell').each do |c|
212
- skip_to_col = c['Index'].to_i
213
- col = skip_to_col if skip_to_col > 0
214
- if c['StyleID']
215
- style_name = c['StyleID']
216
- elsif
217
- style_name ||= column_attributes[c['Index']]
218
- end
219
- c.xpath('./ss:Data').each do |cell|
220
- formula = cell['Formula']
221
- value_type = cell['ss:Type'].downcase.to_sym
222
- v = cell.content
223
- str_v = v
224
- case value_type
225
- when :number
226
- v = v.to_f
227
- value_type = :float
228
- when :datetime
229
- if v =~ /^1899-12-31T(\d{2}:\d{2}:\d{2})/
230
- v = $1
231
- value_type = :time
232
- elsif v =~ /([^T]+)T00:00:00.000/
233
- v = $1
234
- value_type = :date
235
- end
236
- when :boolean
237
- v = cell['boolean-value']
238
- end
239
- set_cell_values(sheet,col,row,0,v,value_type,formula,cell,str_v,style_name)
240
- end
241
- col += 1
242
- end
243
- row += 1
244
- col = 1
245
- end
246
- end
247
- if !sheet_found
248
- raise RangeError, "Unable to find sheet #{sheet} for reading"
249
- end
250
- @cells_read[sheet] = true
251
- end
252
-
253
- def read_styles
254
- @doc.xpath("/ss:Workbook/ss:Styles/ss:Style").each do |style|
255
- style_id = style['ID']
256
- @style_definitions[style_id] = Roo::Excel2003XML::Font.new
257
- if font = style.at_xpath('./ss:Font')
258
- @style_definitions[style_id].bold = font['Bold']
259
- @style_definitions[style_id].italic = font['Italic']
260
- @style_definitions[style_id].underline = font['Underline']
261
- end
262
- end
263
- end
264
-
265
- A_ROO_TYPE = {
266
- "float" => :float,
267
- "string" => :string,
268
- "date" => :date,
269
- "percentage" => :percentage,
270
- "time" => :time,
271
- }
272
-
273
- def self.oo_type_2_roo_type(ootype)
274
- return A_ROO_TYPE[ootype]
275
- end
276
-
277
- # helper method to convert compressed spaces and other elements within
278
- # an text into a string
279
- def children_to_string(children)
280
- result = ''
281
- children.each {|child|
282
- if child.text?
283
- result = result + child.content
284
- else
285
- if child.name == 's'
286
- compressed_spaces = child['c'].to_i
287
- # no explicit number means a count of 1:
288
- if compressed_spaces == 0
289
- compressed_spaces = 1
290
- end
291
- result = result + " "*compressed_spaces
292
- else
293
- result = result + child.content
294
- end
295
- end
296
- }
297
- result
298
- end
299
-
300
- end # class