slayer-surpass 0.1.0

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.
@@ -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