rubyXL 1.2.10 → 2.1.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.
- data/Gemfile +14 -10
- data/Gemfile.lock +80 -21
- data/LICENSE.txt +1 -1
- data/README.rdoc +88 -82
- data/Rakefile +7 -2
- data/VERSION +1 -1
- data/lib/rubyXL.rb +13 -7
- data/lib/rubyXL/cell.rb +108 -268
- data/lib/rubyXL/generic_storage.rb +40 -0
- data/lib/rubyXL/objects/border.rb +66 -0
- data/lib/rubyXL/objects/calculation_chain.rb +28 -0
- data/lib/rubyXL/objects/cell_style.rb +75 -0
- data/lib/rubyXL/objects/color.rb +25 -0
- data/lib/rubyXL/objects/column_range.rb +74 -0
- data/lib/rubyXL/objects/container_nodes.rb +122 -0
- data/lib/rubyXL/objects/data_validation.rb +43 -0
- data/lib/rubyXL/objects/document_properties.rb +76 -0
- data/lib/rubyXL/objects/extensions.rb +36 -0
- data/lib/rubyXL/objects/fill.rb +57 -0
- data/lib/rubyXL/objects/font.rb +111 -0
- data/lib/rubyXL/objects/formula.rb +24 -0
- data/lib/rubyXL/objects/ooxml_object.rb +295 -0
- data/lib/rubyXL/objects/reference.rb +110 -0
- data/lib/rubyXL/objects/relationships.rb +59 -0
- data/lib/rubyXL/objects/shared_strings.rb +57 -0
- data/lib/rubyXL/objects/sheet_data.rb +149 -0
- data/lib/rubyXL/objects/sheet_view.rb +71 -0
- data/lib/rubyXL/objects/stylesheet.rb +200 -0
- data/lib/rubyXL/objects/text.rb +87 -0
- data/lib/rubyXL/objects/theme.rb +64 -0
- data/lib/rubyXL/objects/workbook.rb +233 -0
- data/lib/rubyXL/objects/worksheet.rb +485 -0
- data/lib/rubyXL/parser.rb +78 -442
- data/lib/rubyXL/workbook.rb +216 -385
- data/lib/rubyXL/worksheet.rb +509 -1062
- data/lib/rubyXL/writer/content_types_writer.rb +104 -68
- data/lib/rubyXL/writer/core_writer.rb +26 -43
- data/lib/rubyXL/writer/generic_writer.rb +43 -0
- data/lib/rubyXL/writer/root_rels_writer.rb +11 -19
- data/lib/rubyXL/writer/styles_writer.rb +6 -398
- data/lib/rubyXL/writer/theme_writer.rb +321 -327
- data/lib/rubyXL/writer/workbook_writer.rb +63 -67
- data/lib/rubyXL/writer/worksheet_writer.rb +29 -218
- data/rdoc/created.rid +39 -0
- data/rdoc/fonts.css +167 -0
- data/rdoc/fonts/Lato-Light.ttf +0 -0
- data/rdoc/fonts/Lato-LightItalic.ttf +0 -0
- data/rdoc/fonts/Lato-Regular.ttf +0 -0
- data/rdoc/fonts/Lato-RegularItalic.ttf +0 -0
- data/rdoc/fonts/SourceCodePro-Bold.ttf +0 -0
- data/rdoc/fonts/SourceCodePro-Regular.ttf +0 -0
- data/rdoc/images/add.png +0 -0
- data/rdoc/images/arrow_up.png +0 -0
- data/rdoc/images/brick.png +0 -0
- data/rdoc/images/brick_link.png +0 -0
- data/rdoc/images/bug.png +0 -0
- data/rdoc/images/bullet_black.png +0 -0
- data/rdoc/images/bullet_toggle_minus.png +0 -0
- data/rdoc/images/bullet_toggle_plus.png +0 -0
- data/rdoc/images/date.png +0 -0
- data/rdoc/images/delete.png +0 -0
- data/rdoc/images/find.png +0 -0
- data/rdoc/images/loadingAnimation.gif +0 -0
- data/rdoc/images/macFFBgHack.png +0 -0
- data/rdoc/images/package.png +0 -0
- data/rdoc/images/page_green.png +0 -0
- data/rdoc/images/page_white_text.png +0 -0
- data/rdoc/images/page_white_width.png +0 -0
- data/rdoc/images/plugin.png +0 -0
- data/rdoc/images/ruby.png +0 -0
- data/rdoc/images/tag_blue.png +0 -0
- data/rdoc/images/tag_green.png +0 -0
- data/rdoc/images/transparent.png +0 -0
- data/rdoc/images/wrench.png +0 -0
- data/rdoc/images/wrench_orange.png +0 -0
- data/rdoc/images/zoom.png +0 -0
- data/rdoc/js/darkfish.js +140 -0
- data/rdoc/js/jquery.js +18 -0
- data/rdoc/js/navigation.js +142 -0
- data/rdoc/js/search.js +109 -0
- data/rdoc/js/search_index.js +1 -0
- data/rdoc/js/searcher.js +228 -0
- data/rdoc/rdoc.css +580 -0
- data/rubyXL.gemspec +90 -34
- data/spec/lib/cell_spec.rb +29 -59
- data/spec/lib/parser_spec.rb +35 -19
- data/spec/lib/reference_spec.rb +29 -0
- data/spec/lib/stylesheet_spec.rb +29 -0
- data/spec/lib/workbook_spec.rb +22 -17
- data/spec/lib/worksheet_spec.rb +47 -202
- metadata +185 -148
- data/lib/.DS_Store +0 -0
- data/lib/rubyXL/Hash.rb +0 -60
- data/lib/rubyXL/color.rb +0 -14
- data/lib/rubyXL/private_class.rb +0 -265
- data/lib/rubyXL/writer/app_writer.rb +0 -62
- data/lib/rubyXL/writer/calc_chain_writer.rb +0 -33
- data/lib/rubyXL/writer/shared_strings_writer.rb +0 -30
- data/lib/rubyXL/writer/workbook_rels_writer.rb +0 -59
- data/lib/rubyXL/zip.rb +0 -20
- data/spec/lib/hash_spec.rb +0 -28
data/lib/rubyXL/workbook.rb
CHANGED
|
@@ -1,450 +1,281 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
require 'rubyXL/zip'
|
|
12
|
-
require 'date'
|
|
1
|
+
require 'rubyXL/writer/generic_writer'
|
|
2
|
+
require 'rubyXL/writer/content_types_writer'
|
|
3
|
+
require 'rubyXL/writer/root_rels_writer'
|
|
4
|
+
require 'rubyXL/writer/core_writer'
|
|
5
|
+
require 'rubyXL/writer/theme_writer'
|
|
6
|
+
require 'rubyXL/writer/workbook_writer'
|
|
7
|
+
require 'rubyXL/writer/styles_writer'
|
|
8
|
+
require 'rubyXL/writer/worksheet_writer'
|
|
9
|
+
require 'tmpdir'
|
|
10
|
+
require 'zip'
|
|
13
11
|
|
|
14
12
|
module RubyXL
|
|
15
|
-
|
|
13
|
+
module LegacyWorkbook
|
|
16
14
|
include Enumerable
|
|
17
|
-
attr_accessor :worksheets, :filepath, :creator, :modifier, :created_at,
|
|
18
|
-
:
|
|
19
|
-
:
|
|
20
|
-
:num_strings, :size, :date1904, :external_links, :style_corrector, :drawings,
|
|
21
|
-
:worksheet_rels, :printer_settings, :macros, :colors, :shared_strings_XML, :defined_names, :column_lookup_hash
|
|
15
|
+
attr_accessor :worksheets, :filepath, :creator, :modifier, :created_at, :modified_at, :theme,
|
|
16
|
+
:media, :external_links, :external_links_rels, :drawings, :drawings_rels, :charts, :chart_rels,
|
|
17
|
+
:worksheet_rels, :printer_settings, :macros
|
|
22
18
|
|
|
19
|
+
attr_accessor :stylesheet, :shared_strings_container, :document_properties, :calculation_chain,
|
|
20
|
+
:relationship_container
|
|
23
21
|
|
|
22
|
+
SHEET_NAME_TEMPLATE = 'Sheet%d'
|
|
24
23
|
APPLICATION = 'Microsoft Macintosh Excel'
|
|
25
24
|
APPVERSION = '12.0000'
|
|
26
|
-
|
|
25
|
+
|
|
27
26
|
def initialize(worksheets=[], filepath=nil, creator=nil, modifier=nil, created_at=nil,
|
|
28
27
|
company='', application=APPLICATION,
|
|
29
28
|
appversion=APPVERSION, date1904=0)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@
|
|
36
|
-
|
|
37
|
-
@
|
|
38
|
-
@
|
|
39
|
-
@
|
|
40
|
-
|
|
41
|
-
@
|
|
42
|
-
@
|
|
43
|
-
@
|
|
44
|
-
@
|
|
45
|
-
@
|
|
46
|
-
@
|
|
47
|
-
@
|
|
48
|
-
@
|
|
49
|
-
@
|
|
50
|
-
@
|
|
51
|
-
@
|
|
52
|
-
|
|
53
|
-
@
|
|
54
|
-
@
|
|
55
|
-
@
|
|
56
|
-
@
|
|
57
|
-
@
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@defined_names = nil
|
|
63
|
-
@column_lookup_hash = {}
|
|
29
|
+
super()
|
|
30
|
+
|
|
31
|
+
# Order of sheets in the +worksheets+ array corresponds to the order of pages in Excel UI.
|
|
32
|
+
# SheetId's, rId's, etc. are completely unrelated to ordering.
|
|
33
|
+
@worksheets = worksheets
|
|
34
|
+
add_worksheet if @worksheets.empty?
|
|
35
|
+
|
|
36
|
+
@filepath = filepath
|
|
37
|
+
@creator = creator
|
|
38
|
+
@modifier = modifier
|
|
39
|
+
self.date1904 = date1904 > 0
|
|
40
|
+
@media = RubyXL::GenericStorage.new(File.join('xl', 'media')).binary
|
|
41
|
+
@external_links = RubyXL::GenericStorage.new(File.join('xl', 'externalLinks'))
|
|
42
|
+
@external_links_rels = RubyXL::GenericStorage.new(File.join('xl', 'externalLinks', '_rels'))
|
|
43
|
+
@drawings = RubyXL::GenericStorage.new(File.join('xl', 'drawings'))
|
|
44
|
+
@drawings_rels = RubyXL::GenericStorage.new(File.join('xl', 'drawings', '_rels'))
|
|
45
|
+
@charts = RubyXL::GenericStorage.new(File.join('xl', 'charts'))
|
|
46
|
+
@chart_rels = RubyXL::GenericStorage.new(File.join('xl', 'charts', '_rels'))
|
|
47
|
+
@worksheet_rels = RubyXL::GenericStorage.new(File.join('xl', 'worksheets', '_rels'))
|
|
48
|
+
@theme = RubyXL::GenericStorage.new(File.join('xl', 'theme'))
|
|
49
|
+
@printer_settings = RubyXL::GenericStorage.new(File.join('xl', 'printerSettings')).binary
|
|
50
|
+
@macros = RubyXL::GenericStorage.new('xl').binary
|
|
51
|
+
|
|
52
|
+
@shared_strings_container = RubyXL::SharedStringsTable.new
|
|
53
|
+
@stylesheet = RubyXL::Stylesheet.default
|
|
54
|
+
@document_properties = RubyXL::DocumentProperties.new
|
|
55
|
+
@relationship_container = RubyXL::WorkbookRelationships.new
|
|
56
|
+
@calculation_chain = nil
|
|
57
|
+
|
|
58
|
+
self.company = company
|
|
59
|
+
self.application = application
|
|
60
|
+
self.appversion = appversion
|
|
64
61
|
|
|
65
62
|
begin
|
|
66
63
|
@created_at = DateTime.parse(created_at).strftime('%Y-%m-%dT%TZ')
|
|
67
64
|
rescue
|
|
68
|
-
|
|
69
|
-
@created_at = t.strftime('%Y-%m-%dT%TZ')
|
|
65
|
+
@created_at = Time.now.strftime('%Y-%m-%dT%TZ')
|
|
70
66
|
end
|
|
71
67
|
@modified_at = @created_at
|
|
68
|
+
end
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
# Finds worksheet by its name or numerical index
|
|
71
|
+
def [](ind)
|
|
72
|
+
case ind
|
|
73
|
+
when Integer then worksheets[ind]
|
|
74
|
+
when String then worksheets.find { |ws| ws.sheet_name == ind }
|
|
75
|
+
end
|
|
75
76
|
end
|
|
76
77
|
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
# Create new simple worksheet and add it to the workbook worksheets
|
|
79
|
+
#
|
|
80
|
+
# @param [String] The name for the new worksheet
|
|
81
|
+
def add_worksheet(name = nil)
|
|
82
|
+
if name.nil? then
|
|
83
|
+
n = 0
|
|
84
|
+
|
|
85
|
+
begin
|
|
86
|
+
name = SHEET_NAME_TEMPLATE % (n += 1)
|
|
87
|
+
end until self[name].nil?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
new_worksheet = Worksheet.new(:workbook => self, :sheet_name => name || get_default_name)
|
|
91
|
+
worksheets << new_worksheet
|
|
92
|
+
new_worksheet
|
|
80
93
|
end
|
|
81
94
|
|
|
82
95
|
def each
|
|
83
96
|
worksheets.each{|i| yield i}
|
|
84
97
|
end
|
|
85
98
|
|
|
86
|
-
def num_fmts_by_id
|
|
87
|
-
|
|
88
|
-
return @num_fmts_hash unless @num_fmts_hash.nil?
|
|
89
|
-
if num_fmts
|
|
90
|
-
@num_fmts_hash={}
|
|
91
|
-
num_fmts[:numFmt].each do |num_fmt|
|
|
92
|
-
@num_fmts_hash[num_fmt[:attributes][:numFmtId]]=num_fmt
|
|
93
|
-
end
|
|
94
|
-
@num_fmts_hash
|
|
95
|
-
else
|
|
96
|
-
{}
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
99
|
#filepath of xlsx file (including file itself)
|
|
101
|
-
def write(filepath
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
raise "Only xlsx and xlsm files are supported. Unsupported
|
|
105
|
-
end
|
|
106
|
-
dirpath = ''
|
|
107
|
-
extension = 'xls'
|
|
108
|
-
if(filepath =~ /((.|\s)*)\.xls(x|m)$/)
|
|
109
|
-
dirpath = $1.to_s()
|
|
110
|
-
extension += $3.to_s
|
|
111
|
-
end
|
|
112
|
-
filename = ''
|
|
113
|
-
if(filepath =~ /\/((.|\s)*)\/((.|\s)*)\.xls(x|m)$/)
|
|
114
|
-
filename = $3.to_s()
|
|
100
|
+
def write(filepath = @filepath)
|
|
101
|
+
extension = File.extname(filepath)
|
|
102
|
+
unless %w{.xlsx .xlsm}.include?(extension)
|
|
103
|
+
raise "Only xlsx and xlsm files are supported. Unsupported extension: #{extension}"
|
|
115
104
|
end
|
|
116
105
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
File.
|
|
121
|
-
|
|
122
|
-
Zip::
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
zipfile.get_output_stream(File.join('xl','workbook.xml')) {|f| f.puts(writer.write())}
|
|
143
|
-
|
|
144
|
-
writer = Writer::StylesWriter.new(dirpath,self)
|
|
145
|
-
zipfile.get_output_stream(File.join('xl','styles.xml')) {|f| f.puts(writer.write())}
|
|
146
|
-
|
|
147
|
-
unless @shared_strings.nil?
|
|
148
|
-
writer = Writer::SharedStringsWriter.new(dirpath,self)
|
|
149
|
-
zipfile.get_output_stream(File.join('xl','sharedStrings.xml')) {|f| f.puts(writer.write())}
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
#preserves external links (exactly, no modification allowed)
|
|
153
|
-
unless @external_links.nil?
|
|
154
|
-
#-1 because of rels
|
|
155
|
-
1.upto(@external_links.size-1) do |i|
|
|
156
|
-
zipfile.get_output_stream(
|
|
157
|
-
File.join('xl','externalLinks',"externalLink#{i}.xml")) {|f|
|
|
158
|
-
f.puts(@external_links[i])
|
|
159
|
-
}
|
|
160
|
-
end
|
|
161
|
-
@external_links['rels'].each_index do |i|
|
|
162
|
-
unless @external_links['rels'][i].nil?
|
|
163
|
-
zipfile.get_output_stream(
|
|
164
|
-
File.join('xl','externalLinks','_rels',"externalLink#{i}.xml.rels")) {|f|
|
|
165
|
-
f.puts(@external_links['rels'][i])
|
|
166
|
-
}
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
#preserves drawings (exactly, no modification allowed)
|
|
172
|
-
unless @drawings.nil?
|
|
173
|
-
1.upto(@drawings.size) do |i|
|
|
174
|
-
zipfile.get_output_stream(
|
|
175
|
-
File.join('xl','drawings',"vmlDrawing#{i}.vml")) {|f|
|
|
176
|
-
f.puts(@drawings[i])
|
|
177
|
-
}
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
unless @printer_settings.nil?
|
|
182
|
-
1.upto(@printer_settings.size) do |i|
|
|
183
|
-
zipfile.get_output_stream(
|
|
184
|
-
File.join('xl','printerSettings',"printerSettings#{i}.bin")) {|f|
|
|
185
|
-
f.puts(@printer_settings[i])
|
|
186
|
-
}
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
unless @worksheet_rels.nil?
|
|
191
|
-
1.upto(@worksheet_rels.size) do |i|
|
|
192
|
-
zipfile.get_output_stream(
|
|
193
|
-
File.join('xl','worksheets','_rels',"sheet#{i}.xml.rels")) {|f|
|
|
194
|
-
f.puts(@worksheet_rels[i])
|
|
195
|
-
}
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
unless @macros.nil?
|
|
200
|
-
zipfile.get_output_stream(File.join('xl','vbaProject.bin')) {|f| f.puts(@macros)}
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
@worksheets.each_with_index do |sheet,i|
|
|
204
|
-
writer = Writer::WorksheetWriter.new(dirpath,self,i)
|
|
205
|
-
zipfile.get_output_stream(File.join('xl','worksheets',"sheet#{i+1}.xml")) {|f| f.puts(writer.write())}
|
|
206
|
-
end
|
|
207
|
-
end
|
|
106
|
+
dirpath = File.dirname(filepath)
|
|
107
|
+
temppath = File.join(dirpath, Dir::Tmpname.make_tmpname([ File.basename(filepath), '.tmp' ], nil))
|
|
108
|
+
FileUtils.mkdir_p(temppath)
|
|
109
|
+
zippath = File.join(temppath, 'file.zip')
|
|
110
|
+
|
|
111
|
+
::Zip::File.open(zippath, ::Zip::File::CREATE) { |zipfile|
|
|
112
|
+
[ Writer::ContentTypesWriter, Writer::RootRelsWriter, Writer::CoreWriter,
|
|
113
|
+
Writer::ThemeWriter, Writer::WorkbookWriter, Writer::StylesWriter
|
|
114
|
+
].each { |writer_class| writer_class.new(self).add_to_zip(zipfile) }
|
|
115
|
+
|
|
116
|
+
calculation_chain && calculation_chain.add_to_zip(zipfile)
|
|
117
|
+
shared_strings_container && shared_strings_container.add_to_zip(zipfile)
|
|
118
|
+
document_properties.add_to_zip(zipfile)
|
|
119
|
+
relationship_container.workbook = self
|
|
120
|
+
relationship_container.add_to_zip(zipfile)
|
|
121
|
+
|
|
122
|
+
[ @media, @external_links, @external_links_rels,
|
|
123
|
+
@drawings, @drawings_rels, @charts, @chart_rels,
|
|
124
|
+
@printer_settings, @worksheet_rels, @macros ].each { |s| s.add_to_zip(zipfile) }
|
|
125
|
+
|
|
126
|
+
@worksheets.each_index { |i| Writer::WorksheetWriter.new(self, i).add_to_zip(zipfile) }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
FileUtils.mv(zippath, filepath)
|
|
130
|
+
FileUtils.rm_rf(temppath) if File.exist?(filepath)
|
|
208
131
|
|
|
209
|
-
FileUtils.cp(zippath,File.join(dirpath,filename+".#{extension}"))
|
|
210
|
-
FileUtils.cp(File.join(dirpath,filename+".#{extension}"),filepath)
|
|
211
|
-
if File.exist?(filepath)
|
|
212
|
-
FileUtils.rm_rf(dirpath)
|
|
213
|
-
end
|
|
214
132
|
return filepath
|
|
215
133
|
end
|
|
216
134
|
|
|
217
|
-
def
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
compare_date = DateTime.parse('December 31, 1903')
|
|
135
|
+
def base_date
|
|
136
|
+
if date1904 then
|
|
137
|
+
Date.new(1904, 1, 1)
|
|
221
138
|
else
|
|
222
|
-
|
|
139
|
+
# Subtracting one day to accomodate for erroneous 1900 leap year compatibility only for 1900 based dates
|
|
140
|
+
Date.new(1899, 12, 31) - 1
|
|
223
141
|
end
|
|
224
|
-
|
|
225
|
-
|
|
142
|
+
end
|
|
143
|
+
private :base_date
|
|
144
|
+
|
|
145
|
+
def date_to_num(date)
|
|
146
|
+
date && (date.ajd - base_date().ajd).to_i
|
|
226
147
|
end
|
|
227
148
|
|
|
228
149
|
def num_to_date(num)
|
|
229
|
-
|
|
230
|
-
if @date1904
|
|
231
|
-
compare_date = DateTime.parse('December 31, 1903')
|
|
232
|
-
else
|
|
233
|
-
compare_date = DateTime.parse('December 31, 1899')
|
|
234
|
-
end
|
|
235
|
-
# subtract one day to compare date for erroneous 1900 leap year compatibility
|
|
236
|
-
compare_date - 1 + num
|
|
150
|
+
num && (base_date + num)
|
|
237
151
|
end
|
|
238
152
|
|
|
239
|
-
def
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return @num_fmt_date_hash[num_fmt]
|
|
153
|
+
def get_fill_color(xf)
|
|
154
|
+
fill = fills[xf.fill_id]
|
|
155
|
+
pattern = fill && fill.pattern_fill
|
|
156
|
+
color = pattern && pattern.fg_color
|
|
157
|
+
color && color.rgb || 'ffffff'
|
|
245
158
|
end
|
|
246
159
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
state = 0
|
|
254
|
-
s = ''
|
|
255
|
-
num_fmt.split(//).each do |c|
|
|
256
|
-
if state == 0
|
|
257
|
-
if c == '"'
|
|
258
|
-
state = 1
|
|
259
|
-
elsif ['\\', '_', '*'].include?(c)
|
|
260
|
-
state = 2
|
|
261
|
-
elsif skip_chars.include?(c)
|
|
262
|
-
next
|
|
263
|
-
else
|
|
264
|
-
s << c
|
|
265
|
-
end
|
|
266
|
-
elsif state == 1
|
|
267
|
-
if c == '"'
|
|
268
|
-
state = 0
|
|
269
|
-
end
|
|
270
|
-
elsif state == 2
|
|
271
|
-
state = 0
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
s.gsub!(/\[[^\]]*\]/, '')
|
|
275
|
-
if non_date_formats.include?(s)
|
|
276
|
-
return false
|
|
277
|
-
end
|
|
278
|
-
separator = ';'
|
|
279
|
-
got_sep = 0
|
|
280
|
-
date_count = 0
|
|
281
|
-
num_count = 0
|
|
282
|
-
s.split(//).each do |c|
|
|
283
|
-
if date_chars.include?(c)
|
|
284
|
-
date_count += 1
|
|
285
|
-
elsif num_chars.include?(c)
|
|
286
|
-
num_count += 1
|
|
287
|
-
elsif c == separator
|
|
288
|
-
got_sep = 1
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
if date_count > 0 && num_count == 0
|
|
292
|
-
return true
|
|
293
|
-
elsif num_count > 0 && date_count == 0
|
|
294
|
-
return false
|
|
295
|
-
elsif date_count
|
|
296
|
-
# ambiguous result
|
|
297
|
-
elsif got_sep == 0
|
|
298
|
-
# constant result
|
|
160
|
+
def register_new_fill(new_fill, old_xf)
|
|
161
|
+
new_xf = old_xf.dup
|
|
162
|
+
|
|
163
|
+
unless fills[old_xf.fill_id].count == 1 && old_xf.fill_id > 2 # If the old fill is not used anymore, just replace it
|
|
164
|
+
new_xf.fill_id = fills.find_index { |x| x == new_fill } # Use existing fill, if it exists
|
|
165
|
+
new_xf.fill_id ||= fills.size # If this fill has never existed before, add it to collection.
|
|
299
166
|
end
|
|
300
|
-
|
|
167
|
+
|
|
168
|
+
fills[old_xf.fill_id].count -= 1
|
|
169
|
+
new_fill.count += 1
|
|
170
|
+
fills[new_xf.fill_id] = new_fill
|
|
171
|
+
|
|
172
|
+
new_xf.apply_fill = true
|
|
173
|
+
new_xf
|
|
301
174
|
end
|
|
302
175
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if !@cell_xfs[:xf].is_a?Array
|
|
306
|
-
@cell_xfs[:xf] = [@cell_xfs[:xf]]
|
|
307
|
-
end
|
|
176
|
+
def register_new_font(new_font, old_xf)
|
|
177
|
+
new_xf = old_xf.dup
|
|
308
178
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
179
|
+
unless fonts[old_xf.font_id].count == 1 && old_xf.font_id > 1 # If the old font is not used anymore, just replace it
|
|
180
|
+
new_xf.font_id = fonts.find_index { |x| x == new_font } # Use existing font, if it exists
|
|
181
|
+
new_xf.font_id ||= fonts.size # If this font has never existed before, add it to collection.
|
|
312
182
|
end
|
|
313
|
-
|
|
183
|
+
|
|
184
|
+
fonts[old_xf.font_id].count -= 1
|
|
185
|
+
new_font.count += 1
|
|
186
|
+
fonts[new_xf.font_id] = new_font
|
|
187
|
+
|
|
188
|
+
new_xf.apply_font = true
|
|
189
|
+
new_xf
|
|
314
190
|
end
|
|
315
191
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
192
|
+
def register_new_xf(new_xf, old_style_index)
|
|
193
|
+
new_xf_id = cell_xfs.find_index { |xf| xf == new_xf } # Use existing XF, if it exists
|
|
194
|
+
new_xf_id ||= cell_xfs.size # If this XF has never existed before, add it to collection.
|
|
195
|
+
|
|
196
|
+
cell_xfs[old_style_index].count -= 1
|
|
197
|
+
new_xf.count += 1
|
|
198
|
+
cell_xfs[new_xf_id] = new_xf
|
|
199
|
+
|
|
200
|
+
new_xf_id
|
|
325
201
|
end
|
|
326
202
|
|
|
327
|
-
def
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
203
|
+
def modify_text_wrap(style_index, wrap = false)
|
|
204
|
+
xf = cell_xfs[style_index].dup
|
|
205
|
+
xf.alignment = RubyXL::Alignment.new(:wrap_text => wrap, :apply_alignment => true)
|
|
206
|
+
register_new_xf(xf, style_index)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def modify_alignment(style_index, is_horizontal, alignment)
|
|
210
|
+
xf = cell_xfs[style_index].dup
|
|
211
|
+
xf.alignment = RubyXL::Alignment.new(:apply_alignment => true,
|
|
212
|
+
:horizontal => is_horizontal ? alignment : nil,
|
|
213
|
+
:vertical => is_horizontal ? nil : alignment)
|
|
214
|
+
register_new_xf(xf, style_index)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def modify_fill(style_index, rgb)
|
|
218
|
+
xf = cell_xfs[style_index].dup
|
|
219
|
+
new_fill = RubyXL::Fill.new(:pattern_fill =>
|
|
220
|
+
RubyXL::PatternFill.new(:pattern_type => 'solid',
|
|
221
|
+
:fg_color => RubyXL::Color.new(:rgb => rgb)))
|
|
222
|
+
new_xf = register_new_fill(new_fill, xf)
|
|
223
|
+
register_new_xf(new_xf, style_index)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def modify_border(style_index, direction, weight)
|
|
227
|
+
old_xf = cell_xfs[style_index].dup
|
|
228
|
+
new_border = borders[old_xf.border_id].dup
|
|
229
|
+
new_border.set_edge_style(direction, weight)
|
|
230
|
+
|
|
231
|
+
new_xf = old_xf.dup
|
|
232
|
+
|
|
233
|
+
unless borders[old_xf.border_id].count == 1 && old_xf.border_id > 0 # If the old border not used anymore, just replace it
|
|
234
|
+
new_xf.border_id = borders.find_index { |x| x == new_border } # Use existing border, if it exists
|
|
235
|
+
new_xf.border_id ||= borders.size # If this border has never existed before, add it to collection.
|
|
332
236
|
end
|
|
237
|
+
|
|
238
|
+
borders[old_xf.border_id].count -= 1
|
|
239
|
+
new_border.count += 1
|
|
240
|
+
borders[new_xf.border_id] = new_border
|
|
241
|
+
|
|
242
|
+
new_xf.apply_border = true
|
|
243
|
+
|
|
244
|
+
register_new_xf(new_xf, style_index)
|
|
333
245
|
end
|
|
334
246
|
|
|
247
|
+
def cell_xfs # Stylesheet should be pre-filled with defaults on initialize()
|
|
248
|
+
stylesheet.cell_xf_container.xfs
|
|
249
|
+
end
|
|
335
250
|
|
|
336
|
-
|
|
251
|
+
def fonts # Stylesheet should be pre-filled with defaults on initialize()
|
|
252
|
+
stylesheet.font_container.fonts
|
|
253
|
+
end
|
|
337
254
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def fill_styles()
|
|
341
|
-
@fonts = {
|
|
342
|
-
'0' => {
|
|
343
|
-
:font => {
|
|
344
|
-
:sz => { :attributes => { :val => 10 } },
|
|
345
|
-
:name => { :attributes => { :val => "Verdana" } }
|
|
346
|
-
},
|
|
347
|
-
:count=>1
|
|
348
|
-
},
|
|
349
|
-
'1' => {
|
|
350
|
-
:font => {
|
|
351
|
-
:sz => { :attributes => { :val => 8 } },
|
|
352
|
-
:name => { :attributes => { :val => "Verdana" } }
|
|
353
|
-
},
|
|
354
|
-
:count=>0
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
@fills = {
|
|
359
|
-
'0' => {
|
|
360
|
-
:fill => {
|
|
361
|
-
:patternFill => { :attributes => { :patternType => "none" } }
|
|
362
|
-
},
|
|
363
|
-
:count=>1} ,
|
|
364
|
-
'1' => {
|
|
365
|
-
:fill => {
|
|
366
|
-
:patternFill => { :attributes => { :patternType => "gray125" } }
|
|
367
|
-
},
|
|
368
|
-
:count=>0
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
@borders = {
|
|
373
|
-
'0' => {
|
|
374
|
-
:border => {
|
|
375
|
-
:left => { },
|
|
376
|
-
:right => { },
|
|
377
|
-
:top => { },
|
|
378
|
-
:bottom => { },
|
|
379
|
-
:diagonal => { }
|
|
380
|
-
},
|
|
381
|
-
:count => 1 #count = how many styles reference it
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
@cell_style_xfs = {
|
|
386
|
-
:attributes => {
|
|
387
|
-
:count => 1
|
|
388
|
-
},
|
|
389
|
-
:xf => {
|
|
390
|
-
:attributes => { :numFmtId => 0, :fontId => 0, :fillId => 0, :borderId => 0 }
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
@cell_xfs = {
|
|
394
|
-
:attributes => {
|
|
395
|
-
:count => 1
|
|
396
|
-
},
|
|
397
|
-
:xf => {
|
|
398
|
-
:attributes => { :numFmtId => 0, :fontId => 0, :fillId => 0, :borderId => 0, :xfId => 0 }
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
@cell_styles = {
|
|
402
|
-
:cellStyle => {
|
|
403
|
-
:attributes => { :builtinId=>0, :name=>"Normal", :xfId=>0 }
|
|
404
|
-
},
|
|
405
|
-
:attributes => { :count => 1 }
|
|
406
|
-
}
|
|
255
|
+
def fills # Stylesheet should be pre-filled with defaults on initialize()
|
|
256
|
+
stylesheet.fill_container.fills
|
|
407
257
|
end
|
|
408
258
|
|
|
259
|
+
def borders # Stylesheet should be pre-filled with defaults on initialize()
|
|
260
|
+
stylesheet.border_container.borders
|
|
261
|
+
end
|
|
409
262
|
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
=begin
|
|
410
266
|
#fills shared strings hash, contains each unique string
|
|
411
267
|
def fill_shared_strings()
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
@worksheets.each do |sheet|
|
|
418
|
-
unless sheet.nil?
|
|
419
|
-
sheet.sheet_data.each do |row|
|
|
420
|
-
row.each do |cell|
|
|
421
|
-
unless cell.nil? || cell.value.nil?
|
|
422
|
-
#if string not already seen, add it to hash
|
|
423
|
-
if cell.datatype == 's'
|
|
424
|
-
if string_hash[cell.value.to_s].nil?
|
|
425
|
-
string_hash[string_index]=cell.value.to_s
|
|
426
|
-
string_hash[cell.value.to_s]=string_index
|
|
427
|
-
string_index += 1
|
|
428
|
-
end
|
|
429
|
-
@num_strings += 1
|
|
430
|
-
end
|
|
431
|
-
end
|
|
432
|
-
end
|
|
268
|
+
@worksheets.compact.each { |sheet|
|
|
269
|
+
sheet.sheet_data.rows.each { |row|
|
|
270
|
+
row.cells.each { |cell|
|
|
271
|
+
if cell && cell.value && cell.datatype == RubyXL::Cell::SHARED_STRING then
|
|
272
|
+
get_index(cell.value.to_s, :add_if_missing)
|
|
433
273
|
end
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if string_hash.empty?
|
|
438
|
-
@shared_strings = nil
|
|
439
|
-
else
|
|
440
|
-
@shared_strings = string_hash
|
|
441
|
-
@size = string_index
|
|
442
|
-
end
|
|
443
|
-
end
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
444
277
|
end
|
|
278
|
+
=end
|
|
445
279
|
|
|
446
|
-
def validate_before_write
|
|
447
|
-
## TODO CHECK IF STYLE IS OK if not raise
|
|
448
|
-
end
|
|
449
280
|
end
|
|
450
281
|
end
|