rmasalov-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.
- 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/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 +607 -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/surpass_cell.rb +187 -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/rmasalov-surpass +0 -0
- data/surpass.gemspec +39 -0
- metadata +120 -0
@@ -0,0 +1,607 @@
|
|
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 'middle'
|
323
|
+
@vert = VERT_CENTER
|
324
|
+
when 'bottom'
|
325
|
+
@vert = VERT_BOTTOM
|
326
|
+
when nil
|
327
|
+
# Do nothing.
|
328
|
+
else
|
329
|
+
raise "I don't know how to set align to #{a.inspect}."
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def wrap=(arg)
|
335
|
+
case arg
|
336
|
+
when TrueClass, WRAP_AT_RIGHT
|
337
|
+
@wrap = WRAP_AT_RIGHT
|
338
|
+
when FalseClass, NOT_WRAP_AT_RIGHT
|
339
|
+
@wrap = NOT_WRAP_AT_RIGHT
|
340
|
+
when nil
|
341
|
+
# Do nothing.
|
342
|
+
else
|
343
|
+
raise "I don't know how to set wrap to #{arg.inspect}."
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
class Borders
|
349
|
+
attr_reader :left
|
350
|
+
attr_reader :right
|
351
|
+
attr_reader :top
|
352
|
+
attr_reader :bottom
|
353
|
+
attr_reader :diag
|
354
|
+
|
355
|
+
attr_accessor :left_colour
|
356
|
+
attr_accessor :right_colour
|
357
|
+
attr_accessor :top_colour
|
358
|
+
attr_accessor :bottom_colour
|
359
|
+
attr_accessor :diag_colour
|
360
|
+
|
361
|
+
attr_accessor :need_diag1
|
362
|
+
attr_accessor :need_diag2
|
363
|
+
|
364
|
+
NO_LINE = 0x00
|
365
|
+
THIN = 0x01
|
366
|
+
MEDIUM = 0x02
|
367
|
+
DASHED = 0x03
|
368
|
+
DOTTED = 0x04
|
369
|
+
THICK = 0x05
|
370
|
+
DOUBLE = 0x06
|
371
|
+
HAIR = 0x07
|
372
|
+
#The following for BIFF8
|
373
|
+
MEDIUM_DASHED = 0x08
|
374
|
+
THIN_DASH_DOTTED = 0x09
|
375
|
+
MEDIUM_DASH_DOTTED = 0x0A
|
376
|
+
THIN_DASH_DOT_DOTTED = 0x0B
|
377
|
+
MEDIUM_DASH_DOT_DOTTED = 0x0C
|
378
|
+
SLANTED_MEDIUM_DASH_DOTTED = 0x0D
|
379
|
+
|
380
|
+
NEED_DIAG1 = 0x01
|
381
|
+
NEED_DIAG2 = 0x01
|
382
|
+
NO_NEED_DIAG1 = 0x00
|
383
|
+
NO_NEED_DIAG2 = 0x00
|
384
|
+
|
385
|
+
# Want to keep these sorted in this order, so need nested array instead of hash.
|
386
|
+
LINE_TYPE_DIRECTIVES = [
|
387
|
+
['none', NO_LINE],
|
388
|
+
['thin', THIN],
|
389
|
+
['medium', MEDIUM],
|
390
|
+
['dashed', DASHED],
|
391
|
+
['dotted', DOTTED],
|
392
|
+
['thick', THICK],
|
393
|
+
['double', DOUBLE],
|
394
|
+
['hair', HAIR],
|
395
|
+
['medium-dashed', MEDIUM_DASHED],
|
396
|
+
['thin-dash-dotted', THIN_DASH_DOTTED],
|
397
|
+
['medium-dash-dotted', MEDIUM_DASH_DOTTED],
|
398
|
+
['thin-dash-dot-dotted', THIN_DASH_DOT_DOTTED],
|
399
|
+
['medium-dash-dot-dotted', MEDIUM_DASH_DOT_DOTTED],
|
400
|
+
['slanted-medium-dash-dotted', SLANTED_MEDIUM_DASH_DOTTED]
|
401
|
+
]
|
402
|
+
|
403
|
+
def self.line_type_directives
|
404
|
+
LINE_TYPE_DIRECTIVES.collect {|k, v| k}
|
405
|
+
end
|
406
|
+
|
407
|
+
def self.line_type_constants
|
408
|
+
LINE_TYPE_DIRECTIVES.collect {|k, v| v}
|
409
|
+
end
|
410
|
+
|
411
|
+
def self.line_type_directives_hash
|
412
|
+
Hash[*LINE_TYPE_DIRECTIVES.flatten]
|
413
|
+
end
|
414
|
+
|
415
|
+
def initialize(hash = {})
|
416
|
+
@left = NO_LINE
|
417
|
+
@right = NO_LINE
|
418
|
+
@top = NO_LINE
|
419
|
+
@bottom = NO_LINE
|
420
|
+
@diag = NO_LINE
|
421
|
+
|
422
|
+
@left_colour = 0x40
|
423
|
+
@right_colour = 0x40
|
424
|
+
@top_colour = 0x40
|
425
|
+
@bottom_colour = 0x40
|
426
|
+
@diag_colour = 0x40
|
427
|
+
|
428
|
+
@need_diag1 = NO_NEED_DIAG1
|
429
|
+
@need_diag2 = NO_NEED_DIAG2
|
430
|
+
|
431
|
+
hash.each do |k, v|
|
432
|
+
self.send((k.to_s + '=').to_sym, v)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
def all=(directives)
|
437
|
+
self.left = directives
|
438
|
+
self.right = directives
|
439
|
+
self.top = directives
|
440
|
+
self.bottom = directives
|
441
|
+
end
|
442
|
+
|
443
|
+
def process_directives(directives)
|
444
|
+
if directives =~ /\s/
|
445
|
+
args = directives.split
|
446
|
+
else
|
447
|
+
args = [directives] # there's just 1 here, stick it in an array
|
448
|
+
end
|
449
|
+
|
450
|
+
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.
|
451
|
+
raise "too many directives given to process_directives" if args.size > 2
|
452
|
+
|
453
|
+
instructions = [THIN, Formatting::COLOURS['black']]
|
454
|
+
args.each do |a|
|
455
|
+
if Formatting::COLOURS.include?(a)
|
456
|
+
instructions[1] = Formatting::COLOURS[a]
|
457
|
+
next
|
458
|
+
end
|
459
|
+
|
460
|
+
if Borders.line_type_directives.include?(a)
|
461
|
+
instructions[0] = Borders.line_type_directives_hash[a]
|
462
|
+
next
|
463
|
+
end
|
464
|
+
|
465
|
+
if Borders.line_type_constants.include?(a)
|
466
|
+
instructions[0] = a
|
467
|
+
next
|
468
|
+
end
|
469
|
+
|
470
|
+
raise "I don't know how to format a border with #{a.inspect}."
|
471
|
+
end
|
472
|
+
|
473
|
+
instructions
|
474
|
+
end
|
475
|
+
|
476
|
+
def right=(directives)
|
477
|
+
@right, @right_colour = process_directives(directives)
|
478
|
+
end
|
479
|
+
|
480
|
+
def left=(directives)
|
481
|
+
@left, @left_colour = process_directives(directives)
|
482
|
+
end
|
483
|
+
|
484
|
+
def top=(directives)
|
485
|
+
@top, @top_colour = process_directives(directives)
|
486
|
+
end
|
487
|
+
|
488
|
+
def bottom=(directives)
|
489
|
+
@bottom, @bottom_colour = process_directives(directives)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
|
494
|
+
class Pattern
|
495
|
+
NO_PATTERN = 0x00
|
496
|
+
SOLID_FOREGROUND = 0x01
|
497
|
+
SOLID_PATTERN = SOLID_FOREGROUND # for backwards compatibility
|
498
|
+
FINE_DOTS = 0x02
|
499
|
+
ALT_BARS = 0x03
|
500
|
+
SPARSE_DOTS = 0x04
|
501
|
+
THICK_HORZ_BANDS = 0x05
|
502
|
+
THICK_VERT_BANDS = 0x06
|
503
|
+
THICK_BACKWARD_DIAG = 0x07
|
504
|
+
THICK_FORWARD_DIAG = 0x08
|
505
|
+
BIG_SPOTS = 0x09
|
506
|
+
BRICKS = 0x0A
|
507
|
+
THIN_HORZ_BANDS = 0x0B
|
508
|
+
THIN_VERT_BANDS = 0x0C
|
509
|
+
THIN_BACKWARD_DIAG = 0x0D
|
510
|
+
THIN_FORWARD_DIAG = 0x0E
|
511
|
+
SQUARES = 0x0F
|
512
|
+
DIAMONDS = 0x10
|
513
|
+
LESS_DOTS = 0x11
|
514
|
+
LEAST_DOTS = 0x12
|
515
|
+
|
516
|
+
# Want to keep these sorted in this order, so need nested array instead of hash.
|
517
|
+
PATTERN_DIRECTIVES = [
|
518
|
+
['none', NO_PATTERN],
|
519
|
+
['solid', SOLID_FOREGROUND],
|
520
|
+
['fine-dots', FINE_DOTS],
|
521
|
+
['alt-bars', ALT_BARS],
|
522
|
+
['sparse-dots', SPARSE_DOTS],
|
523
|
+
['thick-horz-bands', THICK_HORZ_BANDS],
|
524
|
+
['thick-vert-bands', THICK_VERT_BANDS],
|
525
|
+
['thick-backward-diag', THICK_BACKWARD_DIAG],
|
526
|
+
['thick-forward-diag', THICK_FORWARD_DIAG],
|
527
|
+
['big-spots', BIG_SPOTS],
|
528
|
+
['bricks', BRICKS],
|
529
|
+
['thin-horz-bands', THIN_HORZ_BANDS],
|
530
|
+
['thin-vert-bands', THIN_VERT_BANDS],
|
531
|
+
['thin-backward-diag', THIN_BACKWARD_DIAG],
|
532
|
+
['thin-forward-diag', THIN_FORWARD_DIAG],
|
533
|
+
['squares', SQUARES],
|
534
|
+
['diamonds', DIAMONDS],
|
535
|
+
['less-dots', LESS_DOTS],
|
536
|
+
['least-dots', LEAST_DOTS]
|
537
|
+
]
|
538
|
+
|
539
|
+
attr_reader :pattern
|
540
|
+
attr_reader :pattern_fore_colour
|
541
|
+
attr_reader :pattern_back_colour
|
542
|
+
|
543
|
+
def self.fill_directives
|
544
|
+
PATTERN_DIRECTIVES.collect {|a| a[0]}
|
545
|
+
end
|
546
|
+
|
547
|
+
def self.directives_hash
|
548
|
+
Hash[*PATTERN_DIRECTIVES.flatten]
|
549
|
+
end
|
550
|
+
|
551
|
+
def initialize(hash = {})
|
552
|
+
@pattern = NO_PATTERN
|
553
|
+
@pattern_fore_colour = 0x40
|
554
|
+
@pattern_back_colour = 0x41
|
555
|
+
|
556
|
+
hash.each do |k, v|
|
557
|
+
self.send((k.to_s + '=').to_sym, v)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def pattern=(arg)
|
562
|
+
case arg
|
563
|
+
when String
|
564
|
+
pattern_index = Pattern.directives_hash[arg]
|
565
|
+
when Integer
|
566
|
+
pattern_index = arg
|
567
|
+
else
|
568
|
+
raise "I don't know how to interpret #{arg.inspect} as a pattern!"
|
569
|
+
end
|
570
|
+
raise "invalid pattern #{arg}" if pattern_index.nil?
|
571
|
+
|
572
|
+
@pattern = pattern_index
|
573
|
+
end
|
574
|
+
|
575
|
+
def fore_colour=(arg)
|
576
|
+
colour_index = Formatting::COLOURS[arg]
|
577
|
+
raise "Invalid colour #{arg}" if colour_index.nil?
|
578
|
+
@pattern_fore_colour = colour_index
|
579
|
+
end
|
580
|
+
alias :fore_color= :fore_colour=
|
581
|
+
|
582
|
+
def back_colour=(arg)
|
583
|
+
colour_index = Formatting::COLOURS[arg]
|
584
|
+
raise "Invalid colour #{arg}" if colour_index.nil?
|
585
|
+
@pattern_back_colour = colour_index
|
586
|
+
end
|
587
|
+
alias :back_color= :back_colour=
|
588
|
+
|
589
|
+
# Sets the foreground colour, also if no pattern has been specified
|
590
|
+
# will assume you want a solid colour fill.
|
591
|
+
def colour=(arg)
|
592
|
+
self.fore_colour = arg
|
593
|
+
@pattern = SOLID_PATTERN if @pattern == NO_PATTERN
|
594
|
+
end
|
595
|
+
alias :color= :colour=
|
596
|
+
alias :fill= :colour=
|
597
|
+
end
|
598
|
+
|
599
|
+
class Protection
|
600
|
+
attr_accessor :cell_locked
|
601
|
+
attr_accessor :formula_hidden
|
602
|
+
|
603
|
+
def initialize
|
604
|
+
@cell_locked = 1
|
605
|
+
@formula_hidden = 0
|
606
|
+
end
|
607
|
+
end
|