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,605 @@
1
+ module Formatting
2
+ COLOURS = {
3
+ 'aqua' => 0x31,
4
+ 'black' => 0x08,
5
+ 'blue' => 0x0C,
6
+ 'blue-grey' => 0x36,
7
+ 'bright-green' => 0xb,
8
+ 'brown' => 0x3c,
9
+ 'coral' => 0x1d,
10
+ 'cornflower-blue' => 0x18,
11
+ 'dark-blue' => 0x12,
12
+ 'dark-green' => 0x3a,
13
+ 'dark-red' => 0x10,
14
+ 'dark-teal' => 0x38,
15
+ 'dark-yellow' => 0x13,
16
+ 'fuchsia' => 0x0E,
17
+ 'gold' => 0x33,
18
+ 'gray' => 0x17,
19
+ 'grey' => 0x17,
20
+ 'green' => 0x11,
21
+ 'grey-25-percent' => 0x16,
22
+ 'grey-40-percent' => 0x37,
23
+ 'grey-50-percent' => 0x17,
24
+ 'grey-80-percent' => 0x3f,
25
+ 'indigo' => 0x3e,
26
+ 'lavender' => 0x2e,
27
+ 'lemon-chiffon' => 0x1a,
28
+ 'light-blue' => 0x30,
29
+ 'light-cornflower-blue' => 0x1f,
30
+ 'light-green' => 0x2a,
31
+ 'light-orange' => 0x34,
32
+ 'light-turquoise' => 0x29,
33
+ 'light-yellow' => 0x2b,
34
+ 'lime' => 0x32,
35
+ 'magenta' => 0x0E,
36
+ 'maroon' => 0x19,
37
+ 'olive-green' => 0x3b,
38
+ 'orange' => 0x35,
39
+ 'orchid' => 0x1c,
40
+ 'pale-blue' => 0x2c,
41
+ 'pink' => 0x21,
42
+ 'plum' => 0x3d,
43
+ 'purple' => 0x14,
44
+ 'red' => 0x0A,
45
+ 'rose' => 0x2d,
46
+ 'royal-blue' => 0x1e,
47
+ 'sea-green' => 0x39,
48
+ 'silver' => 0x16,
49
+ 'sky-blue' => 0x28,
50
+ 'tan' => 0x2f,
51
+ 'teal' => 0x15,
52
+ 'turquoise' => 0xf,
53
+ 'violet' => 0x14,
54
+ 'white' => 0x09,
55
+ 'yellow' => 0x0D
56
+ }
57
+
58
+ COLORS = COLOURS
59
+ end
60
+
61
+ class Font
62
+ ESCAPEMENT_NONE = 0x00
63
+ ESCAPEMENT_SUPERSCRIPT = 0x01
64
+ ESCAPEMENT_SUBSCRIPT = 0x02
65
+
66
+ UNDERLINE_NONE = 0x00
67
+ UNDERLINE_SINGLE = 0x01
68
+ UNDERLINE_SINGLE_ACC = 0x21
69
+ UNDERLINE_DOUBLE = 0x02
70
+ UNDERLINE_DOUBLE_ACC = 0x22
71
+
72
+ FAMILY_NONE = 0x00
73
+ FAMILY_ROMAN = 0x01
74
+ FAMILY_SWISS = 0x02
75
+ FAMILY_MODERN = 0x03
76
+ FAMILY_SCRIPT = 0x04
77
+ FAMILY_DECORATIVE = 0x05
78
+
79
+ CHARSET_ANSI_LATIN = 0x00
80
+ CHARSET_SYS_DEFAULT = 0x01
81
+ CHARSET_SYMBOL = 0x02
82
+ CHARSET_APPLE_ROMAN = 0x4D
83
+ CHARSET_ANSI_JAP_SHIFT_JIS = 0x80
84
+ CHARSET_ANSI_KOR_HANGUL = 0x81
85
+ CHARSET_ANSI_KOR_JOHAB = 0x82
86
+ CHARSET_ANSI_CHINESE_GBK = 0x86
87
+ CHARSET_ANSI_CHINESE_BIG5 = 0x88
88
+ CHARSET_ANSI_GREEK = 0xA1
89
+ CHARSET_ANSI_TURKISH = 0xA2
90
+ CHARSET_ANSI_VIETNAMESE = 0xA3
91
+ CHARSET_ANSI_HEBREW = 0xB1
92
+ CHARSET_ANSI_ARABIC = 0xB2
93
+ CHARSET_ANSI_BALTIC = 0xBA
94
+ CHARSET_ANSI_CYRILLIC = 0xCC
95
+ CHARSET_ANSI_THAI = 0xDE
96
+ CHARSET_ANSI_LATIN_II = 0xEE
97
+ CHARSET_OEM_LATIN_I = 0xFF
98
+
99
+ PLAIN = 0x00
100
+ BOLD = 0x01
101
+ ITALIC = 0x02
102
+ UNDERLINE = 0x04
103
+ STRUCK_OUT = 0x08
104
+ OUTLINE = 0x010
105
+ SHADOW = 0x020
106
+
107
+ attr_accessor :height
108
+ attr_accessor :italic
109
+ attr_accessor :struck_out
110
+ attr_accessor :outline
111
+ attr_accessor :shadow
112
+ attr_accessor :colour_index
113
+ attr_accessor :bold
114
+ attr_accessor :weight # Looks like only 400 = normal, 700 = bold are supported so just use bold = true.
115
+ attr_accessor :escapement
116
+ attr_accessor :charset
117
+ attr_accessor :name
118
+
119
+ attr_reader :family
120
+ attr_reader :underline
121
+
122
+ def initialize(hash = {})
123
+ @height = 200 # font size 10
124
+ @italic = false
125
+ @struck_out = false
126
+ @outline = false
127
+ @shadow = false
128
+ @colour_index = 0x7FFF
129
+ @bold = false
130
+ @weight = 400 # regular
131
+ @escapement = ESCAPEMENT_NONE
132
+ @charset = CHARSET_SYS_DEFAULT
133
+ @name = 'Arial'
134
+ @family = FAMILY_NONE
135
+ @underline = UNDERLINE_NONE
136
+
137
+ hash.each do |k, v|
138
+ self.send((k.to_s + '=').to_sym, v)
139
+ end
140
+ end
141
+
142
+ def family=(arg)
143
+ raise "Oops, font_family doesn't take a string. Do you want font_name instead?" if arg.is_a?(String)
144
+ @family = arg
145
+ end
146
+
147
+ # Convert font size in points to native twips
148
+ def size=(points)
149
+ @height = points * 20
150
+ end
151
+
152
+ def strikethrough=(arg)
153
+ @struck_out = arg
154
+ end
155
+
156
+ def subscript=(arg)
157
+ case arg
158
+ when TrueClass
159
+ @escapement = ESCAPEMENT_SUBSCRIPT
160
+ when FalseClass
161
+ @escapement = ESCAPEMENT_NONE
162
+ else
163
+ raise "I don't know how to set subscript to #{arg.inspect}."
164
+ end
165
+ end
166
+
167
+ def superscript=(arg)
168
+ case arg
169
+ when TrueClass
170
+ @escapement = ESCAPEMENT_SUPERSCRIPT
171
+ when FalseClass
172
+ @escapement = ESCAPEMENT_NONE
173
+ else
174
+ raise "I don't know how to set superscript to #{arg.inspect}."
175
+ end
176
+ end
177
+
178
+ # User-friendly underlining directives.
179
+ def underline=(arg)
180
+ case arg
181
+ when UNDERLINE_NONE, UNDERLINE_SINGLE, UNDERLINE_SINGLE_ACC, UNDERLINE_DOUBLE, UNDERLINE_DOUBLE_ACC
182
+ @underline = arg
183
+ when nil
184
+ @underline ||= UNDERLINE_NONE
185
+ when TrueClass
186
+ @underline = UNDERLINE_SINGLE
187
+ when FalseClass
188
+ @underline = UNDERLINE_NONE
189
+ when :none
190
+ @underline = UNDERLINE_NONE
191
+ when :single
192
+ @underline = UNDERLINE_SINGLE
193
+ when :single_acc, :single_accounting
194
+ @underline = UNDERLINE_SINGLE_ACC
195
+ when :double
196
+ @underline = UNDERLINE_DOUBLE
197
+ when :double_acc, :double_accounting
198
+ @underline = UNDERLINE_DOUBLE_ACC
199
+ else
200
+ raise "I don't know how to set underline to #{arg.inspect}."
201
+ end
202
+ end
203
+
204
+ def colour_index_from_name(colour_name)
205
+ Formatting::COLOURS[colour_name.to_s]
206
+ end
207
+
208
+ def colour=(colour_name)
209
+ new_colour = colour_index_from_name(colour_name)
210
+ if new_colour.nil?
211
+ raise "Invalid Colour #{colour_name}"
212
+ else
213
+ @colour_index = new_colour
214
+ end
215
+ end
216
+ alias :color= :colour=
217
+ alias :color_index= :colour_index=
218
+
219
+ def to_biff
220
+ options = PLAIN
221
+ options |= BOLD if @bold
222
+ options |= ITALIC if @italic
223
+ options |= UNDERLINE if (@underline != UNDERLINE_NONE)
224
+ options |= STRUCK_OUT if @struck_out
225
+ options |= OUTLINE if @outline
226
+ options |= SHADOW if @shadow
227
+
228
+ @weight = 700 if @bold
229
+ args = [@height, options, @colour_index, @weight, @escapement, @underline, @family, @charset, @name]
230
+ FontRecord.new(*args).to_biff
231
+ end
232
+ end
233
+
234
+ class Alignment
235
+ HORZ_GENERAL = 0x00
236
+ HORZ_LEFT = 0x01
237
+ HORZ_CENTER = 0x02
238
+ HORZ_RIGHT = 0x03
239
+ HORZ_FILLED = 0x04
240
+ HORZ_JUSTIFIED = 0x05 # BIFF4-BIFF8X
241
+ HORZ_CENTER_ACROSS_SEL = 0x06 # Centred across selection (BIFF4-BIFF8X)
242
+ HORZ_DISTRIBUTED = 0x07 # Distributed (BIFF8X)
243
+
244
+ VERT_TOP = 0x00
245
+ VERT_CENTER = 0x01
246
+ VERT_BOTTOM = 0x02
247
+ VERT_JUSTIFIED = 0x03 # Justified (BIFF5-BIFF8X)
248
+ VERT_DISTRIBUTED = 0x04 # Distributed (BIFF8X)
249
+
250
+ DIRECTION_GENERAL = 0x00 # BIFF8X
251
+ DIRECTION_LR = 0x01
252
+ DIRECTION_RL = 0x02
253
+
254
+ ORIENTATION_NOT_ROTATED = 0x00
255
+ ORIENTATION_STACKED = 0x01
256
+ ORIENTATION_90_CC = 0x02
257
+ ORIENTATION_90_CW = 0x03
258
+
259
+ ROTATION_0_ANGLE = 0x00
260
+ ROTATION_STACKED = 0xFF
261
+
262
+ WRAP_AT_RIGHT = 0x01
263
+ NOT_WRAP_AT_RIGHT = 0x00
264
+
265
+ SHRINK_TO_FIT = 0x01
266
+ NOT_SHRINK_TO_FIT = 0x00
267
+
268
+ attr_accessor :horz
269
+ attr_accessor :vert
270
+ attr_accessor :dire
271
+ attr_accessor :orie
272
+ attr_accessor :rota
273
+ attr_accessor :shri
274
+ attr_accessor :inde
275
+ attr_accessor :merg
276
+
277
+ attr_reader :wrap
278
+
279
+ def initialize(hash = {})
280
+ # Initialize to defaults.
281
+ @horz = HORZ_GENERAL
282
+ @vert = VERT_BOTTOM
283
+ @wrap = NOT_WRAP_AT_RIGHT
284
+ @dire = DIRECTION_GENERAL
285
+ @orie = ORIENTATION_NOT_ROTATED
286
+ @rota = ROTATION_0_ANGLE
287
+ @shri = NOT_SHRINK_TO_FIT
288
+ @inde = 0
289
+ @merg = 0
290
+
291
+ # Allow defaults to be overridden in hash. Where there is no :align key in hash,
292
+ # this just leaves the default value in place.
293
+ self.align = hash[:align]
294
+ self.wrap = hash[:wrap]
295
+ end
296
+
297
+ # Don't support passing constants here because :horz and :vert are exposed
298
+ # so if someone wants to use nasty HORZ_RIGHT they can do align.vert = HORZ_RIGHT
299
+ def align=(alignment_directives)
300
+ if alignment_directives =~ /\s/
301
+ args = alignment_directives.split
302
+ else
303
+ args = [alignment_directives] # there's just 1 here
304
+ end
305
+
306
+ args.each do |a|
307
+ case a
308
+ when 'right'
309
+ @horz = HORZ_RIGHT
310
+ when 'left'
311
+ @horz = HORZ_LEFT
312
+ when 'center', 'centre'
313
+ @horz = HORZ_CENTER
314
+ when 'general'
315
+ @horz = HORZ_GENERAL
316
+ when 'filled'
317
+ @horz = HORZ_FILLED
318
+ when 'justify'
319
+ @horz = HORZ_JUSTIFIED
320
+ when 'top'
321
+ @vert = VERT_TOP
322
+ when 'bottom'
323
+ @vert = VERT_BOTTOM
324
+ when nil
325
+ # Do nothing.
326
+ else
327
+ raise "I don't know how to set align to #{a.inspect}."
328
+ end
329
+ end
330
+ end
331
+
332
+ def wrap=(arg)
333
+ case arg
334
+ when TrueClass, WRAP_AT_RIGHT
335
+ @wrap = WRAP_AT_RIGHT
336
+ when FalseClass, NOT_WRAP_AT_RIGHT
337
+ @wrap = NOT_WRAP_AT_RIGHT
338
+ when nil
339
+ # Do nothing.
340
+ else
341
+ raise "I don't know how to set wrap to #{arg.inspect}."
342
+ end
343
+ end
344
+ end
345
+
346
+ class Borders
347
+ attr_reader :left
348
+ attr_reader :right
349
+ attr_reader :top
350
+ attr_reader :bottom
351
+ attr_reader :diag
352
+
353
+ attr_accessor :left_colour
354
+ attr_accessor :right_colour
355
+ attr_accessor :top_colour
356
+ attr_accessor :bottom_colour
357
+ attr_accessor :diag_colour
358
+
359
+ attr_accessor :need_diag1
360
+ attr_accessor :need_diag2
361
+
362
+ NO_LINE = 0x00
363
+ THIN = 0x01
364
+ MEDIUM = 0x02
365
+ DASHED = 0x03
366
+ DOTTED = 0x04
367
+ THICK = 0x05
368
+ DOUBLE = 0x06
369
+ HAIR = 0x07
370
+ #The following for BIFF8
371
+ MEDIUM_DASHED = 0x08
372
+ THIN_DASH_DOTTED = 0x09
373
+ MEDIUM_DASH_DOTTED = 0x0A
374
+ THIN_DASH_DOT_DOTTED = 0x0B
375
+ MEDIUM_DASH_DOT_DOTTED = 0x0C
376
+ SLANTED_MEDIUM_DASH_DOTTED = 0x0D
377
+
378
+ NEED_DIAG1 = 0x01
379
+ NEED_DIAG2 = 0x01
380
+ NO_NEED_DIAG1 = 0x00
381
+ NO_NEED_DIAG2 = 0x00
382
+
383
+ # Want to keep these sorted in this order, so need nested array instead of hash.
384
+ LINE_TYPE_DIRECTIVES = [
385
+ ['none', NO_LINE],
386
+ ['thin', THIN],
387
+ ['medium', MEDIUM],
388
+ ['dashed', DASHED],
389
+ ['dotted', DOTTED],
390
+ ['thick', THICK],
391
+ ['double', DOUBLE],
392
+ ['hair', HAIR],
393
+ ['medium-dashed', MEDIUM_DASHED],
394
+ ['thin-dash-dotted', THIN_DASH_DOTTED],
395
+ ['medium-dash-dotted', MEDIUM_DASH_DOTTED],
396
+ ['thin-dash-dot-dotted', THIN_DASH_DOT_DOTTED],
397
+ ['medium-dash-dot-dotted', MEDIUM_DASH_DOT_DOTTED],
398
+ ['slanted-medium-dash-dotted', SLANTED_MEDIUM_DASH_DOTTED]
399
+ ]
400
+
401
+ def self.line_type_directives
402
+ LINE_TYPE_DIRECTIVES.collect {|k, v| k}
403
+ end
404
+
405
+ def self.line_type_constants
406
+ LINE_TYPE_DIRECTIVES.collect {|k, v| v}
407
+ end
408
+
409
+ def self.line_type_directives_hash
410
+ Hash[*LINE_TYPE_DIRECTIVES.flatten]
411
+ end
412
+
413
+ def initialize(hash = {})
414
+ @left = NO_LINE
415
+ @right = NO_LINE
416
+ @top = NO_LINE
417
+ @bottom = NO_LINE
418
+ @diag = NO_LINE
419
+
420
+ @left_colour = 0x40
421
+ @right_colour = 0x40
422
+ @top_colour = 0x40
423
+ @bottom_colour = 0x40
424
+ @diag_colour = 0x40
425
+
426
+ @need_diag1 = NO_NEED_DIAG1
427
+ @need_diag2 = NO_NEED_DIAG2
428
+
429
+ hash.each do |k, v|
430
+ self.send((k.to_s + '=').to_sym, v)
431
+ end
432
+ end
433
+
434
+ def all=(directives)
435
+ self.left = directives
436
+ self.right = directives
437
+ self.top = directives
438
+ self.bottom = directives
439
+ end
440
+
441
+ def process_directives(directives)
442
+ if directives =~ /\s/
443
+ args = directives.split
444
+ else
445
+ args = [directives] # there's just 1 here, stick it in an array
446
+ end
447
+
448
+ raise "no directives given to process_directives" if args.empty? # maybe don't need this, just get thin black border? but for development I want to know if this happens.
449
+ raise "too many directives given to process_directives" if args.size > 2
450
+
451
+ instructions = [THIN, Formatting::COLOURS['black']]
452
+ args.each do |a|
453
+ if Formatting::COLOURS.include?(a)
454
+ instructions[1] = Formatting::COLOURS[a]
455
+ next
456
+ end
457
+
458
+ if Borders.line_type_directives.include?(a)
459
+ instructions[0] = Borders.line_type_directives_hash[a]
460
+ next
461
+ end
462
+
463
+ if Borders.line_type_constants.include?(a)
464
+ instructions[0] = a
465
+ next
466
+ end
467
+
468
+ raise "I don't know how to format a border with #{a.inspect}."
469
+ end
470
+
471
+ instructions
472
+ end
473
+
474
+ def right=(directives)
475
+ @right, @right_colour = process_directives(directives)
476
+ end
477
+
478
+ def left=(directives)
479
+ @left, @left_colour = process_directives(directives)
480
+ end
481
+
482
+ def top=(directives)
483
+ @top, @top_colour = process_directives(directives)
484
+ end
485
+
486
+ def bottom=(directives)
487
+ @bottom, @bottom_colour = process_directives(directives)
488
+ end
489
+ end
490
+
491
+
492
+ class Pattern
493
+ NO_PATTERN = 0x00
494
+ SOLID_FOREGROUND = 0x01
495
+ SOLID_PATTERN = SOLID_FOREGROUND # for backwards compatibility
496
+ FINE_DOTS = 0x02
497
+ ALT_BARS = 0x03
498
+ SPARSE_DOTS = 0x04
499
+ THICK_HORZ_BANDS = 0x05
500
+ THICK_VERT_BANDS = 0x06
501
+ THICK_BACKWARD_DIAG = 0x07
502
+ THICK_FORWARD_DIAG = 0x08
503
+ BIG_SPOTS = 0x09
504
+ BRICKS = 0x0A
505
+ THIN_HORZ_BANDS = 0x0B
506
+ THIN_VERT_BANDS = 0x0C
507
+ THIN_BACKWARD_DIAG = 0x0D
508
+ THIN_FORWARD_DIAG = 0x0E
509
+ SQUARES = 0x0F
510
+ DIAMONDS = 0x10
511
+ LESS_DOTS = 0x11
512
+ LEAST_DOTS = 0x12
513
+
514
+ # Want to keep these sorted in this order, so need nested array instead of hash.
515
+ PATTERN_DIRECTIVES = [
516
+ ['none', NO_PATTERN],
517
+ ['solid', SOLID_FOREGROUND],
518
+ ['fine-dots', FINE_DOTS],
519
+ ['alt-bars', ALT_BARS],
520
+ ['sparse-dots', SPARSE_DOTS],
521
+ ['thick-horz-bands', THICK_HORZ_BANDS],
522
+ ['thick-vert-bands', THICK_VERT_BANDS],
523
+ ['thick-backward-diag', THICK_BACKWARD_DIAG],
524
+ ['thick-forward-diag', THICK_FORWARD_DIAG],
525
+ ['big-spots', BIG_SPOTS],
526
+ ['bricks', BRICKS],
527
+ ['thin-horz-bands', THIN_HORZ_BANDS],
528
+ ['thin-vert-bands', THIN_VERT_BANDS],
529
+ ['thin-backward-diag', THIN_BACKWARD_DIAG],
530
+ ['thin-forward-diag', THIN_FORWARD_DIAG],
531
+ ['squares', SQUARES],
532
+ ['diamonds', DIAMONDS],
533
+ ['less-dots', LESS_DOTS],
534
+ ['least-dots', LEAST_DOTS]
535
+ ]
536
+
537
+ attr_reader :pattern
538
+ attr_reader :pattern_fore_colour
539
+ attr_reader :pattern_back_colour
540
+
541
+ def self.fill_directives
542
+ PATTERN_DIRECTIVES.collect {|a| a[0]}
543
+ end
544
+
545
+ def self.directives_hash
546
+ Hash[*PATTERN_DIRECTIVES.flatten]
547
+ end
548
+
549
+ def initialize(hash = {})
550
+ @pattern = NO_PATTERN
551
+ @pattern_fore_colour = 0x40
552
+ @pattern_back_colour = 0x41
553
+
554
+ hash.each do |k, v|
555
+ self.send((k.to_s + '=').to_sym, v)
556
+ end
557
+ end
558
+
559
+ def pattern=(arg)
560
+ case arg
561
+ when String
562
+ pattern_index = Pattern.directives_hash[arg]
563
+ when Integer
564
+ pattern_index = arg
565
+ else
566
+ raise "I don't know how to interpret #{arg.inspect} as a pattern!"
567
+ end
568
+ raise "invalid pattern #{arg}" if pattern_index.nil?
569
+
570
+ @pattern = pattern_index
571
+ end
572
+
573
+ def fore_colour=(arg)
574
+ colour_index = Formatting::COLOURS[arg]
575
+ raise "Invalid colour #{arg}" if colour_index.nil?
576
+ @pattern_fore_colour = colour_index
577
+ end
578
+ alias :fore_color= :fore_colour=
579
+
580
+ def back_colour=(arg)
581
+ colour_index = Formatting::COLOURS[arg]
582
+ raise "Invalid colour #{arg}" if colour_index.nil?
583
+ @pattern_back_colour = colour_index
584
+ end
585
+ alias :back_color= :back_colour=
586
+
587
+ # Sets the foreground colour, also if no pattern has been specified
588
+ # will assume you want a solid colour fill.
589
+ def colour=(arg)
590
+ self.fore_colour = arg
591
+ @pattern = SOLID_PATTERN if @pattern == NO_PATTERN
592
+ end
593
+ alias :color= :colour=
594
+ alias :fill= :colour=
595
+ end
596
+
597
+ class Protection
598
+ attr_accessor :cell_locked
599
+ attr_accessor :formula_hidden
600
+
601
+ def initialize
602
+ @cell_locked = 1
603
+ @formula_hidden = 0
604
+ end
605
+ end