axlsx 1.0.16 → 1.0.18

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 (91) hide show
  1. data/.yardopts +1 -0
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +105 -46
  4. data/examples/example.rb +172 -142
  5. data/lib/axlsx/content_type/content_type.rb +4 -3
  6. data/lib/axlsx/content_type/default.rb +1 -0
  7. data/lib/axlsx/content_type/override.rb +1 -0
  8. data/lib/axlsx/doc_props/app.rb +1 -1
  9. data/lib/axlsx/doc_props/core.rb +1 -0
  10. data/lib/axlsx/drawing/axis.rb +1 -0
  11. data/lib/axlsx/drawing/bar_3D_chart.rb +1 -0
  12. data/lib/axlsx/drawing/bar_series.rb +1 -0
  13. data/lib/axlsx/drawing/cat_axis.rb +1 -0
  14. data/lib/axlsx/drawing/cat_axis_data.rb +1 -0
  15. data/lib/axlsx/drawing/chart.rb +4 -4
  16. data/lib/axlsx/drawing/drawing.rb +1 -0
  17. data/lib/axlsx/drawing/graphic_frame.rb +1 -0
  18. data/lib/axlsx/drawing/hyperlink.rb +1 -1
  19. data/lib/axlsx/drawing/line_3D_chart.rb +1 -0
  20. data/lib/axlsx/drawing/line_series.rb +1 -0
  21. data/lib/axlsx/drawing/marker.rb +1 -0
  22. data/lib/axlsx/drawing/one_cell_anchor.rb +1 -0
  23. data/lib/axlsx/drawing/pic.rb +2 -1
  24. data/lib/axlsx/drawing/picture_locking.rb +1 -0
  25. data/lib/axlsx/drawing/pie_3D_chart.rb +1 -0
  26. data/lib/axlsx/drawing/pie_series.rb +1 -0
  27. data/lib/axlsx/drawing/scaling.rb +1 -0
  28. data/lib/axlsx/drawing/ser_axis.rb +1 -0
  29. data/lib/axlsx/drawing/series.rb +1 -0
  30. data/lib/axlsx/drawing/series_title.rb +1 -0
  31. data/lib/axlsx/drawing/title.rb +1 -0
  32. data/lib/axlsx/drawing/two_cell_anchor.rb +1 -0
  33. data/lib/axlsx/drawing/val_axis.rb +1 -0
  34. data/lib/axlsx/drawing/val_axis_data.rb +1 -0
  35. data/lib/axlsx/drawing/view_3D.rb +1 -0
  36. data/lib/axlsx/package.rb +86 -38
  37. data/lib/axlsx/rels/relationship.rb +4 -3
  38. data/lib/axlsx/rels/relationships.rb +1 -0
  39. data/lib/axlsx/stylesheet/border.rb +1 -0
  40. data/lib/axlsx/stylesheet/border_pr.rb +1 -0
  41. data/lib/axlsx/stylesheet/cell_alignment.rb +1 -0
  42. data/lib/axlsx/stylesheet/cell_protection.rb +1 -0
  43. data/lib/axlsx/stylesheet/cell_style.rb +1 -0
  44. data/lib/axlsx/stylesheet/color.rb +1 -0
  45. data/lib/axlsx/stylesheet/fill.rb +1 -0
  46. data/lib/axlsx/stylesheet/font.rb +8 -0
  47. data/lib/axlsx/stylesheet/gradient_fill.rb +1 -1
  48. data/lib/axlsx/stylesheet/gradient_stop.rb +1 -1
  49. data/lib/axlsx/stylesheet/num_fmt.rb +1 -0
  50. data/lib/axlsx/stylesheet/pattern_fill.rb +1 -0
  51. data/lib/axlsx/stylesheet/styles.rb +12 -9
  52. data/lib/axlsx/stylesheet/table_style.rb +1 -0
  53. data/lib/axlsx/stylesheet/table_style_element.rb +1 -0
  54. data/lib/axlsx/stylesheet/table_styles.rb +1 -0
  55. data/lib/axlsx/stylesheet/xf.rb +1 -0
  56. data/lib/axlsx/util/cbf.rb +1 -0
  57. data/lib/axlsx/util/constants.rb +23 -14
  58. data/lib/axlsx/util/ms_off_crypto.rb +1 -1
  59. data/lib/axlsx/util/parser.rb +1 -0
  60. data/lib/axlsx/util/simple_typed_list.rb +2 -1
  61. data/lib/axlsx/util/storage.rb +1 -0
  62. data/lib/axlsx/util/validators.rb +11 -2
  63. data/lib/axlsx/version.rb +2 -1
  64. data/lib/axlsx/workbook/shared_strings_table.rb +71 -0
  65. data/lib/axlsx/workbook/shared_strings_table.rb~ +69 -0
  66. data/lib/axlsx/workbook/workbook.rb +47 -25
  67. data/lib/axlsx/workbook/worksheet/cell.rb +131 -78
  68. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +29 -0
  69. data/lib/axlsx/workbook/worksheet/page_margins.rb +94 -0
  70. data/lib/axlsx/workbook/worksheet/row.rb +44 -12
  71. data/lib/axlsx/workbook/worksheet/shared_strings_table.rb~ +0 -0
  72. data/lib/axlsx/workbook/worksheet/worksheet.rb +158 -44
  73. data/lib/axlsx.rb +16 -10
  74. data/test/content_type/tc_content_type.rb +2 -0
  75. data/test/content_type/tc_default.rb +2 -0
  76. data/test/content_type/tc_override.rb +1 -0
  77. data/test/drawing/tc_chart.rb +2 -2
  78. data/test/stylesheet/tc_cell_alignment.rb +8 -3
  79. data/test/stylesheet/tc_font.rb +8 -0
  80. data/test/stylesheet/tc_styles.rb +6 -2
  81. data/test/tc_package.rb +31 -0
  82. data/test/workbook/tc_shared_strings_table.rb +39 -0
  83. data/test/workbook/tc_shared_strings_table.rb~ +8 -0
  84. data/test/workbook/tc_workbook.rb +8 -0
  85. data/test/workbook/worksheet/tc_cell.rb +52 -9
  86. data/test/workbook/worksheet/tc_date_time_converter.rb +127 -0
  87. data/test/workbook/worksheet/tc_date_time_converter.rb~ +69 -0
  88. data/test/workbook/worksheet/tc_page_margins.rb +100 -0
  89. data/test/workbook/worksheet/tc_row.rb +36 -0
  90. data/test/workbook/worksheet/tc_worksheet.rb +97 -3
  91. metadata +33 -40
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  module Axlsx
2
3
  # Validate a value against a specific list of allowed values.
3
4
  class RestrictionValidator
@@ -52,6 +53,14 @@ module Axlsx
52
53
  DataTypeValidator.validate(:unsigned_int, [Fixnum, Integer], v, lambda { |arg| arg.respond_to?(:>=) && arg >= 0 })
53
54
  end
54
55
 
56
+ # Requires that the value is a Fixnum Integer or Float and is greater or equal to 0
57
+ # @param [Any] v The value validated
58
+ # @raise [ArgumentError] raised if the value is not a Fixnum or Integer value greater or equal to 0
59
+ # @return [Boolean] true if the data is valid
60
+ def self.validate_unsigned_numeric(v)
61
+ DataTypeValidator.validate("Invalid column width", [Fixnum, Integer, Float], v, lambda { |arg| arg.respond_to?(:>=) && arg >= 0 })
62
+ end
63
+
55
64
  # Requires that the value is a Fixnum or Integer
56
65
  # @param [Any] v The value validated
57
66
  def self.validate_int(v)
@@ -116,10 +125,10 @@ module Axlsx
116
125
  end
117
126
 
118
127
  # Requires that the value is a valid relationship_type
119
- # XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R are allowed
128
+ # 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
120
129
  # @param [Any] v The value validated
121
130
  def self.validate_relationship_type(v)
122
- RestrictionValidator.validate :relationship_type, [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], v
131
+ RestrictionValidator.validate :relationship_type, [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], v
123
132
  end
124
133
 
125
134
  # Requires that the value is a valid table element type
data/lib/axlsx/version.rb CHANGED
@@ -1,9 +1,10 @@
1
+ # encoding: UTF-8
1
2
  module Axlsx
2
3
 
3
4
  # The version of the gem
4
5
  # When using bunle exec rake and referencing the gem on github or locally
5
6
  # it will use the gemspec, which preloads this constant for the gem's version.
6
7
  # We check to make sure that it has not already been loaded
7
- VERSION="1.0.16" unless Axlsx.const_defined? :VERSION
8
+ VERSION="1.0.18" unless Axlsx.const_defined? :VERSION
8
9
 
9
10
  end
@@ -0,0 +1,71 @@
1
+ # encoding: UTF-8
2
+ module Axlsx
3
+
4
+ # The Shared String Table class is responsible for managing and serializing common strings in a workbook.
5
+ # While the ECMA-376 spec allows for both inline and shared strings it seems that at least some applications like iWorks Numbers
6
+ # and Google Docs require that the shared string table is populated in order to interoperate properly.
7
+ # As a developer, you should never need to directly work against this class. Simply set 'use_shared_strings'
8
+ # on the package or workbook to generate a package that uses the shared strings table instead of inline strings.
9
+ # @note Serialization performance is affected by using this serialization method so if you do not need interoperability
10
+ # it is recomended that you use the default inline string method of serialization.
11
+ class SharedStringsTable
12
+
13
+ # The total number of strings in the workbook including duplicates
14
+ # Empty cells are treated as blank strings
15
+ # @return [Integer]
16
+ attr_reader :count
17
+
18
+ # The total number of unique strings in the workbook.
19
+ # @return [Integer]
20
+ def unique_count
21
+ @unique_cells.size
22
+ end
23
+
24
+ # An array of unique cells. Multiple attributes of the cell are used in comparison
25
+ # each of these unique cells is parsed into the shared string table.
26
+ # @see Cell#sharable
27
+ attr_reader :unique_cells
28
+
29
+ # Creates a new Shared Strings Table agains an array of cells
30
+ # @param [Array] cells This is an array of all of the cells in the workbook
31
+ def initialize(cells)
32
+ cells = cells.flatten.reject { |c| c.type != :string || c.value.start_with?('=') }
33
+ @count = cells.size
34
+ @unique_cells = []
35
+ resolve(cells)
36
+ end
37
+
38
+
39
+ # Generate the xml document for the Shared Strings Table
40
+ # @return [String]
41
+ def to_xml
42
+ builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
43
+ xml.sst(:xmlns => Axlsx::XML_NS, :count => count, :uniqueCount => unique_count) {
44
+ @unique_cells.each do |cell|
45
+ xml.si { cell.run_xml(xml) }
46
+ end
47
+ }
48
+ end
49
+ builder.to_xml(:save_with => 0)
50
+ end
51
+
52
+ private
53
+
54
+ # Interate over all of the cells in the array.
55
+ # if our unique cells array does not contain a sharable cell,
56
+ # add the cell to our unique cells array and set the ssti attribute on the index of this cell in the shared strings table
57
+ # if a sharable cell already exists in our unique_cells array, set the ssti attribute of the cell and move on.
58
+ # @return [Array] unique cells
59
+ def resolve(cells)
60
+ cells.each do |cell|
61
+ index = @unique_cells.index { |item| item.shareable(cell) }
62
+ if index == nil
63
+ cell.send :ssti=, @unique_cells.size
64
+ @unique_cells << cell
65
+ else
66
+ cell.send :ssti=, index
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: UTF--8
2
+ module Axlsx
3
+
4
+ # The Shared String Table class is responsible for managing and serializing common strings in a workbook.
5
+ # While the ECMA-376 spec allows for both inline and shared strings it seems that at least some applications like Numbers (Mac)
6
+ # and Google Docs require that the shared string table is populated in order to interoperate properly.
7
+ # As a developer, you should never need to directly work against this class. Simply set 'use_shared_strings'
8
+ # on the package or workbook to generate a package that uses the shared strings table instead of inline strings.
9
+ # @note Serialization performance is affected by using this serialization method so if you do not need interoperability
10
+ # it is recomended that you use the default inline string method of serialization.
11
+ class SharedStringsTable
12
+
13
+ # The total number of strings in the workbook including duplicates
14
+ # Empty cells are treated as blank strings
15
+ # @return [Integer]
16
+ attr_reader :count
17
+
18
+ # The total number of unique strings in the workbook.
19
+ # @return [Integer]
20
+ def unique_count
21
+ @unique_cells.size
22
+ end
23
+
24
+ # An array of unique cells. Multiple attributes of the cell are used in comparison
25
+ # each of these unique cells is parsed into the shared string table.
26
+ # @see Cell#sharable
27
+ attr_reader :unique_cells
28
+
29
+ # Creates a new Shared Strings Table agains an array of cells
30
+ # @param [Array] cells This is an array of all of the cells in the workbook
31
+ def initialize(cells)
32
+ cells = cells.reject { |c| c.type != :string }
33
+ @count = cells.size
34
+ @unique_cells = []
35
+ resolve cells
36
+ end
37
+
38
+ # Interate over all of the cells in the array.
39
+ # if our unique cells array does not contain a sharable cell,
40
+ # add the cell to our unique cells array and set the ssti attribute on the index of this cell in the shared strings table
41
+ # if a sharable cell already exists in our unique_cells array, set the ssti attribute of the cell and move on.
42
+ # @return [Array] unique cells
43
+ def resolve(cells)
44
+ cells.each do |cell|
45
+ index = @unique_cells.index { |item| item.sharable(cell) }
46
+ if index == nil
47
+ cell.ssti = @unique_cells.size
48
+ @unique_cells << cell
49
+ else
50
+ cell.ssti = index
51
+ end
52
+ end
53
+ end
54
+
55
+ # Generate the xml document for the Shared Strings Table
56
+ # @return [String]
57
+ def to_xml
58
+ builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
59
+ xml.sst(:xmlns => Axlsx::XML_NS, :count => count, :uniqueCount => unique_count) {
60
+ @unique_cells.each do |cell|
61
+ xml.si { xml.t cell.value.to_s }
62
+ end
63
+ }
64
+ end
65
+ builder.to_xml(:save_with => 0)
66
+ end
67
+ end
68
+
69
+ end
@@ -1,13 +1,16 @@
1
1
  # -*- coding: utf-8 -*-
2
- module Axlsx
2
+ module Axlsx
3
3
 
4
+ require 'axlsx/workbook/worksheet/date_time_converter.rb'
4
5
  require 'axlsx/workbook/worksheet/cell.rb'
6
+ require 'axlsx/workbook/worksheet/page_margins.rb'
5
7
  require 'axlsx/workbook/worksheet/row.rb'
6
8
  require 'axlsx/workbook/worksheet/worksheet.rb'
9
+ require 'axlsx/workbook/shared_strings_table.rb'
7
10
 
8
11
  # The Workbook class is an xlsx workbook that manages worksheets, charts, drawings and styles.
9
12
  # The following parts of the Office Open XML spreadsheet specification are not implimented in this version.
10
- #
13
+ #
11
14
  # bookViews
12
15
  # calcPr
13
16
  # customWorkbookViews
@@ -30,7 +33,20 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
30
33
  # *workbookPr is only supported to the extend of date1904
31
34
  class Workbook
32
35
 
33
- # A collection of worksheets associated with this workbook.
36
+ # When true, the Package will be generated with a shared string table. This may be required by some OOXML processors that do not
37
+ # adhere to the ECMA specification that dictates string may be inline in the sheet.
38
+ # Using this option will increase the time required to serialize the document as every string in every cell must be analzed and referenced.
39
+ # @return [Boolean]
40
+ attr_reader :use_shared_strings
41
+
42
+ # @see use_shared_strings
43
+ def use_shared_strings=(v)
44
+ Axlsx::validate_boolean(v)
45
+ @use_shared_strings = v
46
+ end
47
+
48
+
49
+ # A collection of worksheets associated with this workbook.
34
50
  # @note The recommended way to manage worksheets is add_worksheet
35
51
  # @see Workbook#add_worksheet
36
52
  # @see Worksheet
@@ -72,40 +88,31 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
72
88
  # Indicates if the epoc date for serialization should be 1904. If false, 1900 is used.
73
89
  @@date1904 = false
74
90
 
75
- # lets come back to this later when we are ready for parsing.
91
+ # lets come back to this later when we are ready for parsing.
76
92
  #def self.parse entry
77
93
  # io = entry.get_input_stream
78
94
  # w = self.new
79
95
  # w.parser_xml = Nokogiri::XML(io.read)
80
- # w.parse_string :date1904, "//xmlns:workbookPr/@date1904"
96
+ # w.parse_string :date1904, "//xmlns:workbookPr/@date1904"
81
97
  # w
82
98
  #end
83
99
 
84
100
  # Creates a new Workbook
85
- #
86
- # @option options [Boolean] date1904. If this is not specified, we try to determine if the platform is bsd/darwin and set date1904 to true automatically.
87
- #
101
+ # The recomended way to work with workbooks is via Package#workbook
102
+ # @option options [Boolean] date1904. If this is not specified, date1904 is set to false. Office 2011 for Mac defaults to false.
88
103
  def initialize(options={})
89
104
  @styles = Styles.new
90
105
  @worksheets = SimpleTypedList.new Worksheet
91
106
  @drawings = SimpleTypedList.new Drawing
92
107
  @charts = SimpleTypedList.new Chart
93
108
  @images = SimpleTypedList.new Pic
94
- self.date1904= options[:date1904].nil? ? is_bsd? : options[:date1904]
95
- yield self if block_given?
96
- end
97
-
98
- # Uses RUBY_PLATFORM constant to determine if the OS is freebsd or darwin
99
- # based on this value we attempt to set date1904.
100
- # @return [Boolean]
101
- def is_bsd?
102
- platform = RUBY_PLATFORM.downcase
103
- platform.include?('freebsd') || platform.include?('darwin')
109
+ self.date1904= !options[:date1904].nil? && options[:date1904]
110
+ yield self if block_given?
104
111
  end
105
112
 
106
113
  # Instance level access to the class variable 1904
107
114
  # @return [Boolean]
108
- def date1904() @@date1904; end
115
+ def date1904() @@date1904; end
109
116
 
110
117
  # see @date1904
111
118
  def date1904=(v) Axlsx::validate_boolean v; @@date1904 = v; end
@@ -121,6 +128,7 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
121
128
  # Adds a worksheet to this workbook
122
129
  # @return [Worksheet]
123
130
  # @option options [String] name The name of the worksheet.
131
+ # @option options [Hash] page_margins The page margins for the worksheet.
124
132
  # @see Worksheet#initialize
125
133
  def add_worksheet(options={})
126
134
  worksheet = Worksheet.new(self, options)
@@ -134,19 +142,28 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
134
142
  r = Relationships.new
135
143
  @worksheets.each do |sheet|
136
144
  r << Relationship.new(WORKSHEET_R, WORKSHEET_PN % (r.size+1))
137
- end
145
+ end
138
146
  r << Relationship.new(STYLES_R, STYLES_PN)
147
+ if use_shared_strings
148
+ r << Relationship.new(SHARED_STRINGS_R, SHARED_STRINGS_PN)
149
+ end
139
150
  r
140
151
  end
141
152
 
153
+ # generates a shared string object against all cells in all worksheets.
154
+ # @return [SharedStringTable]
155
+ def shared_strings
156
+ SharedStringsTable.new(worksheets.collect { |ws| ws.cells })
157
+ end
158
+
142
159
  # returns a range of cells in a worksheet
143
- # @param [String] cell_def The excel style reference defining the worksheet and cells. The range must specify the sheet to
160
+ # @param [String] cell_def The excel style reference defining the worksheet and cells. The range must specify the sheet to
144
161
  # retrieve the cells from. e.g. range('Sheet1!A1:B2') will return an array of four cells [A1, A2, B1, B2] while range('Sheet1!A1') will return a single Cell.
145
162
  # @return [Cell, Array]
146
163
  def [](cell_def)
147
164
  sheet_name = cell_def.split('!')[0] if cell_def.match('!')
148
165
  worksheet = self.worksheets.select { |s| s.name == sheet_name }.first
149
- raise ArgumentError, 'Unknown Sheet' unless sheet_name && worksheet.is_a?(Worksheet)
166
+ raise ArgumentError, 'Unknown Sheet' unless sheet_name && worksheet.is_a?(Worksheet)
150
167
  worksheet[cell_def.gsub(/.+!/,"")]
151
168
  end
152
169
 
@@ -154,17 +171,22 @@ require 'axlsx/workbook/worksheet/worksheet.rb'
154
171
  # @return [String]
155
172
  def to_xml()
156
173
  add_worksheet unless worksheets.size > 0
157
- builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
174
+ builder = Nokogiri::XML::Builder.new(:encoding => ENCODING) do |xml|
158
175
  xml.workbook(:xmlns => XML_NS, :'xmlns:r' => XML_NS_R) {
159
176
  xml.workbookPr(:date1904=>@@date1904)
160
177
  #<x:workbookProtection workbookPassword="xsd:hexBinary data" lockStructure="1" lockWindows="1" />
178
+ # Required to support rubyXL parsing as it requires sheetView, which requires this.
179
+ # and removed because it seems to cause some odd [Grouped] behaviour in excel.
180
+ # xml.bookViews {
181
+ # xml.workbookView :activeTab=>0
182
+ # }
161
183
  xml.sheets {
162
- @worksheets.each_with_index do |sheet, index|
184
+ @worksheets.each_with_index do |sheet, index|
163
185
  xml.sheet(:name=>sheet.name, :sheetId=>index+1, :"r:id"=>sheet.rId)
164
186
  end
165
187
  }
166
188
  }
167
- end
189
+ end
168
190
  builder.to_xml(:save_with => 0)
169
191
  end
170
192
  end