write_xlsx 0.90.0 → 0.97.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/Changes +37 -0
  3. data/README.md +1 -1
  4. data/examples/a_simple.rb +1 -6
  5. data/examples/conditional_format.rb +73 -46
  6. data/examples/demo.rb +1 -7
  7. data/examples/hyperlink1.rb +4 -11
  8. data/lib/write_xlsx/chart.rb +81 -205
  9. data/lib/write_xlsx/chart/axis.rb +2 -2
  10. data/lib/write_xlsx/chart/caption.rb +3 -1
  11. data/lib/write_xlsx/chart/pie.rb +2 -0
  12. data/lib/write_xlsx/chart/series.rb +11 -7
  13. data/lib/write_xlsx/format.rb +15 -11
  14. data/lib/write_xlsx/package/conditional_format.rb +351 -38
  15. data/lib/write_xlsx/package/content_types.rb +10 -0
  16. data/lib/write_xlsx/package/custom.rb +125 -0
  17. data/lib/write_xlsx/package/packager.rb +26 -0
  18. data/lib/write_xlsx/package/styles.rb +53 -21
  19. data/lib/write_xlsx/package/table.rb +11 -4
  20. data/lib/write_xlsx/utility.rb +234 -34
  21. data/lib/write_xlsx/version.rb +1 -1
  22. data/lib/write_xlsx/workbook.rb +88 -1
  23. data/lib/write_xlsx/worksheet.rb +247 -23
  24. data/test/helper.rb +6 -1
  25. data/test/regression/_test_hyperlink31.rb +26 -0
  26. data/test/regression/images/zero_dpi.jpg +0 -0
  27. data/test/regression/test_chart_bar08.rb +3 -0
  28. data/test/regression/test_chart_bar11.rb +3 -0
  29. data/test/regression/test_chart_bar14.rb +3 -0
  30. data/test/regression/test_chart_chartarea05.rb +16 -17
  31. data/test/regression/test_chart_chartarea06.rb +49 -0
  32. data/test/regression/test_chart_data_labels25.rb +61 -0
  33. data/test/regression/test_chart_format26.rb +48 -0
  34. data/test/regression/test_chart_format27.rb +58 -0
  35. data/test/regression/test_chart_format28.rb +52 -0
  36. data/test/regression/test_chart_format29.rb +59 -0
  37. data/test/regression/test_chart_format30.rb +53 -0
  38. data/test/regression/test_chart_format31.rb +60 -0
  39. data/test/regression/test_chart_table03.rb +56 -0
  40. data/test/regression/test_cond_format14.rb +42 -0
  41. data/test/regression/test_cond_format15.rb +53 -0
  42. data/test/regression/test_cond_format16.rb +53 -0
  43. data/test/regression/test_cond_format17.rb +37 -0
  44. data/test/regression/test_cond_format18.rb +136 -0
  45. data/test/regression/test_date_1904_01.rb +1 -1
  46. data/test/regression/test_escapes04.rb +3 -0
  47. data/test/regression/test_escapes05.rb +3 -0
  48. data/test/regression/test_escapes07.rb +3 -0
  49. data/test/regression/test_escapes08.rb +3 -0
  50. data/test/regression/test_hyperlink01.rb +3 -0
  51. data/test/regression/test_hyperlink02.rb +3 -0
  52. data/test/regression/test_hyperlink03.rb +4 -0
  53. data/test/regression/test_hyperlink04.rb +3 -0
  54. data/test/regression/test_hyperlink05.rb +3 -0
  55. data/test/regression/test_hyperlink06.rb +3 -0
  56. data/test/regression/test_hyperlink07.rb +3 -0
  57. data/test/regression/test_hyperlink08.rb +3 -0
  58. data/test/regression/test_hyperlink09.rb +3 -0
  59. data/test/regression/test_hyperlink10.rb +3 -0
  60. data/test/regression/test_hyperlink11.rb +3 -0
  61. data/test/regression/test_hyperlink12.rb +3 -0
  62. data/test/regression/test_hyperlink13.rb +3 -0
  63. data/test/regression/test_hyperlink14.rb +3 -0
  64. data/test/regression/test_hyperlink15.rb +3 -0
  65. data/test/regression/test_hyperlink16.rb +3 -0
  66. data/test/regression/test_hyperlink17.rb +3 -0
  67. data/test/regression/test_hyperlink18.rb +3 -0
  68. data/test/regression/test_hyperlink20.rb +3 -0
  69. data/test/regression/test_hyperlink21.rb +3 -0
  70. data/test/regression/test_hyperlink22.rb +3 -0
  71. data/test/regression/test_hyperlink23.rb +3 -0
  72. data/test/regression/test_hyperlink24.rb +3 -0
  73. data/test/regression/test_hyperlink25.rb +3 -0
  74. data/test/regression/test_hyperlink26.rb +3 -0
  75. data/test/regression/test_hyperlink27.rb +3 -0
  76. data/test/regression/test_hyperlink28.rb +50 -0
  77. data/test/regression/test_hyperlink29.rb +27 -0
  78. data/test/regression/test_hyperlink30.rb +36 -0
  79. data/test/regression/test_image35.rb +26 -0
  80. data/test/regression/test_properties01.rb +1 -4
  81. data/test/regression/test_properties02.rb +1 -4
  82. data/test/regression/test_properties03.rb +26 -0
  83. data/test/regression/test_properties04.rb +61 -0
  84. data/test/regression/test_properties05.rb +30 -0
  85. data/test/regression/test_table03.rb +3 -0
  86. data/test/regression/test_table04.rb +3 -0
  87. data/test/regression/test_table05.rb +3 -0
  88. data/test/regression/test_table06.rb +3 -0
  89. data/test/regression/test_table20.rb +34 -0
  90. data/test/regression/test_table21.rb +36 -0
  91. data/test/regression/test_table22.rb +32 -0
  92. data/test/regression/xlsx_files/chart_chartarea05.xlsx +0 -0
  93. data/test/regression/xlsx_files/chart_chartarea06.xlsx +0 -0
  94. data/test/regression/xlsx_files/chart_data_labels25.xlsx +0 -0
  95. data/test/regression/xlsx_files/chart_format26.xlsx +0 -0
  96. data/test/regression/xlsx_files/chart_format27.xlsx +0 -0
  97. data/test/regression/xlsx_files/chart_format28.xlsx +0 -0
  98. data/test/regression/xlsx_files/chart_format29.xlsx +0 -0
  99. data/test/regression/xlsx_files/chart_format30.xlsx +0 -0
  100. data/test/regression/xlsx_files/chart_format31.xlsx +0 -0
  101. data/test/regression/xlsx_files/chart_table03.xlsx +0 -0
  102. data/test/regression/xlsx_files/cond_format14.xlsx +0 -0
  103. data/test/regression/xlsx_files/cond_format15.xlsx +0 -0
  104. data/test/regression/xlsx_files/cond_format16.xlsx +0 -0
  105. data/test/regression/xlsx_files/cond_format17.xlsx +0 -0
  106. data/test/regression/xlsx_files/cond_format18.xlsx +0 -0
  107. data/test/regression/xlsx_files/date_1904_01.xlsx +0 -0
  108. data/test/regression/xlsx_files/hyperlink28.xlsx +0 -0
  109. data/test/regression/xlsx_files/hyperlink29.xlsx +0 -0
  110. data/test/regression/xlsx_files/hyperlink30.xlsx +0 -0
  111. data/test/regression/xlsx_files/hyperlink31.xlsx +0 -0
  112. data/test/regression/xlsx_files/image35.xlsx +0 -0
  113. data/test/regression/xlsx_files/properties03.xlsx +0 -0
  114. data/test/regression/xlsx_files/properties04.xlsx +0 -0
  115. data/test/regression/xlsx_files/properties05.xlsx +0 -0
  116. data/test/regression/xlsx_files/table21.xlsx +0 -0
  117. data/test/regression/xlsx_files/table22.xlsx +0 -0
  118. data/test/workbook/test_write_workbook_view.rb +81 -0
  119. data/test/worksheet/test_cond_format_22.rb +266 -0
  120. data/test/worksheet/test_cond_format_23.rb +242 -0
  121. data/test/worksheet/test_cond_format_24.rb +303 -0
  122. data/test/worksheet/test_data_bar_01.rb +53 -0
  123. data/test/worksheet/test_data_bar_02.rb +79 -0
  124. data/test/worksheet/test_data_bar_03.rb +147 -0
  125. data/test/worksheet/test_data_bar_04.rb +145 -0
  126. data/test/worksheet/test_data_bar_05.rb +147 -0
  127. data/test/worksheet/test_data_bar_06.rb +145 -0
  128. data/test/worksheet/test_data_bar_07.rb +146 -0
  129. data/test/worksheet/test_data_bar_08.rb +54 -0
  130. data/test/worksheet/test_data_bar_09.rb +80 -0
  131. data/test/worksheet/test_data_bar_10.rb +165 -0
  132. data/test/worksheet/test_data_bar_11.rb +167 -0
  133. data/test/worksheet/test_data_bar_12.rb +104 -0
  134. data/test/worksheet/test_write_data_validation_02.rb +27 -0
  135. metadata +135 -2
@@ -178,6 +178,16 @@ def add_vba_project
178
178
  add_default('bin', 'application/vnd.ms-office.vbaProject')
179
179
  end
180
180
 
181
+ #
182
+ # Add the name of a table to the ContentTypes overrides.
183
+ #
184
+ def add_custom_properties
185
+ custom = "/docProps/custom.xml"
186
+
187
+ add_override(custom, "#{App_document}custom-properties+xml")
188
+ end
189
+
190
+
181
191
  private
182
192
 
183
193
  def change_the_workbook_xml_content_type_from_xlsx_to_xlsm
@@ -0,0 +1,125 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'write_xlsx/package/xml_writer_simple'
3
+ require 'write_xlsx/utility'
4
+
5
+ module Writexlsx
6
+ module Package
7
+ class Custom
8
+
9
+ include Writexlsx::Utility
10
+
11
+ def initialize
12
+ @writer = Package::XMLWriterSimple.new
13
+ @properties = []
14
+ @pid = 1
15
+ end
16
+
17
+ def set_xml_writer(filename)
18
+ @writer.set_xml_writer(filename)
19
+ end
20
+
21
+ def assemble_xml_file
22
+ write_xml_declaration do
23
+ write_properties
24
+ end
25
+ end
26
+
27
+ #
28
+ # Set the document properties.
29
+ #
30
+ def set_properties(properties)
31
+ @properties = properties
32
+ end
33
+
34
+ private
35
+
36
+ def write_properties
37
+ schema = 'http://schemas.openxmlformats.org/officeDocument/2006/'
38
+ xmlns = "#{schema}custom-properties"
39
+ xmlns_vt = "#{schema}docPropsVTypes"
40
+
41
+ attributes = [
42
+ ['xmlns', xmlns],
43
+ ['xmlns:vt', xmlns_vt]
44
+ ]
45
+
46
+ @writer.tag_elements('Properties', attributes ) do
47
+ @properties.each do |property|
48
+ # Write the property element.
49
+ write_property(property)
50
+ end
51
+ end
52
+ end
53
+
54
+ def write_property(property)
55
+ fmtid = '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}'
56
+
57
+ @pid += 1
58
+ name, value, type = property
59
+
60
+ attributes = [
61
+ ['fmtid', fmtid],
62
+ ['pid', @pid],
63
+ ['name', name]
64
+ ]
65
+
66
+ @writer.tag_elements('property', attributes) do
67
+ if type == 'date'
68
+ # Write the vt:filetime element.
69
+ write_vt_filetime(value)
70
+ elsif type == 'number'
71
+ # Write the vt:r8 element.
72
+ write_vt_r8(value)
73
+ elsif type == 'number_int'
74
+ # Write the vt:i4 element.
75
+ write_vt_i4(value)
76
+ elsif type == 'bool'
77
+ # Write the vt:bool element.
78
+ write_vt_bool(value)
79
+ else
80
+ # Write the vt:lpwstr element.
81
+ write_vt_lpwstr(value)
82
+ end
83
+ end
84
+ end
85
+
86
+ def write_vt_lpwstr(data)
87
+ @writer.data_element('vt:lpwstr', data)
88
+ end
89
+
90
+ #
91
+ # Write the <vt:i4> element.
92
+ #
93
+ def write_vt_i4(data)
94
+ @writer.data_element('vt:i4', data)
95
+ end
96
+
97
+ #
98
+ # Write the <vt:r8> element.
99
+ #
100
+ def write_vt_r8(data)
101
+ @writer.data_element('vt:r8', data)
102
+ end
103
+
104
+ #
105
+ # Write the <vt:bool> element.
106
+ #
107
+ def write_vt_bool(data)
108
+ if ptrue?(data)
109
+ data = 'true'
110
+ else
111
+ data = 'false'
112
+ end
113
+
114
+ @writer.data_element('vt:bool', data)
115
+ end
116
+
117
+ #
118
+ # Write the <vt:filetime> element.
119
+ #
120
+ def write_vt_filetime(data)
121
+ @writer.data_element('vt:filetime', data)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -5,6 +5,7 @@
5
5
  require 'write_xlsx/package/comments'
6
6
  require 'write_xlsx/package/content_types'
7
7
  require 'write_xlsx/package/core'
8
+ require 'write_xlsx/package/custom'
8
9
  require 'write_xlsx/package/relationships'
9
10
  require 'write_xlsx/package/shared_strings'
10
11
  require 'write_xlsx/package/styles'
@@ -44,6 +45,7 @@ def create_package
44
45
  write_shared_strings_file
45
46
  write_app_file
46
47
  write_core_file
48
+ write_custom_file
47
49
  write_content_types_file
48
50
  write_styles_file
49
51
  write_theme_file
@@ -172,6 +174,22 @@ def write_core_file
172
174
  core.assemble_xml_file
173
175
  end
174
176
 
177
+ #
178
+ # Write the custom.xml file.
179
+ #
180
+ def write_custom_file
181
+ properties = @workbook.custom_properties
182
+ custom = Package::Custom.new
183
+
184
+ return if properties.empty?
185
+
186
+ FileUtils.mkdir_p("#{@package_dir}/docProps")
187
+
188
+ custom.set_properties(properties)
189
+ custom.set_xml_writer("#{@package_dir}/docProps/custom.xml")
190
+ custom.assemble_xml_file
191
+ end
192
+
175
193
  #
176
194
  # Write the ContentTypes.xml file.
177
195
  #
@@ -190,6 +208,8 @@ def write_content_types_file
190
208
  content.add_shared_strings unless @workbook.shared_strings_empty?
191
209
  # Add vbaProject if present.
192
210
  content.add_vba_project if @workbook.vba_project
211
+ # Add the custom properties if present.
212
+ content.add_custom_properties unless @workbook.custom_properties.empty?
193
213
 
194
214
  content.set_xml_writer("#{@package_dir}/[Content_Types].xml")
195
215
  content.assemble_xml_file
@@ -239,9 +259,15 @@ def write_root_rels_file
239
259
  FileUtils.mkdir_p("#{@package_dir}/_rels")
240
260
 
241
261
  rels.add_document_relationship('/officeDocument', 'xl/workbook.xml')
262
+
242
263
  rels.add_package_relationship('/metadata/core-properties',
243
264
  'docProps/core.xml')
265
+
244
266
  rels.add_document_relationship('/extended-properties', 'docProps/app.xml')
267
+
268
+ unless @workbook.custom_properties.empty?
269
+ rels.add_document_relationship('/custom-properties', 'docProps/custom.xml')
270
+ end
245
271
  rels.set_xml_writer("#{@package_dir}/_rels/.rels" )
246
272
  rels.assemble_xml_file
247
273
  end
@@ -10,14 +10,16 @@ class Styles
10
10
 
11
11
  def initialize
12
12
  @writer = Package::XMLWriterSimple.new
13
- @xf_formats = nil
14
- @palette = []
15
- @font_count = 0
16
- @num_format_count = 0
17
- @border_count = 0
18
- @fill_count = 0
19
- @custom_colors = []
20
- @dxf_formats = []
13
+ @xf_formats = nil
14
+ @palette = []
15
+ @font_count = 0
16
+ @num_format_count = 0
17
+ @border_count = 0
18
+ @fill_count = 0
19
+ @custom_colors = []
20
+ @dxf_formats = []
21
+ @has_hyperlink = 0
22
+ @hyperlink_font_id = 0
21
23
  end
22
24
 
23
25
  def set_xml_writer(filename)
@@ -33,7 +35,10 @@ def assemble_xml_file
33
35
  #
34
36
  # Pass in the Format objects and other properties used to set the styles.
35
37
  #
36
- def set_style_properties(xf_formats, palette, font_count, num_format_count, border_count, fill_count, custom_colors, dxf_formats)
38
+ def set_style_properties(
39
+ xf_formats, palette, font_count, num_format_count, border_count,
40
+ fill_count, custom_colors, dxf_formats
41
+ )
37
42
  @xf_formats = xf_formats
38
43
  @palette = palette
39
44
  @font_count = font_count
@@ -150,7 +155,11 @@ def write_fonts
150
155
 
151
156
  def write_font_base
152
157
  @xf_formats.each do |format|
153
- format.write_font(@writer, self) if format.has_font?
158
+ if format.has_font?
159
+ format.write_font(@writer, self)
160
+ @has_hyperlink = 1 if ptrue?(format.hyperlink)
161
+ @hyperlink_font_id = format.font_index unless ptrue?(@hyperlink_font_id)
162
+ end
154
163
  end
155
164
  end
156
165
 
@@ -365,11 +374,17 @@ def write_sub_border(type, style = 0, color = nil)
365
374
  # Write the <cellStyleXfs> element.
366
375
  #
367
376
  def write_cell_style_xfs
368
- attributes = [ ['count', 1] ]
377
+ count = ptrue?(@has_hyperlink) ? 2 : 1
378
+
379
+ attributes = [ ['count', count] ]
369
380
 
370
381
  @writer.tag_elements('cellStyleXfs', attributes) do
371
382
  # Write the style_xf element.
372
- write_style_xf
383
+ write_style_xf(0, 0)
384
+
385
+ if ptrue?(@has_hyperlink)
386
+ write_style_xf(1, @hyperlink_font_id)
387
+ end
373
388
  end
374
389
  end
375
390
 
@@ -396,15 +411,27 @@ def write_cell_xfs
396
411
  #
397
412
  # Write the style <xf> element.
398
413
  #
399
- def write_style_xf
414
+ def write_style_xf(has_hyperlink, font_id)
400
415
  attributes = [
401
416
  ['numFmtId', 0],
402
- ['fontId', 0],
417
+ ['fontId', font_id],
403
418
  ['fillId', 0],
404
419
  ['borderId', 0]
405
420
  ]
406
421
 
407
- @writer.empty_tag('xf', attributes)
422
+ if ptrue?(has_hyperlink)
423
+ attributes << ['applyNumberFormat', 0]
424
+ attributes << ['applyFill', 0]
425
+ attributes << ['applyBorder', 0]
426
+ attributes << ['applyAlignment', 0]
427
+ attributes << ['applyProtection', 0]
428
+ @writer.tag_elements('xf', attributes) do
429
+ @writer.empty_tag('alignment', [ ['vertical', 'top'] ])
430
+ @writer.empty_tag('protection', [ ['locked', 0] ])
431
+ end
432
+ else
433
+ @writer.empty_tag('xf', attributes)
434
+ end
408
435
  end
409
436
 
410
437
  private
@@ -450,22 +477,27 @@ def write_xf(format)
450
477
  # Write the <cellStyles> element.
451
478
  #
452
479
  def write_cell_styles
453
- attributes = [ ['count', 1] ]
480
+ count = ptrue?(@has_hyperlink) ? 2 : 1
481
+
482
+ attributes = [ ['count', count] ]
454
483
 
455
484
  @writer.tag_elements('cellStyles', attributes) do
456
485
  # Write the cellStyle element.
457
- write_cell_style
486
+ if ptrue?(@has_hyperlink)
487
+ write_cell_style('Hyperlink', 1, 8)
488
+ end
489
+ write_cell_style('Normal', 0, 0)
458
490
  end
459
491
  end
460
492
 
461
493
  #
462
494
  # Write the <cellStyle> element.
463
495
  #
464
- def write_cell_style
496
+ def write_cell_style(name, xf_id, builtin_id)
465
497
  attributes = [
466
- ['name', 'Normal'],
467
- ['xfId', 0],
468
- ['builtinId', 0]
498
+ ['name', name],
499
+ ['xfId', xf_id],
500
+ ['builtinId', builtin_id]
469
501
  ]
470
502
 
471
503
  @writer.empty_tag('cellStyle', attributes)
@@ -32,8 +32,9 @@ def initialize(worksheet, *args)
32
32
  @writer = Package::XMLWriterSimple.new
33
33
 
34
34
  @row1, @row2, @col1, @col2, @param = handle_args(*args)
35
- @columns = []
35
+ @columns = []
36
36
  @col_formats = []
37
+ @seen_name = {}
37
38
 
38
39
  # Set the data range rows (without the header and footer).
39
40
  @first_data_row = @row1
@@ -95,9 +96,15 @@ def overrite_the_defaults_with_any_use_defined_values(col_id, col_data, col_num)
95
96
  col_data.name = user_data[:header]
96
97
  end
97
98
 
99
+ # Excel requires unique case insensitive header names.
100
+ if @seen_name[col_data.name.downcase]
101
+ raise "add_table() contains duplicate name: '#{col_data.name}'"
102
+ else
103
+ @seen_name[col_data.name.downcase] = true
104
+ end
98
105
  # Get the header format if defined.
99
106
  col_data.name_format = user_data[:header_format]
100
-
107
+
101
108
  # Handle the column formula.
102
109
  handle_the_column_formula(
103
110
  col_data, col_num, user_data[:formula], user_data[:format]
@@ -291,8 +298,8 @@ def set_the_table_name
291
298
  # Raise if the name looks like a R1C1.
292
299
  if name =~ /^[rcRC]$/ || name =~ /^[rcRC]\d+[rcRC]\d+$/
293
300
  raise "Invalid name '#{name}' like a RC cell ref in add_table()"
294
- end
295
-
301
+ end
302
+
296
303
  @name = @param[:name]
297
304
  end
298
305
  end
@@ -320,7 +320,7 @@ def check_parameter(params, valid_keys, method)
320
320
  invalids = params.keys - valid_keys
321
321
  unless invalids.empty?
322
322
  raise WriteXLSXOptionParameterError,
323
- "Unknown parameter '#{invalids.join(', ')}' in #{method}."
323
+ "Unknown parameter '#{invalids.join(', ')}' in #{method}."
324
324
  end
325
325
  true
326
326
  end
@@ -372,7 +372,7 @@ def layout_properties(args, is_text = false)
372
372
  # Check for valid properties.
373
373
  args.keys.each do |key|
374
374
  unless properties.include?(key.to_sym)
375
- raise "Property '#{key}' not allowed in layout options\n"
375
+ raise "Property '#{key}' not allowed in layout options\n"
376
376
  end
377
377
  end
378
378
 
@@ -405,9 +405,9 @@ def pixels_to_points(vertices)
405
405
 
406
406
  def v_shape_attributes_base(id, z_index)
407
407
  [
408
- ['id', "_x0000_s#{id}"],
409
- ['type', type],
410
- ['style', (v_shape_style_base(z_index, vertices) + style_addition).join]
408
+ ['id', "_x0000_s#{id}"],
409
+ ['type', type],
410
+ ['style', (v_shape_style_base(z_index, vertices) + style_addition).join]
411
411
  ]
412
412
  end
413
413
 
@@ -425,17 +425,17 @@ def v_shape_style_base(z_index, vertices)
425
425
 
426
426
  def shape_style_base(left_str, top_str, width_str, height_str, z_index_str)
427
427
  [
428
- 'position:absolute;',
429
- 'margin-left:',
430
- left_str, 'pt;',
431
- 'margin-top:',
432
- top_str, 'pt;',
433
- 'width:',
434
- width_str, 'pt;',
435
- 'height:',
436
- height_str, 'pt;',
437
- 'z-index:',
438
- z_index_str, ';'
428
+ 'position:absolute;',
429
+ 'margin-left:',
430
+ left_str, 'pt;',
431
+ 'margin-top:',
432
+ top_str, 'pt;',
433
+ 'width:',
434
+ width_str, 'pt;',
435
+ 'height:',
436
+ height_str, 'pt;',
437
+ 'z-index:',
438
+ z_index_str, ';'
439
439
  ]
440
440
  end
441
441
 
@@ -500,10 +500,10 @@ def write_font(font)
500
500
  color = '#000000'
501
501
 
502
502
  attributes = [
503
- ['face', face],
504
- ['size', size],
505
- ['color', color]
506
- ]
503
+ ['face', face],
504
+ ['size', size],
505
+ ['color', color]
506
+ ]
507
507
  @writer.data_element('font', caption, attributes)
508
508
  end
509
509
 
@@ -612,7 +612,7 @@ def pattern_properties(args) # :nodoc:
612
612
  'small_check' => 'smCheck',
613
613
  'large_check' => 'lgCheck',
614
614
  'outlined_diamond' => 'openDmnd',
615
- 'solid_diamond' => 'solidDmnd'
615
+ 'solid_diamond' => 'solidDmnd'
616
616
  }
617
617
 
618
618
  # Check for valid types.
@@ -627,7 +627,7 @@ def pattern_properties(args) # :nodoc:
627
627
 
628
628
  pattern
629
629
  end
630
-
630
+
631
631
  def line_fill_properties(params)
632
632
  return { :_defined => 0 } unless params
633
633
  ret = params.dup
@@ -690,22 +690,222 @@ def process_workbook_options(*params)
690
690
  [options.dup, default_format_properties.dup]
691
691
  end
692
692
  end
693
+
694
+ #
695
+ # Convert user defined font values into private hash values.
696
+ #
697
+ def convert_font_args(params)
698
+ return unless params
699
+ font = params_to_font(params)
700
+
701
+ # Convert font size units.
702
+ font[:_size] *= 100 if font[:_size] && font[:_size] != 0
703
+
704
+ # Convert rotation into 60,000ths of a degree.
705
+ if ptrue?(font[:_rotation])
706
+ font[:_rotation] = 60_000 * font[:_rotation].to_i
707
+ end
708
+
709
+ font
710
+ end
711
+
712
+ def params_to_font(params)
713
+ {
714
+ :_name => params[:name],
715
+ :_color => params[:color],
716
+ :_size => params[:size],
717
+ :_bold => params[:bold],
718
+ :_italic => params[:italic],
719
+ :_underline => params[:underline],
720
+ :_pitch_family => params[:pitch_family],
721
+ :_charset => params[:charset],
722
+ :_baseline => params[:baseline] || 0,
723
+ :_rotation => params[:rotation]
724
+ }
725
+ end
726
+
727
+ #
728
+ # Write the <c:txPr> element.
729
+ #
730
+ def write_tx_pr(horiz, font) # :nodoc:
731
+ rotation = nil
732
+ if font && font[:_rotation]
733
+ rotation = font[:_rotation]
734
+ end
735
+ @writer.tag_elements('c:txPr') do
736
+ # Write the a:bodyPr element.
737
+ write_a_body_pr(rotation, horiz)
738
+ # Write the a:lstStyle element.
739
+ write_a_lst_style
740
+ # Write the a:p element.
741
+ write_a_p_formula(font)
742
+ end
743
+ end
744
+
745
+ #
746
+ # Write the <a:bodyPr> element.
747
+ #
748
+ def write_a_body_pr(rot, horiz = nil) # :nodoc:
749
+ rot = -5400000 if !rot && ptrue?(horiz)
750
+ attributes = []
751
+ attributes << ['rot', rot] if rot
752
+ attributes << ['vert', 'horz'] if ptrue?(horiz)
753
+
754
+ @writer.empty_tag('a:bodyPr', attributes)
755
+ end
756
+
757
+ #
758
+ # Write the <a:lstStyle> element.
759
+ #
760
+ def write_a_lst_style # :nodoc:
761
+ @writer.empty_tag('a:lstStyle')
762
+ end
763
+
764
+ #
765
+ # Write the <a:p> element for formula titles.
766
+ #
767
+ def write_a_p_formula(font = nil) # :nodoc:
768
+ @writer.tag_elements('a:p') do
769
+ # Write the a:pPr element.
770
+ write_a_p_pr_formula(font)
771
+ # Write the a:endParaRPr element.
772
+ write_a_end_para_rpr
773
+ end
774
+ end
775
+
776
+ #
777
+ # Write the <a:pPr> element for formula titles.
778
+ #
779
+ def write_a_p_pr_formula(font) # :nodoc:
780
+ @writer.tag_elements('a:pPr') { write_a_def_rpr(font) }
781
+ end
782
+
783
+ #
784
+ # Write the <a:defRPr> element.
785
+ #
786
+ def write_a_def_rpr(font = nil) # :nodoc:
787
+ write_def_rpr_r_pr_common(
788
+ font,
789
+ get_font_style_attributes(font),
790
+ 'a:defRPr'
791
+ )
792
+ end
793
+
794
+ def write_def_rpr_r_pr_common(font, style_attributes, tag) # :nodoc:
795
+ latin_attributes = get_font_latin_attributes(font)
796
+ has_color = ptrue?(font) && ptrue?(font[:_color])
797
+
798
+ if !latin_attributes.empty? || has_color
799
+ @writer.tag_elements(tag, style_attributes) do
800
+ if has_color
801
+ write_a_solid_fill(:color => font[:_color])
802
+ end
803
+ if !latin_attributes.empty?
804
+ write_a_latin(latin_attributes)
805
+ end
806
+ end
807
+ else
808
+ @writer.empty_tag(tag, style_attributes)
809
+ end
810
+ end
811
+
812
+ #
813
+ # Get the font latin attributes from a font hash.
814
+ #
815
+ def get_font_latin_attributes(font)
816
+ return [] unless font
817
+
818
+ attributes = []
819
+ attributes << ['typeface', font[:_name]] if ptrue?(font[:_name])
820
+ attributes << ['pitchFamily', font[:_pitch_family]] if font[:_pitch_family]
821
+ attributes << ['charset', font[:_charset]] if font[:_charset]
822
+
823
+ attributes
824
+ end
825
+
826
+ #
827
+ # Write the <a:solidFill> element.
828
+ #
829
+ def write_a_solid_fill(fill) # :nodoc:
830
+ @writer.tag_elements('a:solidFill') do
831
+ if fill[:color]
832
+ # Write the a:srgbClr element.
833
+ write_a_srgb_clr(color(fill[:color]), fill[:transparency])
834
+ end
835
+ end
836
+ end
837
+
838
+ #
839
+ # Write the <a:srgbClr> element.
840
+ #
841
+ def write_a_srgb_clr(color, transparency = nil) # :nodoc:
842
+ tag = 'a:srgbClr'
843
+ attributes = [ ['val', color] ]
844
+
845
+ if ptrue?(transparency)
846
+ @writer.tag_elements(tag, attributes) do
847
+ write_a_alpha(transparency)
848
+ end
849
+ else
850
+ @writer.empty_tag(tag, attributes)
851
+ end
852
+ end
853
+
854
+ #
855
+ # Convert the user specified colour index or string to a rgb colour.
856
+ #
857
+ def color(color_code) # :nodoc:
858
+ if color_code and color_code =~ /^#[0-9a-fA-F]{6}$/
859
+ # Convert a HTML style #RRGGBB color.
860
+ color_code.sub(/^#/, '').upcase
861
+ else
862
+ index = Format.color(color_code)
863
+ raise "Unknown color '#{color_code}' used in chart formatting." unless index
864
+ palette_color(index)
865
+ end
866
+ end
867
+
868
+ #
869
+ # Get the font style attributes from a font hash.
870
+ #
871
+ def get_font_style_attributes(font)
872
+ return [] unless font
873
+
874
+ attributes = []
875
+ attributes << ['sz', font[:_size]] if ptrue?(font[:_size])
876
+ attributes << ['b', font[:_bold]] if font[:_bold]
877
+ attributes << ['i', font[:_italic]] if font[:_italic]
878
+ attributes << ['u', 'sng'] if font[:_underline]
879
+
880
+ # Turn off baseline when testing fonts that don't have it.
881
+ if font[:_baseline] != -1
882
+ attributes << ['baseline', font[:_baseline]]
883
+ end
884
+ attributes
885
+ end
886
+
887
+ #
888
+ # Write the <a:endParaRPr> element.
889
+ #
890
+ def write_a_end_para_rpr # :nodoc:
891
+ @writer.empty_tag('a:endParaRPr', [ ['lang', 'en-US'] ])
892
+ end
693
893
  end
694
894
 
695
895
  module WriteDPtPoint
696
- #
697
- # Write an individual <c:dPt> element. Override the parent method to add
698
- # markers.
699
- #
700
- def write_d_pt_point(index, point)
701
- @writer.tag_elements('c:dPt') do
702
- # Write the c:idx element.
703
- write_idx(index)
704
- @writer.tag_elements('c:marker') do
705
- # Write the c:spPr element.
706
- write_sp_pr(point)
707
- end
896
+ #
897
+ # Write an individual <c:dPt> element. Override the parent method to add
898
+ # markers.
899
+ #
900
+ def write_d_pt_point(index, point)
901
+ @writer.tag_elements('c:dPt') do
902
+ # Write the c:idx element.
903
+ write_idx(index)
904
+ @writer.tag_elements('c:marker') do
905
+ # Write the c:spPr element.
906
+ write_sp_pr(point)
708
907
  end
709
908
  end
909
+ end
710
910
  end
711
911
  end