write_xlsx 1.12.3 → 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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +22 -0
  3. data/Changes +23 -0
  4. data/README.md +1 -1
  5. data/lib/write_xlsx/chart/area.rb +2 -2
  6. data/lib/write_xlsx/chart/axis.rb +55 -32
  7. data/lib/write_xlsx/chart/axis_writer.rb +528 -0
  8. data/lib/write_xlsx/chart/bar.rb +2 -2
  9. data/lib/write_xlsx/chart/caption.rb +16 -9
  10. data/lib/write_xlsx/chart/chart_area.rb +121 -0
  11. data/lib/write_xlsx/chart/column.rb +2 -2
  12. data/lib/write_xlsx/chart/d_pt_point_writer.rb +14 -0
  13. data/lib/write_xlsx/chart/doughnut.rb +0 -3
  14. data/lib/write_xlsx/chart/formatting_writer.rb +652 -0
  15. data/lib/write_xlsx/chart/initialization.rb +100 -0
  16. data/lib/write_xlsx/chart/line.rb +4 -3
  17. data/lib/write_xlsx/chart/pie.rb +6 -2
  18. data/lib/write_xlsx/chart/radar.rb +2 -2
  19. data/lib/write_xlsx/chart/scatter.rb +4 -3
  20. data/lib/write_xlsx/chart/series.rb +35 -15
  21. data/lib/write_xlsx/chart/series_data.rb +132 -0
  22. data/lib/write_xlsx/chart/series_writer.rb +318 -0
  23. data/lib/write_xlsx/chart/settings.rb +226 -0
  24. data/lib/write_xlsx/chart/stock.rb +2 -2
  25. data/lib/write_xlsx/chart/table.rb +50 -0
  26. data/lib/write_xlsx/chart/xml_writer.rb +305 -0
  27. data/lib/write_xlsx/chart.rb +286 -2477
  28. data/lib/write_xlsx/chartsheet.rb +35 -83
  29. data/lib/write_xlsx/constants.rb +11 -0
  30. data/lib/write_xlsx/drawing.rb +5 -3
  31. data/lib/write_xlsx/format/alignment_state.rb +39 -0
  32. data/lib/write_xlsx/format/alignment_style.rb +92 -0
  33. data/lib/write_xlsx/format/border_state.rb +47 -0
  34. data/lib/write_xlsx/format/border_style.rb +116 -0
  35. data/lib/write_xlsx/format/fill_state.rb +26 -0
  36. data/lib/write_xlsx/format/fill_style.rb +52 -0
  37. data/lib/write_xlsx/format/font_state.rb +74 -0
  38. data/lib/write_xlsx/format/font_style.rb +172 -0
  39. data/lib/write_xlsx/format/format_state.rb +65 -0
  40. data/lib/write_xlsx/format/number_format_state.rb +20 -0
  41. data/lib/write_xlsx/format/number_format_style.rb +28 -0
  42. data/lib/write_xlsx/format/protection_state.rb +20 -0
  43. data/lib/write_xlsx/format/protection_style.rb +28 -0
  44. data/lib/write_xlsx/format.rb +1093 -426
  45. data/lib/write_xlsx/formats.rb +0 -2
  46. data/lib/write_xlsx/image_property.rb +4 -1
  47. data/lib/write_xlsx/inserted_chart.rb +1 -1
  48. data/lib/write_xlsx/object_positioning.rb +203 -0
  49. data/lib/write_xlsx/package/app.rb +3 -3
  50. data/lib/write_xlsx/package/button.rb +6 -2
  51. data/lib/write_xlsx/package/comments.rb +11 -3
  52. data/lib/write_xlsx/package/conditional_format.rb +7 -3
  53. data/lib/write_xlsx/package/content_types.rb +2 -2
  54. data/lib/write_xlsx/package/core.rb +2 -2
  55. data/lib/write_xlsx/package/custom.rb +3 -2
  56. data/lib/write_xlsx/package/metadata.rb +2 -2
  57. data/lib/write_xlsx/package/packager.rb +0 -3
  58. data/lib/write_xlsx/package/relationships.rb +2 -2
  59. data/lib/write_xlsx/package/rich_value.rb +4 -2
  60. data/lib/write_xlsx/package/rich_value_rel.rb +2 -2
  61. data/lib/write_xlsx/package/rich_value_structure.rb +2 -2
  62. data/lib/write_xlsx/package/rich_value_types.rb +3 -3
  63. data/lib/write_xlsx/package/shared_strings.rb +2 -2
  64. data/lib/write_xlsx/package/styles.rb +13 -9
  65. data/lib/write_xlsx/package/table.rb +8 -2
  66. data/lib/write_xlsx/package/theme.rb +0 -3
  67. data/lib/write_xlsx/package/vml.rb +2 -2
  68. data/lib/write_xlsx/page_setup.rb +192 -0
  69. data/lib/write_xlsx/shape.rb +97 -100
  70. data/lib/write_xlsx/sheets.rb +9 -4
  71. data/lib/write_xlsx/sparkline.rb +2 -2
  72. data/lib/write_xlsx/utility/cell_reference.rb +124 -0
  73. data/lib/write_xlsx/utility/chart_formatting.rb +262 -0
  74. data/lib/write_xlsx/utility/common.rb +44 -0
  75. data/lib/write_xlsx/utility/date_time.rb +113 -0
  76. data/lib/write_xlsx/utility/dimensions.rb +40 -0
  77. data/lib/write_xlsx/utility/drawing.rb +136 -0
  78. data/lib/write_xlsx/utility/rich_text.rb +184 -0
  79. data/lib/write_xlsx/utility/sheetname_quoting.rb +73 -0
  80. data/lib/write_xlsx/utility/string_width.rb +45 -0
  81. data/lib/write_xlsx/utility/url.rb +27 -0
  82. data/lib/write_xlsx/utility/xml_primitives.rb +32 -0
  83. data/lib/write_xlsx/version.rb +1 -1
  84. data/lib/write_xlsx/workbook/chart_data.rb +188 -0
  85. data/lib/write_xlsx/workbook/format_preparation.rb +199 -0
  86. data/lib/write_xlsx/workbook/initialization.rb +223 -0
  87. data/lib/write_xlsx/workbook/package_preparation.rb +231 -0
  88. data/lib/write_xlsx/workbook/workbook_writer.rb +164 -0
  89. data/lib/write_xlsx/workbook.rb +143 -981
  90. data/lib/write_xlsx/worksheet/asset_manager.rb +60 -0
  91. data/lib/write_xlsx/worksheet/autofilter.rb +390 -0
  92. data/lib/write_xlsx/worksheet/cell_data.rb +13 -6
  93. data/lib/write_xlsx/worksheet/cell_data_manager.rb +47 -0
  94. data/lib/write_xlsx/worksheet/cell_data_store.rb +61 -0
  95. data/lib/write_xlsx/worksheet/columns.rb +204 -0
  96. data/lib/write_xlsx/worksheet/comments_support.rb +61 -0
  97. data/lib/write_xlsx/worksheet/conditional_formats.rb +30 -0
  98. data/lib/write_xlsx/worksheet/data_validation.rb +9 -1
  99. data/lib/write_xlsx/worksheet/data_writing.rb +1017 -0
  100. data/lib/write_xlsx/worksheet/drawing_methods.rb +308 -0
  101. data/lib/write_xlsx/worksheet/drawing_preparation.rb +290 -0
  102. data/lib/write_xlsx/worksheet/drawing_relations.rb +76 -0
  103. data/lib/write_xlsx/worksheet/drawing_xml_writer.rb +50 -0
  104. data/lib/write_xlsx/worksheet/formatting.rb +418 -0
  105. data/lib/write_xlsx/worksheet/hyperlink.rb +9 -1
  106. data/lib/write_xlsx/worksheet/initialization.rb +146 -0
  107. data/lib/write_xlsx/worksheet/panes.rb +64 -0
  108. data/lib/write_xlsx/worksheet/print_options.rb +72 -0
  109. data/lib/write_xlsx/worksheet/protection.rb +65 -0
  110. data/lib/write_xlsx/worksheet/rich_text_helpers.rb +78 -0
  111. data/lib/write_xlsx/worksheet/row_col_sizing.rb +69 -0
  112. data/lib/write_xlsx/worksheet/rows.rb +84 -0
  113. data/lib/write_xlsx/worksheet/selection.rb +41 -0
  114. data/lib/write_xlsx/worksheet/xml_writer.rb +1246 -0
  115. data/lib/write_xlsx/worksheet.rb +376 -4530
  116. metadata +66 -4
  117. data/lib/write_xlsx/utility.rb +0 -986
  118. data/lib/write_xlsx/worksheet/page_setup.rb +0 -192
@@ -0,0 +1,199 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Workbook
6
+ module FormatPreparation
7
+ private
8
+
9
+ #
10
+ # Prepare all of the format properties prior to passing them to Styles.rb.
11
+ #
12
+ def prepare_format_properties # :nodoc:
13
+ # Separate format objects into XF and DXF formats.
14
+ prepare_formats
15
+
16
+ # Set the font index for the format objects.
17
+ prepare_fonts
18
+
19
+ # Set the number format index for the format objects.
20
+ prepare_num_formats
21
+
22
+ # Set the border index for the format objects.
23
+ prepare_borders
24
+
25
+ # Set the fill index for the format objects.
26
+ prepare_fills
27
+ end
28
+
29
+ #
30
+ # Iterate through the XF Format objects and separate them into XF and DXF
31
+ # formats.
32
+ #
33
+ def prepare_formats # :nodoc:
34
+ @formats.formats.each do |format|
35
+ xf_index = format.xf_index
36
+ dxf_index = format.dxf_index
37
+
38
+ @xf_formats[xf_index] = format if xf_index
39
+ @dxf_formats[dxf_index] = format if dxf_index
40
+ end
41
+ end
42
+
43
+ #
44
+ # Iterate through the XF Format objects and give them an index to non-default
45
+ # font elements.
46
+ #
47
+ def prepare_fonts # :nodoc:
48
+ fonts = {}
49
+
50
+ @xf_formats.each { |format| format.set_font_info(fonts) }
51
+
52
+ @font_count = fonts.size
53
+
54
+ # For the DXF formats we only need to check if the properties have changed.
55
+ @dxf_formats.each do |format|
56
+ # The only font properties that can change for a DXF format are: color,
57
+ # bold, italic, underline and strikethrough.
58
+ format.has_dxf_font(true) if format.color? || format.bold? || format.italic? || format.underline? || format.strikeout?
59
+ end
60
+ end
61
+
62
+ #
63
+ # Iterate through the XF Format objects and give them an index to non-default
64
+ # number format elements.
65
+ #
66
+ # User defined records start from index 0xA4.
67
+ #
68
+ def prepare_num_formats # :nodoc:
69
+ num_formats = []
70
+ unique_num_formats = {}
71
+ index = 164
72
+
73
+ (@xf_formats + @dxf_formats).each do |format|
74
+ num_format = format.num_format
75
+
76
+ # Check if num_format is an index to a built-in number format.
77
+ # Also check for a string of zeros, which is a valid number format
78
+ # string but would evaluate to zero.
79
+ #
80
+ if num_format.to_s =~ /^\d+$/ && num_format.to_s !~ /^0+\d/
81
+ # Number format '0' is indexed as 1 in Excel.
82
+ num_format = 1 if num_format == 0
83
+ # Index to a built-in number format.
84
+ format.num_format_index = num_format
85
+ next
86
+ elsif num_format.to_s == 'General'
87
+ # The 'General' format has an number format index of 0.
88
+ format.num_format_index = 0
89
+ next
90
+ end
91
+
92
+ if unique_num_formats[num_format]
93
+ # Number format has already been used.
94
+ format.num_format_index = unique_num_formats[num_format]
95
+ else
96
+ # Add a new number format.
97
+ unique_num_formats[num_format] = index
98
+ format.num_format_index = index
99
+ index += 1
100
+
101
+ # Only store/increase number format count for XF formats
102
+ # (not for DXF formats).
103
+ num_formats << num_format if ptrue?(format.xf_index)
104
+ end
105
+ end
106
+
107
+ @num_formats = num_formats
108
+ end
109
+
110
+ #
111
+ # Iterate through the XF Format objects and give them an index to non-default
112
+ # border elements.
113
+ #
114
+ def prepare_borders # :nodoc:
115
+ borders = {}
116
+
117
+ @xf_formats.each { |format| format.set_border_info(borders) }
118
+
119
+ @border_count = borders.size
120
+
121
+ # For the DXF formats we only need to check if the properties have changed.
122
+ @dxf_formats.each do |format|
123
+ key = format.get_border_key
124
+ format.has_dxf_border(true) if key =~ /[^0:]/
125
+ end
126
+ end
127
+
128
+ #
129
+ # Iterate through the XF Format objects and give them an index to non-default
130
+ # fill elements.
131
+ #
132
+ # The user defined fill properties start from 2 since there are 2 default
133
+ # fills: patternType="none" and patternType="gray125".
134
+ #
135
+ def prepare_fills # :nodoc:
136
+ fills = {}
137
+ index = 2 # Start from 2. See above.
138
+
139
+ # Add the default fills.
140
+ fills['0:0:0'] = 0
141
+ fills['17:0:0'] = 1
142
+
143
+ # Store the DXF colors separately since them may be reversed below.
144
+ @dxf_formats.each do |format|
145
+ next unless format.fill_style.pattern != 0 || format.fill_style.bg_color != 0 || format.fill_style.fg_color != 0
146
+
147
+ format.has_dxf_fill(true)
148
+ format.dxf_bg_color = format.fill_style.bg_color
149
+ format.dxf_fg_color = format.fill_style.fg_color
150
+ end
151
+
152
+ @xf_formats.each do |format|
153
+ # The following logical statements jointly take care of special cases
154
+ # in relation to cell colours and patterns:
155
+ # 1. For a solid fill (_pattern == 1) Excel reverses the role of
156
+ # foreground and background colours, and
157
+ # 2. If the user specifies a foreground or background colour without
158
+ # a pattern they probably wanted a solid fill, so we fill in the
159
+ # defaults.
160
+ #
161
+ if format.fill_style.pattern == 1 && ne_0?(format.fill_style.bg_color) && ne_0?(format.fill_style.fg_color)
162
+ format.fill_style.fg_color, format.fill_style.bg_color = format.fill_style.bg_color, format.fill_style.fg_color
163
+ elsif format.fill_style.pattern <= 1 && ne_0?(format.fill_style.bg_color) && eq_0?(format.fill_style.fg_color)
164
+ format.fill_style.fg_color = format.fill_style.bg_color
165
+ format.fill_style.bg_color = 0
166
+ format.fill_style.pattern = 1
167
+ elsif format.fill_style.pattern <= 1 && eq_0?(format.fill_style.bg_color) && ne_0?(format.fill_style.fg_color)
168
+ format.fill_style.bg_color = 0
169
+ format.fill_style.pattern = 1
170
+ end
171
+
172
+ key = format.get_fill_key
173
+
174
+ if fills[key]
175
+ # Fill has already been used.
176
+ format.fill_index = fills[key]
177
+ format.has_fill(false)
178
+ else
179
+ # This is a new fill.
180
+ fills[key] = index
181
+ format.fill_index = index
182
+ format.has_fill(true)
183
+ index += 1
184
+ end
185
+ end
186
+
187
+ @fill_count = index
188
+ end
189
+
190
+ def eq_0?(val)
191
+ !ptrue?(val)
192
+ end
193
+
194
+ def ne_0?(val)
195
+ !eq_0?(val)
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,223 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Workbook
6
+ #
7
+ # Workbook initialization helpers extracted from Workbook to keep the main
8
+ # class focused on public API and orchestration.
9
+ #
10
+ module Initialization
11
+ private
12
+
13
+ def setup_core_state(file, options, default_formats)
14
+ @options = options.dup # for test
15
+ @default_formats = default_formats.dup # for test
16
+ @writer = Package::XMLWriterSimple.new
17
+
18
+ @file = file
19
+ @tempdir = options[:tempdir] ||
20
+ File.join(
21
+ Dir.tmpdir,
22
+ Digest::MD5.hexdigest("#{Time.now.to_f}-#{Process.pid}")
23
+ )
24
+
25
+ @date_1904 = options[:date_1904] || false
26
+ @activesheet = 0
27
+ @firstsheet = 0
28
+ @selected = 0
29
+ @fileclosed = false
30
+ @optimization = options[:optimization] || 0
31
+ @excel2003_style = options[:excel2003_style] || false
32
+ @read_only = 0
33
+
34
+ @strings_to_urls =
35
+ options[:strings_to_urls].nil? || options[:strings_to_urls] ? true : false
36
+
37
+ @max_url_length = if options[:max_url_length]
38
+ [options[:max_url_length].to_i, 255].max
39
+ else
40
+ MAX_URL_LENGTH
41
+ end
42
+ end
43
+
44
+ def setup_workbook_state(_options)
45
+ @worksheets = Sheets.new
46
+ @charts = []
47
+ @drawings = []
48
+ @defined_names = []
49
+ @named_ranges = []
50
+ @custom_colors = []
51
+ @doc_properties = {}
52
+ @custom_properties = []
53
+ @image_types = {}
54
+ @images = []
55
+ @has_comments = false
56
+ @has_metadata = false
57
+ @has_embedded_images = false
58
+ @has_embedded_descriptions = false
59
+
60
+ @x_window = 240
61
+ @y_window = 15
62
+ @window_width = 16_095
63
+ @window_height = 9_660
64
+ @tab_ratio = 600
65
+ end
66
+
67
+ def setup_format_state(_default_formats)
68
+ @formats = Formats.new
69
+ @xf_formats = []
70
+ @dxf_formats = []
71
+ @num_formats = []
72
+ end
73
+
74
+ def setup_shared_strings
75
+ @shared_strings = Package::SharedStrings.new
76
+ end
77
+
78
+ def setup_embedded_assets
79
+ @embedded_image_indexes = {}
80
+ @embedded_images = []
81
+ end
82
+
83
+ def setup_calculation_state
84
+ @calc_id = 124519
85
+ @calc_mode = 'auto'
86
+ @calc_on_load = true
87
+ end
88
+
89
+ def setup_default_formats
90
+ if @excel2003_style
91
+ add_format(
92
+ @default_formats.merge(
93
+ xf_index: 0,
94
+ font_family: 0,
95
+ font: 'Arial',
96
+ size: 10,
97
+ theme: -1
98
+ )
99
+ )
100
+ else
101
+ add_format(@default_formats.merge(xf_index: 0))
102
+ end
103
+
104
+ # Add a default URL format.
105
+ @default_url_format = add_format(hyperlink: 1)
106
+ end
107
+
108
+ #
109
+ # Workbook の生成時のオプションハッシュを解析する
110
+ #
111
+ def process_workbook_options(*params)
112
+ case params.size
113
+ when 0
114
+ [{}, {}]
115
+ when 1 # one hash
116
+ options_keys = %i[tempdir date_1904 optimization excel2003_style strings_to_urls max_url_length]
117
+
118
+ hash = params.first
119
+ options = hash.select { |k, _v| options_keys.include?(k) }
120
+
121
+ default_format_properties =
122
+ hash[:default_format_properties] ||
123
+ hash.reject { |k, _v| options_keys.include?(k) }
124
+
125
+ [options, default_format_properties.dup]
126
+ when 2 # array which includes options and default_format_properties
127
+ options, default_format_properties = params
128
+ default_format_properties ||= {}
129
+
130
+ [options.dup, default_format_properties.dup]
131
+ end
132
+ end
133
+
134
+ def filename
135
+ setup_filename unless @filename
136
+ @filename
137
+ end
138
+
139
+ def fileobj
140
+ setup_filename unless @fileobj
141
+ @fileobj
142
+ end
143
+
144
+ def setup_filename # :nodoc:
145
+ if @file.respond_to?(:to_str) && @file != ''
146
+ @filename = @file
147
+ @fileobj = nil
148
+ elsif @file.respond_to?(:write)
149
+ @filename = File.join(tempdir, Digest::MD5.hexdigest(Time.now.to_s) + '.xlsx.tmp')
150
+ @fileobj = @file
151
+ else
152
+ raise "'#{@file}' must be valid filename String of IO object."
153
+ end
154
+ end
155
+
156
+ attr_reader :tempdir
157
+
158
+ #
159
+ # Sets the colour palette to the Excel defaults.
160
+ #
161
+ def set_color_palette # :nodoc:
162
+ @palette = [
163
+ [0x00, 0x00, 0x00, 0x00], # 8
164
+ [0xff, 0xff, 0xff, 0x00], # 9
165
+ [0xff, 0x00, 0x00, 0x00], # 10
166
+ [0x00, 0xff, 0x00, 0x00], # 11
167
+ [0x00, 0x00, 0xff, 0x00], # 12
168
+ [0xff, 0xff, 0x00, 0x00], # 13
169
+ [0xff, 0x00, 0xff, 0x00], # 14
170
+ [0x00, 0xff, 0xff, 0x00], # 15
171
+ [0x80, 0x00, 0x00, 0x00], # 16
172
+ [0x00, 0x80, 0x00, 0x00], # 17
173
+ [0x00, 0x00, 0x80, 0x00], # 18
174
+ [0x80, 0x80, 0x00, 0x00], # 19
175
+ [0x80, 0x00, 0x80, 0x00], # 20
176
+ [0x00, 0x80, 0x80, 0x00], # 21
177
+ [0xc0, 0xc0, 0xc0, 0x00], # 22
178
+ [0x80, 0x80, 0x80, 0x00], # 23
179
+ [0x99, 0x99, 0xff, 0x00], # 24
180
+ [0x99, 0x33, 0x66, 0x00], # 25
181
+ [0xff, 0xff, 0xcc, 0x00], # 26
182
+ [0xcc, 0xff, 0xff, 0x00], # 27
183
+ [0x66, 0x00, 0x66, 0x00], # 28
184
+ [0xff, 0x80, 0x80, 0x00], # 29
185
+ [0x00, 0x66, 0xcc, 0x00], # 30
186
+ [0xcc, 0xcc, 0xff, 0x00], # 31
187
+ [0x00, 0x00, 0x80, 0x00], # 32
188
+ [0xff, 0x00, 0xff, 0x00], # 33
189
+ [0xff, 0xff, 0x00, 0x00], # 34
190
+ [0x00, 0xff, 0xff, 0x00], # 35
191
+ [0x80, 0x00, 0x80, 0x00], # 36
192
+ [0x80, 0x00, 0x00, 0x00], # 37
193
+ [0x00, 0x80, 0x80, 0x00], # 38
194
+ [0x00, 0x00, 0xff, 0x00], # 39
195
+ [0x00, 0xcc, 0xff, 0x00], # 40
196
+ [0xcc, 0xff, 0xff, 0x00], # 41
197
+ [0xcc, 0xff, 0xcc, 0x00], # 42
198
+ [0xff, 0xff, 0x99, 0x00], # 43
199
+ [0x99, 0xcc, 0xff, 0x00], # 44
200
+ [0xff, 0x99, 0xcc, 0x00], # 45
201
+ [0xcc, 0x99, 0xff, 0x00], # 46
202
+ [0xff, 0xcc, 0x99, 0x00], # 47
203
+ [0x33, 0x66, 0xff, 0x00], # 48
204
+ [0x33, 0xcc, 0xcc, 0x00], # 49
205
+ [0x99, 0xcc, 0x00, 0x00], # 50
206
+ [0xff, 0xcc, 0x00, 0x00], # 51
207
+ [0xff, 0x99, 0x00, 0x00], # 52
208
+ [0xff, 0x66, 0x00, 0x00], # 53
209
+ [0x66, 0x66, 0x99, 0x00], # 54
210
+ [0x96, 0x96, 0x96, 0x00], # 55
211
+ [0x00, 0x33, 0x66, 0x00], # 56
212
+ [0x33, 0x99, 0x66, 0x00], # 57
213
+ [0x00, 0x33, 0x00, 0x00], # 58
214
+ [0x33, 0x33, 0x00, 0x00], # 59
215
+ [0x99, 0x33, 0x00, 0x00], # 60
216
+ [0x99, 0x33, 0x66, 0x00], # 61
217
+ [0x33, 0x33, 0x99, 0x00], # 62
218
+ [0x33, 0x33, 0x33, 0x00] # 63
219
+ ]
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,231 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Workbook
6
+ module PackagePreparation
7
+ private
8
+
9
+ #
10
+ # Assemble worksheets into a workbook.
11
+ #
12
+ def store_workbook # :nodoc:
13
+ # Add a default worksheet if non have been added.
14
+ add_worksheet if @worksheets.empty?
15
+
16
+ # Ensure that at least one worksheet has been selected.
17
+ @worksheets.visible_first.select if @activesheet == 0
18
+
19
+ # Set the active sheet.
20
+ @activesheet = @worksheets.visible_first.index if @activesheet == 0
21
+ @worksheets[@activesheet].activate
22
+
23
+ # Convert the SST strings data structure.
24
+ prepare_sst_string_data
25
+
26
+ # Prepare the worksheet VML elements such as comments and buttons.
27
+ prepare_vml_objects
28
+ # Set the defined names for the worksheets such as Print Titles.
29
+ prepare_defined_names
30
+ # Prepare the drawings, charts and images.
31
+ prepare_drawings
32
+ # Add cached data to charts.
33
+ add_chart_data
34
+
35
+ # Prepare the worksheet tables.
36
+ prepare_tables
37
+
38
+ # Prepare the metadata file links.
39
+ prepare_metadata
40
+
41
+ # Package the workbook.
42
+ packager = Package::Packager.new(self)
43
+ packager.set_package_dir(tempdir)
44
+ packager.create_package
45
+
46
+ # Free up the Packager object.
47
+ packager = nil
48
+
49
+ # Store the xlsx component files with the temp dir name removed.
50
+ ZipFileUtils.zip(tempdir.to_s, filename)
51
+
52
+ IO.copy_stream(filename, fileobj) if fileobj
53
+ delete_tempdir(tempdir)
54
+ end
55
+
56
+ #
57
+ # Iterate through the worksheets and store any defined names in addition to
58
+ # any user defined names. Stores the defined names for the Workbook.xml and
59
+ # the named ranges for App.xml.
60
+ #
61
+ def prepare_defined_names # :nodoc:
62
+ @worksheets.each do |sheet|
63
+ # Check for Print Area settings.
64
+ if sheet.autofilter_area
65
+ @defined_names << [
66
+ '_xlnm._FilterDatabase',
67
+ sheet.index,
68
+ sheet.autofilter_area,
69
+ 1
70
+ ]
71
+ end
72
+
73
+ # Check for Print Area settings.
74
+ unless sheet.print_area.empty?
75
+ @defined_names << [
76
+ '_xlnm.Print_Area',
77
+ sheet.index,
78
+ sheet.print_area
79
+ ]
80
+ end
81
+
82
+ # Check for repeat rows/cols. aka, Print Titles.
83
+ next unless !sheet.print_repeat_cols.empty? || !sheet.print_repeat_rows.empty?
84
+
85
+ range = if !sheet.print_repeat_cols.empty? && !sheet.print_repeat_rows.empty?
86
+ sheet.print_repeat_cols + ',' + sheet.print_repeat_rows
87
+ else
88
+ sheet.print_repeat_cols + sheet.print_repeat_rows
89
+ end
90
+
91
+ # Store the defined names.
92
+ @defined_names << ['_xlnm.Print_Titles', sheet.index, range]
93
+ end
94
+
95
+ @defined_names = sort_defined_names(@defined_names)
96
+ @named_ranges = extract_named_ranges(@defined_names)
97
+ end
98
+
99
+ #
100
+ # Iterate through the worksheets and set up the VML objects.
101
+ #
102
+ def prepare_vml_objects # :nodoc:
103
+ comment_id = 0
104
+ vml_drawing_id = 0
105
+ vml_data_id = 1
106
+ vml_header_id = 0
107
+ vml_shape_id = 1024
108
+ has_button = false
109
+
110
+ @worksheets.each do |sheet|
111
+ next if !sheet.has_vml? && !sheet.has_header_vml?
112
+
113
+ if sheet.has_vml?
114
+ if sheet.has_comments?
115
+ comment_id += 1
116
+ @has_comments = true
117
+ end
118
+ vml_drawing_id += 1
119
+
120
+ sheet.prepare_vml_objects(
121
+ vml_data_id, vml_shape_id,
122
+ vml_drawing_id, comment_id
123
+ )
124
+
125
+ # Each VML file should start with a shape id incremented by 1024.
126
+ vml_data_id += 1 * (1 + sheet.num_comments_block)
127
+ vml_shape_id += 1024 * (1 + sheet.num_comments_block)
128
+ end
129
+
130
+ if sheet.has_header_vml?
131
+ vml_header_id += 1
132
+ vml_drawing_id += 1
133
+ sheet.prepare_header_vml_objects(vml_header_id, vml_drawing_id)
134
+ end
135
+
136
+ # Set the sheet vba_codename if it has a button and the workbook
137
+ # has a vbaProject binary.
138
+ unless sheet.buttons_data.empty?
139
+ has_button = true
140
+ sheet.set_vba_name if @vba_project && !sheet.vba_codename
141
+ end
142
+ end
143
+
144
+ # Set the workbook vba_codename if one of the sheets has a button and
145
+ # the workbook has a vbaProject binary.
146
+ set_vba_name if has_button && @vba_project && !@vba_codename
147
+ end
148
+
149
+ #
150
+ # Set the table ids for the worksheet tables.
151
+ #
152
+ def prepare_tables
153
+ table_id = 0
154
+ seen = {}
155
+
156
+ sheets.each do |sheet|
157
+ table_id += sheet.prepare_tables(table_id + 1, seen)
158
+ end
159
+ end
160
+
161
+ #
162
+ # Set the metadata rel link.
163
+ #
164
+ def prepare_metadata
165
+ @worksheets.each do |sheet|
166
+ next unless sheet.has_dynamic_functions? || sheet.has_embedded_images?
167
+
168
+ @has_metadata = true
169
+ @has_dynamic_functions ||= sheet.has_dynamic_functions?
170
+ @has_embedded_images ||= sheet.has_embedded_images?
171
+ end
172
+ end
173
+
174
+ #
175
+ # Iterate through the worksheets and set up any chart or image drawings.
176
+ #
177
+ def prepare_drawings # :nodoc:
178
+ # Store the image types for any embedded images.
179
+ @embedded_images.each do |image|
180
+ store_image_types(image.type)
181
+
182
+ @has_embedded_descriptions = true if ptrue?(image.description)
183
+ end
184
+
185
+ prepare_drawings_of_all_sheets
186
+
187
+ # Sort the workbook charts references into the order that the were
188
+ # written from the worksheets above.
189
+ @charts = @charts.reject { |chart| chart.id == -1 }
190
+ .sort_by(&:id)
191
+ end
192
+
193
+ def prepare_drawings_of_all_sheets
194
+ drawing_id = 0
195
+ chart_ref_id = 0
196
+ image_ids = {}
197
+ header_image_ids = {}
198
+ background_ids = {}
199
+
200
+ # The image IDs start from after the embedded images.
201
+ image_ref_id = @embedded_images.size
202
+
203
+ @worksheets.each do |sheet|
204
+ drawing_id, chart_ref_id, image_ref_id =
205
+ sheet.prepare_drawings(
206
+ drawing_id, chart_ref_id, image_ref_id, image_ids,
207
+ header_image_ids, background_ids
208
+ )
209
+ end
210
+ end
211
+
212
+ #
213
+ # prepare_sst_string_data
214
+ #
215
+ def prepare_sst_string_data; end
216
+
217
+ def delete_tempdir(path)
218
+ if FileTest.file?(path)
219
+ File.delete(path)
220
+ elsif FileTest.directory?(path)
221
+ Dir.foreach(path) do |file|
222
+ next if file =~ /^\.\.?$/ # '.' or '..'
223
+
224
+ delete_tempdir(path.sub(%r{/+$}, "") + '/' + file)
225
+ end
226
+ Dir.rmdir(path)
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end