axlsx 1.0.12 → 1.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/README.md +7 -3
  2. data/lib/axlsx.rb +57 -0
  3. data/lib/axlsx/content_type/content_type.rb +23 -0
  4. data/lib/axlsx/content_type/default.rb +37 -0
  5. data/lib/axlsx/content_type/override.rb +37 -0
  6. data/lib/axlsx/doc_props/app.rb +178 -0
  7. data/lib/axlsx/doc_props/core.rb +34 -0
  8. data/lib/axlsx/drawing/axis.rb +90 -0
  9. data/lib/axlsx/drawing/bar_3D_chart.rb +128 -0
  10. data/lib/axlsx/drawing/bar_series.rb +64 -0
  11. data/lib/axlsx/drawing/cat_axis.rb +63 -0
  12. data/lib/axlsx/drawing/cat_axis_data.rb +35 -0
  13. data/lib/axlsx/drawing/chart.rb +179 -0
  14. data/lib/axlsx/drawing/drawing.rb +137 -0
  15. data/lib/axlsx/drawing/graphic_frame.rb +52 -0
  16. data/lib/axlsx/drawing/line_3D_chart.rb +106 -0
  17. data/lib/axlsx/drawing/line_series.rb +46 -0
  18. data/lib/axlsx/drawing/marker.rb +61 -0
  19. data/lib/axlsx/drawing/one_cell_anchor.rb +89 -0
  20. data/lib/axlsx/drawing/pic.rb +153 -0
  21. data/lib/axlsx/drawing/picture_locking.rb +72 -0
  22. data/lib/axlsx/drawing/picture_locking.rb~ +36 -0
  23. data/lib/axlsx/drawing/pie_3D_chart.rb +41 -0
  24. data/lib/axlsx/drawing/pie_series.rb +56 -0
  25. data/lib/axlsx/drawing/scaling.rb +59 -0
  26. data/lib/axlsx/drawing/ser_axis.rb +45 -0
  27. data/lib/axlsx/drawing/series.rb +71 -0
  28. data/lib/axlsx/drawing/series_title.rb +22 -0
  29. data/lib/axlsx/drawing/title.rb +61 -0
  30. data/lib/axlsx/drawing/two_cell_anchor.rb +76 -0
  31. data/lib/axlsx/drawing/val_axis.rb +34 -0
  32. data/lib/axlsx/drawing/val_axis_data.rb +28 -0
  33. data/lib/axlsx/drawing/view_3D.rb +85 -0
  34. data/lib/axlsx/package.rb +215 -0
  35. data/lib/axlsx/rels/relationship.rb +44 -0
  36. data/lib/axlsx/rels/relationships.rb +25 -0
  37. data/lib/axlsx/stylesheet/border.rb +57 -0
  38. data/lib/axlsx/stylesheet/border_pr.rb +68 -0
  39. data/lib/axlsx/stylesheet/cell_alignment.rb +105 -0
  40. data/lib/axlsx/stylesheet/cell_protection.rb +36 -0
  41. data/lib/axlsx/stylesheet/cell_style.rb +65 -0
  42. data/lib/axlsx/stylesheet/color.rb +69 -0
  43. data/lib/axlsx/stylesheet/fill.rb +32 -0
  44. data/lib/axlsx/stylesheet/font.rb +139 -0
  45. data/lib/axlsx/stylesheet/gradient_fill.rb +76 -0
  46. data/lib/axlsx/stylesheet/gradient_stop.rb +33 -0
  47. data/lib/axlsx/stylesheet/num_fmt.rb +63 -0
  48. data/lib/axlsx/stylesheet/pattern_fill.rb +66 -0
  49. data/lib/axlsx/stylesheet/styles.rb +298 -0
  50. data/lib/axlsx/stylesheet/table_style.rb +47 -0
  51. data/lib/axlsx/stylesheet/table_style_element.rb +71 -0
  52. data/lib/axlsx/stylesheet/table_styles.rb +39 -0
  53. data/lib/axlsx/stylesheet/xf.rb +138 -0
  54. data/lib/axlsx/util/constants.rb +220 -0
  55. data/lib/axlsx/util/parser.rb +43 -0
  56. data/lib/axlsx/util/parser.rb~ +6 -0
  57. data/lib/axlsx/util/simple_typed_list.rb +160 -0
  58. data/lib/axlsx/util/validators.rb +132 -0
  59. data/lib/axlsx/version.rb +4 -0
  60. data/lib/axlsx/workbook/#workbook.rb# +165 -0
  61. data/lib/axlsx/workbook/workbook.rb +160 -0
  62. data/lib/axlsx/workbook/worksheet/cell.rb +337 -0
  63. data/lib/axlsx/workbook/worksheet/row.rb +107 -0
  64. data/lib/axlsx/workbook/worksheet/worksheet.rb +279 -0
  65. metadata +93 -141
  66. data/examples/follow_20111202.xlsx +0 -0
  67. data/test/content_type/tc_content_type.rb +0 -81
  68. data/test/content_type/tc_default.rb +0 -40
  69. data/test/content_type/tc_override.rb +0 -40
  70. data/test/doc_props/tc_app.rb +0 -19
  71. data/test/doc_props/tc_core.rb +0 -34
  72. data/test/drawing/tc_axis.rb +0 -40
  73. data/test/drawing/tc_bar_3D_chart.rb +0 -66
  74. data/test/drawing/tc_bar_series.rb +0 -34
  75. data/test/drawing/tc_cat_axis.rb +0 -32
  76. data/test/drawing/tc_cat_axis_data.rb +0 -18
  77. data/test/drawing/tc_chart.rb +0 -73
  78. data/test/drawing/tc_drawing.rb +0 -80
  79. data/test/drawing/tc_graphic_frame.rb +0 -26
  80. data/test/drawing/tc_line_3d_chart.rb +0 -48
  81. data/test/drawing/tc_line_series.rb +0 -27
  82. data/test/drawing/tc_marker.rb +0 -45
  83. data/test/drawing/tc_one_cell_anchor.rb +0 -67
  84. data/test/drawing/tc_pic.rb +0 -71
  85. data/test/drawing/tc_picture_locking.rb +0 -73
  86. data/test/drawing/tc_pie_3D_chart.rb +0 -33
  87. data/test/drawing/tc_pie_series.rb +0 -35
  88. data/test/drawing/tc_scaling.rb +0 -37
  89. data/test/drawing/tc_ser_axis.rb +0 -31
  90. data/test/drawing/tc_series.rb +0 -24
  91. data/test/drawing/tc_series_title.rb +0 -34
  92. data/test/drawing/tc_title.rb +0 -34
  93. data/test/drawing/tc_two_cell_anchor.rb +0 -38
  94. data/test/drawing/tc_val_axis.rb +0 -25
  95. data/test/drawing/tc_val_axis_data.rb +0 -18
  96. data/test/drawing/tc_view_3D.rb +0 -55
  97. data/test/rels/tc_relationship.rb +0 -16
  98. data/test/rels/tc_relationships.rb +0 -27
  99. data/test/stylesheet/tc_border.rb +0 -38
  100. data/test/stylesheet/tc_border_pr.rb +0 -33
  101. data/test/stylesheet/tc_cell_alignment.rb +0 -77
  102. data/test/stylesheet/tc_cell_protection.rb +0 -30
  103. data/test/stylesheet/tc_cell_style.rb +0 -58
  104. data/test/stylesheet/tc_color.rb +0 -38
  105. data/test/stylesheet/tc_fill.rb +0 -19
  106. data/test/stylesheet/tc_font.rb +0 -114
  107. data/test/stylesheet/tc_gradient_fill.rb +0 -65
  108. data/test/stylesheet/tc_gradient_stop.rb +0 -32
  109. data/test/stylesheet/tc_num_fmt.rb +0 -31
  110. data/test/stylesheet/tc_pattern_fill.rb +0 -38
  111. data/test/stylesheet/tc_styles.rb +0 -52
  112. data/test/stylesheet/tc_table_style.rb +0 -37
  113. data/test/stylesheet/tc_table_style_element.rb +0 -37
  114. data/test/stylesheet/tc_table_styles.rb +0 -30
  115. data/test/stylesheet/tc_xf.rb +0 -121
  116. data/test/tc_package.rb +0 -68
  117. data/test/util/tc_simple_typed_list.rb +0 -66
  118. data/test/util/tc_validators.rb +0 -76
  119. data/test/workbook/tc_workbook.rb +0 -60
  120. data/test/workbook/worksheet/tc_cell.rb +0 -179
  121. data/test/workbook/worksheet/tc_row.rb +0 -36
  122. data/test/workbook/worksheet/tc_worksheet.rb +0 -138
@@ -0,0 +1,71 @@
1
+ module Axlsx
2
+ # A Series defines the common series attributes and is the super class for all concrete series types.
3
+ # @note The recommended way to manage series is to use Chart#add_series
4
+ # @see Worksheet#add_chart
5
+ # @see Chart#add_series
6
+ class Series
7
+
8
+ # The chart that owns this series
9
+ # @return [Chart]
10
+ attr_reader :chart
11
+
12
+ # The title of the series
13
+ # @return [SeriesTitle]
14
+ attr_reader :title
15
+
16
+ # Creates a new series
17
+ # @param [Chart] chart
18
+ # @option options [Integer] order
19
+ # @option options [String] title
20
+ def initialize(chart, options={})
21
+ @order = nil
22
+ self.chart = chart
23
+ @chart.series << self
24
+ options.each do |o|
25
+ self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
26
+ end
27
+ end
28
+
29
+ # The index of this series in the chart's series.
30
+ # @return [Integer]
31
+ def index
32
+ @chart.series.index(self)
33
+ end
34
+
35
+
36
+ # The order of this series in the chart's series. By default the order is the index of the series.
37
+ # @return [Integer]
38
+ def order
39
+ @order || index
40
+ end
41
+
42
+ # @see order
43
+ def order=(v) Axlsx::validate_unsigned_int(v); @order = v; end
44
+
45
+ # @see title
46
+ def title=(v)
47
+ v = SeriesTitle.new(v) if v.is_a?(String) || v.is_a?(Cell)
48
+ DataTypeValidator.validate "#{self.class}.title", SeriesTitle, v
49
+ @title = v
50
+ end
51
+
52
+ private
53
+
54
+ # assigns the chart for this series
55
+ def chart=(v) DataTypeValidator.validate "Series.chart", Chart, v; @chart = v; end
56
+
57
+ # Serializes the series
58
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
59
+ # @return [String]
60
+ def to_xml(xml)
61
+ xml.send('c:ser') {
62
+ xml.send('c:idx', :val=>index)
63
+ xml.send('c:order', :val=>order || index)
64
+ title.to_xml(xml) unless title.nil?
65
+ yield xml if block_given?
66
+ }
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,22 @@
1
+ module Axlsx
2
+ # A series title is a Title with a slightly different serialization than chart titles.
3
+ class SeriesTitle < Title
4
+
5
+ # Serializes the series title
6
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
7
+ # @return [String]
8
+ def to_xml(xml)
9
+ xml.send('c:tx') {
10
+ xml.send('c:strRef') {
11
+ xml.send('c:f', Axlsx::cell_range([@cell]))
12
+ xml.send('c:strCache') {
13
+ xml.send('c:ptCount', :val=>1)
14
+ xml.send('c:pt', :idx=>0) {
15
+ xml.send('c:v', @text)
16
+ }
17
+ }
18
+ }
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,61 @@
1
+ module Axlsx
2
+ # A Title stores information about the title of a chart
3
+ class Title
4
+
5
+ # The text to be shown. Setting this property directly with a string will remove the cell reference.
6
+ # @return [String]
7
+ attr_reader :text
8
+
9
+ # The cell that holds the text for the title. Setting this property will automatically update the text attribute.
10
+ # @return [Cell]
11
+ attr_reader :cell
12
+
13
+ # Creates a new Title object
14
+ # @param [String, Cell] title The cell or string to be used for the chart's title
15
+ def initialize(title="")
16
+ self.cell = title if title.is_a?(Cell)
17
+ self.text = title.to_s unless title.is_a?(Cell)
18
+ end
19
+
20
+ # @see text
21
+ def text=(v)
22
+ DataTypeValidator.validate 'Title.text', String, v
23
+ @text = v
24
+ @cell = nil
25
+ v
26
+ end
27
+
28
+ # @see cell
29
+ def cell=(v)
30
+ DataTypeValidator.validate 'Title.text', Cell, v
31
+ @cell = v
32
+ @text = v.value.to_s
33
+ v
34
+ end
35
+
36
+ # Not implemented at this time.
37
+ #def layout=(v) DataTypeValidator.validate 'Title.layout', Layout, v; @layout = v; end
38
+ #def overlay=(v) Axlsx::validate_boolean v; @overlay=v; end
39
+ #def spPr=(v) DataTypeValidator.validate 'Title.spPr', SpPr, v; @spPr = v; end
40
+
41
+ # Serializes the chart title
42
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
43
+ # @return [String]
44
+ def to_xml(xml)
45
+ xml.send('c:title') {
46
+ xml.send('c:tx') {
47
+ xml.send('c:strRef') {
48
+ xml.send('c:f', Axlsx::cell_range([@cell]))
49
+ xml.send('c:strCache') {
50
+ xml.send('c:ptCount', :val=>1)
51
+ xml.send('c:pt', :idx=>0) {
52
+ xml.send('c:v', @text)
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,76 @@
1
+ module Axlsx
2
+ # This class details the anchor points for drawings.
3
+ # @note The recommended way to manage drawings and charts is Worksheet#add_chart. Anchors are specified by the :start_at and :end_at options to that method.
4
+ # @see Worksheet#add_chart
5
+ class TwoCellAnchor
6
+
7
+ # A marker that defines the from cell anchor. The default from column and row are 0 and 0 respectively
8
+ # @return [Marker]
9
+ attr_reader :from
10
+ # A marker that returns the to cell anchor. The default to column and row are 5 and 10 respectively
11
+ # @return [Marker]
12
+ attr_reader :to
13
+
14
+ # The frame for your chart
15
+ # @note this will be discontinued in version 2.0 please use object
16
+ # @return [GraphicFrame]
17
+ # attr_reader :graphic_frame
18
+
19
+ # The object this anchor hosts
20
+ # @return [Pic, GraphicFrame]
21
+ attr_reader :object
22
+
23
+ # The drawing that holds this anchor
24
+ # @return [Drawing]
25
+ attr_reader :drawing
26
+
27
+
28
+ # Creates a new TwoCellAnchor object and sets up a reference to the from and to markers in the
29
+ # graphic_frame's chart. That means that you can do stuff like
30
+ # c = worksheet.add_chart Axlsx::Chart
31
+ # c.start_at 5, 9
32
+ # @note the chart_type parameter will be replaced with object in v. 2.0.0
33
+ # @param [Drawing] drawing
34
+ # @param [Class] chart_type This is passed to the graphic frame for instantiation. must be Chart or a subclass of Chart
35
+ # @param object The object this anchor holds.
36
+ # @option options [Array] start_at the col, row to start at
37
+ # @option options [Array] end_at the col, row to end at
38
+ def initialize(drawing, options={})
39
+ @drawing = drawing
40
+ drawing.anchors << self
41
+ @from, @to = Marker.new, Marker.new(:col => 5, :row=>10)
42
+ end
43
+
44
+ # Creates a graphic frame and chart object associated with this anchor
45
+ # @return [Chart]
46
+ def add_chart(chart_type, options)
47
+ @object = GraphicFrame.new(self, chart_type, options)
48
+ @object.chart
49
+ end
50
+
51
+ # The index of this anchor in the drawing
52
+ # @return [Integer]
53
+ def index
54
+ @drawing.anchors.index(self)
55
+ end
56
+ # Serializes the two cell anchor
57
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
58
+ # @return [String]
59
+ def to_xml(xml)
60
+ #build it for now, break it down later!
61
+ xml.send('xdr:twoCellAnchor') {
62
+ xml.send('xdr:from') {
63
+ from.to_xml(xml)
64
+ }
65
+ xml.send('xdr:to') {
66
+ to.to_xml(xml)
67
+ }
68
+ @object.to_xml(xml)
69
+ xml.send('xdr:clientData')
70
+ }
71
+ end
72
+
73
+ private
74
+
75
+ end
76
+ end
@@ -0,0 +1,34 @@
1
+ module Axlsx
2
+ # the ValAxis class defines a chart value axis.
3
+ class ValAxis < Axis
4
+
5
+ # This element specifies how the value axis crosses the category axis.
6
+ # must be one of [:between, :midCat]
7
+ # @return [Symbol]
8
+ attr_reader :crossBetween
9
+
10
+ # Creates a new ValAxis object
11
+ # @param [Integer] axId the id of this axis
12
+ # @param [Integer] crossAx the id of the perpendicular axis
13
+ # @option options [Symbol] axPos
14
+ # @option options [Symbol] tickLblPos
15
+ # @option options [Symbol] crosses
16
+ # @option options [Symbol] crossesBetween
17
+ def initialize(axId, crossAx, options={})
18
+ self.crossBetween = :between
19
+ super(axId, crossAx, options)
20
+ end
21
+ # @see crossBetween
22
+ def crossBetween=(v) RestrictionValidator.validate "ValAxis.crossBetween", [:between, :midCat], v; @crossBetween = v; end
23
+
24
+ # Serializes the value axis
25
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
26
+ # @return [String]
27
+ def to_xml(xml)
28
+ xml.send('c:valAx') {
29
+ super(xml)
30
+ xml.send('c:crossBetween', :val=>@crossBetween)
31
+ }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ module Axlsx
2
+ # The ValAxisData class manages the values for a chart value series.
3
+ class ValAxisData < CatAxisData
4
+
5
+ # Serializes the value axis data
6
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
7
+ # @return [String]
8
+ def to_xml(xml)
9
+ xml.send('c:val') {
10
+ xml.send('c:numRef') {
11
+ xml.send('c:f', Axlsx::cell_range(@list))
12
+ xml.send('c:numCache') {
13
+ xml.send('c:formatCode', 'General')
14
+ xml.send('c:ptCount', :val=>size)
15
+ each_with_index do |item, index|
16
+ v = item.is_a?(Cell) ? item.value : item
17
+ xml.send('c:pt', :idx=>index) {
18
+ xml.send('c:v', v)
19
+ }
20
+ end
21
+ }
22
+ }
23
+ }
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,85 @@
1
+ module Axlsx
2
+ # 3D attributes for a chart.
3
+ class View3D
4
+
5
+ # Validation for hPercent
6
+ H_PERCENT_REGEX = /0*(([5-9])|([1-9][0-9])|([1-4][0-9][0-9])|500)%/
7
+
8
+ # validation for depthPercent
9
+ DEPTH_PERCENT_REGEX = /0*(([2-9][0-9])|([1-9][0-9][0-9])|(1[0-9][0-9][0-9])|2000)%/
10
+
11
+ # x rotation for the chart
12
+ # must be between -90 and 90
13
+ # @return [Integer]
14
+ attr_reader :rotX
15
+
16
+ # height of chart as % of chart
17
+ # must be between 5% and 500%
18
+ # @return [String]
19
+ attr_reader :hPercent
20
+
21
+ # y rotation for the chart
22
+ # must be between 0 and 360
23
+ # @return [Integer]
24
+ attr_reader :rotY
25
+
26
+ # depth or chart as % of chart width
27
+ # must be between 20% and 2000%
28
+ # @return [String]
29
+ attr_reader :depthPercent
30
+
31
+ # Chart axis are at right angles
32
+ # @return [Boolean]
33
+ attr_reader :rAngAx
34
+
35
+ # field of view angle
36
+ # @return [Integer]
37
+ attr_reader :perspective
38
+
39
+ # Creates a new View3D for charts
40
+ # @option options [Integer] rotX
41
+ # @option options [String] hPercent
42
+ # @option options [Integer] rotY
43
+ # @option options [String] depthPercent
44
+ # @option options [Boolean] rAngAx
45
+ # @option options [Integer] perspective
46
+ def initialize(options={})
47
+ @rotX, @hPercent, @rotY, @depthPercent, @rAngAx, @perspective = nil, nil, nil, nil, nil, nil
48
+ options.each do |o|
49
+ self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
50
+ end
51
+ end
52
+
53
+ # @see rotX
54
+ def rotX=(v) DataTypeValidator.validate "#{self.class}.rotX", [Integer, Fixnum], v, lambda {|arg| arg >= -90 && arg <= 90 }; @rotX = v; end
55
+
56
+ # @see hPercent
57
+ def hPercent=(v) RegexValidator.validate "#{self.class}.rotX", H_PERCENT_REGEX, v; @hPercent = v; end
58
+
59
+ # @see rotY
60
+ def rotY=(v) DataTypeValidator.validate "#{self.class}.rotY", [Integer, Fixnum], v, lambda {|arg| arg >= 0 && arg <= 360 }; @rotY = v; end
61
+
62
+ # @see depthPercent
63
+ def depthPercent=(v) RegexValidator.validate "#{self.class}.depthPercent", DEPTH_PERCENT_REGEX, v; @depthPercent = v; end
64
+
65
+ # @see rAngAx
66
+ def rAngAx=(v) Axlsx::validate_boolean(v); @rAngAx = v; end
67
+
68
+ # @see perspective
69
+ def perspective=(v) DataTypeValidator.validate "#{self.class}.perspective", [Integer, Fixnum], v, lambda {|arg| arg >= 0 && arg <= 240 }; @perspective = v; end
70
+
71
+ # Serializes the view3D properties
72
+ # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
73
+ # @return [String]
74
+ def to_xml(xml)
75
+ xml.send('c:view3D') {
76
+ xml.send('c:rotX', :val=>@rotX) unless @rotX.nil?
77
+ xml.send('c:hPercent', :val=>@hPercent) unless @hPercent.nil?
78
+ xml.send('c:rotY', :val=>@rotY) unless @rotY.nil?
79
+ xml.send('c:depthPercent', :val=>@depthPercent) unless @depthPercent.nil?
80
+ xml.send('c:rAngAx', :val=>@rAngAx) unless @rAngAx.nil?
81
+ xml.send('c:perspective', :val=>@perspective) unless @perspective.nil?
82
+ }
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,215 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Axlsx
3
+ # Package is responsible for managing all the bits and peices that Open Office XML requires to make a valid
4
+ # xlsx document including valdation and serialization.
5
+ class Package
6
+
7
+ # Initializes your package
8
+ #
9
+ # @param [Hash] options A hash that you can use to specify the author and workbook for this package.
10
+ # @option options [String] :author The author of the document
11
+ # @example Package.new :author => 'you!', :workbook => Workbook.new
12
+ def initialize(options={})
13
+ @workbook = nil
14
+ @core, @app = Core.new, App.new
15
+ @core.creator = options[:author] || @core.creator
16
+ yield self if block_given?
17
+ end
18
+
19
+ # The workbook this package will serialize or validate.
20
+ # @return [Workbook] If no workbook instance has been assigned with this package a new Workbook instance is returned.
21
+ # @raise ArgumentError if workbook parameter is not a Workbook instance.
22
+ # @note As there are multiple ways to instantiate a workbook for the package,
23
+ # here are a few examples:
24
+ # # assign directly during package instanciation
25
+ # wb = Package.new(:workbook => Workbook.new).workbook
26
+ #
27
+ # # get a fresh workbook automatically from the package
28
+ # wb = Pacakge.new().workbook
29
+ # # # set the workbook after creating the package
30
+ # wb = Package.new().workbook = Workbook.new
31
+ def workbook
32
+ @workbook || @workbook = Workbook.new
33
+ yield @workbook if block_given?
34
+ @workbook
35
+ end
36
+
37
+ #def self.parse(input, confirm_valid = false)
38
+ # p = Package.new
39
+ # z = Zip::ZipFile.open(input)
40
+ # p.workbook = Workbook.parse z.get_entry(WORKBOOK_PN)
41
+ # p
42
+ #end
43
+
44
+ # @see workbook
45
+ def workbook=(workbook) DataTypeValidator.validate "Package.workbook", Workbook, workbook; @workbook = workbook; end
46
+
47
+ # Serialize your workbook to disk as an xlsx document.
48
+ #
49
+ # @param [File] output The file you want to serialize your package to
50
+ # @param [Boolean] confirm_valid Validate the package prior to serialization.
51
+ # @return [Boolean] False if confirm_valid and validation errors exist. True if the package was serialized
52
+ # @note A tremendous amount of effort has gone into ensuring that you cannot create invalid xlsx documents.
53
+ # confirm_valid should be used in the rare case that you cannot open the serialized file.
54
+ # @see Package#validate
55
+ # @example
56
+ # # This is how easy it is to create a valid xlsx file. Of course you might want to add a sheet or two, and maybe some data, styles and charts.
57
+ # # Take a look at the README for an example of how to do it!
58
+ # f = File.open('test.xlsx', 'w')
59
+ # Package.new.serialize(f)
60
+ #
61
+ # # You will find a file called test.xlsx
62
+ def serialize(output, confirm_valid=false)
63
+ return false unless !confirm_valid || self.validate.empty?
64
+ p = parts
65
+ Zip::ZipOutputStream.open(output) do |zip|
66
+ p.each do |part|
67
+ unless part[:doc].nil?
68
+ zip.put_next_entry(part[:entry]);
69
+ entry = ['1.9.2', '1.9.3'].include?(RUBY_VERSION) ? part[:doc].force_encoding('BINARY') : part[:doc]
70
+ zip.puts(entry)
71
+ end
72
+ unless part[:path].nil?
73
+ zip.put_next_entry(part[:entry]);
74
+ # binread for 1.9.3
75
+ zip.write IO.respond_to?(:binread) ? IO.binread(part[:path]) : IO.read(part[:path])
76
+ end
77
+ end
78
+ end
79
+ true
80
+ end
81
+
82
+
83
+ # Validate all parts of the package against xsd schema.
84
+ # @return [Array] An array of all validation errors found.
85
+ # @note This gem includes all schema from OfficeOpenXML-XMLSchema-Transitional.zip and OpenPackagingConventions-XMLSchema.zip
86
+ # as per ECMA-376, Third edition. opc schema require an internet connection to import remote schema from dublin core for dc,
87
+ # dcterms and xml namespaces. Those remote schema are included in this gem, and the original files have been altered to
88
+ # refer to the local versions.
89
+ #
90
+ # If by chance you are able to creat a package that does not validate it indicates that the internal
91
+ # validation is not robust enough and needs to be improved. Please report your errors to the gem author.
92
+ # @see http://www.ecma-international.org/publications/standards/Ecma-376.htm
93
+ # @example
94
+ # # The following will output any error messages found in serialization.
95
+ # p = Axlsx::Package.new
96
+ # # ... code to create sheets, charts, styles etc.
97
+ # p.validate.each { |error| puts error.message }
98
+ def validate
99
+ errors = []
100
+ parts.each { |part| errors.concat validate_single_doc(part[:schema], part[:doc]) unless part[:schema].nil? }
101
+ errors
102
+ end
103
+
104
+ private
105
+
106
+ # The parts of a package
107
+ # @return [Array] An array of hashes that define the entry, document and schema for each part of the package.
108
+ # @private
109
+ def parts
110
+ @parts = [
111
+ {:entry => RELS_PN, :doc => relationships.to_xml, :schema => RELS_XSD},
112
+ {:entry => "xl/#{STYLES_PN}", :doc => workbook.styles.to_xml, :schema => SML_XSD},
113
+ {:entry => CORE_PN, :doc => @core.to_xml, :schema => CORE_XSD},
114
+ {:entry => APP_PN, :doc => @app.to_xml, :schema => APP_XSD},
115
+ {:entry => WORKBOOK_RELS_PN, :doc => workbook.relationships.to_xml, :schema => RELS_XSD},
116
+ {:entry => CONTENT_TYPES_PN, :doc => content_types.to_xml, :schema => CONTENT_TYPES_XSD},
117
+ {:entry => WORKBOOK_PN, :doc => workbook.to_xml, :schema => SML_XSD}
118
+ ]
119
+ workbook.drawings.each do |drawing|
120
+ @parts << {:entry => "xl/#{drawing.rels_pn}", :doc => drawing.relationships.to_xml, :schema => RELS_XSD}
121
+ @parts << {:entry => "xl/#{drawing.pn}", :doc => drawing.to_xml, :schema => DRAWING_XSD}
122
+ end
123
+
124
+ workbook.charts.each do |chart|
125
+ @parts << {:entry => "xl/#{chart.pn}", :doc => chart.to_xml, :schema => DRAWING_XSD}
126
+ end
127
+
128
+ workbook.images.each do |image|
129
+ @parts << {:entry => "xl/#{image.pn}", :path => image.image_src}
130
+ end
131
+
132
+ workbook.worksheets.each do |sheet|
133
+ @parts << {:entry => "xl/#{sheet.rels_pn}", :doc => sheet.relationships.to_xml, :schema => RELS_XSD}
134
+ @parts << {:entry => "xl/#{sheet.pn}", :doc => sheet.to_xml, :schema => SML_XSD}
135
+ end
136
+ @parts
137
+ end
138
+
139
+ # Performs xsd validation for a signle document
140
+ #
141
+ # @param [String] schema path to the xsd schema to be used in validation.
142
+ # @param [String] doc The xml text to be validated
143
+ # @return [Array] An array of all validation errors encountered.
144
+ # @private
145
+ def validate_single_doc(schema, doc)
146
+ schema = Nokogiri::XML::Schema(File.open(schema))
147
+ doc = Nokogiri::XML(doc)
148
+
149
+ errors = []
150
+ schema.validate(doc).each do |error|
151
+ errors << error
152
+ end
153
+ errors
154
+ end
155
+
156
+ # Appends override objects for drawings, charts, and sheets as they exist in your workbook to the default content types.
157
+ # @return [ContentType]
158
+ # @private
159
+ def content_types
160
+ c_types = base_content_types
161
+ workbook.drawings.each do |drawing|
162
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{drawing.pn}",
163
+ :ContentType => DRAWING_CT)
164
+ end
165
+ workbook.charts.each do |chart|
166
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{chart.pn}",
167
+ :ContentType => CHART_CT)
168
+ end
169
+ workbook.worksheets.each do |sheet|
170
+ c_types << Axlsx::Override.new(:PartName => "/xl/#{sheet.pn}",
171
+ :ContentType => WORKSHEET_CT)
172
+ end
173
+ exts = workbook.images.map { |image| image.extname }
174
+ exts.uniq.each do |ext|
175
+ ct = if ['jpeg', 'jpg'].include?(ext)
176
+ JPEG_CT
177
+ elsif ext == 'gif'
178
+ GIF_CT
179
+ elsif ext == 'png'
180
+ PNG_CT
181
+ end
182
+ c_types << Axlsx::Default.new(:ContentType => ct, :Extension => ext )
183
+ end
184
+ c_types
185
+ end
186
+
187
+ # Creates the minimum content types for generating a valid xlsx document.
188
+ # @return [ContentType]
189
+ # @private
190
+ def base_content_types
191
+ c_types = ContentType.new()
192
+ c_types << Default.new(:ContentType => RELS_CT, :Extension => RELS_EX)
193
+ c_types << Default.new(:Extension => XML_EX, :ContentType => XML_CT)
194
+ c_types << Override.new(:PartName => "/#{APP_PN}", :ContentType => APP_CT)
195
+ c_types << Override.new(:PartName => "/#{CORE_PN}", :ContentType => CORE_CT)
196
+ c_types << Override.new(:PartName => "/xl/#{STYLES_PN}", :ContentType => STYLES_CT)
197
+ c_types << Axlsx::Override.new(:PartName => "/#{WORKBOOK_PN}", :ContentType => WORKBOOK_CT)
198
+ c_types.lock
199
+ c_types
200
+ end
201
+
202
+ # Creates the relationships required for a valid xlsx document
203
+ # @return [Relationships]
204
+ # @private
205
+ def relationships
206
+ rels = Axlsx::Relationships.new
207
+ rels << Relationship.new(WORKBOOK_R, WORKBOOK_PN)
208
+ rels << Relationship.new(CORE_R, CORE_PN)
209
+ rels << Relationship.new(APP_R, APP_PN)
210
+ rels.lock
211
+ rels
212
+ end
213
+ end
214
+ end
215
+