slayer-surpass 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.Rakefile.swp +0 -0
- data/.bnsignore +26 -0
- data/History.txt +4 -0
- data/LICENSE.txt +110 -0
- data/README.txt +26 -0
- data/Rakefile +36 -0
- data/bin/surpass +8 -0
- data/lib/surpass.rb +64 -0
- data/lib/surpass/ExcelFormula.g +393 -0
- data/lib/surpass/ExcelFormula.tokens +32 -0
- data/lib/surpass/ExcelFormulaLexer.rb +1490 -0
- data/lib/surpass/ExcelFormulaParser.rb +1822 -0
- data/lib/surpass/biff_record.rb +2173 -0
- data/lib/surpass/bitmap.rb +218 -0
- data/lib/surpass/cell.rb +187 -0
- data/lib/surpass/chart.rb +16 -0
- data/lib/surpass/column.rb +40 -0
- data/lib/surpass/document.rb +406 -0
- data/lib/surpass/excel_magic.rb +1016 -0
- data/lib/surpass/formatting.rb +605 -0
- data/lib/surpass/formula.rb +25 -0
- data/lib/surpass/row.rb +173 -0
- data/lib/surpass/style.rb +194 -0
- data/lib/surpass/tokens.txt +2 -0
- data/lib/surpass/utilities.rb +118 -0
- data/lib/surpass/workbook.rb +207 -0
- data/lib/surpass/worksheet.rb +574 -0
- data/surpass.gemspec +39 -0
- metadata +119 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
class Workbook
|
2
|
+
MACROS = {
|
3
|
+
'Consolidate_Area' => 0x00,
|
4
|
+
'Auto_Open' => 0x01,
|
5
|
+
'Auto_Close' => 0x02,
|
6
|
+
'Extract' => 0x03,
|
7
|
+
'Database' => 0x04,
|
8
|
+
'Criteria' => 0x05,
|
9
|
+
'Print_Area' => 0x06,
|
10
|
+
'Print_Titles' => 0x07, # in the docs it says Pint_Titles, I think its a mistake
|
11
|
+
'Recorder' => 0x08,
|
12
|
+
'Data_Form' => 0x09,
|
13
|
+
'Auto_Activate' => 0x0A,
|
14
|
+
'Auto_Deactivate' => 0x0B,
|
15
|
+
'Sheet_Title' => 0x0C,
|
16
|
+
'_FilterDatabase' => 0x0D
|
17
|
+
}
|
18
|
+
|
19
|
+
attr_accessor :owner
|
20
|
+
attr_accessor :country_code
|
21
|
+
attr_accessor :wnd_protect
|
22
|
+
attr_accessor :obj_protect
|
23
|
+
attr_accessor :protect
|
24
|
+
attr_accessor :backup_on_save
|
25
|
+
attr_accessor :styles
|
26
|
+
attr_accessor :sst
|
27
|
+
|
28
|
+
def hpos_twips=(value)
|
29
|
+
@hpos_twips = value & 0xFFFF
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :vpos_twips
|
33
|
+
def vpos_twips=(value)
|
34
|
+
@vpos_twips = value & 0xFFFF
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :width_twips
|
38
|
+
def width_twips=(value)
|
39
|
+
@width_twips = value & 0xFFFF
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :height_twips
|
43
|
+
def height_twips=(value)
|
44
|
+
@height_twips = value & 0xFFFF
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :active_sheet
|
48
|
+
def active_sheet=(value)
|
49
|
+
@active_sheet = value & 0xFFFF
|
50
|
+
@first_tab_index = @active_sheet
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :tab_width_twips
|
54
|
+
def tab_width_twips=(value)
|
55
|
+
@tab_width_twips = value & 0xFFFF
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :default_style
|
59
|
+
|
60
|
+
def initialize(filename = nil)
|
61
|
+
@owner = 'None'
|
62
|
+
@wnd_protect = 0
|
63
|
+
@obj_protect = 0
|
64
|
+
@protect = 0
|
65
|
+
@backup_on_save = 0
|
66
|
+
|
67
|
+
@hpos_twips = 0x01E0
|
68
|
+
@vpos_twips = 0x005A
|
69
|
+
@width_twips = 0x3FCF
|
70
|
+
@height_twips = 0x2A4E
|
71
|
+
|
72
|
+
@active_sheet = 0
|
73
|
+
@first_tab_index = 0
|
74
|
+
@selected_tabs = 0x01
|
75
|
+
@tab_width_twips = 0x0258
|
76
|
+
|
77
|
+
@wnd_hidden = false
|
78
|
+
@wnd_mini = false
|
79
|
+
@hscroll_visible = true
|
80
|
+
@vscroll_visible = true
|
81
|
+
@tabs_visible = true
|
82
|
+
|
83
|
+
@styles = ::StyleCollection.new
|
84
|
+
|
85
|
+
@dates_1904 = false
|
86
|
+
@use_cell_values = true
|
87
|
+
|
88
|
+
@sst = SharedStringTable.new
|
89
|
+
|
90
|
+
@worksheets = []
|
91
|
+
@names = []
|
92
|
+
@refs = []
|
93
|
+
|
94
|
+
@filename = filename
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_sheet(name = nil)
|
98
|
+
name ||= "Sheet#{@worksheets.length + 1}"
|
99
|
+
s = Worksheet.new(name, self)
|
100
|
+
@worksheets << s
|
101
|
+
s
|
102
|
+
end
|
103
|
+
|
104
|
+
def print_area(sheetnum, rstart, rend, cstart, cend)
|
105
|
+
if !sheetnum.is_a?(Integer)
|
106
|
+
i = 0
|
107
|
+
@worksheets.each_with_index do |w, i|
|
108
|
+
sheetnum = i+1 if w.name === sheetnum
|
109
|
+
break if sheetnum.is_a?(Integer)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
options = 0x0020 # see Options Flags for Name record
|
114
|
+
|
115
|
+
# FIXME: this is just a bad hack, need to use Formula to make the rpn
|
116
|
+
#~ rpn = Formula.Formula('').rpn()[2:] # minus the size field
|
117
|
+
rpn = [0x3B, 0x0000, rstart, rend, cstart, cend].pack('Cv5')
|
118
|
+
args = [options, 0x00, MACROS['Print_Area'], sheetnum, rpn]
|
119
|
+
@names << NameRecord.new(*args).to_biff
|
120
|
+
end
|
121
|
+
|
122
|
+
def to_biff
|
123
|
+
raise "You cannot save a workbook with no worksheets" if @worksheets.empty?
|
124
|
+
|
125
|
+
### @export "to-biff"
|
126
|
+
section_1_array = []
|
127
|
+
section_1_array << Biff8BOFRecord.new(Biff8BOFRecord::BOOK_GLOBAL).to_biff
|
128
|
+
section_1_array << InterfaceHeaderRecord.new.to_biff
|
129
|
+
section_1_array << MMSRecord.new.to_biff
|
130
|
+
section_1_array << InterfaceEndRecord.new.to_biff
|
131
|
+
section_1_array << WriteAccessRecord.new(owner).to_biff
|
132
|
+
section_1_array << CodepageBiff8Record.new.to_biff
|
133
|
+
section_1_array << DSFRecord.new.to_biff
|
134
|
+
section_1_array << TabIDRecord.new(@worksheets.length).to_biff
|
135
|
+
section_1_array << FnGroupCountRecord.new.to_biff
|
136
|
+
section_1_array << WindowProtectRecord.new(as_numeric(@wnd_protect)).to_biff
|
137
|
+
section_1_array << ProtectRecord.new(as_numeric(@protect)).to_biff
|
138
|
+
section_1_array << ObjectProtectRecord.new(as_numeric(@obj_protect)).to_biff
|
139
|
+
section_1_array << PasswordRecord.new.to_biff
|
140
|
+
section_1_array << Prot4RevRecord.new.to_biff
|
141
|
+
section_1_array << Prot4RevPassRecord.new.to_biff
|
142
|
+
section_1_array << BackupRecord.new(@backup_on_save).to_biff
|
143
|
+
section_1_array << HideObjRecord.new.to_biff
|
144
|
+
section_1_array << window_1_record
|
145
|
+
section_1_array << DateModeRecord.new(@dates_1904).to_biff
|
146
|
+
section_1_array << PrecisionRecord.new(@use_cell_values).to_biff
|
147
|
+
section_1_array << RefreshAllRecord.new.to_biff
|
148
|
+
section_1_array << BookBoolRecord.new.to_biff
|
149
|
+
section_1_array << @styles.to_biff
|
150
|
+
section_1_array << '' # Palette
|
151
|
+
section_1_array << UseSelfsRecord.new.to_biff
|
152
|
+
section_1 = section_1_array.join
|
153
|
+
### @end
|
154
|
+
section_3_array = []
|
155
|
+
section_3_array << CountryRecord.new(@country_code, @country_code).to_biff unless @country_code.nil?
|
156
|
+
# section_3_array << InternalReferenceSupBookRecord.new(@worksheets.length).to_biff
|
157
|
+
# section_3_array << ExternSheetRecord.new(@refs).to_biff
|
158
|
+
# section_3_array << @names.collect {|n| n.to_biff}.join
|
159
|
+
section_3_array << @sst.to_biff
|
160
|
+
section_3 = section_3_array.join
|
161
|
+
|
162
|
+
section_4 = '' # ExtSSTRecord
|
163
|
+
section_5 = EOFRecord.new.to_biff
|
164
|
+
|
165
|
+
@worksheets[@active_sheet].selected = true
|
166
|
+
worksheet_biff_data = @worksheets.collect {|w| w.to_biff }
|
167
|
+
worksheet_biff_data_lengths = worksheet_biff_data.collect {|w| w.length }
|
168
|
+
section_6 = worksheet_biff_data.join
|
169
|
+
|
170
|
+
# Need to know how long the bound sheet records will be
|
171
|
+
boundsheet_data_lengths = @worksheets.collect {|w| BoundSheetRecord.new(0x00, w.visibility, w.name).to_biff.length }
|
172
|
+
total_boundsheet_data_length = boundsheet_data_lengths.inject(0) {|sum, l| sum + l}
|
173
|
+
start_position = section_1.length + total_boundsheet_data_length + section_3.length + section_4.length + section_5.length
|
174
|
+
|
175
|
+
boundsheet_records = []
|
176
|
+
@worksheets.each_with_index do |w, i|
|
177
|
+
boundsheet_records << BoundSheetRecord.new(start_position, w.visibility, w.name).to_biff
|
178
|
+
start_position += worksheet_biff_data_lengths[i]
|
179
|
+
end
|
180
|
+
|
181
|
+
section_2 = boundsheet_records.join
|
182
|
+
section_1 + section_2 + section_3 + section_4 + section_5 + section_6
|
183
|
+
end
|
184
|
+
|
185
|
+
def window_1_record
|
186
|
+
flags = 0
|
187
|
+
flags |= (as_numeric(@wnd_hidden)) << 0
|
188
|
+
flags |= (as_numeric(@wnd_mini)) << 1
|
189
|
+
flags |= (as_numeric(@hscroll_visible)) << 3
|
190
|
+
flags |= (as_numeric(@vscroll_visible)) << 4
|
191
|
+
flags |= (as_numeric(@tabs_visible)) << 5
|
192
|
+
|
193
|
+
args = [@hpos_twips, @vpos_twips, @width_twips, @height_twips, flags, @active_sheet, @first_tab_index, @selected_tabs, @tab_width_twips]
|
194
|
+
Window1Record.new(*args).to_biff
|
195
|
+
end
|
196
|
+
|
197
|
+
def data
|
198
|
+
doc = ExcelDocument.new
|
199
|
+
doc.data(to_biff).read
|
200
|
+
end
|
201
|
+
|
202
|
+
def save(filename = nil)
|
203
|
+
@filename = filename unless filename.nil?
|
204
|
+
doc = ExcelDocument.new
|
205
|
+
doc.save(@filename, to_biff)
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,574 @@
|
|
1
|
+
class Worksheet
|
2
|
+
include Utilities
|
3
|
+
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :parent
|
6
|
+
attr_accessor :rows
|
7
|
+
attr_accessor :cols
|
8
|
+
attr_accessor :merged_ranges
|
9
|
+
attr_accessor :bmp_rec
|
10
|
+
attr_accessor :show_formulas
|
11
|
+
attr_accessor :show_grid
|
12
|
+
attr_accessor :show_headers
|
13
|
+
attr_accessor :panes_frozen
|
14
|
+
attr_accessor :show_empty_as_zero
|
15
|
+
attr_accessor :auto_colour_grid
|
16
|
+
attr_accessor :cols_right_to_left
|
17
|
+
attr_accessor :show_outline
|
18
|
+
attr_accessor :remove_splits
|
19
|
+
attr_accessor :selected
|
20
|
+
# RED HERRING ALERT: "sheet_visible" is a clone of the "selected" attribute.
|
21
|
+
# Typically a workbook created by the Excel UI will have one sheet
|
22
|
+
# (the sheet that was selected when the user saved it)
|
23
|
+
# with both bits set to 1, and all other sheets will have both
|
24
|
+
# bits set to 0. The true visibility of the sheet is found in the "visibility"
|
25
|
+
# attribute obtained from the BOUNDSHEET record.
|
26
|
+
attr_accessor :sheet_visible
|
27
|
+
attr_accessor :page_preview
|
28
|
+
attr_accessor :first_visible_row
|
29
|
+
attr_accessor :first_visible_col
|
30
|
+
attr_accessor :grid_colour
|
31
|
+
attr_accessor :preview_magn
|
32
|
+
attr_accessor :normal_magn
|
33
|
+
attr_accessor :visibility
|
34
|
+
attr_accessor :vert_split_pos
|
35
|
+
attr_accessor :horz_split_pos
|
36
|
+
attr_accessor :vert_split_first_visible
|
37
|
+
attr_accessor :horz_split_first_visible
|
38
|
+
|
39
|
+
attr_accessor :delta
|
40
|
+
attr_accessor :save_recalc
|
41
|
+
attr_accessor :formula_options
|
42
|
+
attr_accessor :print_headers
|
43
|
+
attr_accessor :print_grid
|
44
|
+
attr_accessor :grid_set
|
45
|
+
attr_accessor :vert_page_breaks
|
46
|
+
attr_accessor :horz_page_breaks
|
47
|
+
attr_accessor :header_str
|
48
|
+
attr_accessor :footer_str
|
49
|
+
attr_accessor :print_centered_vert
|
50
|
+
attr_accessor :print_centered_horz
|
51
|
+
attr_accessor :left_margin
|
52
|
+
attr_accessor :right_margin
|
53
|
+
attr_accessor :top_margin
|
54
|
+
attr_accessor :bottom_margin
|
55
|
+
attr_accessor :paper_size_code
|
56
|
+
attr_accessor :print_scaling
|
57
|
+
attr_accessor :start_page_number
|
58
|
+
attr_accessor :fit_width_to_pages
|
59
|
+
attr_accessor :fit_height_to_pages
|
60
|
+
attr_accessor :print_in_rows
|
61
|
+
attr_accessor :portrait
|
62
|
+
attr_accessor :print_not_colour
|
63
|
+
attr_accessor :print_draft
|
64
|
+
attr_accessor :print_notes
|
65
|
+
attr_accessor :print_notes_at_end
|
66
|
+
attr_accessor :print_omit_errors
|
67
|
+
attr_accessor :print_hres
|
68
|
+
attr_accessor :print_vres
|
69
|
+
attr_accessor :header_margin
|
70
|
+
attr_accessor :footer_margin
|
71
|
+
attr_accessor :copies_num
|
72
|
+
|
73
|
+
attr_accessor :show_auto_page_breaks
|
74
|
+
attr_accessor :dialogue_sheet
|
75
|
+
attr_accessor :auto_style_outline
|
76
|
+
attr_accessor :outline_below
|
77
|
+
attr_accessor :outline_right
|
78
|
+
attr_accessor :fit_num_pages
|
79
|
+
attr_accessor :show_row_outline
|
80
|
+
attr_accessor :show_col_outline
|
81
|
+
attr_accessor :alt_expr_eval
|
82
|
+
attr_accessor :alt_formula_entries
|
83
|
+
|
84
|
+
attr_accessor :col_default_width
|
85
|
+
attr_reader :calc_mode
|
86
|
+
attr_accessor :calc_count
|
87
|
+
|
88
|
+
attr_accessor :protect
|
89
|
+
attr_accessor :wnd_protect
|
90
|
+
attr_accessor :obj_protect
|
91
|
+
attr_accessor :scen_protect
|
92
|
+
attr_accessor :password
|
93
|
+
|
94
|
+
def initialize(name, parent)
|
95
|
+
@name = name
|
96
|
+
@parent = parent
|
97
|
+
@rows = {}
|
98
|
+
@cols = {}
|
99
|
+
@merged_ranges = []
|
100
|
+
@bmp_rec = ''
|
101
|
+
@show_formulas = 0
|
102
|
+
@show_grid = 1
|
103
|
+
@show_headers = 1
|
104
|
+
@panes_frozen = 0
|
105
|
+
@show_empty_as_zero = 1
|
106
|
+
@auto_colour_grid = 1
|
107
|
+
@cols_right_to_left = 0
|
108
|
+
@show_outline = 1
|
109
|
+
@remove_splits = 0
|
110
|
+
@selected = 0
|
111
|
+
@sheet_visible = 0
|
112
|
+
@page_preview = 0
|
113
|
+
|
114
|
+
@first_visible_row = 0
|
115
|
+
@first_visible_col = 0
|
116
|
+
@grid_colour = 0x40
|
117
|
+
@preview_magn = 0
|
118
|
+
@normal_magn = 0
|
119
|
+
@visibility = 0
|
120
|
+
|
121
|
+
@vert_split_pos = nil
|
122
|
+
@horz_split_pos = nil
|
123
|
+
@vert_split_first_visible = nil
|
124
|
+
@horz_split_first_visible = nil
|
125
|
+
@split_active_pane = nil # TODO test implications of converting None -> Nil
|
126
|
+
|
127
|
+
@row_gut_width = 0
|
128
|
+
@col_gut_height = 0
|
129
|
+
|
130
|
+
@show_auto_page_breaks = 1
|
131
|
+
@dialogue_sheet = 0
|
132
|
+
@auto_style_outline = 0
|
133
|
+
@outline_below = 0
|
134
|
+
@outline_right = 0
|
135
|
+
@fit_num_pages = 0
|
136
|
+
@show_row_outline = 1
|
137
|
+
@show_col_outline = 1
|
138
|
+
@alt_expr_eval = 0
|
139
|
+
@alt_formula_entries = 0
|
140
|
+
|
141
|
+
@row_default_height = 0x00FF
|
142
|
+
@col_default_width = 0x0008
|
143
|
+
|
144
|
+
@default_row_height_mismatch = 0
|
145
|
+
@default_row_hidden = 0
|
146
|
+
@default_row_space_above = 0
|
147
|
+
@default_row_space_below = 0
|
148
|
+
|
149
|
+
@calc_mode = 1
|
150
|
+
@calc_count = 0x0064
|
151
|
+
@rc_ref_mode = 1
|
152
|
+
@iterations_on = 0
|
153
|
+
@delta = 0.001
|
154
|
+
@save_recalc = 0
|
155
|
+
@formula_options = Formula::RECALC_ALWAYS | Formula::CALC_ON_OPEN
|
156
|
+
|
157
|
+
@print_headers = 0
|
158
|
+
@print_grid = 0
|
159
|
+
@grid_set = 1
|
160
|
+
@vert_page_breaks = []
|
161
|
+
@horz_page_breaks = []
|
162
|
+
@header_str = '&P'
|
163
|
+
@footer_str = '&F'
|
164
|
+
@print_centered_vert = 0
|
165
|
+
@print_centered_horz = 1
|
166
|
+
@left_margin = 0.3 #0.5
|
167
|
+
@right_margin = 0.3 #0.5
|
168
|
+
@top_margin = 0.61 #1.0
|
169
|
+
@bottom_margin = 0.37 #1.0
|
170
|
+
@paper_size_code = 9 # A4
|
171
|
+
@print_scaling = 100
|
172
|
+
@start_page_number = 1
|
173
|
+
@fit_width_to_pages = 1
|
174
|
+
@fit_height_to_pages = 1
|
175
|
+
@print_in_rows = 1
|
176
|
+
@portrait = 1
|
177
|
+
@print_not_colour = 0
|
178
|
+
@print_draft = 0
|
179
|
+
@print_notes = 0
|
180
|
+
@print_notes_at_end = 0
|
181
|
+
@print_omit_errors = 0
|
182
|
+
@print_hres = 0x012C # 300 dpi
|
183
|
+
@print_vres = 0x012C # 300 dpi
|
184
|
+
@header_margin = 0.1
|
185
|
+
@footer_margin = 0.1
|
186
|
+
@copies_num = 1
|
187
|
+
|
188
|
+
@wnd_protect = 0
|
189
|
+
@obj_protect = 0
|
190
|
+
@protect = 0
|
191
|
+
@scen_protect = 0
|
192
|
+
@password = ''
|
193
|
+
|
194
|
+
@charts = []
|
195
|
+
end
|
196
|
+
|
197
|
+
# Accessors Performing Conversions
|
198
|
+
def row_default_height
|
199
|
+
twips_to_pixels(@row_default_height)
|
200
|
+
end
|
201
|
+
|
202
|
+
def row_default_height=(pixels)
|
203
|
+
@row_default_height = pixels_to_twips(pixels)
|
204
|
+
end
|
205
|
+
|
206
|
+
def calc_mode=(value)
|
207
|
+
@calc_mode = (value == 0xFFFF && value) || value & 0x01
|
208
|
+
end
|
209
|
+
|
210
|
+
def set_cell_style(r, c, style, create_blanks = false)
|
211
|
+
cell = rows[r].cell(c)
|
212
|
+
if cell.nil?
|
213
|
+
write(r, c, nil, style) if create_blanks
|
214
|
+
else
|
215
|
+
cell.set_style(style)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def hide_columns(col_range)
|
220
|
+
col_range.each do |c|
|
221
|
+
hide_column(c)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def unhide_columns(col_range)
|
226
|
+
col_range.each do |c|
|
227
|
+
unhide_column(c)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def set_column_widths(col_range, width)
|
232
|
+
col_range.each do |c|
|
233
|
+
set_column_width(c, width)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def hide_column(c)
|
238
|
+
col(c).hidden = true
|
239
|
+
end
|
240
|
+
|
241
|
+
def unhide_column(c)
|
242
|
+
col(c).hidden = false
|
243
|
+
end
|
244
|
+
|
245
|
+
# TODO fix this if column doesn't exist yet.
|
246
|
+
def set_column_width(c, width)
|
247
|
+
if width < 100
|
248
|
+
# Assume we are trying to use Excel-user style widths, scale up accordingly.
|
249
|
+
# You can call col's width method directly to avoid this.
|
250
|
+
width = width * 260
|
251
|
+
end
|
252
|
+
col(c).width = width
|
253
|
+
end
|
254
|
+
|
255
|
+
# Change the style for a range of cells. If nil is supplied for row_range,
|
256
|
+
# the new style is supplied to every row (i.e. the entire column). Only
|
257
|
+
# changes style for cells which actually exist, so this does not paint
|
258
|
+
# anything which has not been written to.
|
259
|
+
def set_range_style(row_range, col_range, style, create_blanks = false)
|
260
|
+
row_range ||= 0..65535
|
261
|
+
col_range ||= 0..255
|
262
|
+
|
263
|
+
@rows.each do |i, r|
|
264
|
+
next unless row_range.include?(i)
|
265
|
+
r.cells.each do |c|
|
266
|
+
next unless col_range.include?(c.col)
|
267
|
+
c.set_style(style)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# TODO get rid of meaningless default value for label, should be required?
|
273
|
+
### @export "write-method"
|
274
|
+
def write(r, c, label = "", style = nil)
|
275
|
+
if label.is_a?(Array)
|
276
|
+
if label[0].is_a?(Array)
|
277
|
+
write_arrays(r, c, label, style || true)
|
278
|
+
else
|
279
|
+
write_array_to_row(label, r, c, style || true)
|
280
|
+
end
|
281
|
+
else
|
282
|
+
row(r).write(c, label, style)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
### @export "write-arrays"
|
287
|
+
def write_array_to_row(array, r, c = 0, style = true)
|
288
|
+
array.each_with_index do |a, i|
|
289
|
+
row(r).write(c + i, a, style)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def write_array_to_column(array, c, r = 0, style = true)
|
294
|
+
array.each_with_index do |a, i|
|
295
|
+
row(r + i).write(c, a, style)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def write_arrays(r, c, array_of_arrays, style = true)
|
300
|
+
array_of_arrays.each_with_index do |a, i|
|
301
|
+
raise "not an array of arrays!" unless a.is_a?(Array)
|
302
|
+
write_array_to_row(a, r + i, c, style)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
### @end
|
306
|
+
|
307
|
+
# Comment from xlwt:
|
308
|
+
## Stand-alone merge of previously written cells.
|
309
|
+
## Problems: (1) style to be used should be existing style of
|
310
|
+
## the top-left cell, not an arg.
|
311
|
+
## (2) should ensure that any previous data value in
|
312
|
+
## non-top-left cells is nobbled.
|
313
|
+
## Note: if a cell is set by a data record then later
|
314
|
+
## is referenced by a [MUL]BLANK record, Excel will blank
|
315
|
+
## out the cell on the screen, but OOo & Gnu will not
|
316
|
+
## blank it out. Need to do something better than writing
|
317
|
+
## multiple records. In the meantime, avoid this method and use
|
318
|
+
## write_merge() instead.
|
319
|
+
def merge(r1, r2, c1, c2, style = @parent.styles.default_style)
|
320
|
+
row(r1).write_blanks(c1 + 1, c2, style) if c2 > c1
|
321
|
+
((r1+1)...(r2+1)).each do |r|
|
322
|
+
row(r).write_blanks(c1, c2, style)
|
323
|
+
end
|
324
|
+
@merged_ranges << [r1, r2, c1, c2]
|
325
|
+
end
|
326
|
+
|
327
|
+
def write_merge(r1, r2, c1, c2, label="", style = @parent.styles.default_style)
|
328
|
+
write(r1, c1, label, style)
|
329
|
+
merge(r1, r2, c1, c2, style)
|
330
|
+
end
|
331
|
+
|
332
|
+
### @export "to-biff"
|
333
|
+
def to_biff
|
334
|
+
result = []
|
335
|
+
result << Biff8BOFRecord.new(Biff8BOFRecord::WORKSHEET).to_biff
|
336
|
+
# Calc Settings
|
337
|
+
result << CalcModeRecord.new(@calc_mode).to_biff
|
338
|
+
result << CalcCountRecord.new(@calc_count & 0xFFFF).to_biff
|
339
|
+
result << RefModeRecord.new(@rc_ref_mode & 0x01).to_biff
|
340
|
+
result << IterationRecord.new(@iterations_on & 0x01).to_biff
|
341
|
+
result << DeltaRecord.new(@delta).to_biff
|
342
|
+
result << SaveRecalcRecord.new(@save_recalc & 0x01).to_biff
|
343
|
+
|
344
|
+
result << guts_record
|
345
|
+
result << default_row_height_record
|
346
|
+
result << wsbool_record
|
347
|
+
result << @cols.sort.collect {|k, v| v.to_biff }.join
|
348
|
+
result << dimensions_rec
|
349
|
+
### @end
|
350
|
+
|
351
|
+
# Print Settings
|
352
|
+
result << PrintHeadersRecord.new(@print_headers).to_biff
|
353
|
+
result << PrintGridLinesRecord.new(@print_grid).to_biff
|
354
|
+
result << GridSetRecord.new(@grid_set).to_biff
|
355
|
+
result << HorizontalPageBreaksRecord.new(@horz_page_breaks.collect {|b| b.is_a?(Integer) ? [b, 0, -1] : b }).to_biff
|
356
|
+
result << VerticalPageBreaksRecord.new(@vert_page_breaks.collect {|b| b.is_a?(Integer) ? [b, 0, -1] : b }).to_biff
|
357
|
+
result << HeaderRecord.new(@header_str).to_biff
|
358
|
+
result << FooterRecord.new(@footer_str).to_biff
|
359
|
+
result << HCenterRecord.new(@print_centered_horz).to_biff
|
360
|
+
result << VCenterRecord.new(@print_centered_vert).to_biff
|
361
|
+
result << LeftMarginRecord.new(@left_margin).to_biff
|
362
|
+
result << RightMarginRecord.new(@right_margin).to_biff
|
363
|
+
result << TopMarginRecord.new(@top_margin).to_biff
|
364
|
+
result << BottomMarginRecord.new(@bottom_margin).to_biff
|
365
|
+
result << setup_page_record
|
366
|
+
|
367
|
+
# Protection Settings
|
368
|
+
result << ProtectRecord.new(as_numeric(@protect)).to_biff()
|
369
|
+
result << ScenarioProtectRecord.new(as_numeric(@scen_protect)).to_biff()
|
370
|
+
result << WindowProtectRecord.new(as_numeric(@wnd_protect)).to_biff()
|
371
|
+
result << ObjectProtectRecord.new(as_numeric(@obj_protect)).to_biff()
|
372
|
+
result << PasswordRecord.new(@password).to_biff()
|
373
|
+
|
374
|
+
### @export "to-biff-rows"
|
375
|
+
keys = @rows.keys.sort
|
376
|
+
keys.each do |i|
|
377
|
+
result << @rows[i].to_biff
|
378
|
+
result << @rows[i].cells_biff
|
379
|
+
end
|
380
|
+
### @end
|
381
|
+
|
382
|
+
# @charts.each do |c|
|
383
|
+
# result << c.to_biff
|
384
|
+
# end
|
385
|
+
result << MergedCellsRecord.new(@merged_ranges).to_biff
|
386
|
+
result << @bmp_rec
|
387
|
+
result << window_2_record
|
388
|
+
result << panes_record
|
389
|
+
# result << hyperlink_table_record
|
390
|
+
result << EOFRecord.new.to_biff
|
391
|
+
|
392
|
+
result.join
|
393
|
+
end
|
394
|
+
|
395
|
+
def guts_record
|
396
|
+
max_row_level = @rows.values.inject(-1) {|level, row| row.level > level ? row.level : level }
|
397
|
+
max_col_level = @cols.values.inject(-1) {|level, col| col.level > level ? col.level : level }
|
398
|
+
|
399
|
+
row_visible_levels = @rows.empty? ? 0 : max_row_level + 1
|
400
|
+
col_visible_levels = @cols.empty? ? 0 : max_col_level + 1
|
401
|
+
|
402
|
+
GutsRecord.new(@row_gut_width, @col_gut_height, row_visible_levels, col_visible_levels).to_biff
|
403
|
+
end
|
404
|
+
|
405
|
+
def default_row_height_record
|
406
|
+
options = 0x00
|
407
|
+
options |= (@default_row_height_mismatch & 0x01) << 0
|
408
|
+
options |= (@default_row_hidden & 0x01) << 1
|
409
|
+
options |= (@default_row_space_above & 0x01) << 2
|
410
|
+
options |= (@default_row_space_below & 0x01) << 3
|
411
|
+
|
412
|
+
DefaultRowHeight.new(options, @row_default_height).to_biff
|
413
|
+
end
|
414
|
+
|
415
|
+
def wsbool_record
|
416
|
+
options = 0x00
|
417
|
+
options |= (@show_auto_page_breaks & 0x01) << 0
|
418
|
+
options |= (@dialogue_sheet & 0x01) << 4
|
419
|
+
options |= (@auto_style_outline & 0x01) << 5
|
420
|
+
options |= (@outline_below & 0x01) << 6
|
421
|
+
options |= (@outline_right & 0x01) << 7
|
422
|
+
options |= (@fit_num_pages & 0x01) << 8
|
423
|
+
options |= (@show_row_outline & 0x01) << 10
|
424
|
+
options |= (@show_col_outline & 0x01) << 11
|
425
|
+
options |= (@alt_expr_eval & 0x01) << 14
|
426
|
+
options |= (@alt_formula_entries & 0x01) << 15
|
427
|
+
|
428
|
+
WSBoolRecord.new(options).to_biff
|
429
|
+
end
|
430
|
+
|
431
|
+
def dimensions_rec
|
432
|
+
first_used_row = 0
|
433
|
+
last_used_row = 0
|
434
|
+
first_used_col = 0
|
435
|
+
last_used_col = 0
|
436
|
+
|
437
|
+
if !@rows.empty?
|
438
|
+
first_used_row = @rows.keys.sort.first
|
439
|
+
last_used_row = @rows.keys.sort.last
|
440
|
+
first_used_col = 0xFFFFFFFF
|
441
|
+
last_used_col = 0
|
442
|
+
end
|
443
|
+
|
444
|
+
first_used_col = @rows.values.inject(first_used_col) {|min_col, r| r.min_col_index < min_col ? min_col = r.min_col_index : min_col }
|
445
|
+
last_used_col = @rows.values.inject(last_used_col) {|max_col, r| r.max_col_index > max_col ? max_col = r.max_col_index : max_col }
|
446
|
+
|
447
|
+
DimensionsRecord.new(first_used_row, last_used_row, first_used_col, last_used_col).to_biff
|
448
|
+
end
|
449
|
+
|
450
|
+
def setup_page_record
|
451
|
+
setup_page_options = (@print_in_rows & 0x01) << 0
|
452
|
+
setup_page_options |= (@portrait & 0x01) << 1
|
453
|
+
setup_page_options |= (0x00 & 0x01) << 2
|
454
|
+
setup_page_options |= (@print_not_colour & 0x01) << 3
|
455
|
+
setup_page_options |= (@print_draft & 0x01) << 4
|
456
|
+
setup_page_options |= (@print_notes & 0x01) << 5
|
457
|
+
setup_page_options |= (0x00 & 0x01) << 6
|
458
|
+
setup_page_options |= (0x01 & 0x01) << 7
|
459
|
+
setup_page_options |= (@print_notes_at_end & 0x01) << 9
|
460
|
+
setup_page_options |= (@print_omit_errors & 0x03) << 10
|
461
|
+
|
462
|
+
args = [
|
463
|
+
@paper_size_code,
|
464
|
+
@print_scaling,
|
465
|
+
@start_page_number,
|
466
|
+
@fit_width_to_pages,
|
467
|
+
@fit_height_to_pages,
|
468
|
+
setup_page_options,
|
469
|
+
@print_hres,
|
470
|
+
@print_vres,
|
471
|
+
@header_margin,
|
472
|
+
@footer_margin,
|
473
|
+
@copies_num
|
474
|
+
]
|
475
|
+
SetupPageRecord.new(*args).to_biff
|
476
|
+
end
|
477
|
+
|
478
|
+
def window_2_record
|
479
|
+
options = 0
|
480
|
+
options |= (as_numeric(@show_formulas ) & 0x01) << 0
|
481
|
+
options |= (as_numeric(@show_grid ) & 0x01) << 1
|
482
|
+
options |= (as_numeric(@show_headers ) & 0x01) << 2
|
483
|
+
options |= (as_numeric(@panes_frozen ) & 0x01) << 3
|
484
|
+
options |= (as_numeric(@show_empty_as_zero ) & 0x01) << 4
|
485
|
+
options |= (as_numeric(@auto_colour_grid ) & 0x01) << 5
|
486
|
+
options |= (as_numeric(@cols_right_to_left ) & 0x01) << 6
|
487
|
+
options |= (as_numeric(@show_outline ) & 0x01) << 7
|
488
|
+
options |= (as_numeric(@remove_splits ) & 0x01) << 8
|
489
|
+
options |= (as_numeric(@selected ) & 0x01) << 9
|
490
|
+
options |= (as_numeric(@sheet_visible ) & 0x01) << 10
|
491
|
+
options |= (as_numeric(@page_preview ) & 0x01) << 11
|
492
|
+
|
493
|
+
if @page_preview != 0
|
494
|
+
if @preview_magn == 0
|
495
|
+
scl_magn = 60
|
496
|
+
else
|
497
|
+
scl_magn = @preview_magn
|
498
|
+
end
|
499
|
+
else
|
500
|
+
scl_magn = @normal_magn
|
501
|
+
end
|
502
|
+
|
503
|
+
Window2Record.new(options, @first_visible_row, @first_visible_col, @grid_colour, @preview_magn, @normal_magn, scl_magn).to_biff
|
504
|
+
end
|
505
|
+
|
506
|
+
def panes_record
|
507
|
+
return '' if @vert_split_pos.nil? && @horz_split_pos.nil?
|
508
|
+
@vert_split_pos = 0 if @vert_split_pos.nil?
|
509
|
+
@horz_split_pos = 0 if @horz_split_pos.nil?
|
510
|
+
if @panes_frozen
|
511
|
+
@vert_split_first_visible = @vert_split_pos if @vert_split_first_visible.nil?
|
512
|
+
@horz_split_first_visible = @horz_split_pos if @horz_split_first_visible.nil?
|
513
|
+
else
|
514
|
+
@vert_split_first_visible = 0 if @vert_split_first_visible.nil?
|
515
|
+
@horz_split_first_visible = 0 if @horz_split_first_visible.nil?
|
516
|
+
# inspired by pyXLWriter
|
517
|
+
@horz_split_pos = 20 * @horz_split_pos + 255
|
518
|
+
@vert_split_pos = 113.879 * @vert_split_pos + 390
|
519
|
+
end
|
520
|
+
@split_active_pane = 0 if @vert_split_pos > 0 and @horz_split_pos > 0
|
521
|
+
@split_active_pane = 1 if @vert_split_pos < 0 and @horz_split_pos == 0
|
522
|
+
@split_active_pane = 2 if @vert_split_pos == 0 and @horz_split_pos > 0
|
523
|
+
@split_active_pane = 3
|
524
|
+
|
525
|
+
args = [@vert_split_pos, @horz_split_pos, @horz_split_first_visible, @vert_split_first_visible, @split_active_pane]
|
526
|
+
PanesRecord.new(*args).to_biff
|
527
|
+
end
|
528
|
+
|
529
|
+
def hyperlink_table_record
|
530
|
+
result = ''
|
531
|
+
return result if @links.nil?
|
532
|
+
@links.each do |a, b|
|
533
|
+
x, y = a
|
534
|
+
url, target, textmark, description = b
|
535
|
+
result += HyperlinkRecord.new(x, x, y, y, url, target, textmark, description).to_biff
|
536
|
+
result += QuicktipRecord(x, x, y, y).to_biff unless description.nil?
|
537
|
+
end
|
538
|
+
result
|
539
|
+
end
|
540
|
+
|
541
|
+
# Fetch the row indicated by index, or create it if necessary.
|
542
|
+
def row(index)
|
543
|
+
rows[index] ||= Row.new(index, self)
|
544
|
+
end
|
545
|
+
|
546
|
+
# Fetch the col indicated by index, or create it if necessary.
|
547
|
+
def col(index)
|
548
|
+
cols[index] ||= Column.new(index, self)
|
549
|
+
end
|
550
|
+
alias :column :col
|
551
|
+
|
552
|
+
def row_height(row)
|
553
|
+
if @rows.include?(row)
|
554
|
+
@rows[row].height_in_pixels
|
555
|
+
else
|
556
|
+
17
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
def col_width(column_index)
|
561
|
+
if cols.keys.include?(column_index)
|
562
|
+
cols[column_index].width_in_pixels
|
563
|
+
else
|
564
|
+
64
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
def insert_bitmap(filename, row, col, x = 0, y = 0, scale_x = 1, scale_y = 1)
|
569
|
+
bmp = ImDataBmpRecord.new(filename)
|
570
|
+
obj = ObjBmpRecord.new(row, col, self, bmp, x, y, scale_x, scale_y)
|
571
|
+
|
572
|
+
@bmp_rec += obj.to_biff + bmp.to_biff
|
573
|
+
end
|
574
|
+
end
|