write_xlsx 1.12.0 → 1.12.2
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 +12 -0
- data/Changes +33 -0
- data/LICENSE.txt +1 -1
- data/examples/autofilter.rb +1 -2
- data/examples/colors.rb +4 -4
- data/examples/formats.rb +14 -14
- data/lib/write_xlsx/chart/area.rb +1 -1
- data/lib/write_xlsx/chart/axis.rb +4 -4
- data/lib/write_xlsx/chart/bar.rb +1 -1
- data/lib/write_xlsx/chart/caption.rb +8 -4
- data/lib/write_xlsx/chart/column.rb +1 -1
- data/lib/write_xlsx/chart/doughnut.rb +2 -2
- data/lib/write_xlsx/chart/line.rb +1 -1
- data/lib/write_xlsx/chart/pie.rb +2 -2
- data/lib/write_xlsx/chart/radar.rb +1 -1
- data/lib/write_xlsx/chart/scatter.rb +1 -1
- data/lib/write_xlsx/chart/series.rb +10 -20
- data/lib/write_xlsx/chart/stock.rb +1 -1
- data/lib/write_xlsx/chart.rb +14 -21
- data/lib/write_xlsx/chartsheet.rb +3 -3
- data/lib/write_xlsx/drawing.rb +108 -114
- data/lib/write_xlsx/format.rb +20 -24
- data/lib/write_xlsx/image.rb +89 -0
- data/lib/write_xlsx/image_property.rb +163 -0
- data/lib/write_xlsx/inserted_chart.rb +42 -0
- data/lib/write_xlsx/package/button.rb +58 -5
- data/lib/write_xlsx/package/conditional_format.rb +4 -4
- data/lib/write_xlsx/package/packager.rb +22 -27
- data/lib/write_xlsx/package/rich_value.rb +1 -1
- data/lib/write_xlsx/package/styles.rb +1 -1
- data/lib/write_xlsx/package/vml.rb +10 -19
- data/lib/write_xlsx/shape.rb +3 -2
- data/lib/write_xlsx/sparkline.rb +1 -1
- data/lib/write_xlsx/utility.rb +8 -203
- data/lib/write_xlsx/version.rb +1 -1
- data/lib/write_xlsx/workbook.rb +87 -175
- data/lib/write_xlsx/worksheet/data_validation.rb +1 -1
- data/lib/write_xlsx/worksheet/hyperlink.rb +2 -2
- data/lib/write_xlsx/worksheet.rb +478 -484
- data/lib/write_xlsx/zip_file_utils.rb +1 -1
- data/write_xlsx.gemspec +3 -3
- metadata +11 -11
data/lib/write_xlsx/worksheet.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require 'write_xlsx/package/xml_writer_simple'
|
5
|
-
require 'write_xlsx/package/button'
|
6
4
|
require 'write_xlsx/colors'
|
7
|
-
require 'write_xlsx/
|
5
|
+
require 'write_xlsx/compatibility'
|
8
6
|
require 'write_xlsx/drawing'
|
7
|
+
require 'write_xlsx/format'
|
8
|
+
require 'write_xlsx/image'
|
9
|
+
require 'write_xlsx/image_property'
|
10
|
+
require 'write_xlsx/inserted_chart'
|
11
|
+
require 'write_xlsx/package/button'
|
12
|
+
require 'write_xlsx/package/conditional_format'
|
13
|
+
require 'write_xlsx/package/xml_writer_simple'
|
9
14
|
require 'write_xlsx/sparkline'
|
10
|
-
require 'write_xlsx/compatibility'
|
11
15
|
require 'write_xlsx/utility'
|
12
|
-
require 'write_xlsx/package/conditional_format'
|
13
16
|
require 'write_xlsx/worksheet/cell_data'
|
14
17
|
require 'write_xlsx/worksheet/data_validation'
|
15
18
|
require 'write_xlsx/worksheet/hyperlink'
|
@@ -21,24 +24,23 @@ module Writexlsx
|
|
21
24
|
class Worksheet
|
22
25
|
include Writexlsx::Utility
|
23
26
|
|
24
|
-
|
25
|
-
PADDING = 5 # :nodoc:
|
26
|
-
COLINFO = Struct.new('ColInfo', :width, :format, :hidden, :level, :collapsed, :autofit)
|
27
|
+
COLINFO = Struct.new('ColInfo', :width, :format, :hidden, :level, :collapsed, :autofit)
|
27
28
|
|
28
|
-
attr_reader :index
|
29
|
+
attr_reader :index, :name # :nodoc:
|
29
30
|
attr_reader :charts, :images, :tables, :shapes, :drawings # :nodoc:
|
30
31
|
attr_reader :header_images, :footer_images, :background_image # :nodoc:
|
31
32
|
attr_reader :vml_drawing_links # :nodoc:
|
32
33
|
attr_reader :vml_data_id # :nodoc:
|
33
34
|
attr_reader :vml_header_id # :nodoc:
|
34
35
|
attr_reader :autofilter_area # :nodoc:
|
35
|
-
attr_reader :writer, :set_rows, :col_info
|
36
|
+
attr_reader :writer, :set_rows, :col_info, :row_sizes # :nodoc:
|
36
37
|
attr_reader :vml_shape_id # :nodoc:
|
37
38
|
attr_reader :comments, :comments_author # :nodoc:
|
38
39
|
attr_accessor :data_bars_2010, :dxf_priority # :nodoc:
|
39
40
|
attr_reader :vba_codename # :nodoc:
|
40
41
|
attr_writer :excel_version # :nodoc:
|
41
42
|
attr_reader :filter_cells # :nodoc:
|
43
|
+
attr_accessor :default_row_height # :nodoc:
|
42
44
|
|
43
45
|
def initialize(workbook, index, name) # :nodoc:
|
44
46
|
rowmax = 1_048_576
|
@@ -78,6 +80,7 @@ module Writexlsx
|
|
78
80
|
|
79
81
|
@set_cols = {}
|
80
82
|
@set_rows = {}
|
83
|
+
@col_size_changed = false
|
81
84
|
@zoom = 100
|
82
85
|
@zoom_scale_normal = true
|
83
86
|
@right_to_left = false
|
@@ -94,7 +97,6 @@ module Writexlsx
|
|
94
97
|
|
95
98
|
@last_shape_id = 1
|
96
99
|
@rel_count = 0
|
97
|
-
@hlink_count = 0
|
98
100
|
@external_hyper_links = []
|
99
101
|
@external_drawing_links = []
|
100
102
|
@external_comment_links = []
|
@@ -116,27 +118,25 @@ module Writexlsx
|
|
116
118
|
@has_dynamic_functions = false
|
117
119
|
@has_embedded_images = false
|
118
120
|
|
119
|
-
@use_future_functions
|
121
|
+
@use_future_functions = false
|
120
122
|
|
121
123
|
@header_images = []
|
122
124
|
@footer_images = []
|
123
|
-
@background_image =
|
125
|
+
@background_image = nil
|
124
126
|
|
125
|
-
@outline_row_level
|
126
|
-
@outline_col_level
|
127
|
+
@outline_row_level = 0
|
128
|
+
@outline_col_level = 0
|
127
129
|
|
128
130
|
@original_row_height = 15
|
129
131
|
@default_row_height = 15
|
130
132
|
@default_row_pixels = 20
|
131
133
|
@default_col_width = 8.43
|
132
|
-
@default_col_pixels = 64
|
133
134
|
@default_row_rezoed = 0
|
134
135
|
@default_date_pixels = 68
|
135
136
|
|
136
137
|
@merge = []
|
137
138
|
|
138
|
-
@has_vml
|
139
|
-
@has_header_vml = false
|
139
|
+
@has_vml = false
|
140
140
|
@comments = Package::Comments.new(self)
|
141
141
|
@buttons_array = []
|
142
142
|
@header_images_array = []
|
@@ -155,8 +155,8 @@ module Writexlsx
|
|
155
155
|
@original_row_height = 12.75
|
156
156
|
@default_row_height = 12.75
|
157
157
|
@default_row_pixels = 17
|
158
|
-
self.margins_left_right
|
159
|
-
self.margins_top_bottom
|
158
|
+
self.margins_left_right = 0.75
|
159
|
+
self.margins_top_bottom = 1
|
160
160
|
@page_setup.margin_header = 0.5
|
161
161
|
@page_setup.margin_footer = 0.5
|
162
162
|
@page_setup.header_footer_aligns = false
|
@@ -204,11 +204,6 @@ module Writexlsx
|
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
207
|
-
#
|
208
|
-
# The name method is used to retrieve the name of a worksheet.
|
209
|
-
#
|
210
|
-
attr_reader :name
|
211
|
-
|
212
207
|
#
|
213
208
|
# Set this worksheet as a selected worksheet, i.e. the worksheet has its tab
|
214
209
|
# highlighted.
|
@@ -296,29 +291,6 @@ module Writexlsx
|
|
296
291
|
@protected_ranges << [range, range_name, password]
|
297
292
|
end
|
298
293
|
|
299
|
-
def protect_default_settings # :nodoc:
|
300
|
-
{
|
301
|
-
sheet: true,
|
302
|
-
content: false,
|
303
|
-
objects: false,
|
304
|
-
scenarios: false,
|
305
|
-
format_cells: false,
|
306
|
-
format_columns: false,
|
307
|
-
format_rows: false,
|
308
|
-
insert_columns: false,
|
309
|
-
insert_rows: false,
|
310
|
-
insert_hyperlinks: false,
|
311
|
-
delete_columns: false,
|
312
|
-
delete_rows: false,
|
313
|
-
select_locked_cells: true,
|
314
|
-
sort: false,
|
315
|
-
autofilter: false,
|
316
|
-
pivot_tables: false,
|
317
|
-
select_unlocked_cells: true
|
318
|
-
}
|
319
|
-
end
|
320
|
-
private :protect_default_settings
|
321
|
-
|
322
294
|
#
|
323
295
|
# :call-seq:
|
324
296
|
# set_column(firstcol, lastcol, width, format, hidden, level, collapsed)
|
@@ -376,7 +348,7 @@ module Writexlsx
|
|
376
348
|
end
|
377
349
|
|
378
350
|
# Store the column change to allow optimisations.
|
379
|
-
@col_size_changed =
|
351
|
+
@col_size_changed = true
|
380
352
|
end
|
381
353
|
|
382
354
|
#
|
@@ -398,12 +370,8 @@ module Writexlsx
|
|
398
370
|
# Ensure at least $first_col, $last_col and $width
|
399
371
|
return if data.size < 3
|
400
372
|
|
401
|
-
first_col = data
|
402
|
-
|
403
|
-
pixels = data[2]
|
404
|
-
format = data[3]
|
405
|
-
hidden = data[4] || 0
|
406
|
-
level = data[5]
|
373
|
+
first_col, last_col, pixels, format, hidden, level = data
|
374
|
+
hidden ||= 0
|
407
375
|
|
408
376
|
width = pixels_to_width(pixels) if ptrue?(pixels)
|
409
377
|
|
@@ -671,7 +639,10 @@ module Writexlsx
|
|
671
639
|
raise 'Header string must be less than 255 characters' if string.length > 255
|
672
640
|
|
673
641
|
# Replace the Excel placeholder &[Picture] with the internal &G.
|
674
|
-
|
642
|
+
header_footer_string = string.gsub("&[Picture]", '&G')
|
643
|
+
# placeholeder /&G/ の数
|
644
|
+
placeholder_count = header_footer_string.scan("&G").count
|
645
|
+
@page_setup.header = header_footer_string
|
675
646
|
|
676
647
|
@page_setup.header_footer_aligns = options[:align_with_margins] if options[:align_with_margins]
|
677
648
|
|
@@ -683,17 +654,13 @@ module Writexlsx
|
|
683
654
|
[
|
684
655
|
[:image_left, 'LH'], [:image_center, 'CH'], [:image_right, 'RH']
|
685
656
|
].each do |p|
|
686
|
-
@header_images <<
|
657
|
+
@header_images << ImageProperty.new(options[p.first], position: p.last) if options[p.first]
|
687
658
|
end
|
688
659
|
|
689
|
-
# placeholeder /&G/ の数
|
690
|
-
placeholder_count = @page_setup.header.scan("&G").count
|
691
|
-
|
692
|
-
image_count = @header_images.count
|
660
|
+
# # placeholeder /&G/ の数
|
661
|
+
# placeholder_count = @page_setup.header.scan("&G").count
|
693
662
|
|
694
|
-
raise "Number of header image (#{
|
695
|
-
|
696
|
-
@has_header_vml = true if image_count > 0
|
663
|
+
raise "Number of header image (#{@header_images.size}) doesn't match placeholder count (#{placeholder_count}) in string: #{@page_setup.header}" if @header_images.size != placeholder_count
|
697
664
|
|
698
665
|
@page_setup.margin_header = margin || 0.3
|
699
666
|
@page_setup.header_footer_changed = true
|
@@ -705,8 +672,6 @@ module Writexlsx
|
|
705
672
|
def set_footer(string = '', margin = 0.3, options = {})
|
706
673
|
raise 'Footer string must be less than 255 characters' if string.length > 255
|
707
674
|
|
708
|
-
@page_setup.footer = string.dup
|
709
|
-
|
710
675
|
# Replace the Excel placeholder &[Picture] with the internal &G.
|
711
676
|
@page_setup.footer = string.gsub("&[Picture]", '&G')
|
712
677
|
|
@@ -720,17 +685,13 @@ module Writexlsx
|
|
720
685
|
[
|
721
686
|
[:image_left, 'LF'], [:image_center, 'CF'], [:image_right, 'RF']
|
722
687
|
].each do |p|
|
723
|
-
@footer_images <<
|
688
|
+
@footer_images << ImageProperty.new(options[p.first], position: p.last) if options[p.first]
|
724
689
|
end
|
725
690
|
|
726
691
|
# placeholeder /&G/ の数
|
727
692
|
placeholder_count = @page_setup.footer.scan("&G").count
|
728
693
|
|
729
|
-
|
730
|
-
|
731
|
-
raise "Number of footer image (#{image_count}) doesn't match placeholder count (#{placeholder_count}) in string: #{@page_setup.footer}" if image_count != placeholder_count
|
732
|
-
|
733
|
-
@has_header_vml = true if image_count > 0
|
694
|
+
raise "Number of footer image (#{@footer_images.size}) doesn't match placeholder count (#{placeholder_count}) in string: #{@page_setup.footer}" if @footer_images.size != placeholder_count
|
734
695
|
|
735
696
|
@page_setup.margin_footer = margin
|
736
697
|
@page_setup.header_footer_changed = true
|
@@ -1327,18 +1288,6 @@ module Writexlsx
|
|
1327
1288
|
store_data_to_table(BlankCellData.new(_format), _row, _col)
|
1328
1289
|
end
|
1329
1290
|
|
1330
|
-
def expand_formula(formula, function, addition = '')
|
1331
|
-
if formula =~ /\b(#{function})/
|
1332
|
-
formula.gsub(
|
1333
|
-
::Regexp.last_match(1),
|
1334
|
-
"_xlfn#{addition}.#{::Regexp.last_match(1)}"
|
1335
|
-
)
|
1336
|
-
else
|
1337
|
-
formula
|
1338
|
-
end
|
1339
|
-
end
|
1340
|
-
private :expand_formula
|
1341
|
-
|
1342
1291
|
#
|
1343
1292
|
# Utility method to strip equal sign and array braces from a formula
|
1344
1293
|
# and also expand out future and dynamic array formulas.
|
@@ -1758,7 +1707,7 @@ module Writexlsx
|
|
1758
1707
|
# The outline_settings() method is used to control the appearance of
|
1759
1708
|
# outlines in Excel.
|
1760
1709
|
#
|
1761
|
-
def outline_settings(visible = 1, symbols_below = 1, symbols_right = 1, auto_style =
|
1710
|
+
def outline_settings(visible = 1, symbols_below = 1, symbols_right = 1, auto_style = false)
|
1762
1711
|
@outline_on = visible
|
1763
1712
|
@outline_below = symbols_below
|
1764
1713
|
@outline_right = symbols_right
|
@@ -1804,14 +1753,15 @@ module Writexlsx
|
|
1804
1753
|
_tip = tip
|
1805
1754
|
_ignore_write_string = ignore_write_string
|
1806
1755
|
end
|
1807
|
-
|
1756
|
+
|
1757
|
+
_format, _str = _str, _format if _str.respond_to?(:xf_index) || (_format && !_format.respond_to?(:xf_index))
|
1808
1758
|
raise WriteXLSXInsufficientArgumentError if [_row, _col, _url].include?(nil)
|
1809
1759
|
|
1810
1760
|
# Check that row and col are valid and store max and min values
|
1811
1761
|
check_dimensions(_row, _col)
|
1812
1762
|
store_row_col_max_min_values(_row, _col)
|
1813
1763
|
|
1814
|
-
hyperlink = Hyperlink.factory(_url, _str, _tip)
|
1764
|
+
hyperlink = Hyperlink.factory(_url, _str, _tip, @max_url_length)
|
1815
1765
|
store_hyperlink(_row, _col, hyperlink)
|
1816
1766
|
|
1817
1767
|
raise "URL '#{url}' added but URL exceeds Excel's limit of 65,530 URLs per worksheet." if hyperlinks_count > 65_530
|
@@ -1914,10 +1864,10 @@ module Writexlsx
|
|
1914
1864
|
x_offset = _chart.x_offset if ptrue?(_chart.x_offset)
|
1915
1865
|
y_offset = _chart.y_offset if ptrue?(_chart.y_offset)
|
1916
1866
|
|
1917
|
-
@charts <<
|
1867
|
+
@charts << InsertedChart.new(
|
1918
1868
|
_row, _col, _chart, x_offset, y_offset,
|
1919
1869
|
x_scale, y_scale, anchor, description, decorative
|
1920
|
-
|
1870
|
+
)
|
1921
1871
|
end
|
1922
1872
|
|
1923
1873
|
#
|
@@ -1959,10 +1909,10 @@ module Writexlsx
|
|
1959
1909
|
y_scale ||= 1
|
1960
1910
|
anchor ||= 2
|
1961
1911
|
|
1962
|
-
@images <<
|
1912
|
+
@images << Image.new(
|
1963
1913
|
_row, _col, _image, x_offset, y_offset,
|
1964
1914
|
x_scale, y_scale, url, tip, anchor, description, decorative
|
1965
|
-
|
1915
|
+
)
|
1966
1916
|
end
|
1967
1917
|
|
1968
1918
|
#
|
@@ -2005,18 +1955,19 @@ module Writexlsx
|
|
2005
1955
|
end
|
2006
1956
|
|
2007
1957
|
# Get the image properties, mainly for the type and checksum.
|
2008
|
-
|
2009
|
-
|
2010
|
-
|
1958
|
+
image_property = ImageProperty.new(
|
1959
|
+
image, description: description, decorative: decorative
|
1960
|
+
)
|
1961
|
+
@workbook.store_image_types(image_property.type)
|
2011
1962
|
|
2012
1963
|
# Check for duplicate images.
|
2013
|
-
image_index = @embedded_image_indexes[md5]
|
1964
|
+
image_index = @embedded_image_indexes[image_property.md5]
|
2014
1965
|
|
2015
1966
|
unless ptrue?(image_index)
|
2016
|
-
@workbook.embedded_images <<
|
1967
|
+
@workbook.embedded_images << image_property
|
2017
1968
|
|
2018
1969
|
image_index = @workbook.embedded_images.size
|
2019
|
-
@embedded_image_indexes[md5] = image_index
|
1970
|
+
@embedded_image_indexes[image_property.md5] = image_index
|
2020
1971
|
end
|
2021
1972
|
|
2022
1973
|
# Write the cell placeholder.
|
@@ -2024,6 +1975,76 @@ module Writexlsx
|
|
2024
1975
|
@has_embedded_images = true
|
2025
1976
|
end
|
2026
1977
|
|
1978
|
+
#
|
1979
|
+
# :call-seq:
|
1980
|
+
# insert_shape(row, col, shape [ , x, y, x_scale, y_scale ])
|
1981
|
+
#
|
1982
|
+
# Insert a shape into the worksheet.
|
1983
|
+
#
|
1984
|
+
def insert_shape(
|
1985
|
+
row_start, column_start, shape = nil, x_offset = nil, y_offset = nil,
|
1986
|
+
x_scale = nil, y_scale = nil, anchor = nil
|
1987
|
+
)
|
1988
|
+
# Check for a cell reference in A1 notation and substitute row and column.
|
1989
|
+
if (row_col_array = row_col_notation(row_start))
|
1990
|
+
_row_start, _column_start = row_col_array
|
1991
|
+
_shape = column_start
|
1992
|
+
_x_offset = shape
|
1993
|
+
_y_offset = x_offset
|
1994
|
+
_x_scale = y_offset
|
1995
|
+
_y_scale = x_scale
|
1996
|
+
_anchor = y_scale
|
1997
|
+
else
|
1998
|
+
_row_start = row_start
|
1999
|
+
_column_start = column_start
|
2000
|
+
_shape = shape
|
2001
|
+
_x_offset = x_offset
|
2002
|
+
_y_offset = y_offset
|
2003
|
+
_x_scale = x_scale
|
2004
|
+
_y_scale = y_scale
|
2005
|
+
_anchor = anchor
|
2006
|
+
end
|
2007
|
+
raise "Insufficient arguments in insert_shape()" if [_row_start, _column_start, _shape].include?(nil)
|
2008
|
+
|
2009
|
+
_shape.set_position(
|
2010
|
+
_row_start, _column_start, _x_offset, _y_offset,
|
2011
|
+
_x_scale, _y_scale, _anchor
|
2012
|
+
)
|
2013
|
+
# Assign a shape ID.
|
2014
|
+
while true
|
2015
|
+
id = _shape.id || 0
|
2016
|
+
used = @shape_hash[id]
|
2017
|
+
|
2018
|
+
# Test if shape ID is already used. Otherwise assign a new one.
|
2019
|
+
if !used && id != 0
|
2020
|
+
break
|
2021
|
+
else
|
2022
|
+
@last_shape_id += 1
|
2023
|
+
_shape.id = @last_shape_id
|
2024
|
+
end
|
2025
|
+
end
|
2026
|
+
|
2027
|
+
# Allow lookup of entry into shape array by shape ID.
|
2028
|
+
@shape_hash[_shape.id] = _shape.element = @shapes.size
|
2029
|
+
|
2030
|
+
insert = if ptrue?(_shape.stencil)
|
2031
|
+
# Insert a copy of the shape, not a reference so that the shape is
|
2032
|
+
# used as a stencil. Previously stamped copies don't get modified
|
2033
|
+
# if the stencil is modified.
|
2034
|
+
_shape.dup
|
2035
|
+
else
|
2036
|
+
_shape
|
2037
|
+
end
|
2038
|
+
|
2039
|
+
# For connectors change x/y coords based on location of connected shapes.
|
2040
|
+
insert.auto_locate_connectors(@shapes, @shape_hash)
|
2041
|
+
|
2042
|
+
# Insert a link to the shape on the list of shapes. Connection to
|
2043
|
+
# the parent shape is maintained.
|
2044
|
+
@shapes << insert
|
2045
|
+
insert
|
2046
|
+
end
|
2047
|
+
|
2027
2048
|
#
|
2028
2049
|
# :call-seq:
|
2029
2050
|
# repeat_formula(row, column, formula [ , format ])
|
@@ -2087,9 +2108,6 @@ module Writexlsx
|
|
2087
2108
|
level = args[4] || 0
|
2088
2109
|
collapsed = args[5] || 0
|
2089
2110
|
|
2090
|
-
# Get the default row height.
|
2091
|
-
default_height = @default_row_height
|
2092
|
-
|
2093
2111
|
# Use min col in check_dimensions. Default to 0 if undefined.
|
2094
2112
|
min_col = @dim_colmin || 0
|
2095
2113
|
|
@@ -2097,12 +2115,12 @@ module Writexlsx
|
|
2097
2115
|
check_dimensions(row, min_col)
|
2098
2116
|
store_row_col_max_min_values(row, min_col)
|
2099
2117
|
|
2100
|
-
height ||=
|
2118
|
+
height ||= @default_row_height
|
2101
2119
|
|
2102
2120
|
# If the height is 0 the row is hidden and the height is the default.
|
2103
2121
|
if height == 0
|
2104
2122
|
hidden = 1
|
2105
|
-
height =
|
2123
|
+
height = @default_row_height
|
2106
2124
|
end
|
2107
2125
|
|
2108
2126
|
# Set the limits for the outline levels (0 <= x <= 7).
|
@@ -2311,8 +2329,11 @@ module Writexlsx
|
|
2311
2329
|
_col = col
|
2312
2330
|
_properties = properties
|
2313
2331
|
end
|
2314
|
-
|
2315
|
-
@
|
2332
|
+
|
2333
|
+
@buttons_array << Writexlsx::Package::Button.new(
|
2334
|
+
self, _row, _col, _properties, @default_row_pixels, @buttons_array.size + 1
|
2335
|
+
)
|
2336
|
+
@has_vml = true
|
2316
2337
|
end
|
2317
2338
|
|
2318
2339
|
#
|
@@ -2415,7 +2436,7 @@ module Writexlsx
|
|
2415
2436
|
|
2416
2437
|
tokens = extract_filter_tokens(expression)
|
2417
2438
|
|
2418
|
-
raise "Incorrect number of tokens in expression '#{expression}'" unless
|
2439
|
+
raise "Incorrect number of tokens in expression '#{expression}'" unless [3, 7].include?(tokens.size)
|
2419
2440
|
|
2420
2441
|
tokens = parse_filter_expression(expression, tokens)
|
2421
2442
|
|
@@ -2494,7 +2515,7 @@ module Writexlsx
|
|
2494
2515
|
end
|
2495
2516
|
|
2496
2517
|
def has_header_vml? # :nodoc:
|
2497
|
-
@
|
2518
|
+
!(@header_images.empty? && @footer_images.empty?)
|
2498
2519
|
end
|
2499
2520
|
|
2500
2521
|
def has_comments? # :nodoc:
|
@@ -2509,49 +2530,29 @@ module Writexlsx
|
|
2509
2530
|
!!@is_chartsheet
|
2510
2531
|
end
|
2511
2532
|
|
2512
|
-
def set_external_vml_links(vml_drawing_id) # :nodoc:
|
2513
|
-
@external_vml_links <<
|
2514
|
-
['/vmlDrawing', "../drawings/vmlDrawing#{vml_drawing_id}.vml"]
|
2515
|
-
end
|
2516
|
-
|
2517
|
-
def set_external_comment_links(comment_id) # :nodoc:
|
2518
|
-
@external_comment_links <<
|
2519
|
-
['/comments', "../comments#{comment_id}.xml"]
|
2520
|
-
end
|
2521
|
-
|
2522
2533
|
#
|
2523
2534
|
# Set up chart/drawings.
|
2524
2535
|
#
|
2525
2536
|
def prepare_chart(index, chart_id, drawing_id) # :nodoc:
|
2526
2537
|
drawing_type = 1
|
2527
2538
|
|
2528
|
-
|
2529
|
-
|
2530
|
-
|
2531
|
-
chart.id = chart_id - 1
|
2532
|
-
x_scale ||= 0
|
2533
|
-
y_scale ||= 0
|
2539
|
+
inserted_chart = @charts[index]
|
2540
|
+
inserted_chart.chart.id = chart_id - 1
|
2534
2541
|
|
2535
|
-
|
2536
|
-
width = chart.width if ptrue?(chart.width)
|
2537
|
-
height = chart.height if ptrue?(chart.height)
|
2538
|
-
|
2539
|
-
width = (0.5 + (width * x_scale)).to_i
|
2540
|
-
height = (0.5 + (height * y_scale)).to_i
|
2541
|
-
|
2542
|
-
dimensions = position_object_emus(col, row, x_offset, y_offset, width, height, anchor)
|
2543
|
-
|
2544
|
-
# Set the chart name for the embedded object if it has been specified.
|
2545
|
-
name = chart.name
|
2542
|
+
dimensions = position_object_emus(inserted_chart)
|
2546
2543
|
|
2547
2544
|
# Create a Drawing object to use with worksheet unless one already exists.
|
2548
|
-
drawing = Drawing.new(
|
2545
|
+
drawing = Drawing.new(
|
2546
|
+
drawing_type, dimensions, 0, 0, nil, inserted_chart.anchor,
|
2547
|
+
drawing_rel_index, 0, nil, inserted_chart.name,
|
2548
|
+
inserted_chart.description, inserted_chart.decorative
|
2549
|
+
)
|
2549
2550
|
if drawings?
|
2550
2551
|
@drawings.add_drawing_object(drawing)
|
2551
2552
|
else
|
2552
2553
|
@drawings = Drawings.new
|
2553
2554
|
@drawings.add_drawing_object(drawing)
|
2554
|
-
@drawings.embedded =
|
2555
|
+
@drawings.embedded = true
|
2555
2556
|
|
2556
2557
|
@external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml"]
|
2557
2558
|
end
|
@@ -2589,85 +2590,6 @@ module Writexlsx
|
|
2589
2590
|
data
|
2590
2591
|
end
|
2591
2592
|
|
2592
|
-
#
|
2593
|
-
# Calculate the vertices that define the position of a graphical object within
|
2594
|
-
# the worksheet in pixels.
|
2595
|
-
#
|
2596
|
-
def position_object_pixels(col_start, row_start, x1, y1, width, height, anchor = nil) # :nodoc:
|
2597
|
-
# Adjust start column for negative offsets.
|
2598
|
-
while x1 < 0 && col_start > 0
|
2599
|
-
x1 += size_col(col_start - 1)
|
2600
|
-
col_start -= 1
|
2601
|
-
end
|
2602
|
-
|
2603
|
-
# Adjust start row for negative offsets.
|
2604
|
-
while y1 < 0 && row_start > 0
|
2605
|
-
y1 += size_row(row_start - 1)
|
2606
|
-
row_start -= 1
|
2607
|
-
end
|
2608
|
-
|
2609
|
-
# Ensure that the image isn't shifted off the page at top left.
|
2610
|
-
x1 = 0 if x1 < 0
|
2611
|
-
y1 = 0 if y1 < 0
|
2612
|
-
|
2613
|
-
# Calculate the absolute x offset of the top-left vertex.
|
2614
|
-
x_abs = if @col_size_changed
|
2615
|
-
(0..col_start - 1).inject(0) { |sum, col| sum += size_col(col, anchor) }
|
2616
|
-
else
|
2617
|
-
# Optimisation for when the column widths haven't changed.
|
2618
|
-
@default_col_pixels * col_start
|
2619
|
-
end
|
2620
|
-
x_abs += x1
|
2621
|
-
|
2622
|
-
# Calculate the absolute y offset of the top-left vertex.
|
2623
|
-
# Store the column change to allow optimisations.
|
2624
|
-
y_abs = if @row_size_changed
|
2625
|
-
(0..row_start - 1).inject(0) { |sum, row| sum += size_row(row, anchor) }
|
2626
|
-
else
|
2627
|
-
# Optimisation for when the row heights haven't changed.
|
2628
|
-
@default_row_pixels * row_start
|
2629
|
-
end
|
2630
|
-
y_abs += y1
|
2631
|
-
|
2632
|
-
# Adjust start column for offsets that are greater than the col width.
|
2633
|
-
while x1 >= size_col(col_start, anchor)
|
2634
|
-
x1 -= size_col(col_start)
|
2635
|
-
col_start += 1
|
2636
|
-
end
|
2637
|
-
|
2638
|
-
# Adjust start row for offsets that are greater than the row height.
|
2639
|
-
while y1 >= size_row(row_start, anchor)
|
2640
|
-
y1 -= size_row(row_start)
|
2641
|
-
row_start += 1
|
2642
|
-
end
|
2643
|
-
|
2644
|
-
# Initialise end cell to the same as the start cell.
|
2645
|
-
col_end = col_start
|
2646
|
-
row_end = row_start
|
2647
|
-
|
2648
|
-
# Only offset the image in the cell if the row/col isn't hidden.
|
2649
|
-
width += x1 if size_col(col_start, anchor) > 0
|
2650
|
-
height += y1 if size_row(row_start, anchor) > 0
|
2651
|
-
|
2652
|
-
# Subtract the underlying cell widths to find the end cell of the object.
|
2653
|
-
while width >= size_col(col_end, anchor)
|
2654
|
-
width -= size_col(col_end, anchor)
|
2655
|
-
col_end += 1
|
2656
|
-
end
|
2657
|
-
|
2658
|
-
# Subtract the underlying cell heights to find the end cell of the object.
|
2659
|
-
while height >= size_row(row_end, anchor)
|
2660
|
-
height -= size_row(row_end, anchor)
|
2661
|
-
row_end += 1
|
2662
|
-
end
|
2663
|
-
|
2664
|
-
# The end vertices are whatever is left from the width and height.
|
2665
|
-
x2 = width
|
2666
|
-
y2 = height
|
2667
|
-
|
2668
|
-
[col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs]
|
2669
|
-
end
|
2670
|
-
|
2671
2593
|
def comments_visible? # :nodoc:
|
2672
2594
|
!!@comments_visible
|
2673
2595
|
end
|
@@ -2725,7 +2647,7 @@ module Writexlsx
|
|
2725
2647
|
if index.to_s =~ /^#([0-9A-F]{6})$/i
|
2726
2648
|
"FF#{::Regexp.last_match(1).upcase}"
|
2727
2649
|
else
|
2728
|
-
"FF#{
|
2650
|
+
"FF#{palette_color_from_index(index)}"
|
2729
2651
|
end
|
2730
2652
|
end
|
2731
2653
|
|
@@ -2872,42 +2794,242 @@ module Writexlsx
|
|
2872
2794
|
@has_embedded_images
|
2873
2795
|
end
|
2874
2796
|
|
2875
|
-
|
2797
|
+
# Check that some image or drawing needs to be processed.
|
2798
|
+
def some_image_or_drawing_to_be_processed?
|
2799
|
+
charts.size + images.size + shapes.size + header_images.size + footer_images.size + (background_image ? 1 : 0) == 0
|
2800
|
+
end
|
2876
2801
|
|
2877
|
-
|
2878
|
-
|
2879
|
-
#
|
2880
|
-
def compare_col_info(col_options, previous_options)
|
2881
|
-
if !col_options.width.nil? != !previous_options.width.nil?
|
2882
|
-
return nil
|
2883
|
-
end
|
2884
|
-
if col_options.width && previous_options.width &&
|
2885
|
-
col_options.width != previous_options.width
|
2886
|
-
return nil
|
2887
|
-
end
|
2802
|
+
def prepare_drawings(drawing_id, chart_ref_id, image_ref_id, image_ids, header_image_ids, background_ids)
|
2803
|
+
has_drawings = false
|
2888
2804
|
|
2889
|
-
|
2890
|
-
|
2891
|
-
end
|
2892
|
-
if col_options.format && previous_options.format &&
|
2893
|
-
col_options.format != previous_options.format
|
2894
|
-
return nil
|
2895
|
-
end
|
2805
|
+
# Check that some image or drawing needs to be processed.
|
2806
|
+
unless some_image_or_drawing_to_be_processed?
|
2896
2807
|
|
2897
|
-
|
2898
|
-
|
2899
|
-
|
2808
|
+
# Don't increase the drawing_id header/footer images.
|
2809
|
+
unless charts.empty? && images.empty? && shapes.empty?
|
2810
|
+
drawing_id += 1
|
2811
|
+
has_drawings = true
|
2812
|
+
end
|
2900
2813
|
|
2901
|
-
|
2902
|
-
|
2814
|
+
# Prepare the background images.
|
2815
|
+
image_ref_id = prepare_background_image(background_ids, image_ref_id)
|
2903
2816
|
|
2904
|
-
|
2905
|
-
|
2906
|
-
|
2907
|
-
|
2908
|
-
|
2909
|
-
#
|
2910
|
-
|
2817
|
+
# Prepare the worksheet images.
|
2818
|
+
images.each do |image|
|
2819
|
+
image_ref_id = prepare_image(image, drawing_id, image_ids, image_ref_id)
|
2820
|
+
end
|
2821
|
+
|
2822
|
+
# Prepare the worksheet charts.
|
2823
|
+
charts.each_with_index do |_chart, index|
|
2824
|
+
chart_ref_id += 1
|
2825
|
+
prepare_chart(index, chart_ref_id, drawing_id)
|
2826
|
+
end
|
2827
|
+
|
2828
|
+
# Prepare the worksheet shapes.
|
2829
|
+
shapes.each_with_index do |_shape, index|
|
2830
|
+
prepare_shape(index, drawing_id)
|
2831
|
+
end
|
2832
|
+
|
2833
|
+
# Prepare the header and footer images.
|
2834
|
+
[header_images, footer_images].each do |images|
|
2835
|
+
images.each do |image|
|
2836
|
+
image_ref_id = prepare_header_footer_image(
|
2837
|
+
image, header_image_ids, image_ref_id
|
2838
|
+
)
|
2839
|
+
end
|
2840
|
+
end
|
2841
|
+
|
2842
|
+
if has_drawings
|
2843
|
+
@workbook.drawings << drawings
|
2844
|
+
end
|
2845
|
+
end
|
2846
|
+
|
2847
|
+
[drawing_id, chart_ref_id, image_ref_id]
|
2848
|
+
end
|
2849
|
+
|
2850
|
+
#
|
2851
|
+
# Set the background image for the worksheet.
|
2852
|
+
#
|
2853
|
+
def set_background(image)
|
2854
|
+
raise "Couldn't locate #{image}: $!" unless File.exist?(image)
|
2855
|
+
|
2856
|
+
@background_image = ImageProperty.new(image)
|
2857
|
+
end
|
2858
|
+
|
2859
|
+
#
|
2860
|
+
# Calculate the vertices that define the position of a graphical object
|
2861
|
+
# within the worksheet in pixels.
|
2862
|
+
#
|
2863
|
+
def position_object_pixels(col_start, row_start, x1, y1, width, height, anchor = nil) # :nodoc:
|
2864
|
+
# Adjust start column for negative offsets.
|
2865
|
+
while x1 < 0 && col_start > 0
|
2866
|
+
x1 += size_col(col_start - 1)
|
2867
|
+
col_start -= 1
|
2868
|
+
end
|
2869
|
+
|
2870
|
+
# Adjust start row for negative offsets.
|
2871
|
+
while y1 < 0 && row_start > 0
|
2872
|
+
y1 += size_row(row_start - 1)
|
2873
|
+
row_start -= 1
|
2874
|
+
end
|
2875
|
+
|
2876
|
+
# Ensure that the image isn't shifted off the page at top left.
|
2877
|
+
x1 = 0 if x1 < 0
|
2878
|
+
y1 = 0 if y1 < 0
|
2879
|
+
|
2880
|
+
# Calculate the absolute x offset of the top-left vertex.
|
2881
|
+
x_abs = if @col_size_changed
|
2882
|
+
(0..(col_start - 1)).inject(0) { |sum, col| sum += size_col(col, anchor) }
|
2883
|
+
else
|
2884
|
+
# Optimisation for when the column widths haven't changed.
|
2885
|
+
DEFAULT_COL_PIXELS * col_start
|
2886
|
+
end
|
2887
|
+
x_abs += x1
|
2888
|
+
|
2889
|
+
# Calculate the absolute y offset of the top-left vertex.
|
2890
|
+
# Store the column change to allow optimisations.
|
2891
|
+
y_abs = if @row_size_changed
|
2892
|
+
(0..(row_start - 1)).inject(0) { |sum, row| sum += size_row(row, anchor) }
|
2893
|
+
else
|
2894
|
+
# Optimisation for when the row heights haven't changed.
|
2895
|
+
@default_row_pixels * row_start
|
2896
|
+
end
|
2897
|
+
y_abs += y1
|
2898
|
+
|
2899
|
+
# Adjust start column for offsets that are greater than the col width.
|
2900
|
+
while x1 >= size_col(col_start, anchor)
|
2901
|
+
x1 -= size_col(col_start)
|
2902
|
+
col_start += 1
|
2903
|
+
end
|
2904
|
+
|
2905
|
+
# Adjust start row for offsets that are greater than the row height.
|
2906
|
+
while y1 >= size_row(row_start, anchor)
|
2907
|
+
y1 -= size_row(row_start)
|
2908
|
+
row_start += 1
|
2909
|
+
end
|
2910
|
+
|
2911
|
+
# Initialise end cell to the same as the start cell.
|
2912
|
+
col_end = col_start
|
2913
|
+
row_end = row_start
|
2914
|
+
|
2915
|
+
# Only offset the image in the cell if the row/col isn't hidden.
|
2916
|
+
width += x1 if size_col(col_start, anchor) > 0
|
2917
|
+
height += y1 if size_row(row_start, anchor) > 0
|
2918
|
+
|
2919
|
+
# Subtract the underlying cell widths to find the end cell of the object.
|
2920
|
+
while width >= size_col(col_end, anchor)
|
2921
|
+
width -= size_col(col_end, anchor)
|
2922
|
+
col_end += 1
|
2923
|
+
end
|
2924
|
+
|
2925
|
+
# Subtract the underlying cell heights to find the end cell of the object.
|
2926
|
+
while height >= size_row(row_end, anchor)
|
2927
|
+
height -= size_row(row_end, anchor)
|
2928
|
+
row_end += 1
|
2929
|
+
end
|
2930
|
+
|
2931
|
+
# The end vertices are whatever is left from the width and height.
|
2932
|
+
x2 = width
|
2933
|
+
y2 = height
|
2934
|
+
|
2935
|
+
[col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs]
|
2936
|
+
end
|
2937
|
+
|
2938
|
+
private
|
2939
|
+
|
2940
|
+
#
|
2941
|
+
# Convert the width of a cell from user's units to pixels. Excel rounds
|
2942
|
+
# the column width to the nearest pixel. If the width hasn't been set
|
2943
|
+
# by the user we use the default value. A hidden column is treated as
|
2944
|
+
# having a width of zero unless it has the special "object_position" of
|
2945
|
+
# 4 (size with cells).
|
2946
|
+
#
|
2947
|
+
def size_col(col, anchor = 0) # :nodoc:
|
2948
|
+
# Look up the cell value to see if it has been changed.
|
2949
|
+
if col_info[col]
|
2950
|
+
width = col_info[col].width || @default_col_width
|
2951
|
+
hidden = col_info[col].hidden
|
2952
|
+
|
2953
|
+
# Convert to pixels.
|
2954
|
+
pixels = if hidden == 1 && anchor != 4
|
2955
|
+
0
|
2956
|
+
elsif width < 1
|
2957
|
+
((width * (MAX_DIGIT_WIDTH + PADDING)) + 0.5).to_i
|
2958
|
+
else
|
2959
|
+
((width * MAX_DIGIT_WIDTH) + 0.5).to_i + PADDING
|
2960
|
+
end
|
2961
|
+
else
|
2962
|
+
pixels = DEFAULT_COL_PIXELS
|
2963
|
+
end
|
2964
|
+
pixels
|
2965
|
+
end
|
2966
|
+
|
2967
|
+
#
|
2968
|
+
# Convert the height of a cell from user's units to pixels. If the height
|
2969
|
+
# hasn't been set by the user we use the default value. A hidden row is
|
2970
|
+
# treated as having a height of zero unless it has the special
|
2971
|
+
# "object_position" of 4 (size with cells).
|
2972
|
+
#
|
2973
|
+
def size_row(row, anchor = 0) # :nodoc:
|
2974
|
+
# Look up the cell value to see if it has been changed
|
2975
|
+
if row_sizes[row]
|
2976
|
+
height, hidden = row_sizes[row]
|
2977
|
+
|
2978
|
+
pixels = if hidden == 1 && anchor != 4
|
2979
|
+
0
|
2980
|
+
else
|
2981
|
+
(4 / 3.0 * height).to_i
|
2982
|
+
end
|
2983
|
+
else
|
2984
|
+
pixels = (4 / 3.0 * default_row_height).to_i
|
2985
|
+
end
|
2986
|
+
pixels
|
2987
|
+
end
|
2988
|
+
|
2989
|
+
#
|
2990
|
+
# Compare adjacent column information structures.
|
2991
|
+
#
|
2992
|
+
def compare_col_info(col_options, previous_options)
|
2993
|
+
if !col_options.width.nil? != !previous_options.width.nil?
|
2994
|
+
return nil
|
2995
|
+
end
|
2996
|
+
if col_options.width && previous_options.width &&
|
2997
|
+
col_options.width != previous_options.width
|
2998
|
+
return nil
|
2999
|
+
end
|
3000
|
+
|
3001
|
+
if !col_options.format.nil? != !previous_options.format.nil?
|
3002
|
+
return nil
|
3003
|
+
end
|
3004
|
+
if col_options.format && previous_options.format &&
|
3005
|
+
col_options.format != previous_options.format
|
3006
|
+
return nil
|
3007
|
+
end
|
3008
|
+
|
3009
|
+
return nil if col_options.hidden != previous_options.hidden
|
3010
|
+
return nil if col_options.level != previous_options.level
|
3011
|
+
return nil if col_options.collapsed != previous_options.collapsed
|
3012
|
+
|
3013
|
+
true
|
3014
|
+
end
|
3015
|
+
|
3016
|
+
def set_external_vml_links(vml_drawing_id) # :nodoc:
|
3017
|
+
@external_vml_links <<
|
3018
|
+
['/vmlDrawing', "../drawings/vmlDrawing#{vml_drawing_id}.vml"]
|
3019
|
+
end
|
3020
|
+
|
3021
|
+
def set_external_comment_links(comment_id) # :nodoc:
|
3022
|
+
@external_comment_links <<
|
3023
|
+
['/comments', "../comments#{comment_id}.xml"]
|
3024
|
+
end
|
3025
|
+
|
3026
|
+
#
|
3027
|
+
# Get the index used to address a drawing rel link.
|
3028
|
+
#
|
3029
|
+
def drawing_rel_index(target = nil)
|
3030
|
+
if !target
|
3031
|
+
# Undefined values for drawings like charts will always be unique.
|
3032
|
+
@drawing_rels_id += 1
|
2911
3033
|
elsif ptrue?(@drawing_rels[target])
|
2912
3034
|
@drawing_rels[target]
|
2913
3035
|
else
|
@@ -3185,12 +3307,13 @@ module Writexlsx
|
|
3185
3307
|
end
|
3186
3308
|
|
3187
3309
|
#
|
3188
|
-
# Calculate the vertices that define the position of a graphical object
|
3189
|
-
# the worksheet in EMUs.
|
3310
|
+
# Calculate the vertices that define the position of a graphical object
|
3311
|
+
# within the worksheet in EMUs.
|
3190
3312
|
#
|
3191
|
-
def position_object_emus(
|
3313
|
+
def position_object_emus(graphical_object) # :nodoc:
|
3314
|
+
go = graphical_object
|
3192
3315
|
col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs =
|
3193
|
-
position_object_pixels(
|
3316
|
+
position_object_pixels(go.col, go.row, go.x_offset, go.y_offset, go.scaled_width, go.scaled_height, go.anchor)
|
3194
3317
|
|
3195
3318
|
# Convert the pixel values to EMUs. See above.
|
3196
3319
|
x1 = (0.5 + (9_525 * x1)).to_i
|
@@ -3203,54 +3326,6 @@ module Writexlsx
|
|
3203
3326
|
[col_start, row_start, x1, y1, col_end, row_end, x2, y2, x_abs, y_abs]
|
3204
3327
|
end
|
3205
3328
|
|
3206
|
-
#
|
3207
|
-
# Convert the width of a cell from user's units to pixels. Excel rounds the
|
3208
|
-
# column width to the nearest pixel. If the width hasn't been set by the user
|
3209
|
-
# we use the default value. A hidden column is treated as having a width of
|
3210
|
-
# zero unless it has the special "object_position" of 4 (size with cells).
|
3211
|
-
#
|
3212
|
-
def size_col(col, anchor = 0) # :nodoc:
|
3213
|
-
# Look up the cell value to see if it has been changed.
|
3214
|
-
if @col_info[col]
|
3215
|
-
width = @col_info[col].width || @default_col_width
|
3216
|
-
hidden = @col_info[col].hidden
|
3217
|
-
|
3218
|
-
# Convert to pixels.
|
3219
|
-
pixels = if hidden == 1 && anchor != 4
|
3220
|
-
0
|
3221
|
-
elsif width < 1
|
3222
|
-
((width * (MAX_DIGIT_WIDTH + PADDING)) + 0.5).to_i
|
3223
|
-
else
|
3224
|
-
((width * MAX_DIGIT_WIDTH) + 0.5).to_i + PADDING
|
3225
|
-
end
|
3226
|
-
else
|
3227
|
-
pixels = @default_col_pixels
|
3228
|
-
end
|
3229
|
-
pixels
|
3230
|
-
end
|
3231
|
-
|
3232
|
-
#
|
3233
|
-
# Convert the height of a cell from user's units to pixels. If the height
|
3234
|
-
# hasn't been set by the user we use the default value. A hidden row is
|
3235
|
-
# treated as having a height of zero unless it has the special
|
3236
|
-
# "object_position" of 4 (size with cells).
|
3237
|
-
#
|
3238
|
-
def size_row(row, anchor = 0) # :nodoc:
|
3239
|
-
# Look up the cell value to see if it has been changed
|
3240
|
-
if @row_sizes[row]
|
3241
|
-
height, hidden = @row_sizes[row]
|
3242
|
-
|
3243
|
-
pixels = if hidden == 1 && anchor != 4
|
3244
|
-
0
|
3245
|
-
else
|
3246
|
-
(4 / 3.0 * height).to_i
|
3247
|
-
end
|
3248
|
-
else
|
3249
|
-
pixels = (4 / 3.0 * @default_row_height).to_i
|
3250
|
-
end
|
3251
|
-
pixels
|
3252
|
-
end
|
3253
|
-
|
3254
3329
|
#
|
3255
3330
|
# Convert the width of a cell from pixels to character units.
|
3256
3331
|
#
|
@@ -3277,48 +3352,44 @@ module Writexlsx
|
|
3277
3352
|
#
|
3278
3353
|
# Set up image/drawings.
|
3279
3354
|
#
|
3280
|
-
def prepare_image(
|
3281
|
-
|
3282
|
-
|
3355
|
+
def prepare_image(image, drawing_id, image_ids, image_ref_id) # :nodoc:
|
3356
|
+
image_type = image.type
|
3357
|
+
x_dpi = image.x_dpi || 96
|
3358
|
+
y_dpi = image.y_dpi || 96
|
3359
|
+
md5 = image.md5
|
3283
3360
|
drawing_type = 2
|
3284
3361
|
|
3285
|
-
|
3286
|
-
x_scale, y_scale, url, tip, anchor, description, decorative = @images[index]
|
3362
|
+
@workbook.store_image_types(image_type)
|
3287
3363
|
|
3288
|
-
|
3289
|
-
|
3290
|
-
|
3291
|
-
|
3292
|
-
|
3293
|
-
|
3294
|
-
|
3364
|
+
if image_ids[md5]
|
3365
|
+
image_id = image_ids[md5]
|
3366
|
+
else
|
3367
|
+
image_ref_id += 1
|
3368
|
+
image_ids[md5] = image_id = image_ref_id
|
3369
|
+
@workbook.images << image
|
3370
|
+
end
|
3295
3371
|
|
3296
|
-
|
3297
|
-
width = (0.5 + (width * 9_525)).to_i
|
3298
|
-
height = (0.5 + (height * 9_525)).to_i
|
3372
|
+
dimensions = position_object_emus(image)
|
3299
3373
|
|
3300
3374
|
# Create a Drawing object to use with worksheet unless one already exists.
|
3301
|
-
drawing = Drawing.new(
|
3302
|
-
|
3303
|
-
|
3304
|
-
|
3305
|
-
|
3306
|
-
|
3307
|
-
|
3308
|
-
@drawings =
|
3375
|
+
drawing = Drawing.new(
|
3376
|
+
drawing_type, dimensions, image.width_emus, image.height_emus,
|
3377
|
+
nil, image.anchor, 0, 0, image.tip, image.name,
|
3378
|
+
image.description || image.name, image.decorative
|
3379
|
+
)
|
3380
|
+
unless drawings?
|
3381
|
+
@drawings = Drawings.new
|
3382
|
+
@drawings.embedded = true
|
3309
3383
|
|
3310
3384
|
@external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml"]
|
3311
3385
|
end
|
3312
|
-
drawings.add_drawing_object(drawing)
|
3313
|
-
|
3314
|
-
drawing.description = name unless description
|
3386
|
+
@drawings.add_drawing_object(drawing)
|
3315
3387
|
|
3316
|
-
if url
|
3317
|
-
rel_type = '/hyperlink'
|
3388
|
+
if image.url
|
3318
3389
|
target_mode = 'External'
|
3319
|
-
target = escape_url(url) if url =~ %r{^[fh]tt?ps?://} || url =~ /^mailto:/
|
3320
|
-
if url =~ /^external:/
|
3321
|
-
target = escape_url(url.sub(/^external:/, ''))
|
3390
|
+
target = escape_url(image.url) if image.url =~ %r{^[fh]tt?ps?://} || image.url =~ /^mailto:/
|
3391
|
+
if image.url =~ /^external:/
|
3392
|
+
target = escape_url(image.url.sub(/^external:/, ''))
|
3322
3393
|
|
3323
3394
|
# Additional escape not required in worksheet hyperlinks
|
3324
3395
|
target = target.gsub("#", '%23')
|
@@ -3331,8 +3402,8 @@ module Writexlsx
|
|
3331
3402
|
end
|
3332
3403
|
end
|
3333
3404
|
|
3334
|
-
if url =~ /^internal:/
|
3335
|
-
target = url.sub(/^internal:/, '#')
|
3405
|
+
if image.url =~ /^internal:/
|
3406
|
+
target = image.url.sub(/^internal:/, '#')
|
3336
3407
|
target_mode = nil
|
3337
3408
|
end
|
3338
3409
|
|
@@ -3342,37 +3413,28 @@ Ignoring URL #{target} where link or anchor > 255 characters since it exceeds Ex
|
|
3342
3413
|
EOS
|
3343
3414
|
end
|
3344
3415
|
|
3345
|
-
@drawing_links << [
|
3346
|
-
drawing.url_rel_index = drawing_rel_index(url)
|
3416
|
+
@drawing_links << ['/hyperlink', target, target_mode] if target && !@drawing_rels[image.url]
|
3417
|
+
drawing.url_rel_index = drawing_rel_index(image.url)
|
3347
3418
|
end
|
3348
3419
|
|
3349
3420
|
@drawing_links << ['/image', "../media/image#{image_id}.#{image_type}"] unless @drawing_rels[md5]
|
3350
3421
|
|
3351
3422
|
drawing.rel_index = drawing_rel_index(md5)
|
3423
|
+
|
3424
|
+
image_ref_id
|
3352
3425
|
end
|
3353
|
-
public :prepare_image
|
3354
3426
|
|
3355
|
-
def prepare_header_image(image_id,
|
3427
|
+
def prepare_header_image(image_id, image_property)
|
3356
3428
|
# Strip the extension from the filename.
|
3357
|
-
body = name.dup
|
3429
|
+
body = image_property.name.dup
|
3358
3430
|
body[/\.[^.]+$/, 0] = ''
|
3431
|
+
image_property.body = body
|
3359
3432
|
|
3360
|
-
@vml_drawing_links << ['/image', "../media/image#{image_id}.#{
|
3433
|
+
@vml_drawing_links << ['/image', "../media/image#{image_id}.#{image_property.type}"] unless @vml_drawing_rels[image_property.md5]
|
3361
3434
|
|
3362
|
-
ref_id = get_vml_drawing_rel_index(md5)
|
3363
|
-
@header_images_array <<
|
3435
|
+
image_property.ref_id = get_vml_drawing_rel_index(image_property.md5)
|
3436
|
+
@header_images_array << image_property
|
3364
3437
|
end
|
3365
|
-
public :prepare_header_image
|
3366
|
-
|
3367
|
-
#
|
3368
|
-
# Set the background image for the worksheet.
|
3369
|
-
#
|
3370
|
-
def set_background(image)
|
3371
|
-
raise "Couldn't locate #{image}: $!" unless File.exist?(image)
|
3372
|
-
|
3373
|
-
@background_image = image
|
3374
|
-
end
|
3375
|
-
public :set_background
|
3376
3438
|
|
3377
3439
|
#
|
3378
3440
|
# Set up an image without a drawing object for the background image.
|
@@ -3381,78 +3443,25 @@ EOS
|
|
3381
3443
|
@external_background_links <<
|
3382
3444
|
['/image', "../media/image#{image_id}.#{image_type}"]
|
3383
3445
|
end
|
3384
|
-
public :prepare_background
|
3385
3446
|
|
3386
|
-
|
3387
|
-
|
3388
|
-
|
3389
|
-
#
|
3390
|
-
# Insert a shape into the worksheet.
|
3391
|
-
#
|
3392
|
-
def insert_shape(
|
3393
|
-
row_start, column_start, shape = nil, x_offset = nil, y_offset = nil,
|
3394
|
-
x_scale = nil, y_scale = nil, anchor = nil
|
3395
|
-
)
|
3396
|
-
# Check for a cell reference in A1 notation and substitute row and column.
|
3397
|
-
if (row_col_array = row_col_notation(row_start))
|
3398
|
-
_row_start, _column_start = row_col_array
|
3399
|
-
_shape = column_start
|
3400
|
-
_x_offset = shape
|
3401
|
-
_y_offset = x_offset
|
3402
|
-
_x_scale = y_offset
|
3403
|
-
_y_scale = x_scale
|
3404
|
-
_anchor = y_scale
|
3405
|
-
else
|
3406
|
-
_row_start = row_start
|
3407
|
-
_column_start = column_start
|
3408
|
-
_shape = shape
|
3409
|
-
_x_offset = x_offset
|
3410
|
-
_y_offset = y_offset
|
3411
|
-
_x_scale = x_scale
|
3412
|
-
_y_scale = y_scale
|
3413
|
-
_anchor = anchor
|
3414
|
-
end
|
3415
|
-
raise "Insufficient arguments in insert_shape()" if [_row_start, _column_start, _shape].include?(nil)
|
3447
|
+
def prepare_background_image(background_ids, image_ref_id)
|
3448
|
+
unless background_image.nil?
|
3449
|
+
@workbook.store_image_types(background_image.type)
|
3416
3450
|
|
3417
|
-
|
3418
|
-
|
3419
|
-
_x_scale, _y_scale, _anchor
|
3420
|
-
)
|
3421
|
-
# Assign a shape ID.
|
3422
|
-
while true
|
3423
|
-
id = _shape.id || 0
|
3424
|
-
used = @shape_hash[id]
|
3425
|
-
|
3426
|
-
# Test if shape ID is already used. Otherwise assign a new one.
|
3427
|
-
if !used && id != 0
|
3428
|
-
break
|
3451
|
+
if background_ids[background_image.md5]
|
3452
|
+
ref_id = background_ids[background_image.md5]
|
3429
3453
|
else
|
3430
|
-
|
3431
|
-
|
3454
|
+
image_ref_id += 1
|
3455
|
+
ref_id = image_ref_id
|
3456
|
+
background_ids[background_image.md5] = ref_id
|
3457
|
+
@workbook.images << background_image
|
3432
3458
|
end
|
3433
|
-
end
|
3434
|
-
|
3435
|
-
# Allow lookup of entry into shape array by shape ID.
|
3436
|
-
@shape_hash[_shape.id] = _shape.element = @shapes.size
|
3437
3459
|
|
3438
|
-
|
3439
|
-
|
3440
|
-
# used as a stencil. Previously stamped copies don't get modified
|
3441
|
-
# if the stencil is modified.
|
3442
|
-
_shape.dup
|
3443
|
-
else
|
3444
|
-
_shape
|
3445
|
-
end
|
3446
|
-
|
3447
|
-
# For connectors change x/y coords based on location of connected shapes.
|
3448
|
-
insert.auto_locate_connectors(@shapes, @shape_hash)
|
3460
|
+
prepare_background(ref_id, background_image.type)
|
3461
|
+
end
|
3449
3462
|
|
3450
|
-
|
3451
|
-
# the parent shape is maintained.
|
3452
|
-
@shapes << insert
|
3453
|
-
insert
|
3463
|
+
image_ref_id
|
3454
3464
|
end
|
3455
|
-
public :insert_shape
|
3456
3465
|
|
3457
3466
|
#
|
3458
3467
|
# Set up drawing shapes
|
@@ -3463,7 +3472,7 @@ EOS
|
|
3463
3472
|
# Create a Drawing object to use with worksheet unless one already exists.
|
3464
3473
|
unless drawings?
|
3465
3474
|
@drawings = Drawings.new
|
3466
|
-
@drawings.embedded =
|
3475
|
+
@drawings.embedded = true
|
3467
3476
|
@external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml"]
|
3468
3477
|
@has_shapes = true
|
3469
3478
|
end
|
@@ -3479,70 +3488,6 @@ EOS
|
|
3479
3488
|
)
|
3480
3489
|
drawings.add_drawing_object(drawing)
|
3481
3490
|
end
|
3482
|
-
public :prepare_shape
|
3483
|
-
|
3484
|
-
#
|
3485
|
-
# This method handles the parameters passed to insert_button as well as
|
3486
|
-
# calculating the button object position and vertices.
|
3487
|
-
#
|
3488
|
-
def button_params(row, col, params)
|
3489
|
-
button = Writexlsx::Package::Button.new
|
3490
|
-
|
3491
|
-
button_number = 1 + @buttons_array.size
|
3492
|
-
|
3493
|
-
# Set the button caption.
|
3494
|
-
caption = params[:caption] || "Button #{button_number}"
|
3495
|
-
|
3496
|
-
button.font = { _caption: caption }
|
3497
|
-
|
3498
|
-
# Set the macro name.
|
3499
|
-
button.macro = if params[:macro]
|
3500
|
-
"[0]!#{params[:macro]}"
|
3501
|
-
else
|
3502
|
-
"[0]!Button#{button_number}_Click"
|
3503
|
-
end
|
3504
|
-
|
3505
|
-
# Set the alt text for the button.
|
3506
|
-
button.description = params[:description]
|
3507
|
-
|
3508
|
-
# Ensure that a width and height have been set.
|
3509
|
-
default_width = @default_col_pixels
|
3510
|
-
default_height = @default_row_pixels
|
3511
|
-
params[:width] = default_width unless params[:width]
|
3512
|
-
params[:height] = default_height unless params[:height]
|
3513
|
-
|
3514
|
-
# Set the x/y offsets.
|
3515
|
-
params[:x_offset] = 0 unless params[:x_offset]
|
3516
|
-
params[:y_offset] = 0 unless params[:y_offset]
|
3517
|
-
|
3518
|
-
# Scale the size of the button box if required.
|
3519
|
-
params[:width] = params[:width] * params[:x_scale] if params[:x_scale]
|
3520
|
-
params[:height] = params[:height] * params[:y_scale] if params[:y_scale]
|
3521
|
-
|
3522
|
-
# Round the dimensions to the nearest pixel.
|
3523
|
-
params[:width] = (0.5 + params[:width]).to_i
|
3524
|
-
params[:height] = (0.5 + params[:height]).to_i
|
3525
|
-
|
3526
|
-
params[:start_row] = row
|
3527
|
-
params[:start_col] = col
|
3528
|
-
|
3529
|
-
# Calculate the positions of button object.
|
3530
|
-
vertices = position_object_pixels(
|
3531
|
-
params[:start_col],
|
3532
|
-
params[:start_row],
|
3533
|
-
params[:x_offset],
|
3534
|
-
params[:y_offset],
|
3535
|
-
params[:width],
|
3536
|
-
params[:height]
|
3537
|
-
)
|
3538
|
-
|
3539
|
-
# Add the width and height for VML.
|
3540
|
-
vertices << [params[:width], params[:height]]
|
3541
|
-
|
3542
|
-
button.vertices = vertices
|
3543
|
-
|
3544
|
-
button
|
3545
|
-
end
|
3546
3491
|
|
3547
3492
|
#
|
3548
3493
|
# Hash a worksheet password. Based on the algorithm in ECMA-376-4:2016,
|
@@ -3868,7 +3813,7 @@ EOS
|
|
3868
3813
|
end
|
3869
3814
|
else
|
3870
3815
|
# Row attributes only.
|
3871
|
-
write_empty_row(row_num, span,
|
3816
|
+
write_empty_row(row_num, span, *@set_rows[row_num])
|
3872
3817
|
end
|
3873
3818
|
end
|
3874
3819
|
end
|
@@ -4374,7 +4319,7 @@ EOS
|
|
4374
4319
|
return unless outline_changed?
|
4375
4320
|
|
4376
4321
|
attributes = []
|
4377
|
-
attributes << ["applyStyles", 1] if @outline_style
|
4322
|
+
attributes << ["applyStyles", 1] if @outline_style
|
4378
4323
|
attributes << ["summaryBelow", 0] if @outline_below == 0
|
4379
4324
|
attributes << ["summaryRight", 0] if @outline_right == 0
|
4380
4325
|
attributes << ["showOutlineSymbols", 0] if @outline_on == 0
|
@@ -4471,7 +4416,7 @@ EOS
|
|
4471
4416
|
# Write the <picture> element.
|
4472
4417
|
#
|
4473
4418
|
def write_picture
|
4474
|
-
return unless
|
4419
|
+
return unless @background_image
|
4475
4420
|
|
4476
4421
|
# Increment the relationship id.
|
4477
4422
|
@rel_count += 1
|
@@ -4704,7 +4649,7 @@ EOS
|
|
4704
4649
|
def write_sparklines(sparkline)
|
4705
4650
|
# Write the sparkline elements.
|
4706
4651
|
@writer.tag_elements('x14:sparklines') do
|
4707
|
-
(0..sparkline[:count] - 1).each do |i|
|
4652
|
+
(0..(sparkline[:count] - 1)).each do |i|
|
4708
4653
|
range = sparkline[:ranges][i]
|
4709
4654
|
location = sparkline[:locations][i]
|
4710
4655
|
|
@@ -4984,5 +4929,54 @@ EOS
|
|
4984
4929
|
|
4985
4930
|
@writer.empty_tag('ignoredError', attributes)
|
4986
4931
|
end
|
4932
|
+
|
4933
|
+
def prepare_header_footer_image(image, header_image_ids, image_ref_id)
|
4934
|
+
@workbook.store_image_types(image.type)
|
4935
|
+
|
4936
|
+
if header_image_ids[image.md5]
|
4937
|
+
ref_id = header_image_ids[image.md5]
|
4938
|
+
else
|
4939
|
+
image_ref_id += 1
|
4940
|
+
header_image_ids[image.md5] = ref_id = image_ref_id
|
4941
|
+
@workbook.images << image
|
4942
|
+
end
|
4943
|
+
|
4944
|
+
prepare_header_image(ref_id, image)
|
4945
|
+
|
4946
|
+
image_ref_id
|
4947
|
+
end
|
4948
|
+
|
4949
|
+
def protect_default_settings # :nodoc:
|
4950
|
+
{
|
4951
|
+
sheet: true,
|
4952
|
+
content: false,
|
4953
|
+
objects: false,
|
4954
|
+
scenarios: false,
|
4955
|
+
format_cells: false,
|
4956
|
+
format_columns: false,
|
4957
|
+
format_rows: false,
|
4958
|
+
insert_columns: false,
|
4959
|
+
insert_rows: false,
|
4960
|
+
insert_hyperlinks: false,
|
4961
|
+
delete_columns: false,
|
4962
|
+
delete_rows: false,
|
4963
|
+
select_locked_cells: true,
|
4964
|
+
sort: false,
|
4965
|
+
autofilter: false,
|
4966
|
+
pivot_tables: false,
|
4967
|
+
select_unlocked_cells: true
|
4968
|
+
}
|
4969
|
+
end
|
4970
|
+
|
4971
|
+
def expand_formula(formula, function, addition = '')
|
4972
|
+
if formula =~ /\b(#{function})/
|
4973
|
+
formula.gsub(
|
4974
|
+
::Regexp.last_match(1),
|
4975
|
+
"_xlfn#{addition}.#{::Regexp.last_match(1)}"
|
4976
|
+
)
|
4977
|
+
else
|
4978
|
+
formula
|
4979
|
+
end
|
4980
|
+
end
|
4987
4981
|
end
|
4988
4982
|
end
|