write_xlsx 1.13.0 → 1.15.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.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +15 -0
  3. data/Changes +20 -0
  4. data/lib/write_xlsx/chart/area.rb +2 -2
  5. data/lib/write_xlsx/chart/axis.rb +55 -32
  6. data/lib/write_xlsx/chart/axis_writer.rb +528 -0
  7. data/lib/write_xlsx/chart/bar.rb +2 -2
  8. data/lib/write_xlsx/chart/caption.rb +16 -9
  9. data/lib/write_xlsx/chart/chart_area.rb +121 -0
  10. data/lib/write_xlsx/chart/column.rb +2 -2
  11. data/lib/write_xlsx/chart/d_pt_point_writer.rb +14 -0
  12. data/lib/write_xlsx/chart/doughnut.rb +0 -3
  13. data/lib/write_xlsx/chart/formatting_writer.rb +652 -0
  14. data/lib/write_xlsx/chart/initialization.rb +100 -0
  15. data/lib/write_xlsx/chart/line.rb +4 -3
  16. data/lib/write_xlsx/chart/pie.rb +6 -2
  17. data/lib/write_xlsx/chart/radar.rb +2 -2
  18. data/lib/write_xlsx/chart/scatter.rb +4 -3
  19. data/lib/write_xlsx/chart/series.rb +35 -15
  20. data/lib/write_xlsx/chart/series_data.rb +132 -0
  21. data/lib/write_xlsx/chart/series_writer.rb +318 -0
  22. data/lib/write_xlsx/chart/settings.rb +226 -0
  23. data/lib/write_xlsx/chart/stock.rb +2 -2
  24. data/lib/write_xlsx/chart/table.rb +50 -0
  25. data/lib/write_xlsx/chart/xml_writer.rb +305 -0
  26. data/lib/write_xlsx/chart.rb +286 -2477
  27. data/lib/write_xlsx/chartsheet.rb +31 -82
  28. data/lib/write_xlsx/constants.rb +11 -0
  29. data/lib/write_xlsx/drawing.rb +5 -3
  30. data/lib/write_xlsx/format/alignment_state.rb +39 -0
  31. data/lib/write_xlsx/format/alignment_style.rb +92 -0
  32. data/lib/write_xlsx/format/border_state.rb +47 -0
  33. data/lib/write_xlsx/format/border_style.rb +116 -0
  34. data/lib/write_xlsx/format/fill_state.rb +26 -0
  35. data/lib/write_xlsx/format/fill_style.rb +52 -0
  36. data/lib/write_xlsx/format/font_state.rb +74 -0
  37. data/lib/write_xlsx/format/font_style.rb +172 -0
  38. data/lib/write_xlsx/format/format_state.rb +65 -0
  39. data/lib/write_xlsx/format/number_format_state.rb +20 -0
  40. data/lib/write_xlsx/format/number_format_style.rb +28 -0
  41. data/lib/write_xlsx/format/protection_state.rb +20 -0
  42. data/lib/write_xlsx/format/protection_style.rb +28 -0
  43. data/lib/write_xlsx/format.rb +1093 -426
  44. data/lib/write_xlsx/formats.rb +0 -2
  45. data/lib/write_xlsx/image_property.rb +4 -1
  46. data/lib/write_xlsx/inserted_chart.rb +1 -1
  47. data/lib/write_xlsx/object_positioning.rb +15 -1
  48. data/lib/write_xlsx/package/app.rb +2 -2
  49. data/lib/write_xlsx/package/button.rb +6 -2
  50. data/lib/write_xlsx/package/comments.rb +11 -3
  51. data/lib/write_xlsx/package/conditional_format.rb +7 -3
  52. data/lib/write_xlsx/package/content_types.rb +2 -2
  53. data/lib/write_xlsx/package/core.rb +2 -2
  54. data/lib/write_xlsx/package/custom.rb +3 -2
  55. data/lib/write_xlsx/package/metadata.rb +2 -2
  56. data/lib/write_xlsx/package/packager.rb +0 -3
  57. data/lib/write_xlsx/package/relationships.rb +2 -2
  58. data/lib/write_xlsx/package/rich_value.rb +4 -2
  59. data/lib/write_xlsx/package/rich_value_rel.rb +2 -2
  60. data/lib/write_xlsx/package/rich_value_structure.rb +2 -2
  61. data/lib/write_xlsx/package/rich_value_types.rb +3 -3
  62. data/lib/write_xlsx/package/shared_strings.rb +2 -2
  63. data/lib/write_xlsx/package/styles.rb +13 -9
  64. data/lib/write_xlsx/package/table.rb +8 -2
  65. data/lib/write_xlsx/package/theme.rb +0 -3
  66. data/lib/write_xlsx/package/vml.rb +2 -2
  67. data/lib/write_xlsx/page_setup.rb +3 -1
  68. data/lib/write_xlsx/shape.rb +97 -100
  69. data/lib/write_xlsx/sheets.rb +6 -1
  70. data/lib/write_xlsx/sparkline.rb +2 -2
  71. data/lib/write_xlsx/utility/cell_reference.rb +124 -0
  72. data/lib/write_xlsx/utility/chart_formatting.rb +262 -0
  73. data/lib/write_xlsx/utility/common.rb +44 -0
  74. data/lib/write_xlsx/utility/date_time.rb +113 -0
  75. data/lib/write_xlsx/utility/dimensions.rb +40 -0
  76. data/lib/write_xlsx/utility/drawing.rb +136 -0
  77. data/lib/write_xlsx/utility/rich_text.rb +184 -0
  78. data/lib/write_xlsx/utility/sheetname_quoting.rb +73 -0
  79. data/lib/write_xlsx/utility/string_width.rb +45 -0
  80. data/lib/write_xlsx/utility/url.rb +27 -0
  81. data/lib/write_xlsx/utility/xml_primitives.rb +32 -0
  82. data/lib/write_xlsx/version.rb +1 -1
  83. data/lib/write_xlsx/workbook/chart_data.rb +188 -0
  84. data/lib/write_xlsx/workbook/format_preparation.rb +199 -0
  85. data/lib/write_xlsx/workbook/initialization.rb +223 -0
  86. data/lib/write_xlsx/workbook/package_preparation.rb +231 -0
  87. data/lib/write_xlsx/workbook/workbook_writer.rb +164 -0
  88. data/lib/write_xlsx/workbook.rb +143 -981
  89. data/lib/write_xlsx/worksheet/autofilter.rb +3 -1
  90. data/lib/write_xlsx/worksheet/cell_data.rb +5 -1
  91. data/lib/write_xlsx/worksheet/columns.rb +8 -3
  92. data/lib/write_xlsx/worksheet/data_validation.rb +9 -1
  93. data/lib/write_xlsx/worksheet/data_writing.rb +37 -10
  94. data/lib/write_xlsx/worksheet/formatting.rb +3 -1
  95. data/lib/write_xlsx/worksheet/hyperlink.rb +9 -1
  96. data/lib/write_xlsx/worksheet/row_col_sizing.rb +3 -1
  97. data/lib/write_xlsx/worksheet/xml_writer.rb +9 -4
  98. data/lib/write_xlsx/worksheet.rb +19 -2
  99. metadata +41 -2
  100. data/lib/write_xlsx/utility.rb +0 -1034
@@ -1,90 +1,58 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'write_xlsx/utility'
4
+ require 'write_xlsx/utility/common'
5
+ require 'write_xlsx/utility/xml_primitives'
6
+ require 'write_xlsx/format/fill_style'
7
+ require 'write_xlsx/format/fill_state'
8
+ require 'write_xlsx/format/border_style'
9
+ require 'write_xlsx/format/border_state'
10
+ require 'write_xlsx/format/font_style'
11
+ require 'write_xlsx/format/font_state'
12
+ require 'write_xlsx/format/alignment_style'
13
+ require 'write_xlsx/format/alignment_state'
14
+ require 'write_xlsx/format/protection_style'
15
+ require 'write_xlsx/format/protection_state'
16
+ require 'write_xlsx/format/number_format_style'
17
+ require 'write_xlsx/format/number_format_state'
18
+ require 'write_xlsx/format/format_state'
5
19
 
6
20
  module Writexlsx
21
+ #
22
+ # A cell format in the workbook.
23
+ #
24
+ # Format represents the formatting properties associated with worksheet
25
+ # cells. These properties are written to the workbook style tables
26
+ # (styles.xml) and referenced by worksheet cell records.
27
+ #
28
+ # Responsibilities of this class include:
29
+ #
30
+ # * storing formatting properties such as font, fill, border, alignment,
31
+ # number format, and protection settings
32
+ # * exposing the public `set_*` formatting API used by Workbook and Worksheet
33
+ # * delegating grouped property access through style facade objects such as
34
+ # FillStyle, BorderStyle, FontStyle, AlignmentStyle, ProtectionStyle,
35
+ # and NumberFormatStyle
36
+ # * generating unique format keys used by Workbook to deduplicate style
37
+ # records
38
+ # * providing helper methods used when writing styles.xml
39
+ #
40
+ # Format instances are created by Workbook#add_format and reused across
41
+ # worksheets through their computed format keys.
42
+ #
7
43
  class Format
8
- include Writexlsx::Utility
44
+ include Writexlsx::Utility::Common
45
+ include Writexlsx::Utility::XmlPrimitives
9
46
 
10
- attr_reader :xf_index, :dxf_index, :num_format # :nodoc:
11
- attr_reader :underline, :font_script, :size, :theme, :font, :font_family, :hyperlink, :xf_id # :nodoc:
12
- attr_reader :diag_type, :diag_color, :font_only, :color_indexed # :nodoc:
13
- attr_reader :left, :left_color, :right, :right_color, :top, :top_color, :bottom, :bottom_color # :nodoc:
14
- attr_reader :font_scheme # :nodoc:
15
- attr_accessor :quote_prefix, :num_format_index, :border_index, :font_index # :nodoc:
16
- attr_accessor :fill_index, :font_condense, :font_extend, :diag_border # :nodoc:
17
- attr_accessor :bg_color, :fg_color, :pattern # :nodoc:
18
-
19
- attr_accessor :dxf_bg_color, :dxf_fg_color # :nodoc:
20
- attr_reader :rotation, :bold, :italic, :font_strikeout # :nodoc:
47
+ ###########################################################################
48
+ #
49
+ # Lifecycle
50
+ #
51
+ ###########################################################################
21
52
 
22
- def initialize(formats, params = {}) # :nodoc:
53
+ def initialize(formats, params = {}) # :nodoc:
23
54
  @formats = formats
24
-
25
- @xf_index = nil
26
- @dxf_index = nil
27
-
28
- @num_format = 'General'
29
- @num_format_index = 0
30
- @font_index = 0
31
- @font = 'Calibri'
32
- @size = 11
33
- @bold = 0
34
- @italic = 0
35
- @color = 0x0
36
- @underline = 0
37
- @font_strikeout = 0
38
- @font_outline = 0
39
- @font_shadow = 0
40
- @font_script = 0
41
- @font_family = 2
42
- @font_charset = 0
43
- @font_scheme = 'minor'
44
- @font_condense = 0
45
- @font_extend = 0
46
- @theme = 0
47
- @hyperlink = 0
48
- @xf_id = 0
49
-
50
- @hidden = 0
51
- @locked = 1
52
-
53
- @text_h_align = 0
54
- @text_wrap = 0
55
- @text_v_align = 0
56
- @text_justlast = 0
57
- @rotation = 0
58
-
59
- @fg_color = 0x00
60
- @bg_color = 0x00
61
- @pattern = 0
62
- @fill_index = 0
63
- @fill_count = 0
64
-
65
- @border_index = 0
66
- @border_count = 0
67
-
68
- @bottom = 0
69
- @bottom_color = 0x0
70
- @diag_border = 0
71
- @diag_color = 0x0
72
- @diag_type = 0
73
- @left = 0
74
- @left_color = 0x0
75
- @right = 0
76
- @right_color = 0x0
77
- @top = 0
78
- @top_color = 0x0
79
-
80
- @indent = 0
81
- @shrink = 0
82
- @merge_range = 0
83
- @reading_order = 0
84
- @just_distrib = 0
85
- @color_indexed = 0
86
- @font_only = 0
87
- @quote_prefix = 0
55
+ @state = FormatState.new
88
56
 
89
57
  set_format_properties(params) unless params.empty?
90
58
  end
@@ -94,361 +62,949 @@ module Writexlsx
94
62
  #
95
63
  def copy(other)
96
64
  reserve = %i[
97
- xf_index
98
- dxf_index
99
- xdf_format_indices
100
- palette
65
+ @xdf_format_indices
66
+ @palette
101
67
  ]
68
+
102
69
  (instance_variables - reserve).each do |v|
103
- instance_variable_set(v, other.instance_variable_get(v))
70
+ value = other.instance_variable_get(v)
71
+ value = value.dup if v == :@state && !value.nil?
72
+ instance_variable_set(v, value)
104
73
  end
105
74
  end
106
75
 
76
+ ###########################################################################
77
+ #
78
+ # Style facade accessors
79
+ #
80
+ ###########################################################################
81
+
82
+ def state
83
+ @state
84
+ end
85
+
86
+ def fill_style
87
+ FillStyle.new(self)
88
+ end
89
+
90
+ def border_style
91
+ BorderStyle.new(self)
92
+ end
93
+
94
+ def font_style
95
+ FontStyle.new(self)
96
+ end
97
+
98
+ def alignment_style
99
+ AlignmentStyle.new(self)
100
+ end
101
+
102
+ def protection_style
103
+ ProtectionStyle.new(self)
104
+ end
105
+
106
+ def number_format_style
107
+ NumberFormatStyle.new(self)
108
+ end
109
+
110
+ ###########################################################################
111
+ #
112
+ # Explicit format property accessors
113
+ #
114
+ ###########################################################################
115
+
116
+ #
117
+ # Workbook indexes
118
+ #
119
+ def xf_index
120
+ state.xf_index
121
+ end
122
+
123
+ def xf_index=(value)
124
+ state.xf_index = value
125
+ end
126
+
127
+ def dxf_index
128
+ state.dxf_index
129
+ end
130
+
131
+ def dxf_index=(value)
132
+ state.dxf_index = value
133
+ end
134
+
135
+ def xf_id
136
+ state.xf_id
137
+ end
138
+
139
+ def xf_id=(value)
140
+ state.xf_id = value
141
+ end
142
+
143
+ #
144
+ # Miscellaneous flags and compatibility ivars
145
+ #
146
+ def has_fill
147
+ @has_fill
148
+ end
149
+
150
+ def has_fill=(value)
151
+ @has_fill = value
152
+ end
153
+
154
+ def quote_prefix
155
+ @quote_prefix
156
+ end
157
+
158
+ def quote_prefix=(value)
159
+ @quote_prefix = value
160
+ end
161
+
162
+ def dxf_fg_color
163
+ @dxf_fg_color
164
+ end
165
+
166
+ def dxf_fg_color=(value)
167
+ @dxf_fg_color = value
168
+ end
169
+
170
+ def dxf_bg_color
171
+ @dxf_bg_color
172
+ end
173
+
174
+ def dxf_bg_color=(value)
175
+ @dxf_bg_color = value
176
+ end
177
+
178
+ #
179
+ # Number format properties
107
180
  #
108
- # :call-seq:
109
- # set_format_properties( :bold => 1 [, :color => 'red'..] )
110
- # set_format_properties( font [, shade, ..])
111
- # set_format_properties( :bold => 1, font, ...)
112
- # *) font = { :color => 'red', :bold => 1 }
113
- # shade = { :bg_color => 'green', :pattern => 1 }
181
+ def num_format
182
+ number_format_style.format_code
183
+ end
184
+
185
+ def num_format=(value)
186
+ number_format_style.format_code = value
187
+ end
188
+
189
+ def num_format_index
190
+ number_format_style.index
191
+ end
192
+
193
+ def num_format_index=(value)
194
+ number_format_style.index = value.to_i
195
+ end
196
+
114
197
  #
115
- # Convert hashes of properties to method calls.
198
+ # Font properties
116
199
  #
117
- def set_format_properties(*properties) # :nodoc:
200
+ def font
201
+ font_style.name
202
+ end
203
+
204
+ def font=(value)
205
+ font_style.name = value
206
+ end
207
+
208
+ def size
209
+ font_style.size
210
+ end
211
+
212
+ def size=(value)
213
+ font_style.size = value
214
+ end
215
+
216
+ def font_color
217
+ font_style.color
218
+ end
219
+
220
+ def font_color=(value)
221
+ font_style.color = value
222
+ end
223
+
224
+ def bold
225
+ font_style.bold
226
+ end
227
+
228
+ def bold=(value)
229
+ font_style.bold = value
230
+ end
231
+
232
+ def italic
233
+ font_style.italic
234
+ end
235
+
236
+ def italic=(value)
237
+ font_style.italic = value
238
+ end
239
+
240
+ def underline
241
+ font_style.underline
242
+ end
243
+
244
+ def underline=(value)
245
+ font_style.underline = value
246
+ end
247
+
248
+ def font_strikeout
249
+ font_style.strikeout
250
+ end
251
+
252
+ def font_strikeout=(value)
253
+ font_style.strikeout = value
254
+ end
255
+
256
+ def font_script
257
+ font_style.script
258
+ end
259
+
260
+ def font_script=(value)
261
+ font_style.script = value
262
+ end
263
+
264
+ def font_outline
265
+ font_style.outline
266
+ end
267
+
268
+ def font_outline=(value)
269
+ font_style.outline = value
270
+ end
271
+
272
+ def font_shadow
273
+ font_style.shadow
274
+ end
275
+
276
+ def font_shadow=(value)
277
+ font_style.shadow = value
278
+ end
279
+
280
+ def font_charset
281
+ font_style.charset
282
+ end
283
+
284
+ def font_charset=(value)
285
+ font_style.charset = value
286
+ end
287
+
288
+ def font_family
289
+ font_style.family
290
+ end
291
+
292
+ def font_family=(value)
293
+ font_style.family = value
294
+ end
295
+
296
+ def font_scheme
297
+ font_style.scheme
298
+ end
299
+
300
+ def font_scheme=(value)
301
+ font_style.scheme = value
302
+ end
303
+
304
+ def font_condense
305
+ font_style.condense
306
+ end
307
+
308
+ def font_condense=(value)
309
+ font_style.condense = value
310
+ end
311
+
312
+ def font_extend
313
+ font_style.extend
314
+ end
315
+
316
+ def font_extend=(value)
317
+ font_style.extend = value
318
+ end
319
+
320
+ def color_indexed
321
+ font_style.color_indexed
322
+ end
323
+
324
+ def color_indexed=(value)
325
+ font_style.color_indexed = value
326
+ end
327
+
328
+ def theme
329
+ font_style.theme
330
+ end
331
+
332
+ def theme=(value)
333
+ font_style.theme = value
334
+ end
335
+
336
+ def hyperlink
337
+ font_style.hyperlink
338
+ end
339
+
340
+ def hyperlink=(value)
341
+ font_style.hyperlink = value
342
+ end
343
+
344
+ def font_index
345
+ font_style.index
346
+ end
347
+
348
+ def font_index=(value)
349
+ font_style.index = value
350
+ end
351
+
352
+ #
353
+ # Protection properties
354
+ #
355
+ def locked
356
+ protection_style.locked
357
+ end
358
+
359
+ def locked=(value)
360
+ protection_style.locked = value
361
+ end
362
+
363
+ def hidden
364
+ protection_style.hidden
365
+ end
366
+
367
+ def hidden=(value)
368
+ protection_style.hidden = value
369
+ end
370
+
371
+ #
372
+ # Alignment properties
373
+ #
374
+ def text_h_align
375
+ alignment_style.horizontal
376
+ end
377
+
378
+ def text_h_align=(value)
379
+ alignment_style.horizontal = value
380
+ end
381
+
382
+ def text_wrap
383
+ alignment_style.wrap
384
+ end
385
+
386
+ def text_wrap=(value)
387
+ alignment_style.wrap = value
388
+ end
389
+
390
+ def text_v_align
391
+ alignment_style.vertical
392
+ end
393
+
394
+ def text_v_align=(value)
395
+ alignment_style.vertical = value
396
+ end
397
+
398
+ def text_justlast
399
+ alignment_style.justlast
400
+ end
401
+
402
+ def text_justlast=(value)
403
+ alignment_style.justlast = value
404
+ end
405
+
406
+ def rotation
407
+ alignment_style.rotation
408
+ end
409
+
410
+ def rotation=(value)
411
+ alignment_style.rotation = value
412
+ end
413
+
414
+ def indent
415
+ alignment_style.indent
416
+ end
417
+
418
+ def indent=(value)
419
+ alignment_style.indent = value
420
+ end
421
+
422
+ def shrink
423
+ alignment_style.shrink
424
+ end
425
+
426
+ def shrink=(value)
427
+ alignment_style.shrink = value
428
+ end
429
+
430
+ def merge_range
431
+ alignment_style.merge_range
432
+ end
433
+
434
+ def merge_range=(value)
435
+ alignment_style.merge_range = value
436
+ end
437
+
438
+ def reading_order
439
+ alignment_style.reading_order
440
+ end
441
+
442
+ def reading_order=(value)
443
+ alignment_style.reading_order = value
444
+ end
445
+
446
+ def just_distrib
447
+ alignment_style.just_distrib
448
+ end
449
+
450
+ def just_distrib=(value)
451
+ alignment_style.just_distrib = value
452
+ end
453
+
454
+ #
455
+ # Fill properties
456
+ #
457
+ def fg_color
458
+ fill_style.fg_color
459
+ end
460
+
461
+ def bg_color
462
+ fill_style.bg_color
463
+ end
464
+
465
+ def pattern
466
+ fill_style.pattern
467
+ end
468
+
469
+ def fill_index
470
+ fill_style.index
471
+ end
472
+
473
+ def fill_index=(value)
474
+ fill_style.index = value
475
+ end
476
+
477
+ def fill_count
478
+ fill_style.count
479
+ end
480
+
481
+ def fill_count=(value)
482
+ fill_style.count = value
483
+ end
484
+
485
+ #
486
+ # Border properties
487
+ #
488
+ def border_index
489
+ border_style.index
490
+ end
491
+
492
+ def border_index=(value)
493
+ border_style.index = value
494
+ end
495
+
496
+ def border_count
497
+ border_style.count
498
+ end
499
+
500
+ def border_count=(value)
501
+ border_style.count = value
502
+ end
503
+
504
+ def left
505
+ border_style.left
506
+ end
507
+
508
+ def left=(value)
509
+ border_style.left = value
510
+ end
511
+
512
+ def left_color
513
+ border_style.left_color
514
+ end
515
+
516
+ def left_color=(value)
517
+ border_style.left_color = value
518
+ end
519
+
520
+ def right
521
+ border_style.right
522
+ end
523
+
524
+ def right=(value)
525
+ border_style.right = value
526
+ end
527
+
528
+ def right_color
529
+ border_style.right_color
530
+ end
531
+
532
+ def right_color=(value)
533
+ border_style.right_color = value
534
+ end
535
+
536
+ def top
537
+ border_style.top
538
+ end
539
+
540
+ def top=(value)
541
+ border_style.top = value
542
+ end
543
+
544
+ def top_color
545
+ border_style.top_color
546
+ end
547
+
548
+ def top_color=(value)
549
+ border_style.top_color = value
550
+ end
551
+
552
+ def bottom
553
+ border_style.bottom
554
+ end
555
+
556
+ def bottom=(value)
557
+ border_style.bottom = value
558
+ end
559
+
560
+ def bottom_color
561
+ border_style.bottom_color
562
+ end
563
+
564
+ def bottom_color=(value)
565
+ border_style.bottom_color = value
566
+ end
567
+
568
+ def diag_border
569
+ border_style.diag_border
570
+ end
571
+
572
+ def diag_border=(value)
573
+ border_style.diag_border = value
574
+ end
575
+
576
+ def diag_color
577
+ border_style.diag_color
578
+ end
579
+
580
+ def diag_color=(value)
581
+ border_style.diag_color = value
582
+ end
583
+
584
+ def diag_type
585
+ border_style.diag_type
586
+ end
587
+
588
+ def diag_type=(value)
589
+ border_style.diag_type = value
590
+ end
591
+
592
+ ###########################################################################
593
+ #
594
+ # Public formatting property API
595
+ #
596
+ ###########################################################################
597
+
598
+ def set_format_properties(*properties) # :nodoc:
118
599
  return if properties.empty?
119
600
 
120
- properties.each do |property|
121
- property.each do |key, value|
122
- # Strip leading "-" from Tk style properties e.g. "-color" => 'red'.
123
- key = key.sub(/^-/, '') if key.respond_to?(:to_str)
124
-
125
- # Create a sub to set the property.
126
- if value.respond_to?(:to_str) || !value.respond_to?(:+)
127
- send("set_#{key}", value.to_s)
128
- else
129
- send("set_#{key}", value)
130
- end
131
- end
132
- end
601
+ properties.each do |property|
602
+ property.each do |key, value|
603
+ send("set_#{key}", value)
604
+ end
605
+ end
606
+ end
607
+
608
+ #
609
+ # Font setters
610
+ #
611
+ def set_font(value)
612
+ self.font = normalize_format_property_value(value)
613
+ end
614
+
615
+ def set_font_family(value)
616
+ self.font_family = value
617
+ end
618
+
619
+ def set_font_charset(value)
620
+ self.font_charset = value
621
+ end
622
+
623
+ def set_font_condense(value)
624
+ self.font_condense = value
625
+ end
626
+
627
+ def set_size(value)
628
+ self.size = value
629
+ end
630
+
631
+ def set_color(value)
632
+ self.font_color = color(normalize_format_property_value(value))
633
+ end
634
+
635
+ def set_bold(weight = 1)
636
+ self.bold = weight
637
+ end
638
+
639
+ def set_italic(value = 1)
640
+ self.italic = value
641
+ end
642
+
643
+ def set_underline(value = 1)
644
+ self.underline = value
645
+ end
646
+
647
+ def set_font_strikeout(value = 1)
648
+ self.font_strikeout = value
649
+ end
650
+
651
+ def set_font_script(value)
652
+ self.font_script = value
653
+ end
654
+
655
+ def set_font_outline(value = 1)
656
+ self.font_outline = value
657
+ end
658
+
659
+ def set_font_shadow(value = 1)
660
+ self.font_shadow = value
661
+ end
662
+
663
+ def set_font_extend(value = 1)
664
+ self.font_extend = value
665
+ end
666
+
667
+ def set_color_indexed(value = 1)
668
+ self.color_indexed = value
669
+ end
670
+
671
+ def set_theme(value = 1)
672
+ self.theme = value
673
+ end
674
+
675
+ #
676
+ # Alignment setters
677
+ #
678
+ def set_align(location)
679
+ return unless location
680
+
681
+ location = location.downcase
682
+
683
+ case location
684
+ when 'left' then set_text_h_align(1)
685
+ when 'centre', 'center' then set_text_h_align(2)
686
+ when 'right' then set_text_h_align(3)
687
+ when 'fill' then set_text_h_align(4)
688
+ when 'justify' then set_text_h_align(5)
689
+ when 'center_across', 'centre_across', 'merge'
690
+ set_text_h_align(6)
691
+ when 'distributed', 'equal_space', 'justify_distributed'
692
+ set_text_h_align(7)
693
+ when 'top' then set_text_v_align(1)
694
+ when 'vcentre', 'vcenter' then set_text_v_align(2)
695
+ when 'bottom' then set_text_v_align(3)
696
+ when 'vjustify' then set_text_v_align(4)
697
+ when 'vdistributed', 'vequal_space' then set_text_v_align(5)
698
+ end
699
+
700
+ self.just_distrib = 1 if location == 'justify_distributed'
701
+ end
702
+
703
+ def set_valign(location)
704
+ set_align(location)
705
+ end
706
+
707
+ def set_center_across(_flag = 1)
708
+ set_text_h_align(6)
709
+ end
710
+
711
+ def set_merge(_merge = 1)
712
+ set_text_h_align(6)
713
+ end
714
+
715
+ def set_rotation(rotation)
716
+ if rotation == 270
717
+ rotation = 255
718
+ elsif rotation.between?(-90, 90)
719
+ rotation = -rotation + 90 if rotation < 0
720
+ else
721
+ raise "Rotation #{rotation} outside range: -90 <= angle <= 90"
722
+ end
723
+
724
+ self.rotation = rotation
725
+ end
726
+
727
+ def set_text_h_align(value)
728
+ self.text_h_align = value
729
+ end
730
+
731
+ def set_text_v_align(value)
732
+ self.text_v_align = value
733
+ end
734
+
735
+ def set_text_wrap(value = 1)
736
+ self.text_wrap = value
737
+ end
738
+
739
+ def set_text_justlast(value = 1)
740
+ self.text_justlast = value
741
+ end
742
+
743
+ def set_indent(value = 1)
744
+ self.indent = value
745
+ end
746
+
747
+ def set_shrink(value = 1)
748
+ self.shrink = value
133
749
  end
134
750
 
135
751
  #
136
- # Return properties for an Style xf <alignment> sub-element.
752
+ # Fill setters
137
753
  #
138
- def get_align_properties
139
- align = [] # Attributes to return
754
+ def set_fg_color(value)
755
+ fill_style.fg_color = color(normalize_format_property_value(value))
756
+ end
140
757
 
141
- # Check if any alignment options in the format have been changed.
142
- if @text_h_align != 0 || @text_v_align != 0 || @indent != 0 ||
143
- @rotation != 0 || @text_wrap != 0 || @shrink != 0 || @reading_order != 0
144
- changed = 1
145
- else
146
- return
147
- end
758
+ def set_bg_color(value)
759
+ fill_style.bg_color = color(normalize_format_property_value(value))
760
+ end
148
761
 
149
- # Indent is only allowed for some alignment properties. If it is defined
150
- # for any other alignment or no alignment has been set then default to
151
- # left alignment.
152
- @text_h_align = 1 if @indent != 0 && ![1, 3, 7].include?(@text_h_align) && ![1, 3, 5].include?(@text_v_align)
762
+ def set_pattern(value)
763
+ fill_style.pattern = normalize_format_property_value(value)
764
+ end
153
765
 
154
- # Check for properties that are mutually exclusive.
155
- @shrink = 0 if @text_wrap != 0
156
- @shrink = 0 if @text_h_align == 4 # Fill
157
- @shrink = 0 if @text_h_align == 5 # Justify
158
- @shrink = 0 if @text_h_align == 7 # Distributed
159
- @just_distrib = 0 if @text_h_align != 7 # Distributed
160
- @just_distrib = 0 if @indent != 0
766
+ def set_fill_index(value)
767
+ fill_style.index = normalize_format_property_value(value)
768
+ end
161
769
 
162
- continuous = 'centerContinuous'
770
+ def set_fill_count(value)
771
+ fill_style.count = normalize_format_property_value(value)
772
+ end
163
773
 
164
- align << %w[horizontal left] if @text_h_align == 1
165
- align << %w[horizontal center] if @text_h_align == 2
166
- align << %w[horizontal right] if @text_h_align == 3
167
- align << %w[horizontal fill] if @text_h_align == 4
168
- align << %w[horizontal justify] if @text_h_align == 5
169
- align << ['horizontal', continuous] if @text_h_align == 6
170
- align << %w[horizontal distributed] if @text_h_align == 7
774
+ def set_has_fill(value)
775
+ self.has_fill = value
776
+ end
171
777
 
172
- align << ['justifyLastLine', 1] if @just_distrib != 0
778
+ #
779
+ # Border setters
780
+ #
781
+ def set_border(value)
782
+ set_bottom(value)
783
+ set_top(value)
784
+ set_left(value)
785
+ set_right(value)
786
+ end
173
787
 
174
- # Property 'vertical' => 'bottom' is a default. It sets applyAlignment
175
- # without an alignment sub-element.
176
- align << %w[vertical top] if @text_v_align == 1
177
- align << %w[vertical center] if @text_v_align == 2
178
- align << %w[vertical justify] if @text_v_align == 4
179
- align << %w[vertical distributed] if @text_v_align == 5
788
+ def set_border_color(value)
789
+ color_value = color(normalize_format_property_value(value))
790
+ self.bottom_color = color_value
791
+ self.top_color = color_value
792
+ self.left_color = color_value
793
+ self.right_color = color_value
794
+ end
180
795
 
181
- align << ['textRotation', @rotation] if @rotation != 0
182
- align << ['indent', @indent] if @indent != 0
796
+ def set_left(value)
797
+ self.left = value
798
+ end
183
799
 
184
- align << ['wrapText', 1] if @text_wrap != 0
185
- align << ['shrinkToFit', 1] if @shrink != 0
800
+ def set_right(value)
801
+ self.right = value
802
+ end
186
803
 
187
- align << ['readingOrder', 1] if @reading_order == 1
188
- align << ['readingOrder', 2] if @reading_order == 2
804
+ def set_top(value)
805
+ self.top = value
806
+ end
189
807
 
190
- [changed, align]
808
+ def set_bottom(value)
809
+ self.bottom = value
191
810
  end
192
811
 
193
- #
194
- # Return properties for an Excel XML <Protection> element.
195
- #
196
- def get_protection_properties
197
- attributes = []
812
+ def set_diag_border(value)
813
+ self.diag_border = value
814
+ end
198
815
 
199
- attributes << ['locked', 0] unless ptrue?(@locked)
200
- attributes << ['hidden', 1] if ptrue?(@hidden)
816
+ def set_diag_type(value)
817
+ self.diag_type = value
818
+ end
201
819
 
202
- attributes.empty? ? nil : attributes
820
+ def set_left_color(value)
821
+ self.left_color = color(normalize_format_property_value(value))
203
822
  end
204
823
 
205
- def set_bold(bold = 1)
206
- @bold = ptrue?(bold) ? 1 : 0
824
+ def set_right_color(value)
825
+ self.right_color = color(normalize_format_property_value(value))
207
826
  end
208
827
 
209
- def inspect
210
- to_s
828
+ def set_top_color(value)
829
+ self.top_color = color(normalize_format_property_value(value))
211
830
  end
212
831
 
213
- #
214
- # Returns a unique hash key for the Format object.
215
- #
216
- def get_format_key
217
- [get_font_key, get_border_key, get_fill_key, get_alignment_key, @num_format, @locked, @hidden, @quote_prefix].join(':')
832
+ def set_bottom_color(value)
833
+ self.bottom_color = color(normalize_format_property_value(value))
218
834
  end
219
835
 
220
- #
221
- # Returns a unique hash key for a font. Used by Workbook.
222
- #
223
- def get_font_key
224
- [
225
- @bold,
226
- @color,
227
- @font_charset,
228
- @font_family,
229
- @font_outline,
230
- @font_script,
231
- @font_shadow,
232
- @font_strikeout,
233
- @font,
234
- @italic,
235
- @size,
236
- @underline,
237
- @theme
238
- ].join(':')
836
+ def set_diag_color(value)
837
+ self.diag_color = color(normalize_format_property_value(value))
239
838
  end
240
839
 
241
- #
242
- # Returns a unique hash key for a border style. Used by Workbook.
243
- #
244
- def get_border_key
245
- [
246
- @bottom,
247
- @bottom_color,
248
- @diag_border,
249
- @diag_color,
250
- @diag_type,
251
- @left,
252
- @left_color,
253
- @right,
254
- @right_color,
255
- @top,
256
- @top_color
257
- ].join(':')
840
+ def set_border_index(value)
841
+ self.border_index = value
258
842
  end
259
843
 
260
- #
261
- # Returns a unique hash key for a fill style. Used by Workbook.
262
- #
263
- def get_fill_key
264
- [
265
- @pattern,
266
- @bg_color,
267
- @fg_color
268
- ].join(':')
844
+ def set_border_count(value)
845
+ self.border_count = value
269
846
  end
270
847
 
271
848
  #
272
- # Returns a unique hash key for alignment formats.
849
+ # Protection setters
273
850
  #
274
- def get_alignment_key
275
- [@text_h_align, @text_v_align, @indent, @rotation, @text_wrap, @shrink, @reading_order].join(':')
851
+ def set_locked(value = 1)
852
+ self.locked = value
276
853
  end
277
854
 
278
- #
279
- # Returns the index used by Worksheet->_XF()
280
- #
281
- def get_xf_index
282
- if @xf_index
283
- @xf_index
284
- elsif @formats.xf_index_by_key(get_format_key)
285
- @formats.xf_index_by_key(get_format_key)
286
- else
287
- @xf_index = @formats.set_xf_index_by_key(get_format_key)
288
- end
855
+ def set_hidden(value = 1)
856
+ self.hidden = value
289
857
  end
290
858
 
291
859
  #
292
- # Returns the index used by Worksheet->_XF()
860
+ # Number format setters
293
861
  #
294
- def get_dxf_index
295
- if @dxf_index
296
- @dxf_index
297
- elsif @formats.dxf_index_by_key(get_format_key)
298
- @formats.dxf_index_by_key(get_format_key)
299
- else
300
- @dxf_index = @formats.set_dxf_index_by_key(get_format_key)
301
- end
862
+ def set_num_format(format)
863
+ self.num_format = format
302
864
  end
303
865
 
304
- def color(color_code)
305
- Format.color(color_code)
866
+ def set_num_format_index(value)
867
+ self.num_format_index = value
306
868
  end
307
869
 
308
870
  #
309
- # Used in conjunction with the set_xxx_color methods to convert a color
310
- # string into a number. Color range is 0..63 but we will restrict it
311
- # to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15.
871
+ # Hyperlink and misc setters
312
872
  #
313
- def self.color(color_code)
314
- colors = Colors::COLORS
315
-
316
- # Return the default color if nil,
317
- return 0x00 unless color_code
318
-
319
- if color_code.respond_to?(:to_str)
320
- # Return RGB style colors for processing later.
321
- return color_code if color_code =~ /^#[0-9A-F]{6}$/i
322
-
323
- # or the color string converted to an integer,
324
- return colors[color_code.downcase.to_sym] if colors[color_code.downcase.to_sym]
325
-
326
- # or the default color if string is unrecognised,
327
- 0x00 if color_code =~ /\D/
328
- else
329
- # or an index < 8 mapped into the correct range,
330
- return color_code + 8 if color_code < 8
873
+ def set_hyperlink(value)
874
+ self.xf_id = 1
331
875
 
332
- # or the default color if arg is outside range,
333
- return 0x00 if color_code > 63
876
+ set_underline(1)
877
+ set_theme(10)
878
+ self.hyperlink = value
879
+ end
334
880
 
335
- # or an integer in the valid range
336
- color_code
337
- end
881
+ def set_quote_prefix(value = 1)
882
+ self.quote_prefix = value
338
883
  end
339
884
 
340
- #
341
- # Set cell alignment.
342
- #
343
- def set_align(location)
344
- return unless location # No default
885
+ def set_has_font(value = 1)
886
+ self.has_font = value
887
+ end
345
888
 
346
- location = location.downcase
889
+ def set_xf_index(value)
890
+ self.xf_index = value
891
+ end
347
892
 
348
- case location
349
- when 'left' then set_text_h_align(1)
350
- when 'centre', 'center' then set_text_h_align(2)
351
- when 'right' then set_text_h_align(3)
352
- when 'fill' then set_text_h_align(4)
353
- when 'justify' then set_text_h_align(5)
354
- when 'center_across', 'centre_across', 'merge'
355
- set_text_h_align(6)
356
- when 'distributed', 'equal_space', 'justify_distributed'
357
- set_text_h_align(7)
358
- when 'top' then set_text_v_align(1)
359
- when 'vcentre', 'vcenter' then set_text_v_align(2)
360
- when 'bottom' then set_text_v_align(3)
361
- when 'vjustify' then set_text_v_align(4)
362
- when 'vdistributed', 'vequal_space' then set_text_v_align(5)
363
- end
893
+ def set_dxf_index(value)
894
+ self.dxf_index = value
895
+ end
364
896
 
365
- @just_distrib = 1 if location == 'justify_distributed'
897
+ def set_xf_id(value)
898
+ self.xf_id = value
366
899
  end
367
900
 
901
+ ###########################################################################
368
902
  #
369
- # Set vertical cell alignment. This is required by the set_properties() method
370
- # to differentiate between the vertical and horizontal properties.
903
+ # Workbook integration and key generation
371
904
  #
372
- def set_valign(location)
373
- set_align(location)
905
+ ###########################################################################
906
+
907
+ def get_format_key
908
+ [
909
+ get_font_key,
910
+ get_border_key,
911
+ get_fill_key,
912
+ get_alignment_key,
913
+ num_format,
914
+ locked,
915
+ hidden,
916
+ quote_prefix
917
+ ].join(':')
374
918
  end
375
919
 
376
- #
377
- # Implements the Excel5 style "merge".
378
- #
379
- def set_center_across(_flag = 1)
380
- set_text_h_align(6)
920
+ def get_font_key
921
+ [
922
+ font,
923
+ size,
924
+ bold,
925
+ italic,
926
+ font_color,
927
+ underline,
928
+ font_strikeout,
929
+ font_script,
930
+ font_outline,
931
+ font_shadow,
932
+ font_family,
933
+ font_charset,
934
+ font_scheme,
935
+ font_condense,
936
+ font_extend,
937
+ theme,
938
+ hyperlink
939
+ ].join(':')
381
940
  end
382
941
 
383
- #
384
- # This was the way to implement a merge in Excel5. However it should have been
385
- # called "center_across" and not "merge".
386
- # This is now deprecated. Use set_center_across() or better merge_range().
387
- #
388
- def set_merge(_merge = 1)
389
- set_text_h_align(6)
942
+ def get_border_key
943
+ [
944
+ bottom,
945
+ bottom_color,
946
+ diag_border,
947
+ diag_color,
948
+ diag_type,
949
+ left,
950
+ left_color,
951
+ right,
952
+ right_color,
953
+ top,
954
+ top_color
955
+ ].join(':')
390
956
  end
391
957
 
392
- #
393
- # Set cells borders to the same style
394
- #
395
- def set_border(style)
396
- set_bottom(style)
397
- set_top(style)
398
- set_left(style)
399
- set_right(style)
958
+ def get_fill_key
959
+ [
960
+ pattern,
961
+ bg_color,
962
+ fg_color
963
+ ].join(':')
400
964
  end
401
965
 
402
- #
403
- # Set cells border to the same color
404
- #
405
- def set_border_color(color)
406
- set_bottom_color(color)
407
- set_top_color(color)
408
- set_left_color(color)
409
- set_right_color(color)
966
+ def get_alignment_key
967
+ [
968
+ text_h_align,
969
+ text_v_align,
970
+ indent,
971
+ rotation,
972
+ text_wrap,
973
+ shrink,
974
+ reading_order
975
+ ].join(':')
410
976
  end
411
977
 
412
- #
413
- # Set the rotation angle of the text. An alignment property.
414
- #
415
- def set_rotation(rotation)
416
- if rotation == 270
417
- rotation = 255
418
- elsif rotation.between?(-90, 90)
419
- rotation = -rotation + 90 if rotation < 0
978
+ def get_xf_index
979
+ if xf_index
980
+ xf_index
981
+ elsif @formats.xf_index_by_key(get_format_key)
982
+ @formats.xf_index_by_key(get_format_key)
420
983
  else
421
- raise "Rotation #{rotation} outside range: -90 <= angle <= 90"
422
- rotation = 0
984
+ self.xf_index = @formats.set_xf_index_by_key(get_format_key)
423
985
  end
424
-
425
- @rotation = rotation
426
986
  end
427
987
 
428
- #
429
- # Set the properties for the hyperlink style. This isn't a public method. To
430
- # be fixed when styles are supported.
431
- #
432
- def set_hyperlink(hyperlink)
433
- @xf_id = 1
434
-
435
- set_underline(1)
436
- set_theme(10)
437
- @hyperlink = hyperlink
988
+ def get_dxf_index
989
+ if dxf_index
990
+ dxf_index
991
+ elsif @formats.dxf_index_by_key(get_format_key)
992
+ @formats.dxf_index_by_key(get_format_key)
993
+ else
994
+ self.dxf_index = @formats.set_dxf_index_by_key(get_format_key)
995
+ end
438
996
  end
439
997
 
440
998
  def set_font_info(fonts)
441
999
  key = get_font_key
442
1000
 
443
1001
  if fonts[key]
444
- # Font has already been used.
445
- @font_index = fonts[key]
446
- @has_font = false
1002
+ self.font_index = fonts[key]
1003
+ @has_font = false
447
1004
  else
448
- # This is a new font.
449
- @font_index = fonts.size
450
- fonts[key] = fonts.size
451
- @has_font = true
1005
+ self.font_index = fonts.size
1006
+ fonts[key] = fonts.size
1007
+ @has_font = true
452
1008
  end
453
1009
  end
454
1010
 
@@ -456,70 +1012,62 @@ module Writexlsx
456
1012
  key = get_border_key
457
1013
 
458
1014
  if borders[key]
459
- # Border has already been used.
460
- @border_index = borders[key]
461
- @has_border = false
1015
+ self.border_index = borders[key]
1016
+ @has_border = false
462
1017
  else
463
- # This is a new border.
464
- @border_index = borders.size
465
- borders[key] = borders.size
466
- @has_border = true
1018
+ self.border_index = borders.size
1019
+ borders[key] = borders.size
1020
+ @has_border = true
467
1021
  end
468
1022
  end
469
1023
 
470
- def method_missing(name, *args) # :nodoc:
471
- method = "#{name}"
472
-
473
- # Check for a valid method names, i.e. "set_xxx_yyy".
474
- method =~ /set_(\w+)/ or raise "Unknown method: #{method}\n"
475
-
476
- # Match the attribute, i.e. "@xxx_yyy".
477
- attribute = "@#{::Regexp.last_match(1)}"
1024
+ ###########################################################################
1025
+ #
1026
+ # Queries and compatibility helpers
1027
+ #
1028
+ ###########################################################################
478
1029
 
479
- # Check that the attribute exists
480
- # ........
481
- value = if method =~ /set\w+color$/ # for "set_property_color" methods
482
- color(args[0])
483
- else # for "set_xxx" methods
484
- args[0] || 1
485
- end
1030
+ def inspect
1031
+ to_s
1032
+ end
486
1033
 
487
- instance_variable_set(attribute, value)
1034
+ def color(color_code)
1035
+ Format.color(color_code)
488
1036
  end
489
1037
 
490
1038
  def color?
491
- ptrue?(@color)
1039
+ ptrue?(font_color)
492
1040
  end
493
1041
 
494
1042
  def bold?
495
- ptrue?(@bold)
1043
+ ptrue?(bold)
496
1044
  end
497
1045
 
498
1046
  def italic?
499
- ptrue?(@italic)
1047
+ ptrue?(italic)
500
1048
  end
501
1049
 
502
1050
  def strikeout?
503
- ptrue?(@font_strikeout)
1051
+ ptrue?(font_strikeout)
504
1052
  end
505
1053
 
506
1054
  def outline?
507
- ptrue?(@font_outline)
1055
+ ptrue?(font_outline)
508
1056
  end
509
1057
 
510
1058
  def shadow?
511
- ptrue?(@font_shadow)
1059
+ ptrue?(font_shadow)
512
1060
  end
513
1061
 
514
1062
  def underline?
515
- ptrue?(@underline)
1063
+ ptrue?(underline)
516
1064
  end
517
1065
 
518
1066
  def has_border(flag)
519
1067
  @has_border = flag
520
1068
  end
521
1069
 
522
- def has_border? # :nodoc:
1070
+ def has_border?
523
1071
  @has_border
524
1072
  end
525
1073
 
@@ -531,7 +1079,7 @@ module Writexlsx
531
1079
  @has_dxf_border
532
1080
  end
533
1081
 
534
- def has_font(flag)
1082
+ def has_font=(flag)
535
1083
  @has_font = flag
536
1084
  end
537
1085
 
@@ -563,37 +1111,139 @@ module Writexlsx
563
1111
  @has_dxf_fill
564
1112
  end
565
1113
 
1114
+ #
1115
+ # Compatibility accessor used by older internal callers that treat Format
1116
+ # like a hash.
1117
+ #
566
1118
  def [](attr)
567
- instance_variable_get("@#{attr}")
1119
+ case attr.to_sym
1120
+ when :font then font
1121
+ when :size then size
1122
+ when :bold then bold
1123
+ when :italic then italic
1124
+ when :color then font_color
1125
+ when :underline then underline
1126
+ when :font_strikeout then font_strikeout
1127
+ when :font_script then font_script
1128
+ when :rotation then rotation
1129
+ else
1130
+ instance_variable_get("@#{attr}")
1131
+ end
1132
+ end
1133
+
1134
+ def force_text_format?
1135
+ num_format == 49
1136
+ end
1137
+
1138
+ ###########################################################################
1139
+ #
1140
+ # Alignment and protection serialization helpers
1141
+ #
1142
+ ###########################################################################
1143
+
1144
+ def get_align_properties
1145
+ align = []
1146
+
1147
+ h_align = text_h_align
1148
+ v_align = text_v_align
1149
+ indent_value = indent
1150
+ rotation_value = rotation
1151
+ wrap = text_wrap
1152
+ shrink_value = shrink
1153
+ reading_value = reading_order
1154
+ just_distrib_value = just_distrib
1155
+
1156
+ if h_align != 0 ||
1157
+ v_align != 0 ||
1158
+ indent_value != 0 ||
1159
+ rotation_value != 0 ||
1160
+ wrap != 0 ||
1161
+ shrink_value != 0 ||
1162
+ reading_value != 0
1163
+ changed = 1
1164
+ else
1165
+ return
1166
+ end
1167
+
1168
+ if indent_value != 0 && ![1, 3, 7].include?(h_align) && ![1, 3, 5].include?(v_align)
1169
+ h_align = 1
1170
+ end
1171
+
1172
+ shrink_value = 0 if wrap != 0
1173
+ shrink_value = 0 if h_align == 4
1174
+ shrink_value = 0 if h_align == 5
1175
+ shrink_value = 0 if h_align == 7
1176
+ just_distrib_value = 0 if h_align != 7
1177
+ just_distrib_value = 0 if indent_value != 0
1178
+
1179
+ continuous = 'centerContinuous'
1180
+
1181
+ align << %w[horizontal left] if h_align == 1
1182
+ align << %w[horizontal center] if h_align == 2
1183
+ align << %w[horizontal right] if h_align == 3
1184
+ align << %w[horizontal fill] if h_align == 4
1185
+ align << %w[horizontal justify] if h_align == 5
1186
+ align << ['horizontal', continuous] if h_align == 6
1187
+ align << %w[horizontal distributed] if h_align == 7
1188
+
1189
+ align << ['justifyLastLine', 1] if just_distrib_value != 0
1190
+
1191
+ align << %w[vertical top] if v_align == 1
1192
+ align << %w[vertical center] if v_align == 2
1193
+ align << %w[vertical justify] if v_align == 4
1194
+ align << %w[vertical distributed] if v_align == 5
1195
+
1196
+ align << ['textRotation', rotation_value] if rotation_value != 0
1197
+ align << ['indent', indent_value] if indent_value != 0
1198
+
1199
+ align << ['wrapText', 1] if wrap != 0
1200
+ align << ['shrinkToFit', 1] if shrink_value != 0
1201
+
1202
+ align << ['readingOrder', 1] if reading_value == 1
1203
+ align << ['readingOrder', 2] if reading_value == 2
1204
+
1205
+ [changed, align]
1206
+ end
1207
+
1208
+ def get_protection_properties
1209
+ return if locked != 0 && hidden == 0
1210
+
1211
+ attributes = []
1212
+ attributes << ['locked', 0] if locked == 0
1213
+ attributes << ['hidden', 1] if hidden != 0
1214
+ attributes
568
1215
  end
569
1216
 
1217
+ ###########################################################################
1218
+ #
1219
+ # XML writing entry points
1220
+ #
1221
+ ###########################################################################
1222
+
570
1223
  def write_font(writer, worksheet, dxf_format = nil) # :nodoc:
571
1224
  writer.tag_elements('font') do
572
- # The condense and extend elements are mainly used in dxf formats.
573
- write_condense(writer) if ptrue?(@font_condense)
574
- write_extend(writer) if ptrue?(@font_extend)
1225
+ write_condense(writer) if ptrue?(font_condense)
1226
+ write_extend(writer) if ptrue?(font_extend)
575
1227
 
576
1228
  write_font_shapes(writer)
577
1229
 
578
1230
  writer.empty_tag('sz', [['val', size]]) unless dxf_format
579
1231
 
580
1232
  if theme == -1
581
- # Ignore for excel2003_style
1233
+ # Ignore for excel2003_style
582
1234
  elsif ptrue?(theme)
583
1235
  write_color('theme', theme, writer)
584
- elsif ptrue?(@color_indexed)
585
- write_color('indexed', @color_indexed, writer)
586
- elsif ptrue?(@color)
587
- color = worksheet.palette_color(@color)
588
- if color != 'Automatic'
589
- write_color('rgb', color, writer)
590
- end
1236
+ elsif ptrue?(color_indexed)
1237
+ write_color('indexed', color_indexed, writer)
1238
+ elsif ptrue?(font_color)
1239
+ color = worksheet.palette_color(font_color)
1240
+ write_color('rgb', color, writer) if color != 'Automatic'
591
1241
  elsif !ptrue?(dxf_format)
592
1242
  write_color('theme', 1, writer)
593
1243
  end
594
1244
 
595
1245
  unless ptrue?(dxf_format)
596
- writer.empty_tag('name', [['val', @font]])
1246
+ writer.empty_tag('name', [['val', font]])
597
1247
  write_font_family_scheme(writer)
598
1248
  end
599
1249
  end
@@ -606,14 +1256,14 @@ module Writexlsx
606
1256
 
607
1257
  if ptrue?(theme)
608
1258
  write_color('theme', theme, writer)
609
- elsif ptrue?(@color)
610
- color = worksheet.palette_color(@color)
1259
+ elsif ptrue?(font_color)
1260
+ color = worksheet.palette_color(font_color)
611
1261
  write_color('rgb', color, writer)
612
1262
  else
613
1263
  write_color('theme', 1, writer)
614
1264
  end
615
1265
 
616
- writer.empty_tag('rFont', [['val', @font]])
1266
+ writer.empty_tag('rFont', [['val', font]])
617
1267
  write_font_family_scheme(writer)
618
1268
  end
619
1269
  end
@@ -621,15 +1271,15 @@ module Writexlsx
621
1271
  def border_attributes
622
1272
  attributes = []
623
1273
 
624
- # Diagonal borders add attributes to the <border> element.
625
1274
  if diag_type == 1
626
- attributes << ['diagonalUp', 1]
1275
+ attributes << ['diagonalUp', 1]
627
1276
  elsif diag_type == 2
628
1277
  attributes << ['diagonalDown', 1]
629
1278
  elsif diag_type == 3
630
- attributes << ['diagonalUp', 1]
1279
+ attributes << ['diagonalUp', 1]
631
1280
  attributes << ['diagonalDown', 1]
632
1281
  end
1282
+
633
1283
  attributes
634
1284
  end
635
1285
 
@@ -641,30 +1291,65 @@ module Writexlsx
641
1291
  ['borderId', border_index],
642
1292
  ['xfId', xf_id]
643
1293
  ]
1294
+
644
1295
  attributes << ['quotePrefix', 1] if ptrue?(quote_prefix)
645
1296
  attributes << ['applyNumberFormat', 1] if num_format_index > 0
646
- # Add applyFont attribute if XF format uses a font element.
647
- attributes << ['applyFont', 1] if font_index > 0 && !ptrue?(@hyperlink)
648
- # Add applyFill attribute if XF format uses a fill element.
1297
+ attributes << ['applyFont', 1] if font_index > 0 && !ptrue?(hyperlink)
649
1298
  attributes << ['applyFill', 1] if fill_index > 0
650
- # Add applyBorder attribute if XF format uses a border element.
651
1299
  attributes << ['applyBorder', 1] if border_index > 0
652
1300
 
653
- # Check if XF format has alignment properties set.
654
1301
  apply_align, _align = get_align_properties
655
- # We can also have applyAlignment without a sub-element.
656
- attributes << ['applyAlignment', 1] if apply_align || ptrue?(@hyperlink)
1302
+ attributes << ['applyAlignment', 1] if apply_align || ptrue?(hyperlink)
657
1303
  attributes << ['applyProtection', 1] if get_protection_properties || ptrue?(hyperlink)
658
1304
 
659
1305
  attributes
660
1306
  end
661
1307
 
662
- def force_text_format?
663
- @num_format == 49 # Text format ('@')
1308
+ ###########################################################################
1309
+ #
1310
+ # Class-level utilities
1311
+ #
1312
+ ###########################################################################
1313
+
1314
+ def self.color(color_code)
1315
+ colors = Colors::COLORS
1316
+
1317
+ return 0x00 unless color_code
1318
+
1319
+ if color_code.respond_to?(:to_str)
1320
+ return color_code if color_code =~ /^#[0-9A-F]{6}$/i
1321
+ return colors[color_code.downcase.to_sym] if colors[color_code.downcase.to_sym]
1322
+
1323
+ 0x00 if color_code =~ /\D/
1324
+ else
1325
+ return color_code + 8 if color_code < 8
1326
+ return 0x00 if color_code > 63
1327
+
1328
+ color_code
1329
+ end
664
1330
  end
665
1331
 
1332
+ ###########################################################################
1333
+ #
1334
+ # Private helpers
1335
+ #
1336
+ ###########################################################################
666
1337
  private
667
1338
 
1339
+ def normalize_format_property_value(value)
1340
+ if value.respond_to?(:to_str) || !value.respond_to?(:+)
1341
+ value.to_s
1342
+ else
1343
+ value
1344
+ end
1345
+ end
1346
+
1347
+ ###########################################################################
1348
+ #
1349
+ # Private XML font helpers
1350
+ #
1351
+ ###########################################################################
1352
+
668
1353
  def write_font_shapes(writer)
669
1354
  writer.empty_tag('b') if bold?
670
1355
  writer.empty_tag('i') if italic?
@@ -672,7 +1357,6 @@ module Writexlsx
672
1357
  writer.empty_tag('outline') if outline?
673
1358
  writer.empty_tag('shadow') if shadow?
674
1359
 
675
- # Handle the underline variants.
676
1360
  write_underline(writer, underline) if underline?
677
1361
 
678
1362
  write_vert_align(writer, 'superscript') if font_script == 1
@@ -680,26 +1364,18 @@ module Writexlsx
680
1364
  end
681
1365
 
682
1366
  def write_font_family_scheme(writer)
683
- writer.empty_tag('family', [['val', @font_family]]) if ptrue?(@font_family)
684
-
685
- writer.empty_tag('charset', [['val', @font_charset]]) if ptrue?(@font_charset)
686
-
687
- writer.empty_tag('scheme', [['val', @font_scheme]]) if @font == 'Calibri' && !ptrue?(@hyperlink)
1367
+ writer.empty_tag('family', [['val', font_family]]) if ptrue?(font_family)
1368
+ writer.empty_tag('charset', [['val', font_charset]]) if ptrue?(font_charset)
1369
+ writer.empty_tag('scheme', [['val', font_scheme]]) if font == 'Calibri' && !ptrue?(hyperlink)
688
1370
  end
689
1371
 
690
- #
691
- # Write the underline font element.
692
- #
693
1372
  def write_underline(writer, underline)
694
1373
  writer.empty_tag('u', write_underline_attributes(underline))
695
1374
  end
696
1375
 
697
- #
698
- # Write the underline font element.
699
- #
700
1376
  def write_underline_attributes(underline)
701
1377
  val = 'val'
702
- # Handle the underline variants.
1378
+
703
1379
  case underline
704
1380
  when 2
705
1381
  [[val, 'double']]
@@ -712,23 +1388,14 @@ module Writexlsx
712
1388
  end
713
1389
  end
714
1390
 
715
- #
716
- # Write the <vertAlign> font sub-element.
717
- #
718
1391
  def write_vert_align(writer, val) # :nodoc:
719
1392
  writer.empty_tag('vertAlign', [['val', val]])
720
1393
  end
721
1394
 
722
- #
723
- # Write the <condense> element.
724
- #
725
1395
  def write_condense(writer)
726
1396
  writer.empty_tag('condense', [['val', 0]])
727
1397
  end
728
1398
 
729
- #
730
- # Write the <extend> element.
731
- #
732
1399
  def write_extend(writer)
733
1400
  writer.empty_tag('extend', [['val', 0]])
734
1401
  end