write_xlsx 1.04.0 → 1.08.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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/Changes +50 -0
  4. data/README.md +1 -1
  5. data/examples/chart_data_labels.rb +320 -0
  6. data/examples/ignore_errors.rb +39 -0
  7. data/ignore_errors.xlsx +0 -0
  8. data/lib/write_xlsx/chart/series.rb +100 -0
  9. data/lib/write_xlsx/chart.rb +155 -33
  10. data/lib/write_xlsx/drawing.rb +80 -17
  11. data/lib/write_xlsx/package/app.rb +3 -3
  12. data/lib/write_xlsx/package/conditional_format.rb +2 -8
  13. data/lib/write_xlsx/package/packager.rb +1 -0
  14. data/lib/write_xlsx/package/styles.rb +16 -3
  15. data/lib/write_xlsx/package/table.rb +8 -0
  16. data/lib/write_xlsx/utility.rb +9 -3
  17. data/lib/write_xlsx/version.rb +1 -1
  18. data/lib/write_xlsx/workbook.rb +23 -0
  19. data/lib/write_xlsx/worksheet/data_validation.rb +1 -6
  20. data/lib/write_xlsx/worksheet.rb +160 -37
  21. data/test/drawing/{test_write_ext.rb → test_write_xdr_ext.rb} +2 -2
  22. data/test/perl_output/chart_data_labels.xlsx +0 -0
  23. data/test/perl_output/ignore_errors.xlsx +0 -0
  24. data/test/regression/test_array_formula04.rb +31 -0
  25. data/test/regression/test_chart_crossing01.rb +1 -1
  26. data/test/regression/test_chart_crossing05.rb +46 -0
  27. data/test/regression/test_chart_crossing06.rb +46 -0
  28. data/test/regression/test_chart_data_labels26.rb +44 -0
  29. data/test/regression/test_chart_data_labels27.rb +44 -0
  30. data/test/regression/test_chart_data_labels28.rb +52 -0
  31. data/test/regression/test_chart_data_labels29.rb +43 -0
  32. data/test/regression/test_chart_data_labels30.rb +46 -0
  33. data/test/regression/test_chart_data_labels31.rb +49 -0
  34. data/test/regression/test_chart_data_labels32.rb +54 -0
  35. data/test/regression/test_chart_data_labels33.rb +52 -0
  36. data/test/regression/test_chart_data_labels34.rb +54 -0
  37. data/test/regression/test_chart_data_labels35.rb +46 -0
  38. data/test/regression/test_chart_data_labels36.rb +54 -0
  39. data/test/regression/test_chart_data_labels37.rb +51 -0
  40. data/test/regression/test_chart_data_labels38.rb +54 -0
  41. data/test/regression/test_chart_data_labels39.rb +53 -0
  42. data/test/regression/test_chart_data_labels40.rb +53 -0
  43. data/test/regression/test_chart_data_labels41.rb +54 -0
  44. data/test/regression/test_chart_data_labels42.rb +58 -0
  45. data/test/regression/test_chart_data_labels43.rb +58 -0
  46. data/test/regression/test_chart_data_labels44.rb +56 -0
  47. data/test/regression/test_chart_data_labels45.rb +57 -0
  48. data/test/regression/test_chart_data_labels46.rb +61 -0
  49. data/test/regression/test_chart_data_labels47.rb +61 -0
  50. data/test/regression/test_chart_data_labels48.rb +55 -0
  51. data/test/regression/test_chart_data_labels49.rb +55 -0
  52. data/test/regression/test_chart_data_labels50.rb +57 -0
  53. data/test/regression/test_format16.rb +24 -0
  54. data/test/regression/test_format17.rb +24 -0
  55. data/test/regression/test_header04.rb +30 -0
  56. data/test/regression/test_hyperlink50.rb +27 -0
  57. data/test/regression/test_hyperlink51.rb +27 -0
  58. data/test/regression/test_ignore_error01.rb +23 -0
  59. data/test/regression/test_ignore_error02.rb +24 -0
  60. data/test/regression/test_ignore_error03.rb +26 -0
  61. data/test/regression/test_ignore_error04.rb +26 -0
  62. data/test/regression/test_ignore_error05.rb +32 -0
  63. data/test/regression/test_ignore_error06.rb +32 -0
  64. data/test/regression/test_image52.rb +26 -0
  65. data/test/regression/test_image53.rb +26 -0
  66. data/test/regression/test_image54.rb +26 -0
  67. data/test/regression/test_image55.rb +27 -0
  68. data/test/regression/test_protect04.rb +32 -0
  69. data/test/regression/test_protect05.rb +35 -0
  70. data/test/regression/test_protect06.rb +35 -0
  71. data/test/regression/test_protect07.rb +23 -0
  72. data/test/regression/test_table26.rb +38 -0
  73. data/test/regression/xlsx_files/array_formula04.xlsx +0 -0
  74. data/test/regression/xlsx_files/chart_crossing05.xlsx +0 -0
  75. data/test/regression/xlsx_files/chart_crossing06.xlsx +0 -0
  76. data/test/regression/xlsx_files/chart_data_labels26.xlsx +0 -0
  77. data/test/regression/xlsx_files/chart_data_labels27.xlsx +0 -0
  78. data/test/regression/xlsx_files/chart_data_labels28.xlsx +0 -0
  79. data/test/regression/xlsx_files/chart_data_labels29.xlsx +0 -0
  80. data/test/regression/xlsx_files/chart_data_labels30.xlsx +0 -0
  81. data/test/regression/xlsx_files/chart_data_labels31.xlsx +0 -0
  82. data/test/regression/xlsx_files/chart_data_labels32.xlsx +0 -0
  83. data/test/regression/xlsx_files/chart_data_labels33.xlsx +0 -0
  84. data/test/regression/xlsx_files/chart_data_labels34.xlsx +0 -0
  85. data/test/regression/xlsx_files/chart_data_labels35.xlsx +0 -0
  86. data/test/regression/xlsx_files/chart_data_labels36.xlsx +0 -0
  87. data/test/regression/xlsx_files/chart_data_labels37.xlsx +0 -0
  88. data/test/regression/xlsx_files/chart_data_labels38.xlsx +0 -0
  89. data/test/regression/xlsx_files/chart_data_labels39.xlsx +0 -0
  90. data/test/regression/xlsx_files/chart_data_labels40.xlsx +0 -0
  91. data/test/regression/xlsx_files/chart_data_labels41.xlsx +0 -0
  92. data/test/regression/xlsx_files/chart_data_labels42.xlsx +0 -0
  93. data/test/regression/xlsx_files/chart_data_labels43.xlsx +0 -0
  94. data/test/regression/xlsx_files/chart_data_labels44.xlsx +0 -0
  95. data/test/regression/xlsx_files/chart_data_labels45.xlsx +0 -0
  96. data/test/regression/xlsx_files/chart_data_labels46.xlsx +0 -0
  97. data/test/regression/xlsx_files/chart_data_labels47.xlsx +0 -0
  98. data/test/regression/xlsx_files/chart_data_labels48.xlsx +0 -0
  99. data/test/regression/xlsx_files/chart_data_labels49.xlsx +0 -0
  100. data/test/regression/xlsx_files/chart_data_labels50.xlsx +0 -0
  101. data/test/regression/xlsx_files/format16.xlsx +0 -0
  102. data/test/regression/xlsx_files/format17.xlsx +0 -0
  103. data/test/regression/xlsx_files/header04.xlsx +0 -0
  104. data/test/regression/xlsx_files/hyperlink50.xlsx +0 -0
  105. data/test/regression/xlsx_files/hyperlink51.xlsx +0 -0
  106. data/test/regression/xlsx_files/ignore_error01.xlsx +0 -0
  107. data/test/regression/xlsx_files/ignore_error02.xlsx +0 -0
  108. data/test/regression/xlsx_files/ignore_error03.xlsx +0 -0
  109. data/test/regression/xlsx_files/ignore_error04.xlsx +0 -0
  110. data/test/regression/xlsx_files/ignore_error05.xlsx +0 -0
  111. data/test/regression/xlsx_files/ignore_error06.xlsx +0 -0
  112. data/test/regression/xlsx_files/image52.xlsx +0 -0
  113. data/test/regression/xlsx_files/image53.xlsx +0 -0
  114. data/test/regression/xlsx_files/image54.xlsx +0 -0
  115. data/test/regression/xlsx_files/image55.xlsx +0 -0
  116. data/test/regression/xlsx_files/protect04.xlsx +0 -0
  117. data/test/regression/xlsx_files/protect05.xlsx +0 -0
  118. data/test/regression/xlsx_files/protect06.xlsx +0 -0
  119. data/test/regression/xlsx_files/protect07.xlsx +0 -0
  120. data/test/regression/xlsx_files/table26.xlsx +0 -0
  121. data/test/test_example_match.rb +345 -1
  122. data/test/utility/test_range.rb +20 -0
  123. metadata +209 -8
@@ -34,7 +34,7 @@ module Writexlsx
34
34
  @writer.empty_tag('c:showOutline', attributes) if ptrue?(outline)
35
35
  @writer.empty_tag('c:showKeys', attributes) if ptrue?(show_keys)
36
36
  # Write the table font.
37
- write_tx_pr(nil, font) if ptrue?(font)
37
+ write_tx_pr(font) if ptrue?(font)
38
38
  end
39
39
  end
40
40
 
@@ -783,7 +783,7 @@ module Writexlsx
783
783
  elsif @title.formula
784
784
  write_title_formula(@title, nil, nil, @title.layout, @title.overlay)
785
785
  elsif @title.name
786
- write_title_rich(@title, nil, @title.layout, @title.overlay)
786
+ write_title_rich(@title, nil, @title.name_font, @title.layout, @title.overlay)
787
787
  end
788
788
 
789
789
  # Write the c:plotArea element.
@@ -1204,7 +1204,7 @@ module Writexlsx
1204
1204
  if x_axis.formula
1205
1205
  write_title_formula(x_axis, is_y_axis, @x_axis, x_axis.layout)
1206
1206
  elsif x_axis.name
1207
- write_title_rich(x_axis, is_y_axis, x_axis.layout)
1207
+ write_title_rich(x_axis, is_y_axis, x_axis.name_font, x_axis.layout)
1208
1208
  end
1209
1209
 
1210
1210
  # Write the c:numFmt element.
@@ -1281,7 +1281,7 @@ module Writexlsx
1281
1281
  if y_axis.formula
1282
1282
  write_title_formula(y_axis, @horiz_val_axis, nil, y_axis.layout)
1283
1283
  elsif y_axis.name
1284
- write_title_rich(y_axis, @horiz_val_axis, y_axis.layout)
1284
+ write_title_rich(y_axis, @horiz_val_axis, y_axis.name_font, y_axis.layout)
1285
1285
  end
1286
1286
 
1287
1287
  # Write the c:numberFormat element.
@@ -1356,7 +1356,7 @@ module Writexlsx
1356
1356
  if x_axis.formula
1357
1357
  write_title_formula(x_axis, nil, nil, x_axis.layout)
1358
1358
  elsif x_axis.name
1359
- write_title_rich(x_axis, nil, x_axis.layout)
1359
+ write_title_rich(x_axis, nil, x_axis.name_font, x_axis.layout)
1360
1360
  end
1361
1361
  # Write the c:numFmt element.
1362
1362
  write_number_format(x_axis)
@@ -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
@@ -1693,7 +1693,7 @@ module Writexlsx
1693
1693
  # Write the c:spPr element.
1694
1694
  write_sp_pr(@legend)
1695
1695
  # Write the c:txPr element.
1696
- write_tx_pr(nil, @legend.font) if ptrue?(@legend.font)
1696
+ write_tx_pr(@legend.font) if ptrue?(@legend.font)
1697
1697
  end
1698
1698
  end
1699
1699
 
@@ -1808,10 +1808,10 @@ module Writexlsx
1808
1808
  #
1809
1809
  # Write the <c:title> element for a rich string.
1810
1810
  #
1811
- def write_title_rich(title, is_y_axis = nil, layout = nil, overlay = nil) # :nodoc:
1811
+ def write_title_rich(title, is_y_axis, font, layout, overlay = nil) # :nodoc:
1812
1812
  @writer.tag_elements('c:title') do
1813
1813
  # Write the c:tx element.
1814
- write_tx_rich(title, is_y_axis)
1814
+ write_tx_rich(title, is_y_axis, font)
1815
1815
  # Write the c:layout element.
1816
1816
  write_layout(layout, 'text')
1817
1817
  # Write the c:overlay element.
@@ -1831,15 +1831,17 @@ module Writexlsx
1831
1831
  # Write the c:overlay element.
1832
1832
  write_overlay if overlay
1833
1833
  # Write the c:txPr element.
1834
- write_tx_pr(is_y_axis, axis ? axis.name_font : title.name_font)
1834
+ write_tx_pr(axis ? axis.name_font : title.name_font, is_y_axis)
1835
1835
  end
1836
1836
  end
1837
1837
 
1838
1838
  #
1839
1839
  # Write the <c:tx> element.
1840
1840
  #
1841
- def write_tx_rich(title, is_y_axis) # :nodoc:
1842
- @writer.tag_elements('c:tx') { write_rich(title, is_y_axis) }
1841
+ def write_tx_rich(title, is_y_axis, font) # :nodoc:
1842
+ @writer.tag_elements('c:tx') do
1843
+ write_rich(title, font, is_y_axis)
1844
+ end
1843
1845
  end
1844
1846
 
1845
1847
  #
@@ -1861,10 +1863,11 @@ module Writexlsx
1861
1863
  #
1862
1864
  # Write the <c:rich> element.
1863
1865
  #
1864
- def write_rich(title, is_y_axis) # :nodoc:
1866
+ def write_rich(title, font, is_y_axis, ignore_rich_pr = false) # :nodoc:
1865
1867
  rotation = nil
1866
- if title.name_font && title.name_font[:_rotation]
1867
- rotation = title.name_font[:_rotation]
1868
+
1869
+ if font && font[:_rotation]
1870
+ rotation = font[:_rotation]
1868
1871
  end
1869
1872
  @writer.tag_elements('c:rich') do
1870
1873
  # Write the a:bodyPr element.
@@ -1872,18 +1875,18 @@ module Writexlsx
1872
1875
  # Write the a:lstStyle element.
1873
1876
  write_a_lst_style
1874
1877
  # Write the a:p element.
1875
- write_a_p_rich(title)
1878
+ write_a_p_rich(title, font, ignore_rich_pr)
1876
1879
  end
1877
1880
  end
1878
1881
  #
1879
1882
  # Write the <a:p> element for rich string titles.
1880
1883
  #
1881
- def write_a_p_rich(title) # :nodoc:
1884
+ def write_a_p_rich(title, font, ignore_rich_pr) # :nodoc:
1882
1885
  @writer.tag_elements('a:p') do
1883
1886
  # Write the a:pPr element.
1884
- write_a_p_pr_rich(title.name_font)
1887
+ write_a_p_pr_rich(font) if !ignore_rich_pr
1885
1888
  # Write the a:r element.
1886
- write_a_r(title)
1889
+ write_a_r(title, font)
1887
1890
  end
1888
1891
  end
1889
1892
 
@@ -1897,12 +1900,12 @@ module Writexlsx
1897
1900
  #
1898
1901
  # Write the <a:r> element.
1899
1902
  #
1900
- def write_a_r(title) # :nodoc:
1903
+ def write_a_r(title, font) # :nodoc:
1901
1904
  @writer.tag_elements('a:r') do
1902
1905
  # Write the a:rPr element.
1903
- write_a_r_pr(title.name_font)
1906
+ write_a_r_pr(font)
1904
1907
  # Write the a:t element.
1905
- write_a_t(title.name)
1908
+ write_a_t(title.respond_to?(:name) ? title.name : title)
1906
1909
  end
1907
1910
  end
1908
1911
 
@@ -1967,17 +1970,27 @@ module Writexlsx
1967
1970
  @writer.empty_tag('c:symbol', [ ['val', val] ])
1968
1971
  end
1969
1972
 
1973
+ def has_fill_formatting(element)
1974
+ line = series_property(element, :line)
1975
+ fill = series_property(element, :fill)
1976
+ pattern = series_property(element, :pattern)
1977
+ gradient = series_property(element, :gradient)
1978
+
1979
+ (line && ptrue?(line[:_defined])) ||
1980
+ (fill && ptrue?(fill[:_defined])) || pattern || gradient
1981
+ end
1982
+
1983
+
1970
1984
  #
1971
1985
  # Write the <c:spPr> element.
1972
1986
  #
1973
1987
  def write_sp_pr(series) # :nodoc:
1974
- line = series.line
1975
- fill = series.fill
1976
- pattern = series.pattern if series.respond_to?(:pattern)
1977
- gradient = series.gradient if series.respond_to?(:gradient)
1988
+ return if !has_fill_formatting(series)
1978
1989
 
1979
- return if (!line || !ptrue?(line[:_defined])) &&
1980
- (!fill || !ptrue?(fill[:_defined])) && !pattern && !gradient
1990
+ line = series_property(series, :line)
1991
+ fill = series_property(series, :fill)
1992
+ pattern = series_property(series, :pattern)
1993
+ gradient = series_property(series, :gradient)
1981
1994
 
1982
1995
  @writer.tag_elements('c:spPr') do
1983
1996
  # Write the fill elements for solid charts such as pie/doughnut and bar.
@@ -2002,6 +2015,14 @@ module Writexlsx
2002
2015
  end
2003
2016
  end
2004
2017
 
2018
+ def series_property(object, property)
2019
+ if object.respond_to?(property)
2020
+ object.send(property)
2021
+ elsif object.respond_to?(:[])
2022
+ object[property]
2023
+ end
2024
+ end
2025
+
2005
2026
  #
2006
2027
  # Write the <a:ln> element.
2007
2028
  #
@@ -2349,8 +2370,14 @@ module Writexlsx
2349
2370
  return unless labels
2350
2371
 
2351
2372
  @writer.tag_elements('c:dLbls') do
2373
+ # Write the custom c:dLbl elements.
2374
+ if labels[:custom]
2375
+ write_custom_labels(labels, labels[:custom])
2376
+ end
2352
2377
  # Write the c:numFmt element.
2353
2378
  write_data_label_number_format(labels[:num_format]) if labels[:num_format]
2379
+ # Write the c:spPr element.
2380
+ write_sp_pr(labels)
2354
2381
  # Write the data label font elements.
2355
2382
  write_axis_font(labels[:font]) if labels[:font]
2356
2383
  # Write the c:dLblPos element.
@@ -2372,6 +2399,105 @@ module Writexlsx
2372
2399
  end
2373
2400
  end
2374
2401
 
2402
+ #
2403
+ # Write the <c:dLbl> element.
2404
+ #
2405
+ def write_custom_labels(parent, labels)
2406
+ index = 0
2407
+
2408
+ labels.each do |label|
2409
+ index += 1
2410
+ next if !ptrue?(label)
2411
+
2412
+ @writer.tag_elements('c:dLbl') do
2413
+ # Write the c:idx element.
2414
+ write_idx(index - 1)
2415
+
2416
+ if label[:delete] && label[:delete]
2417
+ write_delete(1)
2418
+ elsif label[:formula]
2419
+ write_custom_label_formula(label)
2420
+
2421
+ write_d_lbl_pos(parent[:position]) if parent[:position]
2422
+ write_show_val if parent[:value]
2423
+ write_show_cat_name if parent[:category]
2424
+ write_show_ser_name if parent[:series_name]
2425
+ elsif label[:value]
2426
+ write_custom_label_str(label)
2427
+
2428
+ write_d_lbl_pos(parent[:position]) if parent[:position]
2429
+ write_show_val if parent[:value]
2430
+ write_show_cat_name if parent[:category]
2431
+ write_show_ser_name if parent[:series_name]
2432
+ else
2433
+ write_custom_label_format_only(label)
2434
+ end
2435
+ end
2436
+ end
2437
+ end
2438
+
2439
+ #
2440
+ # Write parts of the <c:dLbl> element for strings.
2441
+ #
2442
+ def write_custom_label_str(label)
2443
+ value = label[:value]
2444
+ font = label[:font]
2445
+ is_y_axis = 0
2446
+ has_formatting = has_fill_formatting(label)
2447
+
2448
+ # Write the c:layout element.
2449
+ write_layout()
2450
+
2451
+ @writer.tag_elements('c:tx') do
2452
+ # Write the c:rich element.
2453
+ write_rich(value, font, is_y_axis, !has_formatting)
2454
+ end
2455
+
2456
+ # Write the c:cpPr element.
2457
+ write_sp_pr(label)
2458
+ end
2459
+
2460
+ #
2461
+ # Write parts of the <c:dLbl> element for formulas.
2462
+ #
2463
+ def write_custom_label_formula(label)
2464
+ formula = label[:formula]
2465
+ data_id = label[:data_id]
2466
+
2467
+ if data_id
2468
+ data = @formula_data[data_id]
2469
+ end
2470
+
2471
+ # Write the c:layout element.
2472
+ write_layout
2473
+
2474
+ @writer.tag_elements('c:tx') do
2475
+ # Write the c:strRef element.
2476
+ write_str_ref(formula, data, 'str')
2477
+ end
2478
+
2479
+ # Write the data label formatting, if any.
2480
+ write_custom_label_format_only(label)
2481
+ end
2482
+
2483
+ #
2484
+ # Write parts of the <c:dLbl> element for labels where only the formatting has
2485
+ # changed.
2486
+ #
2487
+ def write_custom_label_format_only(label)
2488
+ font = label[:font]
2489
+ has_formatting = has_fill_formatting(label)
2490
+
2491
+ if has_formatting
2492
+ # Write the c:spPr element.
2493
+ write_sp_pr(label)
2494
+ write_tx_pr(font)
2495
+ elsif font
2496
+ @writer.empty_tag('c:spPr')
2497
+ write_tx_pr(font)
2498
+ end
2499
+ end
2500
+
2375
2501
  #
2376
2502
  # Write the <c:showLegendKey> element.
2377
2503
  #
@@ -2700,7 +2826,7 @@ module Writexlsx
2700
2826
 
2701
2827
  # Write the a:srgbClr element.
2702
2828
  # TODO: Wait for a feature request to support transparency.
2703
- write_a_srgb_clr( color );
2829
+ write_a_srgb_clr( color )
2704
2830
  end
2705
2831
  end
2706
2832
  end
@@ -2802,9 +2928,5 @@ module Writexlsx
2802
2928
  @writer.empty_tag(tag)
2803
2929
  end
2804
2930
  end
2805
-
2806
- def nil_or_max?(val) # :nodoc:
2807
- val.nil? || val == 'max'
2808
- end
2809
2931
  end
2810
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
 
@@ -189,6 +189,14 @@ module Writexlsx
189
189
  param[:header_row] ||= 1
190
190
  param[:autofilter] ||= 1
191
191
 
192
+ # Check that there are enough rows.
193
+ num_rows = row2 - row1
194
+ num_rows -= 1 if ptrue?(param[:header_row])
195
+
196
+ if num_rows < 0
197
+ raise "Must have at least one data row in in add_table()"
198
+ end
199
+
192
200
  # If the header row if off the default is to turn autofilter off.
193
201
  param[:autofilter] = 0 if param[:header_row] == 0
194
202
 
@@ -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)
@@ -804,9 +808,9 @@ module Writexlsx
804
808
  #
805
809
  # Write the <c:txPr> element.
806
810
  #
807
- def write_tx_pr(is_y_axis, font) # :nodoc:
811
+ def write_tx_pr(font, is_y_axis = nil) # :nodoc:
808
812
  rotation = nil
809
- if font && font[:_rotation]
813
+ if font && font.respond_to?(:[]) && font[:_rotation]
810
814
  rotation = font[:_rotation]
811
815
  end
812
816
  @writer.tag_elements('c:txPr') do
@@ -903,6 +907,7 @@ module Writexlsx
903
907
  #
904
908
  def get_font_latin_attributes(font)
905
909
  return [] unless font
910
+ return [] unless font.respond_to?(:[])
906
911
 
907
912
  attributes = []
908
913
  attributes << ['typeface', font[:_name]] if ptrue?(font[:_name])
@@ -959,6 +964,7 @@ module Writexlsx
959
964
  #
960
965
  def get_font_style_attributes(font)
961
966
  return [] unless font
967
+ return [] unless font.respond_to?(:[])
962
968
 
963
969
  attributes = []
964
970
  attributes << ['sz', font[:_size]] if ptrue?(font[:_size])
@@ -1 +1 @@
1
- WriteXLSX_VERSION = "1.04.0"
1
+ WriteXLSX_VERSION = "1.08.2"