slayer-surpass 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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