Class: Writexlsx::Package::Styles

Inherits:
Object
  • Object
show all
Includes:
Utility::ChartFormatting, Utility::Common, Utility::XmlPrimitives
Defined in:
lib/write_xlsx/package/styles.rb

Constant Summary collapse

FORMAT_CODES =
{
  0  => 'General',
  1  => '0',
  2  => '0.00',
  3  => '#,##0',
  4  => '#,##0.00',
  5  => '($#,##0_);($#,##0)',
  6  => '($#,##0_);[Red]($#,##0)',
  7  => '($#,##0.00_);($#,##0.00)',
  8  => '($#,##0.00_);[Red]($#,##0.00)',
  9  => '0%',
  10 => '0.00%',
  11 => '0.00E+00',
  12 => '# ?/?',
  13 => '# ??/??',
  14 => 'm/d/yy',
  15 => 'd-mmm-yy',
  16 => 'd-mmm',
  17 => 'mmm-yy',
  18 => 'h:mm AM/PM',
  19 => 'h:mm:ss AM/PM',
  20 => 'h:mm',
  21 => 'h:mm:ss',
  22 => 'm/d/yy h:mm',
  37 => '(#,##0_);(#,##0)',
  38 => '(#,##0_);[Red](#,##0)',
  39 => '(#,##0.00_);(#,##0.00)',
  40 => '(#,##0.00_);[Red](#,##0.00)',
  41 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
  42 => '_($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)',
  43 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
  44 => '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)',
  45 => 'mm:ss',
  46 => '[h]:mm:ss',
  47 => 'mm:ss.0',
  48 => '##0.0E+0',
  49 => '@'
}
PATTERNS =
%w[
  none
  solid
  mediumGray
  darkGray
  lightGray
  darkHorizontal
  darkVertical
  darkDown
  darkUp
  darkGrid
  darkTrellis
  lightHorizontal
  lightVertical
  lightDown
  lightUp
  lightGrid
  lightTrellis
  gray125
  gray0625
]
BORDER_STYLES =
%w[
  none
  thin
  medium
  dashed
  dotted
  thick
  double
  hair
  mediumDashed
  dashDot
  mediumDashDot
  dashDotDot
  mediumDashDotDot
  slantDashDot
]

Constants included from Utility::ChartFormatting

Utility::ChartFormatting::PATTERN_TYPES

Constants included from Utility::Common

Utility::Common::PERL_TRUE_VALUES

Instance Method Summary collapse

Methods included from Utility::XmlPrimitives

#r_id_attributes, #write_color, #write_xml_declaration, #xml_str

Methods included from Utility::ChartFormatting

#color, #dash_types, #fill_properties, #layout_properties, #legend_properties, #line_fill_properties, #line_properties, #palette_color_from_index, #pattern_properties, #value_or_raise, #write_a_solid_fill, #write_a_srgb_clr

Methods included from Utility::Common

#absolute_char, #check_parameter, #float_to_str, #ptrue?, #put_deprecate_message

Constructor Details

#initialize ⇒ Styles

Returns a new instance of Styles.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/write_xlsx/package/styles.rb', line 16

def initialize
  @writer = Package::XMLWriterSimple.new
  @xf_formats        = nil
  @palette           = []
  @font_count        = 0
  @num_formats       = []
  @border_count      = 0
  @fill_count        = 0
  @custom_colors     = []
  @dxf_formats       = []
  @has_hyperlink     = 0
  @hyperlink_font_id = 0
  @has_comments      = false
end

Instance Method Details

#assemble_xml_file ⇒ Object



35
36
37
38
39
# File 'lib/write_xlsx/package/styles.rb', line 35

def assemble_xml_file
  write_xml_declaration do
    write_style_sheet { write_style_sheet_base }
  end
end

#bg_and_fg_color(format, dxf_format) ⇒ Object



297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/write_xlsx/package/styles.rb', line 297

def bg_and_fg_color(format, dxf_format)
  bg_color   = format.fill_style.bg_color
  fg_color   = format.fill_style.fg_color

  # Colors for dxf formats are handled differently from normal formats since
  # the normal format reverses the meaning of BG and FG for solid fills.
  if dxf_format && dxf_format != 0
    bg_color = format.dxf_bg_color
    fg_color = format.dxf_fg_color
  end

  [bg_color, fg_color]
end

#palette_color(index) ⇒ Object

Convert from an Excel internal colour index to a XML style #RRGGBB index based on the default or user defined values in the Workbook palette.



63
64
65
66
67
68
69
70
71
# File 'lib/write_xlsx/package/styles.rb', line 63

def palette_color(index)
  if index.to_s =~ /^#([0-9A-F]{6})$/i
    "FF#{::Regexp.last_match(1).upcase}"
  elsif index == 0x40
    "Automatic"
  else
    "FF#{palette_color_from_index(index)}"
  end
end

#pattern_only_case?(format, dxf_format) ⇒ Boolean

Returns:

  • (Boolean)


262
263
264
265
266
# File 'lib/write_xlsx/package/styles.rb', line 262

def pattern_only_case?(format, dxf_format)
  bg_color, fg_color = bg_and_fg_color(format, dxf_format)

  !ptrue?(fg_color) && !ptrue?(bg_color) && ptrue?(format.fill_style.pattern)
end

#set_style_properties(xf_formats, palette, font_count, num_formats, border_count, fill_count, custom_colors, dxf_formats, has_comments) ⇒ Object

Pass in the Format objects and other properties used to set the styles.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/write_xlsx/package/styles.rb', line 44

def set_style_properties(
      xf_formats, palette, font_count, num_formats, border_count,
      fill_count, custom_colors, dxf_formats, has_comments
    )
  @xf_formats    = xf_formats
  @palette       = palette
  @font_count    = font_count
  @num_formats   = num_formats
  @border_count  = border_count
  @fill_count    = fill_count
  @custom_colors = custom_colors
  @dxf_formats   = dxf_formats
  @has_comments  = has_comments
end

#set_xml_writer(filename) ⇒ Object



31
32
33
# File 'lib/write_xlsx/package/styles.rb', line 31

def set_xml_writer(filename)
  @writer.set_xml_writer(filename)
end

#write_border(format, dxf_format = nil) ⇒ Object

Write the element.



335
336
337
338
339
340
# File 'lib/write_xlsx/package/styles.rb', line 335

def write_border(format, dxf_format = nil)
  # Write the start border tag.
  @writer.tag_elements('border', format.border_attributes) do
    write_border_base(format, dxf_format)
  end
end

#write_border_base(format, dxf_format) ⇒ Object



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/write_xlsx/package/styles.rb', line 342

def write_border_base(format, dxf_format)
  # Write the <border> sub elements.
  write_border_sub_elements(format)

  # Condition DXF formats don't allow diagonal borders
  if dxf_format
    write_sub_border('vertical')
    write_sub_border('horizontal')
  else
    # Ensure that a default diag border is set if the diag type is set.
    format.diag_border = 1 if format.diag_type != 0 && format.diag_border == 0

    write_sub_border('diagonal', format.diag_border, format.diag_color)
  end
end

#write_border_sub_elements(format) ⇒ Object



358
359
360
361
362
363
# File 'lib/write_xlsx/package/styles.rb', line 358

def write_border_sub_elements(format)
  write_sub_border('left',   format.left,   format.left_color)
  write_sub_border('right',  format.right,  format.right_color)
  write_sub_border('top',    format.top,    format.top_color)
  write_sub_border('bottom', format.bottom, format.bottom_color)
end

#write_borders ⇒ Object

Write the element.



314
315
316
317
318
# File 'lib/write_xlsx/package/styles.rb', line 314

def write_borders
  write_format_elements('borders', @border_count) do
    write_borders_base
  end
end

#write_borders_base ⇒ Object



320
321
322
323
324
# File 'lib/write_xlsx/package/styles.rb', line 320

def write_borders_base
  @xf_formats.each do |format|
    write_border(format) if format.has_border?
  end
end

#write_cell_style_xfs ⇒ Object

Write the element.



407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/write_xlsx/package/styles.rb', line 407

def write_cell_style_xfs
  count = ptrue?(@has_hyperlink) ? 2 : 1

  attributes = [['count', count]]

  @writer.tag_elements('cellStyleXfs', attributes) do
    # Write the style_xf element.
    write_style_xf(0, 0)

    write_style_xf(1, @hyperlink_font_id) if ptrue?(@has_hyperlink)
  end
end

#write_cell_xfs ⇒ Object

Write the element.



423
424
425
426
427
428
429
430
431
432
# File 'lib/write_xlsx/package/styles.rb', line 423

def write_cell_xfs
  formats = @xf_formats

  attributes = [['count', formats.size]]

  @writer.tag_elements('cellXfs', attributes) do
    # Write the xf elements.
    formats.each { |format| write_xf(format) }
  end
end

#write_comment_font ⇒ Object

Write the element used for comments.



186
187
188
189
190
191
192
193
# File 'lib/write_xlsx/package/styles.rb', line 186

def write_comment_font
  @writer.tag_elements('font') do
    @writer.empty_tag('sz', [['val', 8]])
    write_color('indexed', 81)
    @writer.empty_tag('name',   [%w[val Tahoma]])
    @writer.empty_tag('family', [['val', 2]])
  end
end

#write_default_fill(pattern_type) ⇒ Object

Write the element for the default fills.



220
221
222
223
224
# File 'lib/write_xlsx/package/styles.rb', line 220

def write_default_fill(pattern_type)
  @writer.tag_elements('fill') do
    @writer.empty_tag('patternFill', [['patternType', pattern_type]])
  end
end

#write_fill(format, dxf_format = nil) ⇒ Object

Write the element.



251
252
253
254
255
256
257
258
259
260
# File 'lib/write_xlsx/package/styles.rb', line 251

def write_fill(format, dxf_format = nil)
  # Special handling for pattern only case.
  if pattern_only_case?(format, dxf_format)
    write_default_fill(PATTERNS[format.fill_style.pattern])
  else
    @writer.tag_elements('fill') do
      write_fill_base(format, dxf_format)
    end
  end
end

#write_fill_base(format, dxf_format) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/write_xlsx/package/styles.rb', line 268

def write_fill_base(format, dxf_format)
  # The "none" pattern is handled differently for dxf formats.
  attributes = if dxf_format && format.fill_style.pattern <= 1
                 []
               else
                 [['patternType', PATTERNS[format.fill_style.pattern]]]
               end

  @writer.tag_elements('patternFill', attributes) do
    write_pattern_fill(format, dxf_format)
  end
end

#write_fills ⇒ Object

Write the element.



198
199
200
201
202
203
204
# File 'lib/write_xlsx/package/styles.rb', line 198

def write_fills
  attributes = [['count', @fill_count]]

  @writer.tag_elements('fills', attributes) do
    write_fills_base
  end
end

#write_fills_base ⇒ Object



206
207
208
209
210
211
212
213
214
215
# File 'lib/write_xlsx/package/styles.rb', line 206

def write_fills_base
  # Write the default fill element.
  write_default_fill('none')
  write_default_fill('gray125')

  # Write the fill elements for format objects that have them.
  @xf_formats.each do |format|
    write_fill(format) if format.has_fill?
  end
end

#write_font_base ⇒ Object



172
173
174
175
176
177
178
179
180
181
# File 'lib/write_xlsx/package/styles.rb', line 172

def write_font_base
  @xf_formats.each do |format|
    next unless format.has_font?

    format.write_font(@writer, self)
    @has_hyperlink     = 1 if ptrue?(format.hyperlink)
    @hyperlink_font_id = format.font_index unless ptrue?(@hyperlink_font_id)
  end
  write_comment_font if @has_comments
end

#write_fonts ⇒ Object

Write the element.



159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/write_xlsx/package/styles.rb', line 159

def write_fonts
  count = @font_count

  if @has_comments
    # Add an extra font for comments.
    count += 1
  end

  write_format_elements('fonts', count) do
    write_font_base
  end
end

#write_format_elements(elements, count, &block) ⇒ Object



326
327
328
329
330
# File 'lib/write_xlsx/package/styles.rb', line 326

def write_format_elements(elements, count, &block)
  attributes = [['count', count]]

  @writer.tag_elements(elements, attributes, &block)
end

#write_num_fmt(num_fmt_id, format_code) ⇒ Object

Write the element.



144
145
146
147
148
149
150
151
152
153
154
# File 'lib/write_xlsx/package/styles.rb', line 144

def write_num_fmt(num_fmt_id, format_code)
  # Set the format code for built-in number formats.
  format_code = FORMAT_CODES[num_fmt_id] || 'General' if num_fmt_id < 164

  attributes = [
    ['numFmtId',   num_fmt_id],
    ['formatCode', format_code]
  ]

  @writer.empty_tag('numFmt', attributes)
end

#write_num_fmts ⇒ Object

Write the element.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/write_xlsx/package/styles.rb', line 85

def write_num_fmts
  count = @num_formats.size

  return if count == 0

  attributes = [['count', count]]

  @writer.tag_elements('numFmts', attributes) do
    # Write the numFmts elements.
    index = 164
    @num_formats.each do |num_format|
      write_num_fmt(index, num_format)
      index += 1
    end
  end
end

#write_pattern_fill(format, dxf_format) ⇒ Object



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/write_xlsx/package/styles.rb', line 281

def write_pattern_fill(format, dxf_format)
  bg_color, fg_color = bg_and_fg_color(format, dxf_format)

  if fg_color && fg_color != 0 && fg_color != 0x40  # 'Automatic'
    @writer.empty_tag('fgColor', [['rgb', palette_color(fg_color)]])
  end

  if bg_color && bg_color != 0
    if bg_color != 0x40 # 'Automatic'
      @writer.empty_tag('bgColor', [['rgb', palette_color(bg_color)]])
    end
  elsif !dxf_format && format.fill_style.pattern <= 1
    @writer.empty_tag('bgColor', [['indexed', 64]])
  end
end

#write_style_sheet(&block) ⇒ Object

Write the element.



76
77
78
79
80
# File 'lib/write_xlsx/package/styles.rb', line 76

def write_style_sheet(&block)
  attributes = [['xmlns', XMLWriterSimple::XMLNS]]

  @writer.tag_elements('styleSheet', attributes, &block)
end

#write_style_xf(has_hyperlink, font_id) ⇒ Object

Write the style element.



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/write_xlsx/package/styles.rb', line 437

def write_style_xf(has_hyperlink, font_id)
  attributes = [
    ['numFmtId', 0],
    ['fontId',   font_id],
    ['fillId',   0],
    ['borderId', 0]
  ]

  if ptrue?(has_hyperlink)
    attributes << ['applyNumberFormat', 0]
    attributes << ['applyFill',         0]
    attributes << ['applyBorder',       0]
    attributes << ['applyAlignment',    0]
    attributes << ['applyProtection',   0]
    @writer.tag_elements('xf', attributes) do
      @writer.empty_tag('alignment',  [%w[vertical top]])
      @writer.empty_tag('protection', [['locked',   0]])
    end
  else
    @writer.empty_tag('xf', attributes)
  end
end

#write_sub_border(type, style = 0, color = nil) ⇒ Object

Write the sub elements such as , , etc.



385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/write_xlsx/package/styles.rb', line 385

def write_sub_border(type, style = 0, color = nil)
  if style == 0
    @writer.empty_tag(type)
    return
  end

  attributes = [[:style, BORDER_STYLES[style]]]

  @writer.tag_elements(type, attributes) do
    if [0, 0x40].include?(color) # 'Automatic'
      @writer.empty_tag('color', [['auto', 1]])
    elsif color != 0 && color != 0x40 # 'Automatic'
      color = palette_color(color)

      @writer.empty_tag('color', [['rgb', color]])
    end
  end
end