axlsx 1.2.0 → 2.0.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 (208) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +3 -2
  3. data/.yardopts_guide +19 -0
  4. data/CHANGELOG.md +64 -1
  5. data/README.md +105 -87
  6. data/Rakefile +0 -5
  7. data/examples/2010_comments.rb +17 -0
  8. data/examples/anchor_swapping.rb +28 -0
  9. data/examples/auto_filter.rb +16 -0
  10. data/examples/basic_charts.rb +4 -0
  11. data/examples/colored_links.rb +59 -0
  12. data/examples/conditional_formatting/example_conditional_formatting.rb +4 -2
  13. data/examples/example.rb +569 -258
  14. data/examples/ios_preview.rb +14 -0
  15. data/examples/page_setup.rb +11 -0
  16. data/examples/pivot_table.rb +39 -0
  17. data/examples/skydrive/real_example.rb +8 -8
  18. data/examples/styles.rb +66 -0
  19. data/examples/underline.rb +13 -0
  20. data/examples/wrap_text.rb +21 -0
  21. data/lib/axlsx/content_type/abstract_content_type.rb +32 -0
  22. data/lib/axlsx/content_type/content_type.rb +1 -1
  23. data/lib/axlsx/content_type/default.rb +6 -37
  24. data/lib/axlsx/content_type/override.rb +6 -38
  25. data/lib/axlsx/doc_props/app.rb +10 -7
  26. data/lib/axlsx/doc_props/core.rb +6 -1
  27. data/lib/axlsx/drawing/axes.rb +61 -0
  28. data/lib/axlsx/drawing/axis.rb +20 -17
  29. data/lib/axlsx/drawing/bar_3D_chart.rb +31 -22
  30. data/lib/axlsx/drawing/cat_axis.rb +2 -10
  31. data/lib/axlsx/drawing/chart.rb +33 -6
  32. data/lib/axlsx/drawing/d_lbls.rb +21 -31
  33. data/lib/axlsx/drawing/drawing.rb +11 -17
  34. data/lib/axlsx/drawing/graphic_frame.rb +3 -3
  35. data/lib/axlsx/drawing/hyperlink.rb +42 -36
  36. data/lib/axlsx/drawing/line_3D_chart.rb +28 -70
  37. data/lib/axlsx/drawing/line_chart.rb +99 -0
  38. data/lib/axlsx/drawing/line_series.rb +20 -2
  39. data/lib/axlsx/drawing/marker.rb +38 -18
  40. data/lib/axlsx/drawing/num_data.rb +6 -6
  41. data/lib/axlsx/drawing/num_data_source.rb +17 -16
  42. data/lib/axlsx/drawing/one_cell_anchor.rb +28 -21
  43. data/lib/axlsx/drawing/pic.rb +43 -45
  44. data/lib/axlsx/drawing/picture_locking.rb +12 -44
  45. data/lib/axlsx/drawing/pie_3D_chart.rb +2 -1
  46. data/lib/axlsx/drawing/scaling.rb +13 -13
  47. data/lib/axlsx/drawing/scatter_chart.rb +32 -21
  48. data/lib/axlsx/drawing/ser_axis.rb +15 -16
  49. data/lib/axlsx/drawing/series.rb +3 -6
  50. data/lib/axlsx/drawing/str_data.rb +3 -3
  51. data/lib/axlsx/drawing/str_val.rb +7 -8
  52. data/lib/axlsx/drawing/two_cell_anchor.rb +7 -27
  53. data/lib/axlsx/drawing/val_axis.rb +14 -13
  54. data/lib/axlsx/drawing/view_3D.rb +52 -38
  55. data/lib/axlsx/drawing/vml_shape.rb +20 -79
  56. data/lib/axlsx/package.rb +58 -28
  57. data/lib/axlsx/rels/relationship.rb +75 -9
  58. data/lib/axlsx/rels/relationships.rb +8 -1
  59. data/lib/axlsx/stylesheet/border.rb +29 -20
  60. data/lib/axlsx/stylesheet/border_pr.rb +5 -4
  61. data/lib/axlsx/stylesheet/cell_alignment.rb +55 -29
  62. data/lib/axlsx/stylesheet/cell_protection.rb +7 -4
  63. data/lib/axlsx/stylesheet/cell_style.rb +19 -14
  64. data/lib/axlsx/stylesheet/color.rb +20 -17
  65. data/lib/axlsx/stylesheet/dxf.rb +4 -4
  66. data/lib/axlsx/stylesheet/font.rb +22 -22
  67. data/lib/axlsx/stylesheet/gradient_fill.rb +45 -21
  68. data/lib/axlsx/stylesheet/num_fmt.rb +23 -14
  69. data/lib/axlsx/stylesheet/pattern_fill.rb +12 -21
  70. data/lib/axlsx/stylesheet/styles.rb +37 -22
  71. data/lib/axlsx/stylesheet/table_style.rb +17 -16
  72. data/lib/axlsx/stylesheet/table_style_element.rb +15 -11
  73. data/lib/axlsx/stylesheet/table_styles.rb +14 -11
  74. data/lib/axlsx/stylesheet/xf.rb +28 -26
  75. data/lib/axlsx/util/accessors.rb +64 -0
  76. data/lib/axlsx/util/constants.rb +127 -6
  77. data/lib/axlsx/util/options_parser.rb +15 -0
  78. data/lib/axlsx/util/serialized_attributes.rb +79 -0
  79. data/lib/axlsx/util/simple_typed_list.rb +50 -15
  80. data/lib/axlsx/util/validators.rb +51 -16
  81. data/lib/axlsx/version.rb +2 -2
  82. data/lib/axlsx/workbook/defined_name.rb +14 -59
  83. data/lib/axlsx/workbook/shared_strings_table.rb +13 -4
  84. data/lib/axlsx/workbook/workbook.rb +85 -6
  85. data/lib/axlsx/workbook/worksheet/auto_filter/auto_filter.rb +77 -0
  86. data/lib/axlsx/workbook/worksheet/auto_filter/filter_column.rb +94 -0
  87. data/lib/axlsx/workbook/worksheet/auto_filter/filters.rb +246 -0
  88. data/lib/axlsx/workbook/worksheet/break.rb +37 -0
  89. data/lib/axlsx/workbook/worksheet/cell.rb +109 -107
  90. data/lib/axlsx/workbook/worksheet/cell_serializer.rb +144 -0
  91. data/lib/axlsx/workbook/worksheet/cfvo.rb +15 -15
  92. data/lib/axlsx/workbook/worksheet/cfvos.rb +15 -0
  93. data/lib/axlsx/workbook/worksheet/col.rb +43 -29
  94. data/lib/axlsx/workbook/worksheet/col_breaks.rb +35 -0
  95. data/lib/axlsx/workbook/worksheet/color_scale.rb +62 -28
  96. data/lib/axlsx/workbook/worksheet/comment.rb +28 -37
  97. data/lib/axlsx/workbook/worksheet/comments.rb +3 -3
  98. data/lib/axlsx/workbook/worksheet/conditional_formatting.rb +11 -11
  99. data/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb +31 -27
  100. data/lib/axlsx/workbook/worksheet/data_bar.rb +75 -41
  101. data/lib/axlsx/workbook/worksheet/data_validation.rb +61 -62
  102. data/lib/axlsx/workbook/worksheet/date_time_converter.rb +6 -5
  103. data/lib/axlsx/workbook/worksheet/dimension.rb +0 -1
  104. data/lib/axlsx/workbook/worksheet/header_footer.rb +54 -0
  105. data/lib/axlsx/workbook/worksheet/icon_set.rb +20 -20
  106. data/lib/axlsx/workbook/worksheet/page_margins.rb +21 -19
  107. data/lib/axlsx/workbook/worksheet/page_set_up_pr.rb +44 -0
  108. data/lib/axlsx/workbook/worksheet/page_setup.rb +152 -23
  109. data/lib/axlsx/workbook/worksheet/pane.rb +48 -51
  110. data/lib/axlsx/workbook/worksheet/pivot_table.rb +273 -0
  111. data/lib/axlsx/workbook/worksheet/pivot_table_cache_definition.rb +66 -0
  112. data/lib/axlsx/workbook/worksheet/pivot_tables.rb +24 -0
  113. data/lib/axlsx/workbook/worksheet/print_options.rb +8 -30
  114. data/lib/axlsx/workbook/worksheet/protected_range.rb +16 -13
  115. data/lib/axlsx/workbook/worksheet/row.rb +59 -83
  116. data/lib/axlsx/workbook/worksheet/row_breaks.rb +33 -0
  117. data/lib/axlsx/workbook/worksheet/selection.rb +30 -38
  118. data/lib/axlsx/workbook/worksheet/sheet_calc_pr.rb +29 -0
  119. data/lib/axlsx/workbook/worksheet/sheet_format_pr.rb +60 -0
  120. data/lib/axlsx/workbook/worksheet/sheet_pr.rb +49 -4
  121. data/lib/axlsx/workbook/worksheet/sheet_protection.rb +51 -155
  122. data/lib/axlsx/workbook/worksheet/sheet_view.rb +68 -234
  123. data/lib/axlsx/workbook/worksheet/table.rb +25 -19
  124. data/lib/axlsx/workbook/worksheet/table_style_info.rb +51 -0
  125. data/lib/axlsx/workbook/worksheet/tables.rb +1 -1
  126. data/lib/axlsx/workbook/worksheet/worksheet.rb +174 -35
  127. data/lib/axlsx/workbook/worksheet/worksheet_comments.rb +6 -5
  128. data/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +4 -10
  129. data/lib/axlsx/workbook/worksheet/worksheet_hyperlink.rb +74 -0
  130. data/lib/axlsx/workbook/worksheet/worksheet_hyperlinks.rb +38 -0
  131. data/lib/axlsx.rb +55 -5
  132. data/lib/schema/sml.xsd +4 -0
  133. data/test/axlsx.qcachegrind +2226 -0
  134. data/test/benchmark.rb +2 -3
  135. data/test/content_type/tc_default.rb +0 -11
  136. data/test/content_type/tc_override.rb +0 -13
  137. data/test/doc_props/tc_core.rb +7 -0
  138. data/test/drawing/tc_axes.rb +8 -0
  139. data/test/drawing/tc_axis.rb +15 -17
  140. data/test/drawing/tc_bar_3D_chart.rb +6 -0
  141. data/test/drawing/tc_cat_axis.rb +9 -9
  142. data/test/drawing/tc_chart.rb +20 -0
  143. data/test/drawing/tc_d_lbls.rb +14 -4
  144. data/test/drawing/tc_drawing.rb +0 -5
  145. data/test/drawing/tc_graphic_frame.rb +5 -3
  146. data/test/drawing/tc_hyperlink.rb +0 -4
  147. data/test/drawing/tc_line_chart.rb +39 -0
  148. data/test/drawing/tc_line_series.rb +8 -2
  149. data/test/drawing/tc_pic.rb +14 -3
  150. data/test/drawing/tc_ser_axis.rb +13 -12
  151. data/test/drawing/tc_two_cell_anchor.rb +3 -3
  152. data/test/drawing/tc_val_axis.rb +6 -6
  153. data/test/drawing/tc_vml_shape.rb +9 -3
  154. data/test/profile.rb +11 -21
  155. data/test/rels/tc_relationship.rb +32 -9
  156. data/test/rels/tc_relationships.rb +12 -1
  157. data/test/stylesheet/tc_color.rb +6 -0
  158. data/test/stylesheet/tc_styles.rb +15 -2
  159. data/test/tc_axlsx.rb +35 -2
  160. data/test/tc_helper.rb +7 -1
  161. data/test/tc_package.rb +44 -5
  162. data/test/util/tc_serialized_attributes.rb +19 -0
  163. data/test/util/tc_validators.rb +26 -1
  164. data/test/workbook/tc_defined_name.rb +2 -2
  165. data/test/workbook/tc_shared_strings_table.rb +6 -0
  166. data/test/workbook/tc_workbook.rb +38 -1
  167. data/test/workbook/worksheet/auto_filter/tc_auto_filter.rb +38 -0
  168. data/test/workbook/worksheet/auto_filter/tc_filter_column.rb +76 -0
  169. data/test/workbook/worksheet/auto_filter/tc_filters.rb +50 -0
  170. data/test/workbook/worksheet/tc_break.rb +49 -0
  171. data/test/workbook/worksheet/tc_cell.rb +40 -6
  172. data/test/workbook/worksheet/tc_col.rb +11 -1
  173. data/test/workbook/worksheet/tc_color_scale.rb +31 -2
  174. data/test/workbook/worksheet/tc_comment.rb +21 -6
  175. data/test/workbook/worksheet/tc_comments.rb +2 -2
  176. data/test/workbook/worksheet/tc_conditional_formatting.rb +7 -0
  177. data/test/workbook/worksheet/tc_data_bar.rb +7 -0
  178. data/test/workbook/worksheet/tc_date_time_converter.rb +3 -11
  179. data/test/workbook/worksheet/tc_header_footer.rb +151 -0
  180. data/test/workbook/worksheet/tc_page_set_up_pr.rb +15 -0
  181. data/test/workbook/worksheet/tc_page_setup.rb +6 -1
  182. data/test/workbook/worksheet/tc_pane.rb +5 -45
  183. data/test/workbook/worksheet/tc_pivot_table.rb +120 -0
  184. data/test/workbook/worksheet/tc_pivot_table_cache_definition.rb +54 -0
  185. data/test/workbook/worksheet/tc_row.rb +4 -4
  186. data/test/workbook/worksheet/tc_selection.rb +9 -48
  187. data/test/workbook/worksheet/tc_sheet_calc_pr.rb +18 -0
  188. data/test/workbook/worksheet/tc_sheet_format_pr.rb +88 -0
  189. data/test/workbook/worksheet/tc_sheet_pr.rb +27 -0
  190. data/test/workbook/worksheet/tc_sheet_view.rb +39 -39
  191. data/test/workbook/worksheet/{table/tc_table.rb → tc_table.rb} +8 -3
  192. data/test/workbook/worksheet/tc_table_style_info.rb +53 -0
  193. data/test/workbook/worksheet/tc_worksheet.rb +131 -16
  194. data/test/workbook/worksheet/tc_worksheet_hyperlink.rb +55 -0
  195. metadata +97 -64
  196. data/examples/example.xlsx +0 -0
  197. data/examples/example_streamed.xlsx +0 -0
  198. data/examples/extractive.pdf +0 -0
  199. data/examples/image1.gif +0 -0
  200. data/examples/image1.jpg +0 -0
  201. data/examples/image1.png +0 -0
  202. data/examples/no-use_autowidth.xlsx +0 -0
  203. data/examples/sample.png +0 -0
  204. data/examples/scraping_html.rb +0 -91
  205. data/examples/shared_strings_example.xlsx +0 -0
  206. data/examples/sheet_view.rb +0 -34
  207. data/examples/two_cell_anchor_image.rb +0 -11
  208. data/lib/axlsx/workbook/worksheet/auto_filter.rb +0 -34
data/lib/axlsx/package.rb CHANGED
@@ -3,7 +3,7 @@ module Axlsx
3
3
  # Package is responsible for managing all the bits and peices that Open Office XML requires to make a valid
4
4
  # xlsx document including valdation and serialization.
5
5
  class Package
6
-
6
+ include Axlsx::OptionsParser
7
7
 
8
8
  # provides access to the app doc properties for this package
9
9
  # see App
@@ -17,15 +17,15 @@ module Axlsx
17
17
  #
18
18
  # @param [Hash] options A hash that you can use to specify the author and workbook for this package.
19
19
  # @option options [String] :author The author of the document
20
+ # @option options [Time] :created_at Timestamp in the document properties (defaults to current time).
20
21
  # @option options [Boolean] :use_shared_strings This is passed to the workbook to specify that shared strings should be used when serializing the package.
21
22
  # @example Package.new :author => 'you!', :workbook => Workbook.new
22
23
  def initialize(options={})
23
24
  @workbook = nil
24
25
  @core, @app = Core.new, App.new
25
26
  @core.creator = options[:author] || @core.creator
26
- options.each do |o|
27
- self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
28
- end
27
+ @core.created = options[:created_at]
28
+ parse_options options
29
29
  yield self if block_given?
30
30
  end
31
31
 
@@ -37,12 +37,6 @@ module Axlsx
37
37
  end
38
38
 
39
39
 
40
- # Shortcut to specify that the workbook should use shared strings
41
- # @see Workbook#use_shared_strings
42
- def use_shared_strings=(v)
43
- Axlsx::validate_boolean(v);
44
- workbook.use_shared_strings = v
45
- end
46
40
 
47
41
  # Shortcut to determine if the workbook is configured to use shared strings
48
42
  # @see Workbook#use_shared_strings
@@ -50,6 +44,12 @@ module Axlsx
50
44
  workbook.use_shared_strings
51
45
  end
52
46
 
47
+ # Shortcut to specify that the workbook should use shared strings
48
+ # @see Workbook#use_shared_strings
49
+ def use_shared_strings=(v)
50
+ Axlsx::validate_boolean(v);
51
+ workbook.use_shared_strings = v
52
+ end
53
53
  # The workbook this package will serialize or validate.
54
54
  # @return [Workbook] If no workbook instance has been assigned with this package a new Workbook instance is returned.
55
55
  # @raise ArgumentError if workbook parameter is not a Workbook instance.
@@ -100,6 +100,7 @@ module Axlsx
100
100
  # File.open('example_streamed.xlsx', 'w') { |f| f.write(s.read) }
101
101
  def serialize(output, confirm_valid=false)
102
102
  return false unless !confirm_valid || self.validate.empty?
103
+ Relationship.clear_cached_instances
103
104
  Zip::ZipOutputStream.open(output) do |zip|
104
105
  write_parts(zip)
105
106
  end
@@ -112,6 +113,7 @@ module Axlsx
112
113
  # @return [StringIO|Boolean] False if confirm_valid and validation errors exist. rewound string IO if not.
113
114
  def to_stream(confirm_valid=false)
114
115
  return false unless !confirm_valid || self.validate.empty?
116
+ Relationship.clear_cached_instances
115
117
  zip = write_parts(Zip::ZipOutputStream.new("streamed", true))
116
118
  stream = zip.close_buffer
117
119
  stream.rewind
@@ -158,12 +160,12 @@ module Axlsx
158
160
  p = parts
159
161
  p.each do |part|
160
162
  unless part[:doc].nil?
161
- zip.put_next_entry(part[:entry])
163
+ zip.put_next_entry(zip_entry_for_part(part))
162
164
  entry = ['1.9.2', '1.9.3'].include?(RUBY_VERSION) ? part[:doc].force_encoding('BINARY') : part[:doc]
163
165
  zip.puts(entry)
164
166
  end
165
167
  unless part[:path].nil?
166
- zip.put_next_entry(part[:entry]);
168
+ zip.put_next_entry(zip_entry_for_part(part))
167
169
  # binread for 1.9.3
168
170
  zip.write IO.respond_to?(:binread) ? IO.binread(part[:path]) : IO.read(part[:path])
169
171
  end
@@ -171,11 +173,27 @@ module Axlsx
171
173
  zip
172
174
  end
173
175
 
176
+ # Generate a ZipEntry for the given package part.
177
+ # The important part here is to explicitly set the timestamp for the zip entry: Serializing axlsx packages
178
+ # with identical contents should result in identical zip files – however, the timestamp of a zip entry
179
+ # defaults to the time of serialization and therefore the zip file contents would be different every time
180
+ # the package is serialized.
181
+ #
182
+ # Note: {Core#created} also defaults to the current time – so to generate identical axlsx packages you have
183
+ # to set this explicitly, too (eg. with `Package.new(created_at: Time.local(2013, 1, 1))`).
184
+ #
185
+ # @param part A hash describing a part of this pacakge (see {#parts})
186
+ # @return [Zip::ZipEntry]
187
+ def zip_entry_for_part(part)
188
+ timestamp = Zip::DOSTime.at(@core.created.to_i)
189
+ Zip::ZipEntry.new("", part[:entry], "", "", 0, 0, Zip::ZipEntry::DEFLATED, 0, timestamp)
190
+ end
191
+
174
192
  # The parts of a package
175
193
  # @return [Array] An array of hashes that define the entry, document and schema for each part of the package.
176
194
  # @private
177
195
  def parts
178
- @parts = [
196
+ parts = [
179
197
  {:entry => RELS_PN, :doc => relationships.to_xml_string, :schema => RELS_XSD},
180
198
  {:entry => "xl/#{STYLES_PN}", :doc => workbook.styles.to_xml_string, :schema => SML_XSD},
181
199
  {:entry => CORE_PN, :doc => @core.to_xml_string, :schema => CORE_XSD},
@@ -186,39 +204,45 @@ module Axlsx
186
204
  ]
187
205
 
188
206
  workbook.drawings.each do |drawing|
189
- @parts << {:entry => "xl/#{drawing.rels_pn}", :doc => drawing.relationships.to_xml_string, :schema => RELS_XSD}
190
- @parts << {:entry => "xl/#{drawing.pn}", :doc => drawing.to_xml_string, :schema => DRAWING_XSD}
207
+ parts << {:entry => "xl/#{drawing.rels_pn}", :doc => drawing.relationships.to_xml_string, :schema => RELS_XSD}
208
+ parts << {:entry => "xl/#{drawing.pn}", :doc => drawing.to_xml_string, :schema => DRAWING_XSD}
191
209
  end
192
210
 
193
211
 
194
212
  workbook.tables.each do |table|
195
- @parts << {:entry => "xl/#{table.pn}", :doc => table.to_xml_string, :schema => SML_XSD}
213
+ parts << {:entry => "xl/#{table.pn}", :doc => table.to_xml_string, :schema => SML_XSD}
214
+ end
215
+ workbook.pivot_tables.each do |pivot_table|
216
+ cache_definition = pivot_table.cache_definition
217
+ parts << {:entry => "xl/#{pivot_table.rels_pn}", :doc => pivot_table.relationships.to_xml_string, :schema => RELS_XSD}
218
+ parts << {:entry => "xl/#{pivot_table.pn}", :doc => pivot_table.to_xml_string} #, :schema => SML_XSD}
219
+ parts << {:entry => "xl/#{cache_definition.pn}", :doc => cache_definition.to_xml_string} #, :schema => SML_XSD}
196
220
  end
197
221
 
198
222
  workbook.comments.each do|comment|
199
223
  if comment.size > 0
200
- @parts << { :entry => "xl/#{comment.pn}", :doc => comment.to_xml_string, :schema => SML_XSD }
201
- @parts << { :entry => "xl/#{comment.vml_drawing.pn}", :doc => comment.vml_drawing.to_xml_string, :schema => nil }
224
+ parts << { :entry => "xl/#{comment.pn}", :doc => comment.to_xml_string, :schema => SML_XSD }
225
+ parts << { :entry => "xl/#{comment.vml_drawing.pn}", :doc => comment.vml_drawing.to_xml_string, :schema => nil }
202
226
  end
203
227
  end
204
228
 
205
229
  workbook.charts.each do |chart|
206
- @parts << {:entry => "xl/#{chart.pn}", :doc => chart.to_xml_string, :schema => DRAWING_XSD}
230
+ parts << {:entry => "xl/#{chart.pn}", :doc => chart.to_xml_string, :schema => DRAWING_XSD}
207
231
  end
208
232
 
209
233
  workbook.images.each do |image|
210
- @parts << {:entry => "xl/#{image.pn}", :path => image.image_src}
234
+ parts << {:entry => "xl/#{image.pn}", :path => image.image_src}
211
235
  end
212
236
 
213
237
  if use_shared_strings
214
- @parts << {:entry => "xl/#{SHARED_STRINGS_PN}", :doc => workbook.shared_strings.to_xml_string, :schema => SML_XSD}
238
+ parts << {:entry => "xl/#{SHARED_STRINGS_PN}", :doc => workbook.shared_strings.to_xml_string, :schema => SML_XSD}
215
239
  end
216
240
 
217
241
  workbook.worksheets.each do |sheet|
218
- @parts << {:entry => "xl/#{sheet.rels_pn}", :doc => sheet.relationships.to_xml_string, :schema => RELS_XSD}
219
- @parts << {:entry => "xl/#{sheet.pn}", :doc => sheet.to_xml_string, :schema => SML_XSD}
242
+ parts << {:entry => "xl/#{sheet.rels_pn}", :doc => sheet.relationships.to_xml_string, :schema => RELS_XSD}
243
+ parts << {:entry => "xl/#{sheet.pn}", :doc => sheet.to_xml_string, :schema => SML_XSD}
220
244
  end
221
- @parts
245
+ parts
222
246
  end
223
247
 
224
248
  # Performs xsd validation for a signle document
@@ -242,7 +266,6 @@ module Axlsx
242
266
  # @private
243
267
  def content_types
244
268
  c_types = base_content_types
245
-
246
269
  workbook.drawings.each do |drawing|
247
270
  c_types << Axlsx::Override.new(:PartName => "/xl/#{drawing.pn}",
248
271
  :ContentType => DRAWING_CT)
@@ -258,6 +281,13 @@ module Axlsx
258
281
  :ContentType => TABLE_CT)
259
282
  end
260
283
 
284
+ workbook.pivot_tables.each do |pivot_table|
285
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{pivot_table.pn}",
286
+ :ContentType => PIVOT_TABLE_CT)
287
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{pivot_table.cache_definition.pn}",
288
+ :ContentType => PIVOT_TABLE_CACHE_DEFINITION_CT)
289
+ end
290
+
261
291
  workbook.comments.each do |comment|
262
292
  if comment.size > 0
263
293
  c_types << Axlsx::Override.new(:PartName => "/xl/#{comment.pn}",
@@ -311,9 +341,9 @@ module Axlsx
311
341
  # @private
312
342
  def relationships
313
343
  rels = Axlsx::Relationships.new
314
- rels << Relationship.new(WORKBOOK_R, WORKBOOK_PN)
315
- rels << Relationship.new(CORE_R, CORE_PN)
316
- rels << Relationship.new(APP_R, APP_PN)
344
+ rels << Relationship.new(self, WORKBOOK_R, WORKBOOK_PN)
345
+ rels << Relationship.new(self, CORE_R, CORE_PN)
346
+ rels << Relationship.new(self, APP_R, APP_PN)
317
347
  rels.lock
318
348
  rels
319
349
  end
@@ -3,7 +3,43 @@ module Axlsx
3
3
  # A relationship defines a reference between package parts.
4
4
  # @note Packages automatically manage relationships.
5
5
  class Relationship
6
+
7
+ class << self
8
+ # Keeps track of all instances of this class.
9
+ # @return [Array]
10
+ def instances
11
+ @instances ||= []
12
+ end
13
+
14
+ # Clear cached instances.
15
+ #
16
+ # This should be called before serializing a package (see {Package#serialize} and
17
+ # {Package#to_stream}) to make sure that serialization is idempotent (i.e.
18
+ # Relationship instances are generated with the same IDs everytime the package
19
+ # is serialized).
20
+ #
21
+ # Also, calling this avoids memory leaks (cached instances lingering around
22
+ # forever).
23
+ def clear_cached_instances
24
+ @instances = []
25
+ end
26
+
27
+ # Generate and return a unique id (eg. `rId123`) Used for setting {#Id}.
28
+ #
29
+ # The generated id depends on the number of cached instances, so using
30
+ # {clear_cached_instances} will automatically reset the generated ids, too.
31
+ # @return [String]
32
+ def next_free_id
33
+ "rId#{@instances.size + 1}"
34
+ end
35
+ end
6
36
 
37
+ # The id of the relationship (eg. "rId123"). Most instances get their own unique id.
38
+ # However, some instances need to share the same id – see {#should_use_same_id_as?}
39
+ # for details.
40
+ # @return [String]
41
+ attr_reader :Id
42
+
7
43
  # The location of the relationship target
8
44
  # @return [String]
9
45
  attr_reader :Target
@@ -12,6 +48,7 @@ module Axlsx
12
48
  # @note Supported types are defined as constants in Axlsx:
13
49
  # @see XML_NS_R
14
50
  # @see TABLE_R
51
+ # @see PIVOT_TABLE_R
15
52
  # @see WORKBOOK_R
16
53
  # @see WORKSHEET_R
17
54
  # @see APP_R
@@ -29,14 +66,26 @@ module Axlsx
29
66
  # Target mode must be :external for now.
30
67
  attr_reader :TargetMode
31
68
 
32
- # creates a new relationship
69
+ # The source object the relations belongs to (e.g. a hyperlink, drawing, ...). Needed when
70
+ # looking up the relationship for a specific object (see {Relationships#for}).
71
+ attr_reader :source_obj
72
+
73
+ # Initializes a new relationship.
74
+ # @param [Object] source_obj see {#source_obj}
33
75
  # @param [String] type The type of the relationship
34
76
  # @param [String] target The target for the relationship
35
77
  # @option [Symbol] :target_mode only accepts :external.
36
- def initialize(type, target, options={})
78
+ def initialize(source_obj, type, target, options={})
79
+ @source_obj = source_obj
37
80
  self.Target=target
38
81
  self.Type=type
39
- self.TargetMode = options.delete(:target_mode) if options[:target_mode]
82
+ self.TargetMode = options[:target_mode] if options[:target_mode]
83
+ @Id = if (existing = self.class.instances.find{ |i| should_use_same_id_as?(i) })
84
+ existing.Id
85
+ else
86
+ self.class.next_free_id
87
+ end
88
+ self.class.instances << self
40
89
  end
41
90
 
42
91
  # @see Target
@@ -49,15 +98,32 @@ module Axlsx
49
98
 
50
99
  # serialize relationship
51
100
  # @param [String] str
52
- # @param [Integer] rId the id for this relationship
53
101
  # @return [String]
54
- def to_xml_string(rId, str = '')
55
- h = self.instance_values
56
- h[:Id] = 'rId' << rId.to_s
102
+ def to_xml_string(str = '')
103
+ h = self.instance_values.reject{|k, _| k == "source_obj"}
57
104
  str << '<Relationship '
58
- str << h.map { |key, value| '' << key.to_s << '="' << value.to_s << '"'}.join(' ')
105
+ str << h.map { |key, value| '' << key.to_s << '="' << Axlsx::coder.encode(value.to_s) << '"'}.join(' ')
59
106
  str << '/>'
60
107
  end
61
-
108
+
109
+ # Whether this relationship should use the same id as `other`.
110
+ #
111
+ # Instances designating the same relationship need to use the same id. We can not simply
112
+ # compare the {#Target} attribute, though: `foo/bar.xml`, `../foo/bar.xml`,
113
+ # `../../foo/bar.xml` etc. are all different but probably mean the same file (this
114
+ # is especially an issue for relationships in the context of pivot tables). So lets
115
+ # just ignore this attribute for now (except when {#TargetMode} is set to `:External` –
116
+ # then {#Target} will be an absolute URL and thus can safely be compared).
117
+ #
118
+ # @todo Implement comparison of {#Target} based on normalized path names.
119
+ # @param other [Relationship]
120
+ def should_use_same_id_as?(other)
121
+ result = self.source_obj == other.source_obj && self.Type == other.Type && self.TargetMode == other.TargetMode
122
+ if self.TargetMode == :External
123
+ result &&= self.Target == other.Target
124
+ end
125
+ result
126
+ end
127
+
62
128
  end
63
129
  end
@@ -11,10 +11,17 @@ require 'axlsx/rels/relationship.rb'
11
11
  super Relationship
12
12
  end
13
13
 
14
+ # The relationship instance for the given source object, or nil if none exists.
15
+ # @see Relationship#source_obj
16
+ # @return [Relationship]
17
+ def for(source_obj)
18
+ @list.find{ |rel| rel.source_obj == source_obj }
19
+ end
20
+
14
21
  def to_xml_string(str = '')
15
22
  str << '<?xml version="1.0" encoding="UTF-8"?>'
16
23
  str << '<Relationships xmlns="' << RELS_R << '">'
17
- each_with_index { |rel, index| rel.to_xml_string(index+1, str) }
24
+ each{ |rel| rel.to_xml_string(str) }
18
25
  str << '</Relationships>'
19
26
  end
20
27
 
@@ -3,21 +3,12 @@ module Axlsx
3
3
  # This class details a border used in Office Open XML spreadsheet styles.
4
4
  class Border
5
5
 
6
- # @return [Boolean] The diagonal up property for the border that indicates if the border should include a diagonal line from the bottom left to the top right of the cell.
7
- attr_reader :diagonalUp
8
-
9
- # @return [Boolean] The diagonal down property for the border that indicates if the border should include a diagonal line from the top left to the top right of the cell.
10
- attr_reader :diagonalDown
11
-
12
- # @return [Boolean] The outline property for the border indicating that top, left, right and bottom borders should only be applied to the outside border of a range of cells.
13
- attr_reader :outline
14
-
15
- # @return [SimpleTypedList] A list of BorderPr objects for this border.
16
- attr_reader :prs
6
+ include Axlsx::SerializedAttributes
7
+ include Axlsx::OptionsParser
17
8
 
18
9
  # Creates a new Border object
19
- # @option options [Boolean] diagonalUp
20
- # @option options [Boolean] diagonalDown
10
+ # @option options [Boolean] diagonal_up
11
+ # @option options [Boolean] diagonal_down
21
12
  # @option options [Boolean] outline
22
13
  # @example - Making a border
23
14
  # p = Axlsx::Package.new
@@ -30,15 +21,33 @@ module Axlsx
30
21
  # @see Style#add_style
31
22
  def initialize(options={})
32
23
  @prs = SimpleTypedList.new BorderPr
33
- options.each do |o|
34
- self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
35
- end
24
+ parse_options options
36
25
  end
37
26
 
27
+ serializable_attributes :diagonal_up, :diagonal_down, :outline
28
+
29
+ # @return [Boolean] The diagonal up property for the border that indicates if the border should include a diagonal line from the bottom left to the top right of the cell.
30
+ attr_reader :diagonal_up
31
+ alias :diagonalUp :diagonal_up
32
+
33
+ # @return [Boolean] The diagonal down property for the border that indicates if the border should include a diagonal line from the top left to the top right of the cell.
34
+ attr_reader :diagonal_down
35
+ alias :diagonalDown :diagonal_down
36
+
37
+ # @return [Boolean] The outline property for the border indicating that top, left, right and bottom borders should only be applied to the outside border of a range of cells.
38
+ attr_reader :outline
39
+
40
+ # @return [SimpleTypedList] A list of BorderPr objects for this border.
41
+ attr_reader :prs
42
+
38
43
  # @see diagonalUp
39
- def diagonalUp=(v) Axlsx::validate_boolean v; @diagonalUp = v end
44
+ def diagonal_up=(v) Axlsx::validate_boolean v; @diagonal_up = v end
45
+ alias :diagonalUp= :diagonal_up=
46
+
40
47
  # @see diagonalDown
41
- def diagonalDown=(v) Axlsx::validate_boolean v; @diagonalDown = v end
48
+ def diagonal_down=(v) Axlsx::validate_boolean v; @diagonal_down = v end
49
+ alias :diagonalDown= :diagonal_down=
50
+
42
51
  # @see outline
43
52
  def outline=(v) Axlsx::validate_boolean v; @outline = v end
44
53
 
@@ -47,9 +56,9 @@ module Axlsx
47
56
  # @return [String]
48
57
  def to_xml_string(str = '')
49
58
  str << '<border '
50
- h = self.instance_values.select{ |k,v| [:diagonalUp, :diagonalDown, :outline].include? k }
51
- str << h.map { |key, value| '' << key.to_s << '="' << value.to_s << '"' }.join(' ')
59
+ serialized_attributes str
52
60
  str << '>'
61
+ # enforces order
53
62
  [:start, :end, :left, :right, :top, :bottom, :diagonal, :vertical, :horizontal].each do |k|
54
63
  @prs.select { |pr| pr.name == k }.each do |part|
55
64
  part.to_xml_string(str)
@@ -2,7 +2,7 @@
2
2
  module Axlsx
3
3
  # A border part.
4
4
  class BorderPr
5
-
5
+ include Axlsx::OptionsParser
6
6
  # @return [Color] The color of this border part.
7
7
  attr_reader :color
8
8
 
@@ -45,9 +45,10 @@ module Axlsx
45
45
  # @option options [Symbol] style
46
46
  # @see Axlsx::Border
47
47
  def initialize(options={})
48
- options.each do |o|
49
- self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
50
- end
48
+ parse_options(options)
49
+ #options.each do |o|
50
+ # self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
51
+ #end
51
52
  end
52
53
 
53
54
  # @see name
@@ -1,9 +1,33 @@
1
1
  # encoding: UTF-8
2
2
  module Axlsx
3
+
4
+
3
5
  # CellAlignment stores information about the cell alignment of a style Xf Object.
4
6
  # @note Using Styles#add_style is the recommended way to manage cell alignment.
5
7
  # @see Styles#add_style
6
8
  class CellAlignment
9
+
10
+
11
+ include Axlsx::SerializedAttributes
12
+ include Axlsx::OptionsParser
13
+
14
+ serializable_attributes :horizontal, :vertical, :text_rotation, :wrap_text, :indent, :relative_indent, :justify_last_line, :shrink_to_fit, :reading_order
15
+ # Create a new cell_alignment object
16
+ # @option options [Symbol] horizontal
17
+ # @option options [Symbol] vertical
18
+ # @option options [Integer] text_rotation
19
+ # @option options [Boolean] wrap_text
20
+ # @option options [Integer] indent
21
+ # @option options [Integer] relative_indent
22
+ # @option options [Boolean] justify_last_line
23
+ # @option options [Boolean] shrink_to_fit
24
+ # @option options [Integer] reading_order
25
+ def initialize(options={})
26
+ parse_options options
27
+ end
28
+
29
+
30
+
7
31
  # The horizontal alignment of the cell.
8
32
  # @note
9
33
  # The horizontal cell alignement style must be one of
@@ -31,11 +55,13 @@ module Axlsx
31
55
 
32
56
  # The textRotation of the cell.
33
57
  # @return [Integer]
34
- attr_reader :textRotation
58
+ attr_reader :text_rotation
59
+ alias :textRotation :text_rotation
35
60
 
36
61
  # Indicate if the text of the cell should wrap
37
62
  # @return [Boolean]
38
- attr_reader :wrapText
63
+ attr_reader :wrap_text
64
+ alias :wrapText :wrap_text
39
65
 
40
66
  # The amount of indent
41
67
  # @return [Integer]
@@ -43,64 +69,64 @@ module Axlsx
43
69
 
44
70
  # The amount of relativeIndent
45
71
  # @return [Integer]
46
- attr_reader :relativeIndent
72
+ attr_reader :relative_indent
73
+ alias :relativeIndent :relative_indent
47
74
 
48
75
  # Indicate if the last line should be justified.
49
76
  # @return [Boolean]
50
- attr_reader :justifyLastLine
77
+ attr_reader :justify_last_line
78
+ alias :justifyLastLine :justify_last_line
51
79
 
52
80
  # Indicate if the text should be shrunk to the fit in the cell.
53
81
  # @return [Boolean]
54
- attr_reader :shrinkToFit
82
+ attr_reader :shrink_to_fit
83
+ alias :shrinkToFit :shrink_to_fit
55
84
 
56
85
  # The reading order of the text
57
86
  # 0 Context Dependent
58
87
  # 1 Left-to-Right
59
88
  # 2 Right-to-Left
60
89
  # @return [Integer]
61
- attr_reader :readingOrder
62
-
63
- # Create a new cell_alignment object
64
- # @option options [Symbol] horizontal
65
- # @option options [Symbol] vertical
66
- # @option options [Integer] textRotation
67
- # @option options [Boolean] wrapText
68
- # @option options [Integer] indent
69
- # @option options [Integer] relativeIndent
70
- # @option options [Boolean] justifyLastLine
71
- # @option options [Boolean] shrinkToFit
72
- # @option options [Integer] readingOrder
73
- def initialize(options={})
74
- options.each do |o|
75
- self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
76
- end
77
- end
90
+ attr_reader :reading_order
91
+ alias :readingOrder :reading_order
78
92
 
79
93
  # @see horizontal
80
94
  def horizontal=(v) Axlsx::validate_horizontal_alignment v; @horizontal = v end
81
95
  # @see vertical
82
96
  def vertical=(v) Axlsx::validate_vertical_alignment v; @vertical = v end
83
97
  # @see textRotation
84
- def textRotation=(v) Axlsx::validate_unsigned_int v; @textRotation = v end
98
+ def text_rotation=(v) Axlsx::validate_unsigned_int v; @text_rotation = v end
99
+ alias :textRotation= :text_rotation=
100
+
85
101
  # @see wrapText
86
- def wrapText=(v) Axlsx::validate_boolean v; @wrapText = v end
102
+ def wrap_text=(v) Axlsx::validate_boolean v; @wrap_text = v end
103
+ alias :wrapText= :wrap_text=
104
+
87
105
  # @see indent
88
106
  def indent=(v) Axlsx::validate_unsigned_int v; @indent = v end
107
+
89
108
  # @see relativeIndent
90
- def relativeIndent=(v) Axlsx::validate_int v; @relativeIndent = v end
109
+ def relative_indent=(v) Axlsx::validate_int v; @relative_indent = v end
110
+ alias :relativeIndent= :relative_indent=
111
+
91
112
  # @see justifyLastLine
92
- def justifyLastLine=(v) Axlsx::validate_boolean v; @justifyLastLine = v end
113
+ def justify_last_line=(v) Axlsx::validate_boolean v; @justify_last_line = v end
114
+ alias :justifyLastLine= :justify_last_line=
115
+
93
116
  # @see shrinkToFit
94
- def shrinkToFit=(v) Axlsx::validate_boolean v; @shrinkToFit = v end
117
+ def shrink_to_fit=(v) Axlsx::validate_boolean v; @shrink_to_fit = v end
118
+ alias :shrinkToFit= :shrink_to_fit=
119
+
95
120
  # @see readingOrder
96
- def readingOrder=(v) Axlsx::validate_unsigned_int v; @readingOrder = v end
121
+ def reading_order=(v) Axlsx::validate_unsigned_int v; @reading_order = v end
122
+ alias :readingOrder= :reading_order=
97
123
 
98
124
  # Serializes the object
99
125
  # @param [String] str
100
126
  # @return [String]
101
127
  def to_xml_string(str = '')
102
128
  str << '<alignment '
103
- str << instance_values.map { |key, value| '' << key.to_s << '="' << value.to_s << '"' }.join(' ')
129
+ serialized_attributes str
104
130
  str << '/>'
105
131
  end
106
132
 
@@ -5,6 +5,11 @@ module Axlsx
5
5
  # @see Styles#add_style
6
6
  class CellProtection
7
7
 
8
+ include Axlsx::OptionsParser
9
+ include Axlsx::SerializedAttributes
10
+
11
+ serializable_attributes :hidden, :locked
12
+
8
13
  # specifies locking for cells that have the style containing this protection
9
14
  # @return [Boolean]
10
15
  attr_reader :hidden
@@ -17,9 +22,7 @@ module Axlsx
17
22
  # @option options [Boolean] hidden value for hidden protection
18
23
  # @option options [Boolean] locked value for locked protection
19
24
  def initialize(options={})
20
- options.each do |o|
21
- self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
22
- end
25
+ parse_options options
23
26
  end
24
27
 
25
28
  # @see hidden
@@ -32,7 +35,7 @@ module Axlsx
32
35
  # @return [String]
33
36
  def to_xml_string(str = '')
34
37
  str << '<protection '
35
- str << instance_values.map { |key, value| '' << key.to_s << '="' << value.to_s << '"' }.join(' ')
38
+ serialized_attributes str
36
39
  str << '/>'
37
40
  end
38
41
 
@@ -4,6 +4,23 @@ module Axlsx
4
4
  # @note Using Styles#add_style is the recommended way to manage cell styling.
5
5
  # @see Styles#add_style
6
6
  class CellStyle
7
+
8
+ include Axlsx::OptionsParser
9
+ include Axlsx::SerializedAttributes
10
+
11
+ # Creats a new CellStyle object
12
+ # @option options [String] name
13
+ # @option options [Integer] xfId
14
+ # @option options [Integer] buildinId
15
+ # @option options [Integer] iLevel
16
+ # @option options [Boolean] hidden
17
+ # @option options [Boolean] customBuiltIn
18
+ def initialize(options={})
19
+ parse_options options
20
+ end
21
+
22
+ serializable_attributes :name, :xfId, :buildinId, :iLevel, :hidden, :customBuilin
23
+
7
24
  # The name of this cell style
8
25
  # @return [String]
9
26
  attr_reader :name
@@ -30,19 +47,7 @@ module Axlsx
30
47
  # @return [Boolean]
31
48
  attr_reader :customBuiltin
32
49
 
33
- # Creats a new CellStyle object
34
- # @option options [String] name
35
- # @option options [Integer] xfId
36
- # @option options [Integer] buildinId
37
- # @option options [Integer] iLevel
38
- # @option options [Boolean] hidden
39
- # @option options [Boolean] customBuiltIn
40
- def initialize(options={})
41
- options.each do |o|
42
- self.send("#{o[0]}=", o[1]) if self.respond_to? o[0]
43
- end
44
- end
45
- # @see name
50
+ # @see name
46
51
  def name=(v) Axlsx::validate_string v; @name = v end
47
52
  # @see xfId
48
53
  def xfId=(v) Axlsx::validate_unsigned_int v; @xfId = v end
@@ -60,7 +65,7 @@ module Axlsx
60
65
  # @return [String]
61
66
  def to_xml_string(str = '')
62
67
  str << '<cellStyle '
63
- str << instance_values.map { |key, value| '' << key.to_s << '="' << value.to_s << '"' }.join(' ')
68
+ serialized_attributes str
64
69
  str << '/>'
65
70
  end
66
71