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.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -0
- data/Changes +3 -0
- data/README.md +1 -1
- data/lib/write_xlsx/chartsheet.rb +4 -1
- data/lib/write_xlsx/object_positioning.rb +189 -0
- data/lib/write_xlsx/package/app.rb +1 -1
- data/lib/write_xlsx/page_setup.rb +190 -0
- data/lib/write_xlsx/sheets.rb +3 -3
- data/lib/write_xlsx/utility.rb +57 -9
- data/lib/write_xlsx/version.rb +1 -1
- data/lib/write_xlsx/workbook.rb +34 -34
- data/lib/write_xlsx/worksheet/asset_manager.rb +60 -0
- data/lib/write_xlsx/worksheet/autofilter.rb +388 -0
- data/lib/write_xlsx/worksheet/cell_data.rb +8 -5
- data/lib/write_xlsx/worksheet/cell_data_manager.rb +47 -0
- data/lib/write_xlsx/worksheet/cell_data_store.rb +61 -0
- data/lib/write_xlsx/worksheet/columns.rb +199 -0
- data/lib/write_xlsx/worksheet/comments_support.rb +61 -0
- data/lib/write_xlsx/worksheet/conditional_formats.rb +30 -0
- data/lib/write_xlsx/worksheet/data_writing.rb +990 -0
- data/lib/write_xlsx/worksheet/drawing_methods.rb +308 -0
- data/lib/write_xlsx/worksheet/drawing_preparation.rb +290 -0
- data/lib/write_xlsx/worksheet/drawing_relations.rb +76 -0
- data/lib/write_xlsx/worksheet/drawing_xml_writer.rb +50 -0
- data/lib/write_xlsx/worksheet/formatting.rb +416 -0
- data/lib/write_xlsx/worksheet/initialization.rb +146 -0
- data/lib/write_xlsx/worksheet/panes.rb +64 -0
- data/lib/write_xlsx/worksheet/print_options.rb +72 -0
- data/lib/write_xlsx/worksheet/protection.rb +65 -0
- data/lib/write_xlsx/worksheet/rich_text_helpers.rb +78 -0
- data/lib/write_xlsx/worksheet/row_col_sizing.rb +67 -0
- data/lib/write_xlsx/worksheet/rows.rb +84 -0
- data/lib/write_xlsx/worksheet/selection.rb +41 -0
- data/lib/write_xlsx/worksheet/xml_writer.rb +1241 -0
- data/lib/write_xlsx/worksheet.rb +359 -4530
- metadata +26 -3
- 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
|