write_xlsx 1.11.1 → 1.12.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.
@@ -12,9 +12,15 @@ module Writexlsx
12
12
  class Metadata
13
13
  include Writexlsx::Utility
14
14
 
15
+ attr_writer :has_dynamic_functions
16
+ attr_writer :num_embedded_images
17
+
15
18
  def initialize(workbook)
16
19
  @writer = Package::XMLWriterSimple.new
17
- @workbook = workbook
20
+ @workbook = workbook
21
+ @has_dynamic_functions = false
22
+ @has_embedded_images = false
23
+ @num_embedded_images = 0
18
24
  end
19
25
 
20
26
  def set_xml_writer(filename)
@@ -22,15 +28,23 @@ module Writexlsx
22
28
  end
23
29
 
24
30
  def assemble_xml_file
31
+ @has_embedded_images = true if @num_embedded_images > 0
32
+
25
33
  write_xml_declaration do
26
34
  # Write the metadata element.
27
35
  write_metadata
36
+
28
37
  # Write the metadataTypes element.
29
38
  write_metadata_types
39
+
30
40
  # Write the futureMetadata element.
31
- write_future_metadata
41
+ write_cell_future_metadata if @has_dynamic_functions
42
+ write_value_future_metadata if @has_embedded_images
43
+
32
44
  # Write the cellMetadata element.
33
- write_cell_metadata
45
+ write_cell_metadata if @has_dynamic_functions
46
+ write_value_metadata if @has_embedded_images
47
+
34
48
  @writer.end_tag('metadata')
35
49
  end
36
50
  end
@@ -42,14 +56,23 @@ module Writexlsx
42
56
  #
43
57
  def write_metadata
44
58
  xmlns = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
45
- xmlns_xda =
46
- 'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray'
47
-
48
59
  attributes = [
49
- ['xmlns', xmlns],
50
- ['xmlns:xda', xmlns_xda]
60
+ ['xmlns', xmlns]
51
61
  ]
52
62
 
63
+ if @has_embedded_images
64
+ attributes << [
65
+ 'xmlns:xlrd',
66
+ 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata'
67
+ ]
68
+ end
69
+ if @has_dynamic_functions
70
+ attributes << [
71
+ 'xmlns:xda',
72
+ 'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray'
73
+ ]
74
+ end
75
+
53
76
  @writer.start_tag('metadata', attributes)
54
77
  end
55
78
 
@@ -57,18 +80,23 @@ module Writexlsx
57
80
  # Write the <metadataTypes> element.
58
81
  #
59
82
  def write_metadata_types
60
- attributes = [['count', 1]]
83
+ count =
84
+ (@has_dynamic_functions ? 1 : 0) +
85
+ (@has_embedded_images ? 1 : 0)
86
+
87
+ attributes = [['count', count]]
61
88
 
62
89
  @writer.tag_elements('metadataTypes', attributes) do
63
90
  # Write the metadataType element.
64
- write_metadata_type
91
+ write_cell_metadata_type if @has_dynamic_functions
92
+ write_value_metadata_type if @has_embedded_images
65
93
  end
66
94
  end
67
95
 
68
96
  #
69
97
  # Write the <metadataType> element.
70
98
  #
71
- def write_metadata_type
99
+ def write_cell_metadata_type
72
100
  attributes = [
73
101
  %w[name XLDAPR],
74
102
  ['minSupportedVersion', 120000],
@@ -88,10 +116,32 @@ module Writexlsx
88
116
  @writer.empty_tag('metadataType', attributes)
89
117
  end
90
118
 
119
+ #
120
+ # Write the <metadataType> element.
121
+ #
122
+ def write_value_metadata_type
123
+ attributes = [
124
+ %w[name XLRICHVALUE],
125
+ ['minSupportedVersion', 120000],
126
+ ['copy', 1],
127
+ ['pasteAll', 1],
128
+ ['pasteValues', 1],
129
+ ['merge', 1],
130
+ ['splitFirst', 1],
131
+ ['rowColShift', 1],
132
+ ['clearFormats', 1],
133
+ ['clearComments', 1],
134
+ ['assign', 1],
135
+ ['coerce', 1]
136
+ ]
137
+
138
+ @writer.empty_tag('metadataType', attributes)
139
+ end
140
+
91
141
  #
92
142
  # Write the <futureMetadata> element.
93
143
  #
94
- def write_future_metadata
144
+ def write_cell_future_metadata
95
145
  attributes = [
96
146
  %w[name XLDAPR],
97
147
  ['count', 1]
@@ -101,7 +151,34 @@ module Writexlsx
101
151
  @writer.tag_elements('bk') do
102
152
  @writer.tag_elements('extLst') do
103
153
  # Write the ext element.
104
- write_ext
154
+ write_cell_ext
155
+
156
+ @writer.end_tag('ext')
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ #
163
+ # Write the <futureMetadata> element.
164
+ #
165
+ def write_value_future_metadata
166
+ num_images = @num_embedded_images
167
+
168
+ attributes = [
169
+ %w[name XLRICHVALUE],
170
+ ['count', num_images]
171
+ ]
172
+
173
+ @writer.tag_elements('futureMetadata', attributes) do
174
+ (0..(num_images - 1)).each do |i|
175
+ @writer.tag_elements('bk') do
176
+ @writer.tag_elements('extLst') do
177
+ # Write the ext element.
178
+ write_value_ext(i)
179
+
180
+ @writer.end_tag('ext')
181
+ end
105
182
  end
106
183
  end
107
184
  end
@@ -110,12 +187,23 @@ module Writexlsx
110
187
  #
111
188
  # Write the <ext> element.
112
189
  #
113
- def write_ext
190
+ def write_cell_ext
114
191
  attributes = [['uri', '{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}']]
115
- @writer.tag_elements('ext', attributes) do
116
- # Write the xda:dynamicArrayProperties element.
117
- write_xda_dynamic_array_properties
118
- end
192
+ @writer.start_tag('ext', attributes)
193
+
194
+ # Write the xda:dynamicArrayProperties element.
195
+ write_xda_dynamic_array_properties
196
+ end
197
+
198
+ #
199
+ # Write the <ext> element.
200
+ #
201
+ def write_value_ext(num)
202
+ attributes = [['uri', '{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}']]
203
+ @writer.start_tag('ext', attributes)
204
+
205
+ # Write the <xlrd:rvb> element.
206
+ write_xlrd_rvb(num)
119
207
  end
120
208
 
121
209
  #
@@ -141,7 +229,27 @@ module Writexlsx
141
229
  @writer.tag_elements('cellMetadata', attributes) do
142
230
  @writer.tag_elements('bk') do
143
231
  # Write the rc element.
144
- write_rc
232
+ write_rc(1, 0)
233
+ end
234
+ end
235
+ end
236
+
237
+ #
238
+ # Write the <valueMetadata> element.
239
+ #
240
+ def write_value_metadata
241
+ count = @num_embedded_images
242
+ type = 1
243
+ type = 2 if @has_dynamic_functions
244
+
245
+ attributes = [['count', count]]
246
+
247
+ @writer.tag_elements('valueMetadata', attributes) do
248
+ (0..(count - 1)).each do |i|
249
+ @writer.tag_elements('bk') do
250
+ # Write the rc element.
251
+ write_rc(type, i)
252
+ end
145
253
  end
146
254
  end
147
255
  end
@@ -149,13 +257,22 @@ module Writexlsx
149
257
  #
150
258
  # Write the <rc> element.
151
259
  #
152
- def write_rc
260
+ def write_rc(type, value)
153
261
  attributes = [
154
- ['t', 1],
155
- ['v', 0]
262
+ ['t', type],
263
+ ['v', value]
156
264
  ]
157
265
  @writer.empty_tag('rc', attributes)
158
266
  end
267
+
268
+ #
269
+ # Write the <xlrd:rvb> element.
270
+ #
271
+ def write_xlrd_rvb(value)
272
+ attributes = [['i', value]]
273
+
274
+ @writer.empty_tag('xlrd:rvb', attributes)
275
+ end
159
276
  end
160
277
  end
161
278
  end
@@ -10,6 +10,10 @@ require 'write_xlsx/package/core'
10
10
  require 'write_xlsx/package/custom'
11
11
  require 'write_xlsx/package/metadata'
12
12
  require 'write_xlsx/package/relationships'
13
+ require 'write_xlsx/package/rich_value'
14
+ require 'write_xlsx/package/rich_value_rel'
15
+ require 'write_xlsx/package/rich_value_structure'
16
+ require 'write_xlsx/package/rich_value_types'
13
17
  require 'write_xlsx/package/shared_strings'
14
18
  require 'write_xlsx/package/styles'
15
19
  require 'write_xlsx/package/table'
@@ -56,9 +60,11 @@ module Writexlsx
56
60
  write_worksheet_rels_files
57
61
  write_chartsheet_rels_files
58
62
  write_drawing_rels_files
63
+ write_rich_value_rels_files
59
64
  add_image_files
60
65
  add_vba_project
61
66
  write_metadata_file
67
+ write_rich_value_files
62
68
  end
63
69
 
64
70
  private
@@ -142,13 +148,12 @@ module Writexlsx
142
148
  def write_app_file
143
149
  app = Package::App.new(@workbook)
144
150
 
151
+ # Add the Worksheet parts.
152
+ app.add_worksheet_part_names
145
153
  # Add the Worksheet heading pairs.
146
154
  app.add_worksheet_heading_pairs
147
155
  # Add the Chartsheet heading pairs.
148
156
  app.add_chartsheet_heading_pairs
149
-
150
- # Add the Worksheet parts.
151
- app.add_worksheet_part_names
152
157
  # Add the Chartsheet parts.
153
158
  app.add_chartsheet_part_names
154
159
  # Add the Named Range heading pairs.
@@ -185,12 +190,81 @@ module Writexlsx
185
190
 
186
191
  return unless @workbook.has_metadata?
187
192
 
193
+ metadata.has_dynamic_functions = @workbook.has_dynamic_functions?
194
+ metadata.num_embedded_images = @workbook.embedded_images.size
195
+
188
196
  FileUtils.mkdir_p("#{@package_dir}/xl")
189
197
 
190
198
  metadata.set_xml_writer("#{@package_dir}/xl/metadata.xml")
191
199
  metadata.assemble_xml_file
192
200
  end
193
201
 
202
+ #
203
+ # Write the rdrichvalue(*).xml file.
204
+ #
205
+ def write_rich_value_files
206
+ dir = @package_dir
207
+
208
+ return unless @workbook.has_embedded_images?
209
+
210
+ FileUtils.mkdir_p("#{dir}/xl/richData")
211
+
212
+ write_rich_value_file
213
+ write_rich_value_structure_file
214
+ write_rich_value_types_file
215
+ write_rich_value_rel
216
+ end
217
+
218
+ #
219
+ # Write the rdrichvalue.xml file.
220
+ #
221
+ def write_rich_value_file
222
+ dir = @package_dir
223
+ rich_value = Package::RichValue.new
224
+
225
+ rich_value.embedded_images = @workbook.embedded_images
226
+
227
+ rich_value.set_xml_writer("#{dir}/xl/richData/rdrichvalue.xml")
228
+ rich_value.assemble_xml_file
229
+ end
230
+
231
+ #
232
+ # Write the rdrichvaluestructure.xml file.
233
+ #
234
+ def write_rich_value_structure_file
235
+ dir = @package_dir
236
+ rich_value = Package::RichValueStructure.new
237
+
238
+ rich_value.has_embedded_descriptions = @workbook.has_embedded_descriptions?
239
+
240
+ rich_value.set_xml_writer("#{dir}/xl/richData/rdrichvaluestructure.xml")
241
+ rich_value.assemble_xml_file
242
+ end
243
+
244
+ #
245
+ # Write the rdRichValueTypes.xml file.
246
+ #
247
+ def write_rich_value_types_file
248
+ rich_value = Package::RichValueTypes.new
249
+ dir = @package_dir
250
+
251
+ rich_value.set_xml_writer("#{dir}/xl/richData/rdRichValueTypes.xml")
252
+ rich_value.assemble_xml_file
253
+ end
254
+
255
+ #
256
+ # Write the rdrichvalue.xml file.
257
+ #
258
+ def write_rich_value_rel
259
+ dir = @package_dir
260
+ rich_value = Package::RichValueRel.new
261
+
262
+ rich_value.value_count = @workbook.embedded_images.size
263
+
264
+ rich_value.set_xml_writer("#{dir}/xl/richData/richValueRel.xml")
265
+ rich_value.assemble_xml_file
266
+ end
267
+
194
268
  #
195
269
  # Write the custom.xml file.
196
270
  #
@@ -227,8 +301,10 @@ module Writexlsx
227
301
  content.add_vba_project if @workbook.vba_project
228
302
  # Add the custom properties if present.
229
303
  content.add_custom_properties unless @workbook.custom_properties.empty?
230
- # Add the metadata file if present.
304
+ # Add the RichValue file if present.
231
305
  content.add_metadata if @workbook.has_metadata?
306
+ # Add the metadata file if present.
307
+ content.add_richvalue if @workbook.has_embedded_images?
232
308
 
233
309
  content.set_xml_writer("#{@package_dir}/[Content_Types].xml")
234
310
  content.assemble_xml_file
@@ -341,6 +417,11 @@ module Writexlsx
341
417
  )
342
418
  end
343
419
 
420
+ # Add the RichValue files if present.
421
+ if @workbook.has_embedded_images?
422
+ rels.add_rich_value_relationships
423
+ end
424
+
344
425
  rels.set_xml_writer("#{@package_dir}/xl/_rels/workbook.xml.rels")
345
426
  rels.assemble_xml_file
346
427
  end
@@ -368,15 +449,50 @@ module Writexlsx
368
449
  @workbook.worksheets.write_drawing_rels_files(@package_dir)
369
450
  end
370
451
 
452
+ #
453
+ # Write the richValueRel.xml.rels files for worksheets that contain embedded
454
+ # images.
455
+ #
456
+ def write_rich_value_rels_files
457
+ return if @workbook.embedded_images.empty?
458
+
459
+ dir = @package_dir
460
+
461
+ # Create the .rels dirs.
462
+ FileUtils.mkdir_p("#{dir}/xl/richData/_rels")
463
+
464
+ rels = Package::Relationships.new
465
+
466
+ @workbook.embedded_images.each_with_index do |image_data, index|
467
+ file_type = image_data[1]
468
+ image_file = "../media/image#{index + 1}.#{file_type}"
469
+
470
+ rels.add_worksheet_relationship('/image', image_file)
471
+ end
472
+
473
+ # Create the .rels file.
474
+ rels.set_xml_writer("#{dir}/xl/richData/_rels/richValueRel.xml.rels")
475
+ rels.assemble_xml_file
476
+ end
477
+
371
478
  #
372
479
  # Write the /xl/media/image?.xml files.
373
480
  #
374
481
  def add_image_files
482
+ index = 1
483
+
375
484
  dir = "#{@package_dir}/xl/media"
376
485
 
377
- @workbook.images.each_with_index do |image, index|
486
+ @workbook.embedded_images.each do |image|
487
+ FileUtils.mkdir_p(dir)
488
+ FileUtils.cp(image[0], "#{dir}/image#{index}.#{image[1]}")
489
+ index += 1
490
+ end
491
+
492
+ @workbook.images.each do |image|
378
493
  FileUtils.mkdir_p(dir)
379
- FileUtils.cp(image[0], "#{dir}/image#{index + 1}.#{image[1]}")
494
+ FileUtils.cp(image[0], "#{dir}/image#{index}.#{image[1]}")
495
+ index += 1
380
496
  end
381
497
  end
382
498
 
@@ -58,6 +58,31 @@ module Writexlsx
58
58
  @rels.push([Document_schema + type, target, target_mode])
59
59
  end
60
60
 
61
+ #
62
+ # Add relationships for RichValue files.
63
+ #
64
+ def add_rich_value_relationships
65
+ @rels << [
66
+ 'http://schemas.microsoft.com/office/2022/10/relationships/richValueRel',
67
+ 'richData/richValueRel.xml'
68
+ ]
69
+
70
+ @rels << [
71
+ 'http://schemas.microsoft.com/office/2017/06/relationships/rdRichValue',
72
+ 'richData/rdrichvalue.xml'
73
+ ]
74
+
75
+ @rels << [
76
+ 'http://schemas.microsoft.com/office/2017/06/relationships/rdRichValueStructure',
77
+ 'richData/rdrichvaluestructure.xml'
78
+ ]
79
+
80
+ @rels << [
81
+ 'http://schemas.microsoft.com/office/2017/06/relationships/rdRichValueTypes',
82
+ 'richData/rdRichValueTypes.xml'
83
+ ]
84
+ end
85
+
61
86
  private
62
87
 
63
88
  #
@@ -0,0 +1,70 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'write_xlsx/package/xml_writer_simple'
5
+ require 'write_xlsx/utility'
6
+
7
+ module Writexlsx
8
+ module Package
9
+ class RichValue
10
+ include Writexlsx::Utility
11
+
12
+ attr_accessor :embedded_images
13
+
14
+ def initialize
15
+ @writer = Package::XMLWriterSimple.new
16
+ @embedded_images = []
17
+ end
18
+
19
+ def set_xml_writer(filename)
20
+ @writer.set_xml_writer(filename)
21
+ end
22
+
23
+ def assemble_xml_file
24
+ write_xml_declaration do
25
+ write_rv_data
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def write_rv_data
32
+ xmlns = 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata'
33
+
34
+ attributes = [
35
+ ['xmlns', xmlns],
36
+ ['count', @embedded_images.size]
37
+ ]
38
+
39
+ @writer.tag_elements('rvData', attributes) do
40
+ @embedded_images.each_with_index do |image, index|
41
+ write_rv(index, image[2], image[3])
42
+ end
43
+ end
44
+ end
45
+
46
+ #
47
+ # Write the <rv> element.
48
+ #
49
+ def write_rv(index, description, decorative)
50
+ value = 5
51
+ value = 6 if ptrue?(decorative)
52
+
53
+ attributes = [['s', 0]]
54
+
55
+ @writer.tag_elements('rv', attributes) do
56
+ write_v(index)
57
+ write_v(value)
58
+ write_v(description) if ptrue?(description)
59
+ end
60
+ end
61
+
62
+ #
63
+ # Write the <v> element.
64
+ #
65
+ def write_v(data)
66
+ @writer.data_element('v', data)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,70 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'write_xlsx/package/xml_writer_simple'
5
+ require 'write_xlsx/utility'
6
+
7
+ module Writexlsx
8
+ module Package
9
+ #
10
+ # RichValueRel - A class for writing the Excel XLSX richValueRel.xml file.
11
+ #
12
+ # Used in conjunction with Excel::Writer::XLSX
13
+ #
14
+ # Copyright 2000-2024, John McNamara, jmcnamara@cpan.org
15
+ #
16
+ # Convert to Ruby by Hideo Nakamura, nakamura.hideo@gmail.com
17
+ #
18
+ class RichValueRel
19
+ include Writexlsx::Utility
20
+
21
+ attr_writer :value_count
22
+
23
+ def initialize
24
+ @writer = Package::XMLWriterSimple.new
25
+ @value_count = 0
26
+ end
27
+
28
+ def set_xml_writer(filename)
29
+ @writer.set_xml_writer(filename)
30
+ end
31
+
32
+ def assemble_xml_file
33
+ write_xml_declaration do
34
+ write_rich_value_rels
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ #
41
+ # Write the <richValueRels> element.
42
+ #
43
+ def write_rich_value_rels
44
+ xmlns = 'http://schemas.microsoft.com/office/spreadsheetml/2022/richvaluerel'
45
+ xmlns_r = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
46
+
47
+ attributes = [
48
+ ['xmlns', xmlns],
49
+ ['xmlns:r', xmlns_r]
50
+ ]
51
+
52
+ @writer.tag_elements('richValueRels', attributes) do
53
+ (0..(@value_count - 1)).each do |index|
54
+ # Write the rel element.
55
+ write_rel(index + 1)
56
+ end
57
+ end
58
+ end
59
+
60
+ #
61
+ # Write the <rel> element.
62
+ #
63
+ def write_rel(id)
64
+ attributes = [['r:id', "rId#{id}"]]
65
+
66
+ @writer.empty_tag('rel', attributes)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,83 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'write_xlsx/package/xml_writer_simple'
5
+ require 'write_xlsx/utility'
6
+
7
+ module Writexlsx
8
+ module Package
9
+ #
10
+ # RichValueStructure - A class for writing the Excel XLSX rdrichvaluestructure.xml
11
+ # file.
12
+ #
13
+ # Used in conjunction with Excel::Writer::XLSX
14
+ #
15
+ # Copyright 2000-2024, John McNamara, jmcnamara@cpan.org
16
+ #
17
+ # Convert to Ruby by Hideo NAKAMURA, nakamura.hideo@gmail.com
18
+ #
19
+ class RichValueStructure
20
+ include Writexlsx::Utility
21
+
22
+ attr_writer :has_embedded_descriptions
23
+
24
+ def initialize
25
+ @writer = Package::XMLWriterSimple.new
26
+ @has_embedded_descriptions = false
27
+ end
28
+
29
+ def set_xml_writer(filename)
30
+ @writer.set_xml_writer(filename)
31
+ end
32
+
33
+ def assemble_xml_file
34
+ write_xml_declaration do
35
+ write_rv_structures
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ #
42
+ # Write the <rvStructures> element.
43
+ #
44
+ def write_rv_structures
45
+ xmlns = 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata'
46
+
47
+ attributes = [
48
+ ['xmlns', xmlns],
49
+ ['count', 1]
50
+ ]
51
+
52
+ @writer.tag_elements('rvStructures', attributes) do
53
+ write_s
54
+ end
55
+ end
56
+
57
+ #
58
+ # Write the <s> element.
59
+ #
60
+ def write_s
61
+ attributes = [%w[t _localImage]]
62
+
63
+ @writer.tag_elements('s', attributes) do
64
+ write_k('_rvRel:LocalImageIdentifier', 'i')
65
+ write_k('CalcOrigin', 'i')
66
+ write_k('Text', 's') if @has_embedded_descriptions
67
+ end
68
+ end
69
+
70
+ #
71
+ # Write the <k> element.
72
+ #
73
+ def write_k(n, t)
74
+ attributes = [
75
+ ['n', n],
76
+ ['t', t]
77
+ ]
78
+
79
+ @writer.empty_tag('k', attributes)
80
+ end
81
+ end
82
+ end
83
+ end