write_xlsx 1.07.0 → 1.08.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Changes +31 -0
  3. data/README.md +1 -1
  4. data/lib/write_xlsx/chart.rb +3 -5
  5. data/lib/write_xlsx/drawing.rb +80 -17
  6. data/lib/write_xlsx/package/app.rb +3 -3
  7. data/lib/write_xlsx/package/conditional_format.rb +2 -8
  8. data/lib/write_xlsx/package/packager.rb +1 -0
  9. data/lib/write_xlsx/package/styles.rb +16 -3
  10. data/lib/write_xlsx/utility.rb +5 -1
  11. data/lib/write_xlsx/version.rb +1 -1
  12. data/lib/write_xlsx/workbook.rb +23 -0
  13. data/lib/write_xlsx/worksheet/data_validation.rb +1 -6
  14. data/lib/write_xlsx/worksheet.rb +71 -20
  15. data/test/drawing/{test_write_ext.rb → test_write_xdr_ext.rb} +2 -2
  16. data/test/regression/test_chart_crossing01.rb +1 -1
  17. data/test/regression/test_chart_crossing05.rb +46 -0
  18. data/test/regression/test_chart_crossing06.rb +46 -0
  19. data/test/regression/test_chart_data_labels48.rb +55 -0
  20. data/test/regression/test_chart_data_labels49.rb +55 -0
  21. data/test/regression/test_chart_data_labels50.rb +57 -0
  22. data/test/regression/test_format16.rb +24 -0
  23. data/test/regression/test_format17.rb +24 -0
  24. data/test/regression/test_image52.rb +26 -0
  25. data/test/regression/test_image53.rb +26 -0
  26. data/test/regression/test_image54.rb +26 -0
  27. data/test/regression/test_image55.rb +27 -0
  28. data/test/regression/test_protect04.rb +32 -0
  29. data/test/regression/test_protect05.rb +35 -0
  30. data/test/regression/test_protect06.rb +35 -0
  31. data/test/regression/test_protect07.rb +23 -0
  32. data/test/regression/xlsx_files/chart_crossing05.xlsx +0 -0
  33. data/test/regression/xlsx_files/chart_crossing06.xlsx +0 -0
  34. data/test/regression/xlsx_files/chart_data_labels48.xlsx +0 -0
  35. data/test/regression/xlsx_files/chart_data_labels49.xlsx +0 -0
  36. data/test/regression/xlsx_files/chart_data_labels50.xlsx +0 -0
  37. data/test/regression/xlsx_files/format16.xlsx +0 -0
  38. data/test/regression/xlsx_files/format17.xlsx +0 -0
  39. data/test/regression/xlsx_files/image52.xlsx +0 -0
  40. data/test/regression/xlsx_files/image53.xlsx +0 -0
  41. data/test/regression/xlsx_files/image54.xlsx +0 -0
  42. data/test/regression/xlsx_files/image55.xlsx +0 -0
  43. data/test/regression/xlsx_files/protect04.xlsx +0 -0
  44. data/test/regression/xlsx_files/protect05.xlsx +0 -0
  45. data/test/regression/xlsx_files/protect06.xlsx +0 -0
  46. data/test/regression/xlsx_files/protect07.xlsx +0 -0
  47. data/test/utility/test_range.rb +20 -0
  48. metadata +70 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 677847fdb975ab7253b3a5ed5f6aa779a9bbf2a459e941f5037b7d557e2abfe4
4
- data.tar.gz: 4c4b0b03ce3b510a0ad6423c30a76fc778463afe92f992da52b49bbe65c7a1ac
3
+ metadata.gz: 0bce1d8cc8f140d38ac7d9149e644d8b1d5133c80933fc2faa2a33d25dcbd168
4
+ data.tar.gz: 4edf0897d13ec8cee0b545177fe4117286067ba2e14b89c2866192885414633b
5
5
  SHA512:
6
- metadata.gz: b3f81d2d54e5f7a7170c34e7666f58c7f4f77b993ff3b95263327b0ff8edff5c9a0b1a6ea825ece5b43c5b1d2b91a6f01764237f22ee6442bd8c747de8e04150
7
- data.tar.gz: 3e49064459345dbcc5d97fef0f8154159776e8361cb0709e8a4564f32dd062fe95b6e4f08976df81b5ac478a40287be9a33086726444af3c75d1cbeb5c869dc2
6
+ metadata.gz: 267233a3a8222f817bc455b5d8723bf29808854a7b5410c090becd44baee604201b53792f03b29db84c2fa82f9a4085e11175893abb11bab50b1a74c9f0526e8
7
+ data.tar.gz: bc5138fcae79f92aaa9a5d6d708575e9615cec51e4c229ec46e0e75e45bc1eca37056f27466f321c8ad6376119bd37c61754b8809c991fd2352ca8a1108089f0
data/Changes CHANGED
@@ -1,5 +1,36 @@
1
1
  Change history of write_xlsx rubygem.
2
2
 
3
+ 2021-11-19 v1.08.0
4
+
5
+ Added ability to add accessibility options "description" and
6
+ "decorative" to images via insert_image().
7
+
8
+ Added the workbook read_only_recommended() method to set the Excel
9
+ "Read-only Recommended" option that is available when saving a file.
10
+
11
+ Added option to set a chart crossing to 'min' as well as the existing
12
+ 'max' option. The 'min' option isn't available in the Excel interface
13
+ but can be enabled via VBA.
14
+
15
+ Added option to unprotect ranges in protected worksheets.
16
+
17
+ Added check, and warning, for worksheet tables with no data row. Either
18
+ with or without a header row.
19
+
20
+ Added ignore_errors() worksheet method to ignore Excel worksheet
21
+ errors/warnings in user defined ranges.
22
+
23
+ Fixed issue where pattern formats without colours where given a default
24
+ black fill colour.
25
+
26
+ Fix issue where custom chart data labels didn't inherit the position for
27
+ the data labels in the series.
28
+
29
+ Fixed issue with relative url links in images.
30
+
31
+ Fixed issue where headers/footers were restricted to 254 characters
32
+ instead of 255.
33
+
3
34
  2021-02-17 v1.07.0
4
35
 
5
36
  Added support for Border, Fill, Pattern and Gradient formatting to chart
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  gem to create a new file in the Excel 2007+ XLSX format, and you can use the
7
7
  same interface as writeexcel gem. write_xlsx is converted from Perl's module
8
- [Excel::Writer::XLSX-1.07](https://github.com/jmcnamara/excel-writer-xlsx/tree/CPAN_1.07)
8
+ [Excel::Writer::XLSX-1.08](https://github.com/jmcnamara/excel-writer-xlsx/tree/CPAN_1.08)
9
9
 
10
10
  ## Description
11
11
 
@@ -1397,7 +1397,7 @@ module Writexlsx
1397
1397
 
1398
1398
  def write_crossing(crossing)
1399
1399
  # Note, the category crossing comes from the value axis.
1400
- if nil_or_max?(crossing)
1400
+ if [nil, 'max', 'min'].include?(crossing)
1401
1401
  # Write the c:crosses element.
1402
1402
  write_crosses(crossing)
1403
1403
  else
@@ -2418,12 +2418,14 @@ module Writexlsx
2418
2418
  elsif label[:formula]
2419
2419
  write_custom_label_formula(label)
2420
2420
 
2421
+ write_d_lbl_pos(parent[:position]) if parent[:position]
2421
2422
  write_show_val if parent[:value]
2422
2423
  write_show_cat_name if parent[:category]
2423
2424
  write_show_ser_name if parent[:series_name]
2424
2425
  elsif label[:value]
2425
2426
  write_custom_label_str(label)
2426
2427
 
2428
+ write_d_lbl_pos(parent[:position]) if parent[:position]
2427
2429
  write_show_val if parent[:value]
2428
2430
  write_show_cat_name if parent[:category]
2429
2431
  write_show_ser_name if parent[:series_name]
@@ -2926,9 +2928,5 @@ module Writexlsx
2926
2928
  @writer.empty_tag(tag)
2927
2929
  end
2928
2930
  end
2929
-
2930
- def nil_or_max?(val) # :nodoc:
2931
- val.nil? || val == 'max'
2932
- end
2933
2931
  end
2934
2932
  end
@@ -5,11 +5,11 @@ require 'write_xlsx/utility'
5
5
  module Writexlsx
6
6
  class Drawing
7
7
  attr_accessor :type, :dimensions, :width, :height, :description, :shape, :anchor, :rel_index, :url_rel_index
8
- attr_reader :tip
8
+ attr_reader :tip, :decorative
9
9
 
10
- def initialize(type, dimensions, width, height, description, shape, anchor, rel_index = nil, url_rel_index = nil, tip = nil)
11
- @type, @dimensions, @width, @height, @description, @shape, @anchor, @rel_index, @url_rel_index, @tip =
12
- type, dimensions, width, height, description, shape, anchor, rel_index, url_rel_index, tip
10
+ def initialize(type, dimensions, width, height, description, shape, anchor, rel_index = nil, url_rel_index = nil, tip = nil, decorative = nil)
11
+ @type, @dimensions, @width, @height, @description, @shape, @anchor, @rel_index, @url_rel_index, @tip, @decorative =
12
+ type, dimensions, width, height, description, shape, anchor, rel_index, url_rel_index, tip, decorative
13
13
  end
14
14
  end
15
15
 
@@ -92,6 +92,7 @@ module Writexlsx
92
92
  rel_index = drawing.rel_index
93
93
  url_rel_index = drawing.url_rel_index
94
94
  tip = drawing.tip
95
+ decorative = drawing.decorative
95
96
 
96
97
  col_from, row_from, col_from_offset, row_from_offset,
97
98
  col_to, row_to, col_to_offset, row_to_offset, col_absolute, row_absolute = drawing.dimensions
@@ -124,7 +125,7 @@ module Writexlsx
124
125
  write_pic(
125
126
  index, rel_index, col_absolute,
126
127
  row_absolute, width, height,
127
- description, url_rel_index , tip
128
+ description, url_rel_index , tip, decorative
128
129
  )
129
130
  else
130
131
  # Write the xdr:sp element for shapes.
@@ -148,13 +149,13 @@ module Writexlsx
148
149
  write_pos(0, 0)
149
150
 
150
151
  # Write the xdr:ext element.
151
- write_ext(9308969, 6078325)
152
+ write_xdr_ext(9308969, 6078325)
152
153
  else
153
154
  # Write the xdr:pos element.
154
155
  write_pos(0, -47625)
155
156
 
156
157
  # Write the xdr:ext element.
157
- write_ext(6162675, 6124575)
158
+ write_xdr_ext(6162675, 6124575)
158
159
  end
159
160
 
160
161
  # Write the xdr:graphicFrame element.
@@ -242,7 +243,7 @@ module Writexlsx
242
243
  #
243
244
  # Write the <xdr:ext> element.
244
245
  #
245
- def write_ext(cx, cy)
246
+ def write_xdr_ext(cx, cy)
246
247
  attributes = [
247
248
  ['cx', cx],
248
249
  ['cy', cy]
@@ -286,19 +287,25 @@ module Writexlsx
286
287
  #
287
288
  # Write the <xdr:cNvPr> element.
288
289
  #
289
- def write_c_nv_pr(index, name, description = nil, url_rel_index = nil, tip = nil)
290
+ def write_c_nv_pr(index, name, description = nil, url_rel_index = nil, tip = nil, decorative = nil)
290
291
  attributes = [
291
292
  ['id', index],
292
293
  ['name', name]
293
294
  ]
294
295
 
295
296
  # Add description attribute for images.
296
- attributes << ['descr', description] if description
297
+ attributes << ['descr', description] if ptrue?(description) && !ptrue?(decorative)
297
298
 
298
- if ptrue?(url_rel_index)
299
+ if ptrue?(url_rel_index) || ptrue?(decorative)
299
300
  @writer.tag_elements('xdr:cNvPr', attributes) do
300
- # Write the a:hlinkClick element.
301
- write_a_hlink_click(url_rel_index, tip)
301
+ if ptrue?(url_rel_index)
302
+ # Write the a:hlinkClick element.
303
+ write_a_hlink_click(url_rel_index, tip)
304
+ end
305
+ if ptrue?(decorative)
306
+ # Write the adec:decorative element.
307
+ write_decorative
308
+ end
302
309
  end
303
310
  else
304
311
  @writer.empty_tag('xdr:cNvPr', attributes)
@@ -323,6 +330,62 @@ module Writexlsx
323
330
  @writer.empty_tag('a:hlinkClick', attributes)
324
331
  end
325
332
 
333
+ #
334
+ # Write the <adec:decorative> element.
335
+ #
336
+ def write_decorative
337
+ @writer.tag_elements('a:extLst') do
338
+ write_a_uri_ext('{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}')
339
+ write_a16_creation_id
340
+ @writer.end_tag('a:ext')
341
+
342
+ write_a_uri_ext('{C183D7F6-B498-43B3-948B-1728B52AA6E4}')
343
+ write_adec_decorative
344
+ @writer.end_tag('a:ext')
345
+ end
346
+ end
347
+
348
+ #
349
+ # Write the <a:ext> element.
350
+ #
351
+ def write_a_uri_ext(uri)
352
+ attributes = [
353
+ ['uri', uri]
354
+ ]
355
+
356
+ @writer.start_tag('a:ext', attributes)
357
+ end
358
+
359
+ #
360
+ # Write the <adec:decorative> element.
361
+ #
362
+ def write_adec_decorative
363
+ xmlns_adec = 'http://schemas.microsoft.com/office/drawing/2017/decorative'
364
+ val = 1
365
+
366
+ attributes = [
367
+ ['xmlns:adec', xmlns_adec],
368
+ ['val', val]
369
+ ]
370
+
371
+ @writer.empty_tag('adec:decorative', attributes)
372
+ end
373
+
374
+ #
375
+ # Write the <a16:creationId> element.
376
+ #
377
+ def write_a16_creation_id
378
+ xmlns_a_16 = 'http://schemas.microsoft.com/office/drawing/2014/main'
379
+ id = '{00000000-0008-0000-0000-000002000000}'
380
+
381
+ attributes = [
382
+ ['xmlns:a16', xmlns_a_16],
383
+ ['id', id]
384
+ ]
385
+
386
+ @writer.empty_tag('a16:creationId', attributes)
387
+ end
388
+
326
389
  #
327
390
  # Write the <xdr:cNvGraphicFramePr> element.
328
391
  #
@@ -527,10 +590,10 @@ module Writexlsx
527
590
  #
528
591
  # Write the <xdr:pic> element.
529
592
  #
530
- def write_pic(index, rel_index, col_absolute, row_absolute, width, height, description, url_rel_index, tip)
593
+ def write_pic(index, rel_index, col_absolute, row_absolute, width, height, description, url_rel_index, tip, decorative)
531
594
  @writer.tag_elements('xdr:pic') do
532
595
  # Write the xdr:nvPicPr element.
533
- write_nv_pic_pr(index, rel_index, description, url_rel_index, tip)
596
+ write_nv_pic_pr(index, rel_index, description, url_rel_index, tip, decorative)
534
597
  # Write the xdr:blipFill element.
535
598
  write_blip_fill(rel_index)
536
599
 
@@ -546,12 +609,12 @@ module Writexlsx
546
609
  #
547
610
  # Write the <xdr:nvPicPr> element.
548
611
  #
549
- def write_nv_pic_pr(index, rel_index, description, url_rel_index, tip)
612
+ def write_nv_pic_pr(index, rel_index, description, url_rel_index, tip, decorative)
550
613
  @writer.tag_elements('xdr:nvPicPr') do
551
614
  # Write the xdr:cNvPr element.
552
615
  write_c_nv_pr(
553
616
  index + 1, "Picture #{index}", description,
554
- url_rel_index, tip
617
+ url_rel_index, tip, decorative
555
618
  )
556
619
  # Write the xdr:cNvPicPr element.
557
620
  write_c_nv_pic_pr
@@ -7,6 +7,7 @@ module Writexlsx
7
7
  class App
8
8
 
9
9
  include Writexlsx::Utility
10
+ attr_writer :doc_security
10
11
 
11
12
  def initialize(workbook)
12
13
  @writer = Package::XMLWriterSimple.new
@@ -14,6 +15,7 @@ module Writexlsx
14
15
  @part_names = []
15
16
  @heading_pairs = []
16
17
  @properties = {}
18
+ @doc_security = 0
17
19
  end
18
20
 
19
21
  def set_xml_writer(filename)
@@ -123,9 +125,7 @@ module Writexlsx
123
125
  # Write the <DocSecurity> element.
124
126
  #
125
127
  def write_doc_security
126
- data = 0
127
-
128
- @writer.data_element('DocSecurity', data)
128
+ @writer.data_element('DocSecurity', @doc_security)
129
129
  end
130
130
 
131
131
  #
@@ -397,14 +397,8 @@ module Writexlsx
397
397
  def range_start_cell_for_conditional_formatting(*args) # :nodoc:
398
398
  row1, row2, col1, col2, user_range, param =
399
399
  row_col_param_for_conditional_formatting(*args)
400
- # If the first and last cell are the same write a single cell.
401
- if row1 == row2 && col1 == col2
402
- range = xl_rowcol_to_cell(row1, col1)
403
- @start_cell = range
404
- else
405
- range = xl_range(row1, row2, col1, col2)
406
- @start_cell = xl_rowcol_to_cell(row1, col1)
407
- end
400
+ range = xl_range(row1, row2, col1, col2)
401
+ @start_cell = xl_rowcol_to_cell(row1, col1)
408
402
 
409
403
  # Override with user defined multiple range if provided.
410
404
  range = user_range if user_range
@@ -155,6 +155,7 @@ module Writexlsx
155
155
  app.add_named_ranges_parts
156
156
 
157
157
  app.set_properties(@workbook.doc_properties)
158
+ app.doc_security = @workbook.read_only
158
159
 
159
160
  FileUtils.mkdir_p("#{@package_dir}/docProps")
160
161
  app.set_xml_writer("#{@package_dir}/docProps/app.xml")
@@ -253,11 +253,22 @@ module Writexlsx
253
253
  # Write the <fill> element.
254
254
  #
255
255
  def write_fill(format, dxf_format = nil)
256
- @writer.tag_elements('fill' ) do
257
- write_fill_base(format, dxf_format)
256
+ # Special handling for pattern only case.
257
+ if pattern_only_case?(format, dxf_format)
258
+ write_default_fill(PATTERNS[format.pattern])
259
+ else
260
+ @writer.tag_elements('fill' ) do
261
+ write_fill_base(format, dxf_format)
262
+ end
258
263
  end
259
264
  end
260
265
 
266
+ def pattern_only_case?(format, dxf_format)
267
+ bg_color, fg_color = bg_and_fg_color(format, dxf_format)
268
+
269
+ !ptrue?(fg_color) && !ptrue?(bg_color) && ptrue?(format.pattern)
270
+ end
271
+
261
272
  def write_fill_base(format, dxf_format)
262
273
  # The "none" pattern is handled differently for dxf formats.
263
274
  if dxf_format && format.pattern <= 1
@@ -281,7 +292,9 @@ module Writexlsx
281
292
  if bg_color && bg_color != 0
282
293
  @writer.empty_tag('bgColor', [ ['rgb', palette_color(bg_color)] ])
283
294
  else
284
- @writer.empty_tag('bgColor', [ ['indexed', 64] ]) if !dxf_format
295
+ if !dxf_format && format.pattern <= 1
296
+ @writer.empty_tag('bgColor', [ ['indexed', 64] ])
297
+ end
285
298
  end
286
299
  end
287
300
 
@@ -60,7 +60,11 @@ module Writexlsx
60
60
  range1 = xl_rowcol_to_cell(row_1, col_1, row_abs_1, col_abs_1)
61
61
  range2 = xl_rowcol_to_cell(row_2, col_2, row_abs_2, col_abs_2)
62
62
 
63
- "#{range1}:#{range2}"
63
+ if range1 == range2
64
+ range1
65
+ else
66
+ "#{range1}:#{range2}"
67
+ end
64
68
  end
65
69
 
66
70
  def xl_range_formula(sheetname, row_1, row_2, col_1, col_2)
@@ -1 +1 @@
1
- WriteXLSX_VERSION = "1.07.0"
1
+ WriteXLSX_VERSION = "1.08.0"
@@ -52,6 +52,7 @@ module Writexlsx
52
52
  attr_reader :max_url_length # :nodoc:
53
53
  attr_reader :strings_to_urls # :nodoc:
54
54
  attr_reader :default_url_format # :nodoc:
55
+ attr_reader :read_only # :nodoc:
55
56
 
56
57
  #
57
58
  # A new Excel workbook is created using the +new+ constructor
@@ -133,6 +134,7 @@ module Writexlsx
133
134
 
134
135
  @max_url_length = 2079
135
136
  @has_comments = false
137
+ @read_only = 0
136
138
  if options[:max_url_length]
137
139
  @max_url_length = options[:max_url_length]
138
140
 
@@ -292,6 +294,9 @@ module Writexlsx
292
294
  # Write the XLSX file version.
293
295
  write_file_version
294
296
 
297
+ # Write the fileSharing element.
298
+ write_file_sharing
299
+
295
300
  # Write the workbook properties.
296
301
  write_workbook_pr
297
302
 
@@ -954,6 +959,13 @@ module Writexlsx
954
959
  end
955
960
  end
956
961
 
962
+ #
963
+ # Set the Excel "Read-only recommended" save option.
964
+ #
965
+ def read_only_recommended
966
+ @read_only = 2
967
+ end
968
+
957
969
  #
958
970
  # set_calc_mode()
959
971
  #
@@ -1311,6 +1323,17 @@ module Writexlsx
1311
1323
  @writer.empty_tag('fileVersion', attributes)
1312
1324
  end
1313
1325
 
1326
+ #
1327
+ # Write the <fileSharing> element.
1328
+ #
1329
+ def write_file_sharing
1330
+ return if !ptrue?(@read_only)
1331
+
1332
+ attributes = []
1333
+ attributes << ['readOnlyRecommended', 1]
1334
+ @writer.empty_tag('fileSharing', attributes)
1335
+ end
1336
+
1314
1337
  def write_workbook_pr #:nodoc:
1315
1338
  attributes = []
1316
1339
  attributes << ['codeName', @vba_codename] if ptrue?(@vba_codename)
@@ -138,12 +138,7 @@ module Writexlsx
138
138
  row_first, row_last = row_last, row_first if row_first > row_last
139
139
  col_first, col_last = col_last, col_first if col_first > col_last
140
140
 
141
- # If the first and last cell are the same write a single cell.
142
- if row_first == row_last && col_first == col_last
143
- sqref += xl_rowcol_to_cell(row_first, col_first)
144
- else
145
- sqref += xl_range(row_first, row_last, col_first, col_last)
146
- end
141
+ sqref += xl_range(row_first, row_last, col_first, col_last)
147
142
  end
148
143
 
149
144
  if @validate != 'none'
@@ -388,6 +388,9 @@ module Writexlsx
388
388
  @data_bars_2010 = []
389
389
  @dxf_priority = 1
390
390
 
391
+ @protected_ranges = []
392
+ @num_protected_ranges = 0
393
+
391
394
  if excel2003_style?
392
395
  @original_row_height = 12.75
393
396
  @default_row_height = 12.75
@@ -414,6 +417,7 @@ module Writexlsx
414
417
  write_cols
415
418
  write_sheet_data
416
419
  write_sheet_protection
420
+ write_protected_ranges
417
421
  # write_sheet_calc_pr
418
422
  write_phonetic_pr if excel2003_style?
419
423
  write_auto_filter
@@ -616,7 +620,25 @@ module Writexlsx
616
620
 
617
621
  # Set the password after the user defined values.
618
622
  @protect[:password] =
619
- sprintf("%X", encode_password(password)) if password && password != ''
623
+ encode_password(password) if password && password != ''
624
+ end
625
+
626
+ #
627
+ # Unprotect ranges within a protected worksheet.
628
+ #
629
+ def unprotect_range(range, range_name = nil, password = nil)
630
+ if range.nil?
631
+ raise "The range must be defined in unprotect_range())\n"
632
+ else
633
+ range.gsub!(/\$/, "")
634
+ range.sub!(/^=/, "")
635
+ @num_protected_ranges += 1
636
+ end
637
+
638
+ range_name ||= "Range#{@num_protected_ranges}"
639
+ password &&= encode_password(password)
640
+
641
+ @protected_ranges << [range, range_name, password]
620
642
  end
621
643
 
622
644
  def protect_default_settings # :nodoc:
@@ -818,12 +840,7 @@ module Writexlsx
818
840
  row_first, row_last = row_last, row_first if row_first > row_last
819
841
  col_first, col_last = col_last, col_first if col_first > col_last
820
842
 
821
- # If the first and last cell are the same write a single cell.
822
- if row_first == row_last && col_first == col_last
823
- sqref = active_cell
824
- else
825
- sqref = xl_range(row_first, row_last, col_first, col_last)
826
- end
843
+ sqref = xl_range(row_first, row_last, col_first, col_last)
827
844
  else # Single cell selection.
828
845
  sqref = active_cell
829
846
  end
@@ -3048,14 +3065,16 @@ module Writexlsx
3048
3065
 
3049
3066
  if options.first.class == Hash
3050
3067
  # Newer hash bashed options
3051
- params = options.first
3052
- x_offset = params[:x_offset]
3053
- y_offset = params[:y_offset]
3054
- x_scale = params[:x_scale]
3055
- y_scale = params[:y_scale]
3056
- anchor = params[:object_position]
3057
- url = params[:url]
3058
- tip = params[:tip]
3068
+ params = options.first
3069
+ x_offset = params[:x_offset]
3070
+ y_offset = params[:y_offset]
3071
+ x_scale = params[:x_scale]
3072
+ y_scale = params[:y_scale]
3073
+ anchor = params[:object_position]
3074
+ url = params[:url]
3075
+ tip = params[:tip]
3076
+ description = params[:description]
3077
+ decorative = params[:decorative]
3059
3078
  else
3060
3079
  x_offset, y_offset, x_scale, y_scale, anchor = options
3061
3080
  end
@@ -3067,7 +3086,7 @@ module Writexlsx
3067
3086
 
3068
3087
  @images << [
3069
3088
  row, col, image, x_offset, y_offset,
3070
- x_scale, y_scale, url, tip, anchor
3089
+ x_scale, y_scale, url, tip, anchor, description, decorative
3071
3090
  ]
3072
3091
  end
3073
3092
 
@@ -5744,7 +5763,7 @@ module Writexlsx
5744
5763
  name = chart.name
5745
5764
 
5746
5765
  # Create a Drawing object to use with worksheet unless one already exists.
5747
- drawing = Drawing.new(drawing_type, dimensions, 0, 0, name, nil, anchor, drawing_rel_index, 0, nil)
5766
+ drawing = Drawing.new(drawing_type, dimensions, 0, 0, name, nil, anchor, drawing_rel_index, 0, nil, 0)
5748
5767
  if !drawings?
5749
5768
  @drawings = Drawings.new
5750
5769
  @drawings.add_drawing_object(drawing)
@@ -6470,7 +6489,7 @@ module Writexlsx
6470
6489
  drawing_type = 2
6471
6490
 
6472
6491
  row, col, image, x_offset, y_offset,
6473
- x_scale, y_scale, url, tip, anchor = @images[index]
6492
+ x_scale, y_scale, url, tip, anchor, description, decorative = @images[index]
6474
6493
 
6475
6494
  width *= x_scale
6476
6495
  height *= y_scale
@@ -6485,7 +6504,7 @@ module Writexlsx
6485
6504
  height = (0.5 + (height * 9_525)).to_i
6486
6505
 
6487
6506
  # Create a Drawing object to use with worksheet unless one already exists.
6488
- drawing = Drawing.new(drawing_type, dimensions, width, height, name, nil, anchor, 0, 0, tip)
6507
+ drawing = Drawing.new(drawing_type, dimensions, width, height, name, nil, anchor, 0, 0, tip, decorative)
6489
6508
  if !drawings?
6490
6509
  drawings = Drawings.new
6491
6510
  drawings.embedded = 1
@@ -6498,6 +6517,10 @@ module Writexlsx
6498
6517
  end
6499
6518
  drawings.add_drawing_object(drawing)
6500
6519
 
6520
+ if description
6521
+ drawing.description = description
6522
+ end
6523
+
6501
6524
  if url
6502
6525
  rel_type = '/hyperlink'
6503
6526
  target_mode = 'External'
@@ -6651,7 +6674,7 @@ EOS
6651
6674
  shape.calc_position_emus(self)
6652
6675
 
6653
6676
  drawing_type = 3
6654
- drawing = Drawing.new(drawing_type, shape.dimensions, shape.width_emu, shape.height_emu, shape.name, shape, shape.anchor, drawing_rel_index, 0, nil)
6677
+ drawing = Drawing.new(drawing_type, shape.dimensions, shape.width_emu, shape.height_emu, shape.name, shape, shape.anchor, drawing_rel_index, 0, nil, 0)
6655
6678
  drawings.add_drawing_object(drawing)
6656
6679
  end
6657
6680
  public :prepare_shape
@@ -6741,6 +6764,8 @@ EOS
6741
6764
  chars.each { |c| encoded_password ^= c }
6742
6765
  encoded_password ^= count
6743
6766
  encoded_password ^= 0xCE4B
6767
+
6768
+ sprintf("%X", encoded_password)
6744
6769
  end
6745
6770
 
6746
6771
  #
@@ -7556,6 +7581,32 @@ EOS
7556
7581
  @writer.empty_tag('sheetProtection', attributes)
7557
7582
  end
7558
7583
 
7584
+ #
7585
+ # Write the <protectedRanges> element.
7586
+ #
7587
+ def write_protected_ranges
7588
+ return if @num_protected_ranges == 0
7589
+
7590
+ @writer.tag_elements('protectedRanges') do
7591
+ @protected_ranges.each do |protected_range|
7592
+ write_protected_range(*protected_range)
7593
+ end
7594
+ end
7595
+ end
7596
+
7597
+ #
7598
+ # Write the <protectedRange> element.
7599
+ #
7600
+ def write_protected_range(sqref, name, password)
7601
+ attributes = []
7602
+
7603
+ attributes << ['password', password] if password
7604
+ attributes << ['sqref', sqref]
7605
+ attributes << ['name', name]
7606
+
7607
+ @writer.empty_tag('protectedRange', attributes)
7608
+ end
7609
+
7559
7610
  #
7560
7611
  # Write the <drawing> elements.
7561
7612
  #
@@ -2,7 +2,7 @@
2
2
  require 'helper'
3
3
  require 'write_xlsx/drawing'
4
4
 
5
- class TestWriteExt < Minitest::Test
5
+ class TestWriteXdrExt < Minitest::Test
6
6
  def setup
7
7
  @drawing = Writexlsx::Drawings.new
8
8
  end
@@ -10,7 +10,7 @@ class TestWriteExt < Minitest::Test
10
10
  def test_write_ext
11
11
  expected = '<xdr:ext cx="9308969" cy="6078325"/>'
12
12
 
13
- @drawing.__send__(:write_ext, 9308969, 6078325)
13
+ @drawing.__send__(:write_xdr_ext, 9308969, 6078325)
14
14
  result = @drawing.instance_variable_get(:@writer).string
15
15
 
16
16
  assert_equal(expected, result)
@@ -33,7 +33,7 @@ class TestRegressionChartCrossing01 < Minitest::Test
33
33
 
34
34
  chart.set_y_axis(:crossing => 'max')
35
35
 
36
- # Not stictly required. Just to match reference file.
36
+ # Not strictly required. Just to match reference file.
37
37
  chart.set_x_axis(:position => 't')
38
38
 
39
39
  worksheet.insert_chart('E9', chart)