write_xlsx 1.11.1 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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