write_xlsx 1.12.3 → 1.13.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/Changes +3 -0
  4. data/README.md +1 -1
  5. data/lib/write_xlsx/chartsheet.rb +4 -1
  6. data/lib/write_xlsx/object_positioning.rb +189 -0
  7. data/lib/write_xlsx/package/app.rb +1 -1
  8. data/lib/write_xlsx/page_setup.rb +190 -0
  9. data/lib/write_xlsx/sheets.rb +3 -3
  10. data/lib/write_xlsx/utility.rb +57 -9
  11. data/lib/write_xlsx/version.rb +1 -1
  12. data/lib/write_xlsx/workbook.rb +34 -34
  13. data/lib/write_xlsx/worksheet/asset_manager.rb +60 -0
  14. data/lib/write_xlsx/worksheet/autofilter.rb +388 -0
  15. data/lib/write_xlsx/worksheet/cell_data.rb +8 -5
  16. data/lib/write_xlsx/worksheet/cell_data_manager.rb +47 -0
  17. data/lib/write_xlsx/worksheet/cell_data_store.rb +61 -0
  18. data/lib/write_xlsx/worksheet/columns.rb +199 -0
  19. data/lib/write_xlsx/worksheet/comments_support.rb +61 -0
  20. data/lib/write_xlsx/worksheet/conditional_formats.rb +30 -0
  21. data/lib/write_xlsx/worksheet/data_writing.rb +990 -0
  22. data/lib/write_xlsx/worksheet/drawing_methods.rb +308 -0
  23. data/lib/write_xlsx/worksheet/drawing_preparation.rb +290 -0
  24. data/lib/write_xlsx/worksheet/drawing_relations.rb +76 -0
  25. data/lib/write_xlsx/worksheet/drawing_xml_writer.rb +50 -0
  26. data/lib/write_xlsx/worksheet/formatting.rb +416 -0
  27. data/lib/write_xlsx/worksheet/initialization.rb +146 -0
  28. data/lib/write_xlsx/worksheet/panes.rb +64 -0
  29. data/lib/write_xlsx/worksheet/print_options.rb +72 -0
  30. data/lib/write_xlsx/worksheet/protection.rb +65 -0
  31. data/lib/write_xlsx/worksheet/rich_text_helpers.rb +78 -0
  32. data/lib/write_xlsx/worksheet/row_col_sizing.rb +67 -0
  33. data/lib/write_xlsx/worksheet/rows.rb +84 -0
  34. data/lib/write_xlsx/worksheet/selection.rb +41 -0
  35. data/lib/write_xlsx/worksheet/xml_writer.rb +1241 -0
  36. data/lib/write_xlsx/worksheet.rb +359 -4530
  37. metadata +26 -3
  38. data/lib/write_xlsx/worksheet/page_setup.rb +0 -192
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Writexlsx
4
+ class Worksheet
5
+ # Column-related operations extracted from Worksheet to slim the main class.
6
+ module Columns
7
+ # :call-seq:
8
+ # set_column(firstcol, lastcol, width, format, hidden, level, collapsed)
9
+ #
10
+ # This method can be used to change the default properties of a single
11
+ # column or a range of columns. All parameters apart from +first_col+
12
+ # and +last_col+ are optional.
13
+ def set_column(*args)
14
+ # Check for a cell reference in A1 notation and substitute row and column
15
+ # ruby 3.2 no longer handles =~ for various types
16
+ if args[0].respond_to?(:=~) && args[0].to_s =~ /^\D/
17
+ _row1, firstcol, _row2, lastcol, *data = substitute_cellref(*args)
18
+ else
19
+ firstcol, lastcol, *data = args
20
+ end
21
+
22
+ # Ensure at least firstcol, lastcol and width
23
+ return unless firstcol && lastcol && !data.empty?
24
+
25
+ # Assume second column is the same as first if 0. Avoids KB918419 bug.
26
+ lastcol = firstcol unless ptrue?(lastcol)
27
+
28
+ # Ensure 2nd col is larger than first. Also for KB918419 bug.
29
+ firstcol, lastcol = lastcol, firstcol if firstcol > lastcol
30
+
31
+ width, format, hidden, level, collapsed = data
32
+ autofit = 0
33
+
34
+ # Check that cols are valid and store max and min values with default row.
35
+ # NOTE: The check shouldn't modify the row dimensions and should only modify
36
+ # the column dimensions in certain cases.
37
+ ignore_row = 1
38
+ ignore_col = 1
39
+ ignore_col = 0 if format.respond_to?(:xf_index) # Column has a format.
40
+ ignore_col = 0 if width && ptrue?(hidden) # Column has a width but is hidden
41
+
42
+ check_dimensions_and_update_max_min_values(0, firstcol, ignore_row, ignore_col)
43
+ check_dimensions_and_update_max_min_values(0, lastcol, ignore_row, ignore_col)
44
+
45
+ # Set the limits for the outline levels (0 <= x <= 7).
46
+ level ||= 0
47
+ level = 0 if level < 0
48
+ level = 7 if level > 7
49
+
50
+ # Excel has a maximum column width of 255 characters.
51
+ width = 255.0 if width && width > 255.0
52
+
53
+ @outline_col_level = level if level > @outline_col_level
54
+
55
+ # Store the column data based on the first column. Padded for sorting.
56
+ (firstcol..lastcol).each do |col|
57
+ @col_info[col] =
58
+ COLINFO.new(width, format, hidden, level, collapsed, autofit)
59
+ end
60
+
61
+ # Store the column change to allow optimisations.
62
+ @col_size_changed = true
63
+ end
64
+
65
+ #
66
+ # Set the width (and properties) of a single column or a range of columns in
67
+ # pixels rather than character units.
68
+ #
69
+ def set_column_pixels(*data)
70
+ cell = data[0]
71
+
72
+ # Check for a cell reference in A1 notation and substitute row and column
73
+ if cell =~ /^\D/
74
+ data = substitute_cellref(*data)
75
+
76
+ # Returned values row1 and row2 aren't required here. Remove them.
77
+ data.shift # $row1
78
+ data.delete_at(1) # $row2
79
+ end
80
+
81
+ # Ensure at least $first_col, $last_col and $width
82
+ return if data.size < 3
83
+
84
+ first_col, last_col, pixels, format, hidden, level = data
85
+ hidden ||= 0
86
+
87
+ width = pixels_to_width(pixels) if ptrue?(pixels)
88
+
89
+ set_column(first_col, last_col, width, format, hidden, level)
90
+ end
91
+
92
+ #
93
+ # autofit()
94
+ #
95
+ # Simulate autofit based on the data, and datatypes in each column. We do this
96
+ # by estimating a pixel width for each cell data.
97
+ #
98
+ def autofit
99
+ col_width = {}
100
+
101
+ # Iterate through all the data in the worksheet.
102
+ (@dim_rowmin..@dim_rowmax).each do |row_num|
103
+ # Skip row if it doesn't contain cell data.
104
+ next unless @cell_data_store[row_num]
105
+
106
+ (@dim_colmin..@dim_colmax).each do |col_num|
107
+ length = 0
108
+ case (cell_data = @cell_data_store[row_num][col_num])
109
+ when StringCellData, RichStringCellData
110
+ # Handle strings and rich strings.
111
+ #
112
+ # For standard shared strings we do a reverse lookup
113
+ # from the shared string id to the actual string. For
114
+ # rich strings we use the unformatted string. We also
115
+ # split multiline strings and handle each part
116
+ # separately.
117
+ string = cell_data.raw_string
118
+
119
+ length = if string =~ /\n/
120
+ # Handle multiline strings.
121
+ max = string.split("\n").collect do |str|
122
+ xl_string_pixel_width(str)
123
+ end.max
124
+ else
125
+ xl_string_pixel_width(string)
126
+ end
127
+ when DateTimeCellData
128
+
129
+ # Handle dates.
130
+ #
131
+ # The following uses the default width for mm/dd/yyyy
132
+ # dates. It isn't feasible to parse the number format
133
+ # to get the actual string width for all format types.
134
+ length = @default_date_pixels
135
+ when NumberCellData
136
+
137
+ # Handle numbers.
138
+ #
139
+ # We use a workaround/optimization for numbers since
140
+ # digits all have a pixel width of 7. This gives a
141
+ # slightly greater width for the decimal place and
142
+ # minus sign but only by a few pixels and
143
+ # over-estimation is okay.
144
+ length = 7 * cell_data.token.to_s.length
145
+ when BooleanCellData
146
+
147
+ # Handle boolean values.
148
+ #
149
+ # Use the Excel standard widths for TRUE and FALSE.
150
+ length = if ptrue?(cell_data.token)
151
+ 31
152
+ else
153
+ 36
154
+ end
155
+ when FormulaCellData, FormulaArrayCellData, DynamicFormulaArrayCellData
156
+ # Handle formulas.
157
+ #
158
+ # We only try to autofit a formula if it has a
159
+ # non-zero value.
160
+ if ptrue?(cell_data.data)
161
+ length = xl_string_pixel_width(cell_data.data)
162
+ end
163
+ end
164
+
165
+ # If the cell is in an autofilter header we add an
166
+ # additional 16 pixels for the dropdown arrow.
167
+ if length > 0 &&
168
+ @filter_cells["#{row_num}:#{col_num}"]
169
+ length += 16
170
+ end
171
+
172
+ # Add the string lenght to the lookup hash.
173
+ max = col_width[col_num] || 0
174
+ col_width[col_num] = length if length > max
175
+ end
176
+ end
177
+
178
+ # Apply the width to the column.
179
+ col_width.each do |col_num, pixel_width|
180
+ # Convert the string pixel width to a character width using an
181
+ # additional padding of 7 pixels, like Excel.
182
+ width = pixels_to_width(pixel_width + 7)
183
+
184
+ # The max column character width in Excel is 255.
185
+ width = 255.0 if width > 255.0
186
+
187
+ # Add the width to an existing col info structure or add a new one.
188
+ if @col_info[col_num]
189
+ @col_info[col_num].width = width
190
+ @col_info[col_num].autofit = 1
191
+ else
192
+ @col_info[col_num] =
193
+ COLINFO.new(width, nil, 0, 0, 0, 1)
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,61 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Worksheet
6
+ module CommentsSupport
7
+ #
8
+ # This method is used to make all cell comments visible when a worksheet
9
+ # is opened.
10
+ #
11
+ def show_comments(visible = true)
12
+ @comments_visible = visible
13
+ end
14
+
15
+ def comments_visible? # :nodoc:
16
+ !!@comments_visible
17
+ end
18
+
19
+ #
20
+ # This method is used to set the default author of all cell comments.
21
+ #
22
+ def comments_author=(author)
23
+ @comments_author = author || ''
24
+ end
25
+
26
+ # This method is deprecated. use comments_author=().
27
+ def set_comments_author(author)
28
+ put_deprecate_message("#{self}.set_comments_author")
29
+ self.comments_author = author
30
+ end
31
+
32
+ def sorted_comments # :nodoc:
33
+ @comments.sorted_comments
34
+ end
35
+
36
+ def num_comments_block
37
+ @comments.size / 1024
38
+ end
39
+
40
+ def has_comments? # :nodoc:
41
+ !@comments.empty?
42
+ end
43
+
44
+ def has_vml? # :nodoc:
45
+ @has_vml
46
+ end
47
+
48
+ def has_header_vml? # :nodoc:
49
+ !(header_images.empty? && footer_images.empty?)
50
+ end
51
+
52
+ def buttons_data # :nodoc:
53
+ @buttons_array
54
+ end
55
+
56
+ def header_images_data # :nodoc:
57
+ @header_images_array
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Writexlsx
4
+ class Worksheet
5
+ # Conditional formatting operations extracted from Worksheet to slim the main class.
6
+ module ConditionalFormats
7
+ #
8
+ # :call-seq:
9
+ # conditional_formatting(cell_or_cell_range, options)
10
+ #
11
+ # Conditional formatting is a feature of Excel which allows you to apply a
12
+ # format to a cell or a range of cells based on a certain criteria.
13
+ #
14
+ def conditional_formatting(*args)
15
+ cond_format = Package::ConditionalFormat.factory(self, *args)
16
+ @cond_formats[cond_format.range] ||= []
17
+ @cond_formats[cond_format.range] << cond_format
18
+ end
19
+
20
+ #
21
+ # Write the <conditionalFormatting> element.
22
+ #
23
+ def write_conditional_formatting(range, cond_formats) # :nodoc:
24
+ @writer.tag_elements('conditionalFormatting', [['sqref', range]]) do
25
+ cond_formats.each(&:write_cf_rule)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end