surpass 0.0.3

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.
Files changed (78) hide show
  1. data/History.txt +0 -0
  2. data/README.txt +133 -0
  3. data/Rakefile +35 -0
  4. data/examples/big-16mb.rb +25 -0
  5. data/examples/big-random-strings.rb +28 -0
  6. data/examples/blanks.rb +34 -0
  7. data/examples/col_width.rb +16 -0
  8. data/examples/dates.rb +31 -0
  9. data/examples/format.rb +23 -0
  10. data/examples/hello-world.rb +9 -0
  11. data/examples/image.rb +10 -0
  12. data/examples/merged.rb +36 -0
  13. data/examples/merged0.rb +27 -0
  14. data/examples/merged1.rb +99 -0
  15. data/examples/num_formats.rb +55 -0
  16. data/examples/numbers.rb +24 -0
  17. data/examples/outline.rb +110 -0
  18. data/examples/panes.rb +48 -0
  19. data/examples/protection.rb +132 -0
  20. data/examples/python.bmp +0 -0
  21. data/examples/row_styles.rb +16 -0
  22. data/examples/row_styles_empty.rb +15 -0
  23. data/examples/set_cell_and_range_style.rb +12 -0
  24. data/examples/wrapped-text.rb +13 -0
  25. data/examples/write_arrays.rb +16 -0
  26. data/examples/ws_props.rb +80 -0
  27. data/lib/biff_record.rb +2168 -0
  28. data/lib/bitmap.rb +218 -0
  29. data/lib/cell.rb +214 -0
  30. data/lib/chart.rb +16 -0
  31. data/lib/column.rb +40 -0
  32. data/lib/document.rb +406 -0
  33. data/lib/excel_formula.rb +6 -0
  34. data/lib/excel_magic.rb +1013 -0
  35. data/lib/formatting.rb +554 -0
  36. data/lib/row.rb +137 -0
  37. data/lib/style.rb +179 -0
  38. data/lib/surpass.rb +51 -0
  39. data/lib/utilities.rb +86 -0
  40. data/lib/workbook.rb +206 -0
  41. data/lib/worksheet.rb +561 -0
  42. data/spec/biff_record_spec.rb +268 -0
  43. data/spec/cell_spec.rb +56 -0
  44. data/spec/data/random-strings.txt +10000 -0
  45. data/spec/document_spec.rb +168 -0
  46. data/spec/excel_formula_spec.rb +0 -0
  47. data/spec/formatting_spec.rb +53 -0
  48. data/spec/reference/P-0508-0000507647-3280-5298.xls +0 -0
  49. data/spec/reference/all-cell-styles.bin +0 -0
  50. data/spec/reference/all-number-formats.bin +0 -0
  51. data/spec/reference/all-styles.bin +0 -0
  52. data/spec/reference/mini.xls +0 -0
  53. data/spec/row_spec.rb +19 -0
  54. data/spec/spec_helper.rb +10 -0
  55. data/spec/style_spec.rb +89 -0
  56. data/spec/utilities_spec.rb +57 -0
  57. data/spec/workbook_spec.rb +48 -0
  58. data/spec/worksheet_spec.rb +0 -0
  59. data/stats/cloc.txt +8 -0
  60. data/stats/rcov.txt +0 -0
  61. data/stats/specdoc.txt +158 -0
  62. data/surpass-manual-0-0-3.pdf +0 -0
  63. data/surpass.gemspec +34 -0
  64. data/tasks/ann.rake +80 -0
  65. data/tasks/bones.rake +20 -0
  66. data/tasks/excel.rake +6 -0
  67. data/tasks/gem.rake +201 -0
  68. data/tasks/git.rake +40 -0
  69. data/tasks/metrics.rake +42 -0
  70. data/tasks/notes.rake +27 -0
  71. data/tasks/post_load.rake +34 -0
  72. data/tasks/rdoc.rake +51 -0
  73. data/tasks/rubyforge.rake +55 -0
  74. data/tasks/setup.rb +292 -0
  75. data/tasks/spec.rake +54 -0
  76. data/tasks/svn.rake +47 -0
  77. data/tasks/test.rake +40 -0
  78. metadata +144 -0
data/lib/formatting.rb ADDED
@@ -0,0 +1,554 @@
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
+ 'cyan' => 0x0F,
12
+ 'dark-blue' => 0x12,
13
+ 'dark-green' => 0x3a,
14
+ 'dark-red' => 0x10,
15
+ 'dark-teal' => 0x38,
16
+ 'dark-yellow' => 0x13,
17
+ 'fuchsia' => 0x0E,
18
+ 'gold' => 0x33,
19
+ 'gray' => 0x17,
20
+ 'grey' => 0x17,
21
+ 'green' => 0x11,
22
+ 'grey-25-percent' => 0x16,
23
+ 'grey-40-percent' => 0x37,
24
+ 'grey-50-percent' => 0x17,
25
+ 'grey-80-percent' => 0x3f,
26
+ 'indigo' => 0x3e,
27
+ 'lavender' => 0x2e,
28
+ 'lemon-chiffon' => 0x1a,
29
+ 'light-blue' => 0x30,
30
+ 'light-cornflower-blue' => 0x1f,
31
+ 'light-green' => 0x2a,
32
+ 'light-orange' => 0x34,
33
+ 'light-turquoise' => 0x29,
34
+ 'light-yellow' => 0x2b,
35
+ 'lime' => 0x0B,
36
+ 'lime' => 0x32,
37
+ 'magenta' => 0x0E,
38
+ 'maroon' => 0x19,
39
+ 'navy' => 0x12,
40
+ 'olive-green' => 0x3b,
41
+ 'orange' => 0x35,
42
+ 'orchid' => 0x1c,
43
+ 'pale-blue' => 0x2c,
44
+ 'pink' => 0x21,
45
+ 'pink' => 0xe,
46
+ 'plum' => 0x3d,
47
+ 'purple' => 0x14,
48
+ 'red' => 0x0A,
49
+ 'rose' => 0x2d,
50
+ 'royal-blue' => 0x1e,
51
+ 'sea-green' => 0x39,
52
+ 'silver' => 0x16,
53
+ 'sky-blue' => 0x28,
54
+ 'tan' => 0x2f,
55
+ 'teal' => 0x15,
56
+ 'turquoise' => 0xf,
57
+ 'violet' => 0x14,
58
+ 'white' => 0x09,
59
+ 'yellow' => 0x0D
60
+ }
61
+
62
+ COLORS = COLOURS
63
+ end
64
+
65
+ class Font
66
+ ESCAPEMENT_NONE = 0x00
67
+ ESCAPEMENT_SUPERSCRIPT = 0x01
68
+ ESCAPEMENT_SUBSCRIPT = 0x02
69
+
70
+ UNDERLINE_NONE = 0x00
71
+ UNDERLINE_SINGLE = 0x01
72
+ UNDERLINE_SINGLE_ACC = 0x21
73
+ UNDERLINE_DOUBLE = 0x02
74
+ UNDERLINE_DOUBLE_ACC = 0x22
75
+
76
+ FAMILY_NONE = 0x00
77
+ FAMILY_ROMAN = 0x01
78
+ FAMILY_SWISS = 0x02
79
+ FAMILY_MODERN = 0x03
80
+ FAMILY_SCRIPT = 0x04
81
+ FAMILY_DECORATIVE = 0x05
82
+
83
+ CHARSET_ANSI_LATIN = 0x00
84
+ CHARSET_SYS_DEFAULT = 0x01
85
+ CHARSET_SYMBOL = 0x02
86
+ CHARSET_APPLE_ROMAN = 0x4D
87
+ CHARSET_ANSI_JAP_SHIFT_JIS = 0x80
88
+ CHARSET_ANSI_KOR_HANGUL = 0x81
89
+ CHARSET_ANSI_KOR_JOHAB = 0x82
90
+ CHARSET_ANSI_CHINESE_GBK = 0x86
91
+ CHARSET_ANSI_CHINESE_BIG5 = 0x88
92
+ CHARSET_ANSI_GREEK = 0xA1
93
+ CHARSET_ANSI_TURKISH = 0xA2
94
+ CHARSET_ANSI_VIETNAMESE = 0xA3
95
+ CHARSET_ANSI_HEBREW = 0xB1
96
+ CHARSET_ANSI_ARABIC = 0xB2
97
+ CHARSET_ANSI_BALTIC = 0xBA
98
+ CHARSET_ANSI_CYRILLIC = 0xCC
99
+ CHARSET_ANSI_THAI = 0xDE
100
+ CHARSET_ANSI_LATIN_II = 0xEE
101
+ CHARSET_OEM_LATIN_I = 0xFF
102
+
103
+ PLAIN = 0x00
104
+ BOLD = 0x01
105
+ ITALIC = 0x02
106
+ UNDERLINE = 0x04
107
+ STRUCK_OUT = 0x08
108
+ OUTLINE = 0x010
109
+ SHADOW = 0x020
110
+
111
+ attr_accessor :height
112
+ attr_accessor :italic
113
+ attr_accessor :struck_out
114
+ attr_accessor :outline
115
+ attr_accessor :shadow
116
+ attr_accessor :colour_index
117
+ attr_accessor :bold
118
+ attr_accessor :weight # In practice, seems to be only 400 = normal, 700 = bold so just use bold = true.
119
+ attr_accessor :escapement
120
+ attr_accessor :charset
121
+ attr_accessor :name
122
+
123
+ attr_reader :family
124
+ attr_reader :underline
125
+
126
+ def initialize(hash = {})
127
+ @height = 200 # font size 10
128
+ @italic = false
129
+ @struck_out = false
130
+ @outline = false
131
+ @shadow = false
132
+ @colour_index = 0x7FFF
133
+ @bold = false
134
+ @weight = 400 # regular
135
+ @escapement = ESCAPEMENT_NONE
136
+ @charset = CHARSET_SYS_DEFAULT
137
+ @name = 'Arial'
138
+ @family = FAMILY_NONE
139
+ @underline = UNDERLINE_NONE
140
+
141
+ hash.each do |k, v|
142
+ self.send((k.to_s + '=').to_sym, v)
143
+ end
144
+ end
145
+
146
+ def family=(arg)
147
+ raise "Oops, font_family doesn't take a string. Do you want font_name instead?" if arg.is_a?(String)
148
+ @family = arg
149
+ end
150
+
151
+ # Convert font size in points to native twips
152
+ def size=(points)
153
+ @height = points * 20
154
+ end
155
+
156
+ def strikethrough=(arg)
157
+ @struck_out = arg
158
+ end
159
+
160
+ def subscript=(arg)
161
+ case arg
162
+ when TrueClass
163
+ @escapement = ESCAPEMENT_SUBSCRIPT
164
+ when FalseClass
165
+ @escapement = ESCAPEMENT_NONE
166
+ else
167
+ raise "I don't know how to set subscript to #{arg.inspect}."
168
+ end
169
+ end
170
+
171
+ def superscript=(arg)
172
+ case arg
173
+ when TrueClass
174
+ @escapement = ESCAPEMENT_SUPERSCRIPT
175
+ when FalseClass
176
+ @escapement = ESCAPEMENT_NONE
177
+ else
178
+ raise "I don't know how to set superscript to #{arg.inspect}."
179
+ end
180
+ end
181
+
182
+ # User-friendly underlining directives.
183
+ def underline=(arg)
184
+ case arg
185
+ when UNDERLINE_NONE, UNDERLINE_SINGLE, UNDERLINE_SINGLE_ACC, UNDERLINE_DOUBLE, UNDERLINE_DOUBLE_ACC
186
+ @underline = arg
187
+ when nil
188
+ @underline ||= UNDERLINE_NONE
189
+ when TrueClass
190
+ @underline = UNDERLINE_SINGLE
191
+ when FalseClass
192
+ @underline = UNDERLINE_NONE
193
+ when :none
194
+ @underline = UNDERLINE_NONE
195
+ when :single
196
+ @underline = UNDERLINE_SINGLE
197
+ when :single_acc, :single_accounting
198
+ @underline = UNDERLINE_SINGLE_ACC
199
+ when :double
200
+ @underline = UNDERLINE_DOUBLE
201
+ when :double_acc, :double_accounting
202
+ @underline = UNDERLINE_DOUBLE_ACC
203
+ else
204
+ raise "I don't know how to set underline to #{arg.inspect}."
205
+ end
206
+ end
207
+
208
+ def colour_index_from_name(colour_name)
209
+ Formatting::COLOURS[colour_name.to_s]
210
+ end
211
+
212
+ def colour=(colour_name)
213
+ new_colour = colour_index_from_name(colour_name)
214
+ if new_colour.nil?
215
+ raise "Invalid Colour #{colour_name}"
216
+ else
217
+ @colour_index = new_colour
218
+ end
219
+ end
220
+ alias :color= :colour=
221
+
222
+ def to_biff
223
+ options = PLAIN
224
+ options |= BOLD if @bold
225
+ options |= ITALIC if @italic
226
+ options |= UNDERLINE if (@underline != UNDERLINE_NONE)
227
+ options |= STRUCK_OUT if @struck_out
228
+ options |= OUTLINE if @outline
229
+ options |= SHADOW if @shadow
230
+
231
+ @weight = 700 if @bold
232
+ args = [@height, options, @colour_index, @weight, @escapement, @underline, @family, @charset, @name]
233
+ FontRecord.new(*args).to_biff
234
+ end
235
+ end
236
+
237
+ class Alignment
238
+ HORZ_GENERAL = 0x00
239
+ HORZ_LEFT = 0x01
240
+ HORZ_CENTER = 0x02
241
+ HORZ_RIGHT = 0x03
242
+ HORZ_FILLED = 0x04
243
+ HORZ_JUSTIFIED = 0x05 # BIFF4-BIFF8X
244
+ HORZ_CENTER_ACROSS_SEL = 0x06 # Centred across selection (BIFF4-BIFF8X)
245
+ HORZ_DISTRIBUTED = 0x07 # Distributed (BIFF8X)
246
+
247
+ VERT_TOP = 0x00
248
+ VERT_CENTER = 0x01
249
+ VERT_BOTTOM = 0x02
250
+ VERT_JUSTIFIED = 0x03 # Justified (BIFF5-BIFF8X)
251
+ VERT_DISTRIBUTED = 0x04 # Distributed (BIFF8X)
252
+
253
+ DIRECTION_GENERAL = 0x00 # BIFF8X
254
+ DIRECTION_LR = 0x01
255
+ DIRECTION_RL = 0x02
256
+
257
+ ORIENTATION_NOT_ROTATED = 0x00
258
+ ORIENTATION_STACKED = 0x01
259
+ ORIENTATION_90_CC = 0x02
260
+ ORIENTATION_90_CW = 0x03
261
+
262
+ ROTATION_0_ANGLE = 0x00
263
+ ROTATION_STACKED = 0xFF
264
+
265
+ WRAP_AT_RIGHT = 0x01
266
+ NOT_WRAP_AT_RIGHT = 0x00
267
+
268
+ SHRINK_TO_FIT = 0x01
269
+ NOT_SHRINK_TO_FIT = 0x00
270
+
271
+ attr_accessor :horz
272
+ attr_accessor :vert
273
+ attr_accessor :dire
274
+ attr_accessor :orie
275
+ attr_accessor :rota
276
+ attr_accessor :shri
277
+ attr_accessor :inde
278
+ attr_accessor :merg
279
+
280
+ attr_reader :wrap
281
+
282
+ def initialize(hash = {})
283
+ # Initialize to defaults.
284
+ @horz = HORZ_GENERAL
285
+ @vert = VERT_BOTTOM
286
+ @wrap = NOT_WRAP_AT_RIGHT
287
+ @dire = DIRECTION_GENERAL
288
+ @orie = ORIENTATION_NOT_ROTATED
289
+ @rota = ROTATION_0_ANGLE
290
+ @shri = NOT_SHRINK_TO_FIT
291
+ @inde = 0
292
+ @merg = 0
293
+
294
+ # Allow defaults to be overridden in hash. Where there is no :align key in hash,
295
+ # this just leaves the default value in place.
296
+ self.align = hash[:align]
297
+ self.wrap = hash[:wrap]
298
+ end
299
+
300
+ # Don't support passing constants here because :horz and :vert are exposed
301
+ # so if someone wants to use nasty HORZ_RIGHT they can do align.vert = HORZ_RIGHT
302
+ def align=(alignment_directives)
303
+ if alignment_directives =~ /\s/
304
+ args = alignment_directives.split
305
+ else
306
+ args = [alignment_directives] # there's just 1 here
307
+ end
308
+
309
+ args.each do |a|
310
+ case a
311
+ when 'right'
312
+ @horz = HORZ_RIGHT
313
+ when 'left'
314
+ @horz = HORZ_LEFT
315
+ when 'center', 'centre'
316
+ @horz = HORZ_CENTER
317
+ when 'general'
318
+ @horz = HORZ_GENERAL
319
+ when 'filled'
320
+ @horz = HORZ_FILLED
321
+ when 'justify'
322
+ @horz = HORZ_JUSTIFIED
323
+ when 'top'
324
+ @vert = VERT_TOP
325
+ when 'bottom'
326
+ @vert = VERT_BOTTOM
327
+ when nil
328
+ # Do nothing.
329
+ else
330
+ raise "I don't know how to set align to #{a.inspect}."
331
+ end
332
+ end
333
+ end
334
+
335
+ def wrap=(arg)
336
+ case arg
337
+ when TrueClass, WRAP_AT_RIGHT
338
+ @wrap = WRAP_AT_RIGHT
339
+ when FalseClass, NOT_WRAP_AT_RIGHT
340
+ @wrap = NOT_WRAP_AT_RIGHT
341
+ when nil
342
+ # Do nothing.
343
+ else
344
+ raise "I don't know how to set wrap to #{arg.inspect}."
345
+ end
346
+ end
347
+ end
348
+
349
+ class Borders
350
+ attr_reader :left
351
+ attr_reader :right
352
+ attr_reader :top
353
+ attr_reader :bottom
354
+ attr_reader :diag
355
+
356
+ attr_accessor :left_colour
357
+ attr_accessor :right_colour
358
+ attr_accessor :top_colour
359
+ attr_accessor :bottom_colour
360
+ attr_accessor :diag_colour
361
+
362
+ attr_accessor :need_diag1
363
+ attr_accessor :need_diag2
364
+
365
+ NO_LINE = 0x00
366
+ THIN = 0x01
367
+ MEDIUM = 0x02
368
+ DASHED = 0x03
369
+ DOTTED = 0x04
370
+ THICK = 0x05
371
+ DOUBLE = 0x06
372
+ HAIR = 0x07
373
+ #The following for BIFF8
374
+ MEDIUM_DASHED = 0x08
375
+ THIN_DASH_DOTTED = 0x09
376
+ MEDIUM_DASH_DOTTED = 0x0A
377
+ THIN_DASH_DOT_DOTTED = 0x0B
378
+ MEDIUM_DASH_DOT_DOTTED = 0x0C
379
+ SLANTED_MEDIUM_DASH_DOTTED = 0x0D
380
+
381
+ NEED_DIAG1 = 0x01
382
+ NEED_DIAG2 = 0x01
383
+ NO_NEED_DIAG1 = 0x00
384
+ NO_NEED_DIAG2 = 0x00
385
+
386
+ # Want to keep these sorted in this order, so need nested array instead of hash.
387
+ LINE_TYPE_DIRECTIVES = [
388
+ ['none', NO_LINE],
389
+ ['thin', THIN],
390
+ ['medium', MEDIUM],
391
+ ['dashed', DASHED],
392
+ ['dotted', DOTTED],
393
+ ['thick', THICK],
394
+ ['double', DOUBLE],
395
+ ['hair', HAIR],
396
+ ['medium-dashed', MEDIUM_DASHED],
397
+ ['thin-dash-dotted', THIN_DASH_DOTTED],
398
+ ['medium-dash-dotted', MEDIUM_DASH_DOTTED],
399
+ ['thin-dash-dot-dotted', THIN_DASH_DOT_DOTTED],
400
+ ['medium-dash-dot-dotted', MEDIUM_DASH_DOT_DOTTED],
401
+ ['slanted-medium-dash-dotted', SLANTED_MEDIUM_DASH_DOTTED]
402
+ ]
403
+
404
+ def self.line_type_directives
405
+ LINE_TYPE_DIRECTIVES.collect {|k, v| k}
406
+ end
407
+
408
+ def initialize(hash = {})
409
+ @left = NO_LINE
410
+ @right = NO_LINE
411
+ @top = NO_LINE
412
+ @bottom = NO_LINE
413
+ @diag = NO_LINE
414
+
415
+ @left_colour = 0x40
416
+ @right_colour = 0x40
417
+ @top_colour = 0x40
418
+ @bottom_colour = 0x40
419
+ @diag_colour = 0x40
420
+
421
+ @need_diag1 = NO_NEED_DIAG1
422
+ @need_diag2 = NO_NEED_DIAG2
423
+
424
+ hash.each do |k, v|
425
+ self.send((k.to_s + '=').to_sym, v)
426
+ end
427
+ end
428
+
429
+ def all=(directives)
430
+ self.left = directives
431
+ self.right = directives
432
+ self.top = directives
433
+ self.bottom = directives
434
+ end
435
+
436
+ def process_directives(directives)
437
+ if directives =~ /\s/
438
+ args = directives.split
439
+ else
440
+ args = [directives] # there's just 1 here, stick it in an array
441
+ end
442
+
443
+ 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.
444
+
445
+ instructions = [THIN, Formatting::COLOURS['black']]
446
+ args.each do |a|
447
+ if Formatting::COLOURS.include?(a)
448
+ instructions[1] = Formatting::COLOURS[a]
449
+ next
450
+ end
451
+
452
+ if Borders.line_type_directives.include?(a)
453
+ line_type_directives_hash = Hash[*LINE_TYPE_DIRECTIVES.flatten]
454
+ instructions[0] = line_type_directives_hash[a]
455
+ next
456
+ end
457
+
458
+ raise "I don't know how to format a border with #{a.inspect}."
459
+ end
460
+
461
+ instructions
462
+ end
463
+
464
+ def right=(directives)
465
+ @right, @right_colour = process_directives(directives)
466
+ end
467
+
468
+ def left=(directives)
469
+ @left, @left_colour = process_directives(directives)
470
+ end
471
+
472
+ def top=(directives)
473
+ @top, @top_colour = process_directives(directives)
474
+ end
475
+
476
+ def bottom=(directives)
477
+ @bottom, @bottom_colour = process_directives(directives)
478
+ end
479
+ end
480
+ # public final static short NO_FILL = 0 ;
481
+ # /** Solidly filled */
482
+ # public final static short SOLID_FOREGROUND = 1 ;
483
+ # /** Small fine dots */
484
+ # public final static short FINE_DOTS = 2 ;
485
+ # /** Wide dots */
486
+ # public final static short ALT_BARS = 3 ;
487
+ # /** Sparse dots */
488
+ # public final static short SPARSE_DOTS = 4 ;
489
+ # /** Thick horizontal bands */
490
+ # public final static short THICK_HORZ_BANDS = 5 ;
491
+ # /** Thick vertical bands */
492
+ # public final static short THICK_VERT_BANDS = 6 ;
493
+ # /** Thick backward facing diagonals */
494
+ # public final static short THICK_BACKWARD_DIAG = 7 ;
495
+ # /** Thick forward facing diagonals */
496
+ # public final static short THICK_FORWARD_DIAG = 8 ;
497
+ # /** Large spots */
498
+ # public final static short BIG_SPOTS = 9 ;
499
+ # /** Brick-like layout */
500
+ # public final static short BRICKS = 10 ;
501
+ # /** Thin horizontal bands */
502
+ # public final static short THIN_HORZ_BANDS = 11 ;
503
+ # /** Thin vertical bands */
504
+ # public final static short THIN_VERT_BANDS = 12 ;
505
+ # /** Thin backward diagonal */
506
+ # public final static short THIN_BACKWARD_DIAG = 13 ;
507
+ # /** Thin forward diagonal */
508
+ # public final static short THIN_FORWARD_DIAG = 14 ;
509
+ # /** Squares */
510
+ # public final static short SQUARES = 15 ;
511
+ # /** Diamonds */
512
+ # public final static short DIAMONDS = 16 ;
513
+ # /** Less Dots */
514
+ # public final static short LESS_DOTS = 17 ;
515
+ # /** Least Dots */
516
+ # public final static short LEAST_DOTS = 18 ;
517
+ class Pattern
518
+ NO_PATTERN = 0x00
519
+ SOLID_PATTERN = 0x01
520
+
521
+ attr_accessor :pattern
522
+ attr_accessor :pattern_fore_colour
523
+ attr_accessor :pattern_back_colour
524
+
525
+ def initialize(hash = {})
526
+ @pattern = NO_PATTERN
527
+ @pattern_fore_colour = 0x40
528
+ @pattern_back_colour = 0x41
529
+
530
+ hash.each do |k, v|
531
+ self.send((k.to_s + '=').to_sym, v)
532
+ end
533
+ end
534
+
535
+ # Convenience method to set fill colour
536
+ def colour=(arg)
537
+ colour_index = Formatting::COLOURS[arg]
538
+ raise "Invalid colour #{arg}" if colour_index.nil?
539
+ @pattern = SOLID_PATTERN
540
+ @pattern_fore_colour = colour_index
541
+ end
542
+ alias :color= :colour=
543
+ alias :fill= :colour=
544
+ end
545
+
546
+ class Protection
547
+ attr_accessor :cell_locked
548
+ attr_accessor :formula_hidden
549
+
550
+ def initialize
551
+ @cell_locked = 1
552
+ @formula_hidden = 0
553
+ end
554
+ end
data/lib/row.rb ADDED
@@ -0,0 +1,137 @@
1
+ class Row
2
+ attr_accessor :index
3
+ attr_accessor :parent
4
+ attr_accessor :parent_wb
5
+ attr_accessor :cells
6
+ attr_accessor :min_col_index
7
+ attr_accessor :max_col_index
8
+ attr_accessor :total_str
9
+ attr_accessor :xf_index
10
+ attr_accessor :has_default_format
11
+ attr_accessor :height_in_pixels
12
+
13
+ attr_accessor :height
14
+ attr_accessor :has_default_height
15
+ attr_accessor :height_mismatch
16
+ attr_accessor :level
17
+ attr_accessor :collapse
18
+ attr_accessor :hidden
19
+ attr_accessor :space_above
20
+ attr_accessor :space_below
21
+
22
+ def initialize(index, parent_sheet)
23
+ is_int = index.is_a?(Integer)
24
+ in_range = (index >= 0) && (index <= 65535)
25
+ raise "row index #{index} is not valid" unless is_int && in_range
26
+
27
+ @index = index
28
+ @parent = parent_sheet
29
+ @parent_wb = parent_sheet.parent()
30
+ @cells = []
31
+ @min_col_index = 0
32
+ @max_col_index = 0
33
+ @total_str = 0
34
+ @xf_index = 0x0F
35
+ @has_default_format = 0
36
+ @height_in_pixels = 0x11
37
+
38
+ @height = 0x00FF
39
+ @has_default_height = 0x00
40
+ @height_mismatch = 0
41
+ @level = 0
42
+ @collapse = 0
43
+ @hidden = 0
44
+ @space_above = 0
45
+ @space_below = 0
46
+ end
47
+
48
+ def adjust_height(style)
49
+ twips = style.font.height
50
+ points = twips/20.0
51
+ # Cell height in pixels can be calcuted by following approx. formula:
52
+ # cell height in pixels = font height in points * 83/50 + 2/5
53
+ # It works when screen resolution is 96 dpi
54
+ pix = (points*83.0/50.0 + 2.0/5.0).round
55
+ @height_in_pixels = pix if (pix > @height_in_pixels)
56
+ end
57
+
58
+ def adjust_boundary_column_indexes(*args)
59
+ args.each do |a|
60
+ is_int = (a.to_i == a)
61
+ in_range = (0 <= a) && (a <= 255)
62
+ raise "invalid boundary index #{a}" unless is_int && in_range
63
+ @min_col_index = a if a < @min_col_index
64
+ @max_col_index = a if a > @max_col_index
65
+ end
66
+ end
67
+
68
+ def style=(style)
69
+ adjust_height(style)
70
+ @xf_index = @parent_wb.styles.add(style)
71
+ @has_default_format = 1
72
+ end
73
+
74
+ def cells_count
75
+ @cells.length
76
+ end
77
+
78
+ def to_biff
79
+ height_options = (@height & 0x07FFF)
80
+ height_options |= (@has_default_height & 0x01) << 15
81
+
82
+ options = (@level & 0x07) << 0
83
+ options |= (@collapse & 0x01) << 4
84
+ options |= (@hidden & 0x01) << 5
85
+ options |= (@height_mismatch & 0x01) << 6
86
+ options |= (@has_default_format & 0x01) << 7
87
+ options |= (0x01 & 0x01) << 8
88
+ options |= (@xf_index & 0x0FFF) << 16
89
+ options |= (@space_above & 0x01) << 28
90
+ options |= (@space_below & 0x01) << 29
91
+
92
+ args = [@index, @min_col_index, @max_col_index, height_options, options]
93
+ RowRecord.new(*args).to_biff
94
+ end
95
+
96
+ def cells_biff
97
+ cells.collect {|c| c.to_biff }.join
98
+ end
99
+
100
+ def cell(col_index)
101
+ cells.select {|c| c.index == col_index}.first
102
+ end
103
+
104
+ def write(col, label, style)
105
+ style = StyleFormat.new(style) if style.is_a?(Hash)
106
+ style_index = @parent_wb.styles.add(style)
107
+
108
+ adjust_height(style)
109
+ adjust_boundary_column_indexes(col)
110
+ case label
111
+ when TrueClass, FalseClass
112
+ @cells << BooleanCell.new(self, col, style_index, label)
113
+ when String, NilClass
114
+ if label.to_s.length == 0
115
+ @cells << BlankCell.new(self, col, style_index)
116
+ else
117
+ @cells << StringCell.new(self, col, style_index, @parent_wb.sst.add_str(label))
118
+ @total_str += 1
119
+ end
120
+ when Numeric
121
+ @cells << NumberCell.new(self, col, style_index, label)
122
+ when Date, DateTime, Time
123
+ @cells << NumberCell.new(self, col, style_index, as_excel_date(label))
124
+ when ExcelFormula
125
+ @cells << FormulaCell.new(self, col, style_index, label)
126
+ else
127
+ raise label.class.name
128
+ end
129
+ end
130
+
131
+ def write_blanks(c1, c2, style)
132
+ raise unless c1 <= c2
133
+ adjust_height(style)
134
+ adjust_boundary_column_indexes(c1, c2)
135
+ @cells << MulBlankCell.new(self, c1, c2, @parent_wb.styles.add(style))
136
+ end
137
+ end