caxlsx 4.2.0 → 4.4.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/README.md +17 -8
  4. data/Rakefile +2 -9
  5. data/lib/axlsx/drawing/bar_3D_chart.rb +11 -1
  6. data/lib/axlsx/drawing/bar_chart.rb +11 -1
  7. data/lib/axlsx/drawing/chart.rb +1 -0
  8. data/lib/axlsx/drawing/d_lbls.rb +1 -0
  9. data/lib/axlsx/drawing/vml_shape.rb +1 -1
  10. data/lib/axlsx/package.rb +43 -10
  11. data/lib/axlsx/stylesheet/border_pr.rb +1 -0
  12. data/lib/axlsx/stylesheet/pattern_fill.rb +1 -0
  13. data/lib/axlsx/stylesheet/styles.rb +1 -0
  14. data/lib/axlsx/stylesheet/theme.rb +163 -0
  15. data/lib/axlsx/stylesheet/xf.rb +1 -0
  16. data/lib/axlsx/util/buffered_zip_output_stream.rb +2 -2
  17. data/lib/axlsx/util/constants.rb +12 -0
  18. data/lib/axlsx/util/mime_type_utils.rb +72 -13
  19. data/lib/axlsx/util/simple_typed_list.rb +1 -2
  20. data/lib/axlsx/util/uri_utils.rb +70 -0
  21. data/lib/axlsx/util/validators.rb +5 -5
  22. data/lib/axlsx/version.rb +1 -1
  23. data/lib/axlsx/workbook/defined_name.rb +1 -0
  24. data/lib/axlsx/workbook/workbook.rb +7 -0
  25. data/lib/axlsx/workbook/workbook_view.rb +1 -1
  26. data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +1 -1
  27. data/lib/axlsx/workbook/worksheet/cell.rb +3 -3
  28. data/lib/axlsx/workbook/worksheet/col.rb +1 -0
  29. data/lib/axlsx/workbook/worksheet/header_footer.rb +1 -1
  30. data/lib/axlsx/workbook/worksheet/pane.rb +1 -0
  31. data/lib/axlsx/workbook/worksheet/pivot_table.rb +1 -1
  32. data/lib/axlsx/workbook/worksheet/print_options.rb +1 -0
  33. data/lib/axlsx/workbook/worksheet/sheet_calc_pr.rb +1 -0
  34. data/lib/axlsx/workbook/worksheet/table_style_info.rb +1 -0
  35. data/lib/axlsx/workbook/worksheet/worksheet_comments.rb +1 -1
  36. data/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +1 -1
  37. data/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb +1 -0
  38. data/lib/axlsx.rb +2 -0
  39. metadata +9 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b15095b3a73b9f2e11f59bb674964ce206e11e699937e109831d9422fd26750f
4
- data.tar.gz: e20e938a224f9da043fd4c7cfcd2c35af03a5bb037fc9164a59c44c110e3e248
3
+ metadata.gz: 97d26d1c2165080987473b430781c434b27f62510950ce0c27792058ab493bbf
4
+ data.tar.gz: 6d9f1f55b9b6602d86e2f6e30ce5e0589ac811bf49f8991f7dbc7f8fec42e6e3
5
5
  SHA512:
6
- metadata.gz: 8cdd4b3843dd886f864212ecb4a40f528543d4fd1c682079c5497d3c40ea684bc64d214db4a974d3cea2071663bc3cfbd501543d6868077d9b369340c751bd90
7
- data.tar.gz: 520da353dfbc0022ac6a3aae5006e669fd6f291aad95797b2ff24a2cf1634de4f66c52616efa2af5bfbd7524cf91393d56a6e6a433131f988402e277cacbaca5
6
+ metadata.gz: fd8f19deac0273ac8f59aca129190fc395b23077e34bfaafaa50dc48b9e6565430b94c7bbac8f19fd7272edacf0cac04438a2dc464b22670a575be75259cb2fe
7
+ data.tar.gz: 92ef32593a1ba1132b2b53ae08ecd82eb7e538edfdbb5e764fb325c57f6a2c66b56de6b67e04a48aa3dde5b663468a06d876292103f22fcf456b0da6563df967
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@ CHANGELOG
2
2
  ---------
3
3
  - **Unreleased**
4
4
 
5
+ - **September.01.25**: 4.4.0
6
+ - [PR #477](https://github.com/caxlsx/caxlsx/pull/477) Add package-level encryption and password protection.
7
+ - [PR #476](https://github.com/caxlsx/caxlsx/pull/476) Add Excel for Windows integration testing.
8
+ - [PR #476](https://github.com/caxlsx/caxlsx/pull/476) Add Excel for MacOS integration testing.
9
+ - [PR #469](https://github.com/caxlsx/caxlsx/pull/469) Add default theme file to Excel package.
10
+ - [PR #475](https://github.com/caxlsx/caxlsx/pull/475) Use timecop to fix transient time failure in tests
11
+ - [PR #474](https://github.com/caxlsx/caxlsx/pull/474) Add Windows and MacOS to the CI.
12
+ - [PR #474](https://github.com/caxlsx/caxlsx/pull/474) Fix local image file MIME type detection on Windows.
13
+ - [PR #474](https://github.com/caxlsx/caxlsx/pull/474) Load only HTTP headers when determining remote file MIME type.
14
+
15
+ - **August.16.25**: 4.3.0
16
+ - [PR #421](https://github.com/caxlsx/caxlsx/pull/421) Add Rubyzip >= 2.4 support
17
+ - [PR #448](https://github.com/caxlsx/caxlsx/pull/448) Fix Bar Chart: set axis position for Apple Numbers compatibility
18
+ - [PR #466](https://github.com/caxlsx/caxlsx/pull/466) Use RubyGem's trusted publishing
19
+ - [PR #467](https://github.com/caxlsx/caxlsx/pull/467) Add JRuby 10.0 to the CI
20
+
5
21
  - **December.15.24**: 4.2.0
6
22
  - [PR #359](https://github.com/caxlsx/caxlsx/pull/359) Add `PivotTable#grand_totals` option to remove grand totals row/col
7
23
  - [PR #362](https://github.com/caxlsx/caxlsx/pull/362) Use widest width even if provided as fixed value
data/README.md CHANGED
@@ -51,20 +51,21 @@ cell level input data validation.
51
51
 
52
52
  15. Support for page margins and print options
53
53
 
54
- 16. Support for password and non password based sheet protection.
54
+ 16. Support for workbook-level encryption and password protection (requires [ooxml_crypt](https://github.com/teamsimplepay/ooxml_crypt) gem which only supports MRI Ruby.)
55
55
 
56
- 17. First stage interoperability support for GoogleDocs, LibreOffice,
57
- and Numbers
56
+ 17. Support for sheet-level password and non-password protection.
58
57
 
59
- 18. Support for defined names, which gives you repeated header rows for printing.
58
+ 18. First stage interoperability support for GoogleDocs, LibreOffice, and Numbers.
60
59
 
61
- 19. Data labels for charts as well as series color customization.
60
+ 19. Support for defined names, which gives you repeated header rows for printing.
62
61
 
63
- 20. Support for sheet headers and footers
62
+ 20. Data labels for charts as well as series color customization.
64
63
 
65
- 21. Pivot Tables
64
+ 21. Support for sheet headers and footers
66
65
 
67
- 22. Page Breaks
66
+ 22. Pivot Tables
67
+
68
+ 23. Page Breaks
68
69
 
69
70
 
70
71
  ## Install
@@ -107,6 +108,12 @@ Additional documentation is listed below:
107
108
  - [Style Reference](https://github.com/caxlsx/caxlsx/blob/master/docs/style_reference.md)
108
109
  - [Header and Footer Codes](https://github.com/caxlsx/caxlsx/blob/master/docs/header_and_footer_codes.md)
109
110
 
111
+ You can run the documentation on your local by running the following:
112
+ ```sh
113
+ gem install webrick
114
+ yard server
115
+ ```
116
+
110
117
  ⚠ Please __do not create issues__ for questions regarding the usage of axlsx / caxlsx. Look through this README, the [examples folder](https://github.com/caxlsx/caxlsx/tree/master/examples), and the [FAQ](https://github.com/caxlsx/caxlsx/wiki/FAQ), and also check [questions tagged `axlsx` on Stack Overflow](https://stackoverflow.com/questions/tagged/axlsx).
111
118
 
112
119
  Feel free to add your question (including an answer!) to the FAQ if you think it is of general interest.
@@ -121,6 +128,8 @@ Currently the following additional gems are available:
121
128
  * Provides a `.axlsx` renderer to Rails so you can move all your spreadsheet code from your controller into view files.
122
129
  - [activeadmin-caxlsx](https://github.com/caxlsx/activeadmin-caxlsx)
123
130
  * An Active Admin plugin that includes DSL to create downloadable reports.
131
+ - [ooxml_crypt](https://github.com/teamsimplepay/ooxml_crypt)
132
+ * Required to enable workbook encryption and password protection.
124
133
 
125
134
  ## Security
126
135
 
data/Rakefile CHANGED
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path("#{File.dirname(__FILE__)}/lib/axlsx/version.rb")
4
-
5
- task build: :gendoc do
6
- system "gem build axlsx.gemspec"
7
- end
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
8
5
 
9
6
  task :benchmark do
10
7
  require File.expand_path("#{File.dirname(__FILE__)}/test/benchmark.rb")
@@ -23,8 +20,4 @@ Rake::TestTask.new do |t|
23
20
  t.warning = true
24
21
  end
25
22
 
26
- task release: :build do
27
- system "gem push caxlsx-#{Axlsx::VERSION}.gem"
28
- end
29
-
30
23
  task default: :test
@@ -141,7 +141,17 @@ module Axlsx
141
141
  # category axes specified via axes[:val_axes] and axes[:cat_axis]
142
142
  # @return [Axes]
143
143
  def axes
144
- @axes ||= Axes.new(cat_axis: CatAxis, val_axis: ValAxis)
144
+ @axes ||= begin
145
+ a = Axes.new(cat_axis: CatAxis, val_axis: ValAxis)
146
+
147
+ if bar_dir == :col
148
+ a[:val_axis].ax_pos = :l
149
+ else
150
+ a[:cat_axis].ax_pos = :l
151
+ end
152
+
153
+ a
154
+ end
145
155
  end
146
156
  end
147
157
  end
@@ -131,7 +131,17 @@ module Axlsx
131
131
  # category axes specified via axes[:val_axes] and axes[:cat_axis]
132
132
  # @return [Axes]
133
133
  def axes
134
- @axes ||= Axes.new(cat_axis: CatAxis, val_axis: ValAxis)
134
+ @axes ||= begin
135
+ a = Axes.new(cat_axis: CatAxis, val_axis: ValAxis)
136
+
137
+ if bar_dir == :col
138
+ a[:val_axis].ax_pos = :l
139
+ else
140
+ a[:cat_axis].ax_pos = :l
141
+ end
142
+
143
+ a
144
+ end
135
145
  end
136
146
  end
137
147
  end
@@ -6,6 +6,7 @@ module Axlsx
6
6
  # @see README for examples
7
7
  class Chart
8
8
  include Axlsx::OptionsParser
9
+
9
10
  # Creates a new chart object
10
11
  # @param [GraphicalFrame] frame The frame that holds this chart.
11
12
  # @option options [Cell, String] title
@@ -9,6 +9,7 @@ module Axlsx
9
9
  class DLbls
10
10
  include Axlsx::Accessors
11
11
  include Axlsx::OptionsParser
12
+
12
13
  # creates a new DLbls object
13
14
  def initialize(chart_type, options = {})
14
15
  raise ArgumentError, 'chart_type must inherit from Chart' unless [Chart, LineChart].include?(chart_type.superclass)
@@ -56,7 +56,7 @@ module Axlsx
56
56
  <x:AutoFill>False</x:AutoFill>
57
57
  <x:Row>#{row}</x:Row>
58
58
  <x:Column>#{column}</x:Column>
59
- #{@visible ? '<x:Visible/>' : ''}
59
+ #{'<x:Visible/>' if @visible}
60
60
  </x:ClientData>
61
61
  </v:shape>
62
62
  XML
data/lib/axlsx/package.rb CHANGED
@@ -82,6 +82,8 @@ module Axlsx
82
82
  # @option options [String] :zip_command When `nil`, `#serialize` with RubyZip to
83
83
  # zip the XLSX file contents. When a String, the provided zip command (e.g.,
84
84
  # "zip") is used to zip the file contents (may be faster for large files)
85
+ # @option options [String] :password When specified, the serialized packaged will be
86
+ # encrypted with the password. Requires ooxml_crypt gem.
85
87
  # @return [Boolean] False if confirm_valid and validation errors exist. True if the package was serialized
86
88
  # @note A tremendous amount of effort has gone into ensuring that you cannot create invalid xlsx documents.
87
89
  # options[:confirm_valid] should be used in the rare case that you cannot open the serialized file.
@@ -108,7 +110,7 @@ module Axlsx
108
110
  workbook.apply_styles
109
111
  end
110
112
 
111
- confirm_valid, zip_command = parse_serialize_options(options, secondary_options)
113
+ confirm_valid, zip_command, password = parse_serialize_options(options, secondary_options)
112
114
  return false unless !confirm_valid || validate.empty?
113
115
 
114
116
  zip_provider = if zip_command
@@ -120,15 +122,31 @@ module Axlsx
120
122
  zip_provider.open(output) do |zip|
121
123
  write_parts(zip)
122
124
  end
125
+
126
+ if password && !password.empty?
127
+ require_ooxml_crypt!
128
+ OoxmlCrypt.encrypt_file(output, password, output)
129
+ end
130
+
123
131
  true
124
132
  ensure
125
133
  Relationship.clear_ids_cache
126
134
  end
127
135
 
128
136
  # Serialize your workbook to a StringIO instance
129
- # @param [Boolean] confirm_valid Validate the package prior to serialization.
130
- # @return [StringIO|Boolean] False if confirm_valid and validation errors exist. rewound string IO if not.
131
- def to_stream(confirm_valid = false)
137
+ # @param [Boolean] old_confirm_valid (Deprecated) Validate the package prior to serialization.
138
+ # Use :confirm_valid keyword arg instead.
139
+ # @option kwargs [Boolean] :confirm_valid Validate the package prior to serialization.
140
+ # @option kwargs [String] :password When specified, the serialized packaged will be
141
+ # encrypted with the password. Requires ooxml_crypt gem.
142
+ # @return [StringIO|Boolean] False if confirm_valid and validation errors exist. Rewound string IO if not.
143
+ def to_stream(old_confirm_valid = nil, confirm_valid: false, password: nil)
144
+ unless old_confirm_valid.nil?
145
+ warn "[DEPRECATION] Axlsx::Package#to_stream with confirm_valid as a non-keyword arg is deprecated. " \
146
+ "Use keyword arg instead e.g., package.to_stream(confirm_valid: false)"
147
+ confirm_valid ||= old_confirm_valid
148
+ end
149
+
132
150
  unless workbook.styles_applied
133
151
  workbook.apply_styles
134
152
  end
@@ -140,6 +158,12 @@ module Axlsx
140
158
  write_parts(zip)
141
159
  end
142
160
  stream.rewind
161
+
162
+ if password && !password.empty?
163
+ require_ooxml_crypt!
164
+ stream = StringIO.new(OoxmlCrypt.encrypt(stream.read, password))
165
+ end
166
+
143
167
  stream
144
168
  ensure
145
169
  Relationship.clear_ids_cache
@@ -147,7 +171,7 @@ module Axlsx
147
171
 
148
172
  # Encrypt the package into a CFB using the password provided
149
173
  # This is not ready yet
150
- def encrypt(file_name, password)
174
+ def encrypt(file_name, password) # rubocop:disable Naming/PredicateMethod
151
175
  false
152
176
  # moc = MsOffCrypto.new(file_name, password)
153
177
  # moc.save
@@ -211,7 +235,8 @@ module Axlsx
211
235
  # @return [Zip::Entry]
212
236
  def zip_entry_for_part(part)
213
237
  timestamp = Zip::DOSTime.at(@core.created.to_i)
214
- Zip::Entry.new("", part[:entry], "", "", 0, 0, Zip::Entry::DEFLATED, 0, timestamp)
238
+
239
+ Zip::Entry.new("", part[:entry], time: timestamp)
215
240
  end
216
241
 
217
242
  # The parts of a package
@@ -220,6 +245,7 @@ module Axlsx
220
245
  def parts
221
246
  parts = [
222
247
  { entry: "xl/#{STYLES_PN}", doc: workbook.styles, schema: SML_XSD },
248
+ { entry: "xl/#{THEME_PN}", doc: workbook.theme, schema: THEME_XSD },
223
249
  { entry: CORE_PN, doc: @core, schema: CORE_XSD },
224
250
  { entry: APP_PN, doc: @app, schema: APP_XSD },
225
251
  { entry: WORKBOOK_RELS_PN, doc: workbook.relationships, schema: RELS_XSD },
@@ -356,6 +382,7 @@ module Axlsx
356
382
  c_types << Override.new(PartName: "/#{APP_PN}", ContentType: APP_CT)
357
383
  c_types << Override.new(PartName: "/#{CORE_PN}", ContentType: CORE_CT)
358
384
  c_types << Override.new(PartName: "/xl/#{STYLES_PN}", ContentType: STYLES_CT)
385
+ c_types << Override.new(PartName: "/xl/#{THEME_PN}", ContentType: THEME_CT)
359
386
  c_types << Axlsx::Override.new(PartName: "/#{WORKBOOK_PN}", ContentType: WORKBOOK_CT)
360
387
  c_types.lock
361
388
  c_types
@@ -374,8 +401,8 @@ module Axlsx
374
401
  end
375
402
 
376
403
  # Parse the arguments of `#serialize`
377
- # @return [Boolean, (String or nil)] Returns an array where the first value is
378
- # `confirm_valid` and the second is the `zip_command`.
404
+ # @return [Boolean, (String or nil), (String or nil)] Returns a 3-tuple where values are
405
+ # `confirm_valid`, `zip_command`, and `password`.
379
406
  # @private
380
407
  def parse_serialize_options(options, secondary_options)
381
408
  if secondary_options
@@ -384,17 +411,23 @@ module Axlsx
384
411
  end
385
412
  if options.is_a?(Hash)
386
413
  options.merge!(secondary_options || {})
387
- invalid_keys = options.keys - [:confirm_valid, :zip_command]
414
+ invalid_keys = options.keys - [:confirm_valid, :zip_command, :password]
388
415
  if invalid_keys.any?
389
416
  raise ArgumentError, "Invalid keyword arguments: #{invalid_keys}"
390
417
  end
391
418
 
392
- [options.fetch(:confirm_valid, false), options.fetch(:zip_command, nil)]
419
+ [options.fetch(:confirm_valid, false), options.fetch(:zip_command, nil), options.fetch(:password, nil)]
393
420
  else
394
421
  warn "[DEPRECATION] Axlsx::Package#serialize with confirm_valid as a boolean is deprecated. " \
395
422
  "Use keyword args instead e.g., package.serialize(output, confirm_valid: false)"
396
423
  parse_serialize_options((secondary_options || {}).merge(confirm_valid: options), nil)
397
424
  end
398
425
  end
426
+
427
+ def require_ooxml_crypt!
428
+ return if defined?(OoxmlCrypt)
429
+
430
+ raise 'Axlsx encryption requires ooxml_crypt gem'
431
+ end
399
432
  end
400
433
  end
@@ -4,6 +4,7 @@ module Axlsx
4
4
  # A border part.
5
5
  class BorderPr
6
6
  include Axlsx::OptionsParser
7
+
7
8
  # @return [Color] The color of this border part.
8
9
  attr_reader :color
9
10
 
@@ -6,6 +6,7 @@ module Axlsx
6
6
  # @see Style#add_style
7
7
  class PatternFill
8
8
  include Axlsx::OptionsParser
9
+
9
10
  # Creates a new PatternFill Object
10
11
  # @option options [Symbol] patternType
11
12
  # @option options [Color] fgColor
@@ -15,6 +15,7 @@ module Axlsx
15
15
  require_relative 'table_style'
16
16
  require_relative 'table_styles'
17
17
  require_relative 'table_style_element'
18
+ require_relative 'theme'
18
19
  require_relative 'dxf'
19
20
  require_relative 'xf'
20
21
  require_relative 'cell_protection'
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Axlsx
4
+ # Theme represents the theme part of the package and is responsible for
5
+ # generating the default Office theme that is required for encryption compatibility
6
+ class Theme
7
+ # The part name of this theme
8
+ # @return [String]
9
+ def pn
10
+ THEME_PN
11
+ end
12
+
13
+ # Serializes the default theme to XML
14
+ # @param [String] str
15
+ # @return [String]
16
+ def to_xml_string(str = +'')
17
+ str << <<~XML.delete("\n")
18
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
19
+ <a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">
20
+ <a:themeElements>
21
+ <a:clrScheme name="Office">
22
+ <a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1>
23
+ <a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1>
24
+ <a:dk2><a:srgbClr val="1F497D"/></a:dk2>
25
+ <a:lt2><a:srgbClr val="EEECE1"/></a:lt2>
26
+ <a:accent1><a:srgbClr val="4F81BD"/></a:accent1>
27
+ <a:accent2><a:srgbClr val="C0504D"/></a:accent2>
28
+ <a:accent3><a:srgbClr val="9BBB59"/></a:accent3>
29
+ <a:accent4><a:srgbClr val="8064A2"/></a:accent4>
30
+ <a:accent5><a:srgbClr val="4BACC6"/></a:accent5>
31
+ <a:accent6><a:srgbClr val="F79646"/></a:accent6>
32
+ <a:hlink><a:srgbClr val="0000FF"/></a:hlink>
33
+ <a:folHlink><a:srgbClr val="800080"/></a:folHlink>
34
+ </a:clrScheme>
35
+
36
+ <a:fontScheme name="Office">
37
+ <a:majorFont>
38
+ <a:latin typeface="Cambria"/>
39
+ <a:ea typeface=""/>
40
+ <a:cs typeface=""/>
41
+ </a:majorFont>
42
+ <a:minorFont>
43
+ <a:latin typeface="Calibri"/>
44
+ <a:ea typeface=""/>
45
+ <a:cs typeface=""/>
46
+ </a:minorFont>
47
+ </a:fontScheme>
48
+
49
+ <a:fmtScheme name="Office">
50
+ <a:fillStyleLst>
51
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
52
+ <a:gradFill rotWithShape="1">
53
+ <a:gsLst>
54
+ <a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="50000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
55
+ <a:gs pos="35000"><a:schemeClr val="phClr"><a:tint val="37000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
56
+ <a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="15000"/><a:satMod val="350000"/></a:schemeClr></a:gs>
57
+ </a:gsLst>
58
+ <a:lin ang="16200000" scaled="1"/>
59
+ </a:gradFill>
60
+ <a:gradFill rotWithShape="1">
61
+ <a:gsLst>
62
+ <a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="100000"/><a:shade val="100000"/><a:satMod val="130000"/></a:schemeClr></a:gs>
63
+ <a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="50000"/><a:shade val="100000"/><a:satMod val="350000"/></a:schemeClr></a:gs>
64
+ </a:gsLst>
65
+ <a:lin ang="16200000" scaled="false"/>
66
+ </a:gradFill>
67
+ </a:fillStyleLst>
68
+
69
+ <a:lnStyleLst>
70
+ <a:ln w="9525" cap="flat" cmpd="sng" algn="ctr">
71
+ <a:solidFill><a:schemeClr val="phClr"><a:shade val="95000"/><a:satMod val="105000"/></a:schemeClr></a:solidFill>
72
+ <a:prstDash val="solid"/>
73
+ </a:ln>
74
+ <a:ln w="25400" cap="flat" cmpd="sng" algn="ctr">
75
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
76
+ <a:prstDash val="solid"/>
77
+ </a:ln>
78
+ <a:ln w="38100" cap="flat" cmpd="sng" algn="ctr">
79
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
80
+ <a:prstDash val="solid"/>
81
+ </a:ln>
82
+ </a:lnStyleLst>
83
+
84
+ <a:effectStyleLst>
85
+ <a:effectStyle>
86
+ <a:effectLst>
87
+ <a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="false">
88
+ <a:srgbClr val="000000"><a:alpha val="38000"/></a:srgbClr>
89
+ </a:outerShdw>
90
+ </a:effectLst>
91
+ </a:effectStyle>
92
+ <a:effectStyle>
93
+ <a:effectLst>
94
+ <a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="false">
95
+ <a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr>
96
+ </a:outerShdw>
97
+ </a:effectLst>
98
+ </a:effectStyle>
99
+ <a:effectStyle>
100
+ <a:effectLst>
101
+ <a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="false">
102
+ <a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr>
103
+ </a:outerShdw>
104
+ </a:effectLst>
105
+ <a:scene3d>
106
+ <a:camera prst="orthographicFront"><a:rot lat="0" lon="0" rev="0"/></a:camera>
107
+ <a:lightRig rig="threePt" dir="t"><a:rot lat="0" lon="0" rev="1200000"/></a:lightRig>
108
+ </a:scene3d>
109
+ <a:sp3d><a:bevelT w="63500" h="25400"/></a:sp3d>
110
+ </a:effectStyle>
111
+ </a:effectStyleLst>
112
+
113
+ <a:bgFillStyleLst>
114
+ <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
115
+ <a:gradFill rotWithShape="1">
116
+ <a:gsLst>
117
+ <a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="40000"/><a:satMod val="350000"/></a:schemeClr></a:gs>
118
+ <a:gs pos="40000"><a:schemeClr val="phClr"><a:tint val="45000"/><a:shade val="99000"/><a:satMod val="350000"/></a:schemeClr></a:gs>
119
+ <a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="20000"/><a:satMod val="255000"/></a:schemeClr></a:gs>
120
+ </a:gsLst>
121
+ <a:path path="circle"><a:fillToRect l="50000" t="-80000" r="50000" b="180000"/></a:path>
122
+ </a:gradFill>
123
+ <a:gradFill rotWithShape="1">
124
+ <a:gsLst>
125
+ <a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="80000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
126
+ <a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="30000"/><a:satMod val="200000"/></a:schemeClr></a:gs>
127
+ </a:gsLst>
128
+ <a:path path="circle"><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path>
129
+ </a:gradFill>
130
+ </a:bgFillStyleLst>
131
+ </a:fmtScheme>
132
+ </a:themeElements>
133
+
134
+ <a:objectDefaults>
135
+ <a:spDef>
136
+ <a:spPr/>
137
+ <a:bodyPr/>
138
+ <a:lstStyle/>
139
+ <a:style>
140
+ <a:lnRef idx="1"><a:schemeClr val="accent1"/></a:lnRef>
141
+ <a:fillRef idx="3"><a:schemeClr val="accent1"/></a:fillRef>
142
+ <a:effectRef idx="2"><a:schemeClr val="accent1"/></a:effectRef>
143
+ <a:fontRef idx="minor"><a:schemeClr val="lt1"/></a:fontRef>
144
+ </a:style>
145
+ </a:spDef>
146
+ <a:lnDef>
147
+ <a:spPr/>
148
+ <a:bodyPr/>
149
+ <a:lstStyle/>
150
+ <a:style>
151
+ <a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef>
152
+ <a:fillRef idx="0"><a:schemeClr val="accent1"/></a:fillRef>
153
+ <a:effectRef idx="1"><a:schemeClr val="accent1"/></a:effectRef>
154
+ <a:fontRef idx="minor"><a:schemeClr val="tx1"/></a:fontRef>
155
+ </a:style>
156
+ </a:lnDef>
157
+ </a:objectDefaults>
158
+ <a:extraClrSchemeLst/>
159
+ </a:theme>
160
+ XML
161
+ end
162
+ end
163
+ end
@@ -8,6 +8,7 @@ module Axlsx
8
8
 
9
9
  include Axlsx::SerializedAttributes
10
10
  include Axlsx::OptionsParser
11
+
11
12
  # Creates a new Xf object
12
13
  # @option options [Integer] numFmtId
13
14
  # @option options [Integer] fontId
@@ -19,7 +19,7 @@ module Axlsx
19
19
  #
20
20
  # The directory and its contents are removed at the end of the block.
21
21
  def self.open(file_name, encrypter = nil)
22
- Zip::OutputStream.open(file_name, encrypter) do |zos|
22
+ Zip::OutputStream.open(file_name, encrypter: encrypter) do |zos|
23
23
  bzos = new(zos)
24
24
  yield(bzos)
25
25
  ensure
@@ -28,7 +28,7 @@ module Axlsx
28
28
  end
29
29
 
30
30
  def self.write_buffer(io = ::StringIO.new, encrypter = nil)
31
- Zip::OutputStream.write_buffer(io, encrypter) do |zos|
31
+ Zip::OutputStream.write_buffer(io, encrypter: encrypter) do |zos|
32
32
  bzos = new(zos)
33
33
  yield(bzos)
34
34
  ensure
@@ -79,6 +79,9 @@ module Axlsx
79
79
  # shared strings namespace
80
80
  SHARED_STRINGS_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"
81
81
 
82
+ # theme rels namespace
83
+ THEME_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"
84
+
82
85
  # drawing rels namespace
83
86
  DRAWING_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
84
87
 
@@ -133,6 +136,9 @@ module Axlsx
133
136
  # shared strings content type
134
137
  SHARED_STRINGS_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"
135
138
 
139
+ # theme content type
140
+ THEME_CT = "application/vnd.openxmlformats-officedocument.theme+xml"
141
+
136
142
  # core content type
137
143
  CORE_CT = "application/vnd.openxmlformats-package.core-properties+xml"
138
144
 
@@ -187,6 +193,9 @@ module Axlsx
187
193
  # shared_strings part
188
194
  SHARED_STRINGS_PN = "sharedStrings.xml"
189
195
 
196
+ # theme part
197
+ THEME_PN = "theme/theme1.xml"
198
+
190
199
  # app part
191
200
  APP_PN = "docProps/app.xml"
192
201
 
@@ -259,6 +268,9 @@ module Axlsx
259
268
  # drawing validation schema
260
269
  DRAWING_XSD = "#{SCHEMA_BASE}dml-spreadsheetDrawing.xsd"
261
270
 
271
+ # theme validation schema
272
+ THEME_XSD = "#{SCHEMA_BASE}dml-main.xsd"
273
+
262
274
  # number format id for percentage formatting using the default formatting id.
263
275
  NUM_FMT_PERCENT = 9
264
276
 
@@ -1,22 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open-uri'
4
-
5
3
  module Axlsx
6
4
  # This module defines some utils related with mime type detection
7
5
  module MimeTypeUtils
8
- # Detect a file mime type
9
- # @param [String] v File path
10
- # @return [String] File mime type
11
- def self.get_mime_type(v)
12
- Marcel::MimeType.for(Pathname.new(v))
13
- end
6
+ # Extension to MIME type mapping for Windows fallback
7
+ EXTENSION_FALLBACKS = {
8
+ '.jpg' => 'image/jpeg',
9
+ '.jpeg' => 'image/jpeg',
10
+ '.png' => 'image/png',
11
+ '.gif' => 'image/gif'
12
+ }.freeze
13
+
14
+ class << self
15
+ # Detect a file mime type
16
+ # @param [String] v File path
17
+ # @return [String] File mime type
18
+ def get_mime_type(v)
19
+ mime_type = Marcel::MimeType.for(Pathname.new(v))
20
+
21
+ # Windows fallback: Marcel sometimes returns application/octet-stream for valid image files
22
+ if mime_type == 'application/octet-stream' && windows_platform?
23
+ extension = File.extname(v).downcase
24
+ # Verify it's actually an image by checking the file header
25
+ if EXTENSION_FALLBACKS.key?(extension) && File.exist?(v) && valid_image_file?(v, extension)
26
+ mime_type = EXTENSION_FALLBACKS[extension]
27
+ end
28
+ end
29
+
30
+ mime_type
31
+ end
32
+
33
+ # Detect a file mime type from URI
34
+ # @param [String] v URI
35
+ # @return [String] File mime type
36
+ def get_mime_type_from_uri(v)
37
+ uri = URI.parse(v)
38
+
39
+ unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
40
+ raise URI::InvalidURIError, "Only HTTP/HTTPS URIs are supported"
41
+ end
42
+
43
+ response = UriUtils.fetch_headers(uri)
44
+ mime_type = response&.[]('content-type')&.split(';')&.first&.strip
45
+
46
+ if mime_type.nil? || mime_type.empty?
47
+ raise ArgumentError, "Unable to determine MIME type from response"
48
+ end
49
+
50
+ mime_type
51
+ end
52
+
53
+ private
54
+
55
+ def windows_platform?
56
+ RUBY_PLATFORM =~ /mswin|mingw|cygwin/
57
+ end
58
+
59
+ def valid_image_file?(file_path, extension)
60
+ return false unless File.exist?(file_path)
14
61
 
15
- # Detect a file mime type from URI
16
- # @param [String] v URI
17
- # @return [String] File mime type
18
- def self.get_mime_type_from_uri(v)
19
- Marcel::MimeType.for(URI.parse(v).open)
62
+ # Check magic bytes for common image formats
63
+ begin
64
+ header = File.binread(file_path, 10)
65
+ case extension
66
+ when '.jpg', '.jpeg'
67
+ header[0..2].bytes == [0xFF, 0xD8, 0xFF] # JPEG magic bytes
68
+ when '.png'
69
+ header[0..7].bytes == [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A] # PNG magic bytes
70
+ when '.gif'
71
+ header[0..2] == 'GIF' # GIF magic bytes
72
+ else
73
+ false
74
+ end
75
+ rescue StandardError
76
+ false
77
+ end
78
+ end
20
79
  end
21
80
  end
22
81
  end
@@ -183,8 +183,7 @@ module Axlsx
183
183
  end
184
184
 
185
185
  def to_xml_string(str = +'')
186
- classname = @allowed_types[0].name.split('::').last
187
- el_name = serialize_as.to_s || (classname[0, 1].downcase + classname[1..])
186
+ el_name = serialize_as.to_s
188
187
  str << '<' << el_name << ' count="' << size.to_s << '">'
189
188
  each { |item| item.to_xml_string(str) }
190
189
  str << '</' << el_name << '>'
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module Axlsx
7
+ # This module defines some utils related to mime type detection
8
+ module UriUtils
9
+ class << self
10
+ # Performs an HTTP GeT request fetching only headers
11
+ def fetch_headers(uri, redirect_limit = 5)
12
+ redirect_count = 0
13
+ use_get = false
14
+
15
+ while redirect_count < redirect_limit
16
+ case (response = fetch_headers_request(uri, use_get: use_get))
17
+ when Net::HTTPSuccess
18
+ return response
19
+ when Net::HTTPMethodNotAllowed, Net::HTTPNotImplemented
20
+ fail_response(response) if use_get
21
+ use_get = true
22
+ next # Retry current URL with GET
23
+ when Net::HTTPRedirection
24
+ uri = follow_redirect(uri, response)
25
+ redirect_count += 1
26
+ else
27
+ fail_response(response)
28
+ end
29
+ end
30
+
31
+ raise ArgumentError, "Too many redirects (exceeded #{redirect_limit})"
32
+ end
33
+
34
+ private
35
+
36
+ def fetch_headers_request(uri, use_get: false)
37
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
38
+ path = build_path(uri)
39
+ if use_get
40
+ http.request_get(path) { |response| break(response) } # Exit early with just headers
41
+ else
42
+ http.head(path)
43
+ end
44
+ end
45
+ end
46
+
47
+ def build_path(uri)
48
+ "#{uri.path.empty? ? '/' : uri.path}#{"?#{uri.query}" if uri.query}"
49
+ end
50
+
51
+ def follow_redirect(original_uri, response)
52
+ location = response['location']
53
+
54
+ if location.nil? || location.empty?
55
+ raise ArgumentError, "Redirect response missing Location header"
56
+ end
57
+
58
+ if location.start_with?('http://', 'https://')
59
+ URI.parse(location)
60
+ else
61
+ URI.join(original_uri, location)
62
+ end
63
+ end
64
+
65
+ def fail_response(response)
66
+ raise ArgumentError, "Failed to fetch resource: #{response.code} #{response.message}"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -9,7 +9,7 @@ module Axlsx
9
9
  # @param [Any] v The value to be validated
10
10
  # @raise [ArgumentError] Raised if the value provided is not in the list of choices.
11
11
  # @return [Boolean] true if validation succeeds.
12
- def self.validate(name, choices, v)
12
+ def self.validate(name, choices, v) # rubocop:disable Naming/PredicateMethod
13
13
  raise ArgumentError, format(ERR_RESTRICTION, v.to_s, name, choices.inspect) unless choices.include?(v)
14
14
 
15
15
  true
@@ -268,19 +268,19 @@ module Axlsx
268
268
  RestrictionValidator.validate :vertical_alignment, VALID_VERTICAL_ALIGNMENT_VALUES, v
269
269
  end
270
270
 
271
- VALID_CONTENT_TYPE_VALUES = [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT, COMMENT_CT, VML_DRAWING_CT, PIVOT_TABLE_CT, PIVOT_TABLE_CACHE_DEFINITION_CT].freeze
271
+ VALID_CONTENT_TYPE_VALUES = [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, THEME_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT, COMMENT_CT, VML_DRAWING_CT, PIVOT_TABLE_CT, PIVOT_TABLE_CACHE_DEFINITION_CT].freeze
272
272
 
273
273
  # Requires that the value is a valid content_type
274
- # TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, DRAWING_CT, COMMENT_CT are allowed
274
+ # TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, THEME_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, DRAWING_CT, COMMENT_CT are allowed
275
275
  # @param [Any] v The value validated
276
276
  def self.validate_content_type(v)
277
277
  RestrictionValidator.validate :content_type, VALID_CONTENT_TYPE_VALUES, v
278
278
  end
279
279
 
280
- VALID_RELATIONSHIP_TYPE_VALUES = [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R, COMMENT_R, VML_DRAWING_R, COMMENT_R_NULL, PIVOT_TABLE_R, PIVOT_TABLE_CACHE_DEFINITION_R].freeze
280
+ VALID_RELATIONSHIP_TYPE_VALUES = [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, THEME_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R, COMMENT_R, VML_DRAWING_R, COMMENT_R_NULL, PIVOT_TABLE_R, PIVOT_TABLE_CACHE_DEFINITION_R].freeze
281
281
 
282
282
  # Requires that the value is a valid relationship_type
283
- # XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R are allowed
283
+ # XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, THEME_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R are allowed
284
284
  # @param [Any] v The value validated
285
285
  def self.validate_relationship_type(v)
286
286
  RestrictionValidator.validate :relationship_type, VALID_RELATIONSHIP_TYPE_VALUES, v
data/lib/axlsx/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Axlsx
4
4
  # The current version
5
- VERSION = "4.2.0"
5
+ VERSION = "4.4.0"
6
6
  end
@@ -54,6 +54,7 @@ module Axlsx
54
54
  include Axlsx::SerializedAttributes
55
55
  include Axlsx::OptionsParser
56
56
  include Axlsx::Accessors
57
+
57
58
  # creates a new DefinedName.
58
59
  # @param [String] formula - the formula the defined name references
59
60
  # @param [Hash] options - A hash of key/value pairs that will be mapped to this instances attributes.
@@ -184,6 +184,12 @@ module Axlsx
184
184
  @styles
185
185
  end
186
186
 
187
+ # The theme associated with this workbook
188
+ # @return [Theme]
189
+ def theme
190
+ @theme ||= Theme.new
191
+ end
192
+
187
193
  # An array that holds all cells with styles
188
194
  # @return Set
189
195
  def styled_cells
@@ -373,6 +379,7 @@ module Axlsx
373
379
  r << Relationship.new(pivot_table.cache_definition, PIVOT_TABLE_CACHE_DEFINITION_R, format(PIVOT_TABLE_CACHE_DEFINITION_PN, index + 1))
374
380
  end
375
381
  r << Relationship.new(self, STYLES_R, STYLES_PN)
382
+ r << Relationship.new(self, THEME_R, THEME_PN)
376
383
  if use_shared_strings
377
384
  r << Relationship.new(self, SHARED_STRINGS_R, SHARED_STRINGS_PN)
378
385
  end
@@ -38,7 +38,7 @@ module Axlsx
38
38
  # @option [Boolean] show_horizontal_scroll Specifies a boolean value that indicates whether to display the horizontal scroll bar in the user interface.
39
39
  # @option [Boolean] show_vertical_scroll Specifies a boolean value that indicates whether to display the vertical scroll bar.
40
40
  # @option [Boolean] show_sheet_tabs Specifies a boolean value that indicates whether to display the sheet tabs in the user interface.
41
- # @option [Integer] tab_ratio Specifies ratio between the workbook tabs bar and the horizontal scroll bar.
41
+ # @option [Integer] tab_ratio Specifies the ratio between the workbook tabs bar and the horizontal scroll bar (from 0 to 1000, higher values mean more space for tabs). May only be supported on some clients.
42
42
  # @option [Integer] first_sheet Specifies the index to the first sheet in this book view.
43
43
  # @option [Integer] active_tab Specifies an unsignedInt that contains the index to the active sheet in this book view.
44
44
  # @option [Integer] x_window Specifies the X coordinate for the upper left corner of the workbook window. The unit of measurement for this value is twips.
@@ -41,7 +41,7 @@ module Axlsx
41
41
  # date_group_items restrictions.
42
42
  # @param [Cell] cell The cell to test against items
43
43
  # TODO implement this for date filters as well!
44
- def apply(cell)
44
+ def apply(cell) # rubocop:disable Naming/PredicateMethod
45
45
  return false unless cell
46
46
 
47
47
  filter_items.each do |filter|
@@ -166,7 +166,7 @@ module Axlsx
166
166
 
167
167
  # Indicates that the cell has one or more of the custom cell styles applied.
168
168
  # @return [Boolean]
169
- def is_text_run? # rubocop:disable Naming/PredicateName
169
+ def is_text_run? # rubocop:disable Naming/PredicatePrefix
170
170
  defined?(@is_text_run) && @is_text_run && !contains_rich_text?
171
171
  end
172
172
 
@@ -410,13 +410,13 @@ module Axlsx
410
410
  CellSerializer.to_xml_string r_index, c_index, self, str
411
411
  end
412
412
 
413
- def is_formula? # rubocop:disable Naming/PredicateName
413
+ def is_formula? # rubocop:disable Naming/PredicatePrefix
414
414
  return false if escape_formulas
415
415
 
416
416
  type == :string && @value.to_s.start_with?(FORMULA_PREFIX)
417
417
  end
418
418
 
419
- def is_array_formula? # rubocop:disable Naming/PredicateName
419
+ def is_array_formula? # rubocop:disable Naming/PredicatePrefix
420
420
  return false if escape_formulas
421
421
 
422
422
  type == :string &&
@@ -9,6 +9,7 @@ module Axlsx
9
9
 
10
10
  include Axlsx::OptionsParser
11
11
  include Axlsx::SerializedAttributes
12
+
12
13
  # Create a new Col objects
13
14
  # @param min First column affected by this 'column info' record.
14
15
  # @param max Last column affected by this 'column info' record.
@@ -7,7 +7,7 @@ module Axlsx
7
7
  # of plain text and control characters. A fairly comprehensive list of control
8
8
  # characters can be found here:
9
9
  # https://github.com/randym/axlsx/blob/master/notes_on_header_footer.md
10
- #     
10
+ #
11
11
  # @note The recommended way of managing header/footers is via Worksheet#header_footer
12
12
  # @see Worksheet#initialize
13
13
  class HeaderFooter
@@ -8,6 +8,7 @@ module Axlsx
8
8
  class Pane
9
9
  include Axlsx::OptionsParser
10
10
  include Axlsx::SerializedAttributes
11
+
11
12
  # Creates a new {Pane} object
12
13
  # @option options [Symbol] active_pane Active Pane
13
14
  # @option options [Symbol] state Split State
@@ -343,7 +343,7 @@ module Axlsx
343
343
  attributes << 'dataField="1"'
344
344
  end
345
345
 
346
- "<pivotField #{attributes.join(' ')}>#{include_items_tag ? items_tag : nil}</pivotField>"
346
+ "<pivotField #{attributes.join(' ')}>#{items_tag if include_items_tag}</pivotField>"
347
347
  end
348
348
 
349
349
  def data_refs
@@ -10,6 +10,7 @@ module Axlsx
10
10
  include Axlsx::OptionsParser
11
11
  include Axlsx::SerializedAttributes
12
12
  include Axlsx::Accessors
13
+
13
14
  # Creates a new PrintOptions object
14
15
  # @option options [Boolean] grid_lines Whether grid lines should be printed
15
16
  # @option options [Boolean] headings Whether row and column headings should be printed
@@ -7,6 +7,7 @@ module Axlsx
7
7
  include Axlsx::OptionsParser
8
8
  include Axlsx::SerializedAttributes
9
9
  include Axlsx::Accessors
10
+
10
11
  # creates a new SheetCalcPr
11
12
  # @param [Hash] options Options for this object
12
13
  # @option [Boolean] full_calc_on_load @see full_calc_on_load
@@ -7,6 +7,7 @@ module Axlsx
7
7
  include Axlsx::OptionsParser
8
8
  include Axlsx::SerializedAttributes
9
9
  include Axlsx::Accessors
10
+
10
11
  # creates a new TableStyleInfo instance
11
12
  # @param [Hash] options
12
13
  # @option [Boolean] show_first_column indicates if the first column should
@@ -37,7 +37,7 @@ module Axlsx
37
37
 
38
38
  # Helper method to tell us if there are comments in the comments collection
39
39
  # @return [Boolean]
40
- def has_comments? # rubocop:disable Naming/PredicateName
40
+ def has_comments? # rubocop:disable Naming/PredicatePrefix
41
41
  !comments.empty?
42
42
  end
43
43
 
@@ -42,7 +42,7 @@ module Axlsx
42
42
 
43
43
  # helper method to tell us if the drawing has something in it or not
44
44
  # @return [Boolean]
45
- def has_drawing? # rubocop:disable Naming/PredicateName
45
+ def has_drawing? # rubocop:disable Naming/PredicatePrefix
46
46
  @drawing.is_a? Drawing
47
47
  end
48
48
 
@@ -6,6 +6,7 @@ module Axlsx
6
6
  include Axlsx::OptionsParser
7
7
  include Axlsx::Accessors
8
8
  include Axlsx::SerializedAttributes
9
+
9
10
  # Creates a new hyperlink object.
10
11
  # @note the preferred way to add hyperlinks to your worksheet is the Worksheet#add_hyperlink method
11
12
  # @param [Worksheet] worksheet the Worksheet that owns this hyperlink
data/lib/axlsx.rb CHANGED
@@ -13,6 +13,7 @@ require 'cgi'
13
13
  require 'set'
14
14
  require 'time'
15
15
  require 'uri'
16
+ require 'net/http'
16
17
 
17
18
  require_relative 'axlsx/util/simple_typed_list'
18
19
  require_relative 'axlsx/util/constants'
@@ -20,6 +21,7 @@ require_relative 'axlsx/util/validators'
20
21
  require_relative 'axlsx/util/accessors'
21
22
  require_relative 'axlsx/util/serialized_attributes'
22
23
  require_relative 'axlsx/util/options_parser'
24
+ require_relative 'axlsx/util/uri_utils'
23
25
  require_relative 'axlsx/util/mime_type_utils'
24
26
  require_relative 'axlsx/util/buffered_zip_output_stream'
25
27
  require_relative 'axlsx/util/zip_command'
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caxlsx
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Randy Morgan
8
8
  - Jurriaan Pruis
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2024-12-15 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: htmlentities
@@ -71,20 +70,20 @@ dependencies:
71
70
  requirements:
72
71
  - - ">="
73
72
  - !ruby/object:Gem::Version
74
- version: 1.3.0
73
+ version: '2.4'
75
74
  - - "<"
76
75
  - !ruby/object:Gem::Version
77
- version: '3'
76
+ version: '4'
78
77
  type: :runtime
79
78
  prerelease: false
80
79
  version_requirements: !ruby/object:Gem::Requirement
81
80
  requirements:
82
81
  - - ">="
83
82
  - !ruby/object:Gem::Version
84
- version: 1.3.0
83
+ version: '2.4'
85
84
  - - "<"
86
85
  - !ruby/object:Gem::Version
87
- version: '3'
86
+ version: '4'
88
87
  description: 'xlsx spreadsheet generation with charts, images, automated column width,
89
88
  customizable styles and full schema validation. Axlsx helps you create beautiful
90
89
  Office Open XML Spreadsheet documents (Excel, Google Spreadsheets, Numbers, LibreOffice)
@@ -177,6 +176,7 @@ files:
177
176
  - lib/axlsx/stylesheet/table_style.rb
178
177
  - lib/axlsx/stylesheet/table_style_element.rb
179
178
  - lib/axlsx/stylesheet/table_styles.rb
179
+ - lib/axlsx/stylesheet/theme.rb
180
180
  - lib/axlsx/stylesheet/xf.rb
181
181
  - lib/axlsx/util/accessors.rb
182
182
  - lib/axlsx/util/buffered_zip_output_stream.rb
@@ -186,6 +186,7 @@ files:
186
186
  - lib/axlsx/util/serialized_attributes.rb
187
187
  - lib/axlsx/util/simple_typed_list.rb
188
188
  - lib/axlsx/util/storage.rb
189
+ - lib/axlsx/util/uri_utils.rb
189
190
  - lib/axlsx/util/validators.rb
190
191
  - lib/axlsx/util/zip_command.rb
191
192
  - lib/axlsx/version.rb
@@ -297,7 +298,6 @@ metadata:
297
298
  changelog_uri: https://github.com/caxlsx/caxlsx/blob/master/CHANGELOG.md
298
299
  source_code_uri: https://github.com/caxlsx/caxlsx
299
300
  rubygems_mfa_required: 'true'
300
- post_install_message:
301
301
  rdoc_options: []
302
302
  require_paths:
303
303
  - lib
@@ -312,8 +312,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
312
312
  - !ruby/object:Gem::Version
313
313
  version: '0'
314
314
  requirements: []
315
- rubygems_version: 3.4.19
316
- signing_key:
315
+ rubygems_version: 3.6.9
317
316
  specification_version: 4
318
317
  summary: Excel OOXML (xlsx) with charts, styles, images and autowidth columns.
319
318
  test_files: []