axlsx 1.0.12 → 1.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +7 -3
- data/lib/axlsx.rb +57 -0
- data/lib/axlsx/content_type/content_type.rb +23 -0
- data/lib/axlsx/content_type/default.rb +37 -0
- data/lib/axlsx/content_type/override.rb +37 -0
- data/lib/axlsx/doc_props/app.rb +178 -0
- data/lib/axlsx/doc_props/core.rb +34 -0
- data/lib/axlsx/drawing/axis.rb +90 -0
- data/lib/axlsx/drawing/bar_3D_chart.rb +128 -0
- data/lib/axlsx/drawing/bar_series.rb +64 -0
- data/lib/axlsx/drawing/cat_axis.rb +63 -0
- data/lib/axlsx/drawing/cat_axis_data.rb +35 -0
- data/lib/axlsx/drawing/chart.rb +179 -0
- data/lib/axlsx/drawing/drawing.rb +137 -0
- data/lib/axlsx/drawing/graphic_frame.rb +52 -0
- data/lib/axlsx/drawing/line_3D_chart.rb +106 -0
- data/lib/axlsx/drawing/line_series.rb +46 -0
- data/lib/axlsx/drawing/marker.rb +61 -0
- data/lib/axlsx/drawing/one_cell_anchor.rb +89 -0
- data/lib/axlsx/drawing/pic.rb +153 -0
- data/lib/axlsx/drawing/picture_locking.rb +72 -0
- data/lib/axlsx/drawing/picture_locking.rb~ +36 -0
- data/lib/axlsx/drawing/pie_3D_chart.rb +41 -0
- data/lib/axlsx/drawing/pie_series.rb +56 -0
- data/lib/axlsx/drawing/scaling.rb +59 -0
- data/lib/axlsx/drawing/ser_axis.rb +45 -0
- data/lib/axlsx/drawing/series.rb +71 -0
- data/lib/axlsx/drawing/series_title.rb +22 -0
- data/lib/axlsx/drawing/title.rb +61 -0
- data/lib/axlsx/drawing/two_cell_anchor.rb +76 -0
- data/lib/axlsx/drawing/val_axis.rb +34 -0
- data/lib/axlsx/drawing/val_axis_data.rb +28 -0
- data/lib/axlsx/drawing/view_3D.rb +85 -0
- data/lib/axlsx/package.rb +215 -0
- data/lib/axlsx/rels/relationship.rb +44 -0
- data/lib/axlsx/rels/relationships.rb +25 -0
- data/lib/axlsx/stylesheet/border.rb +57 -0
- data/lib/axlsx/stylesheet/border_pr.rb +68 -0
- data/lib/axlsx/stylesheet/cell_alignment.rb +105 -0
- data/lib/axlsx/stylesheet/cell_protection.rb +36 -0
- data/lib/axlsx/stylesheet/cell_style.rb +65 -0
- data/lib/axlsx/stylesheet/color.rb +69 -0
- data/lib/axlsx/stylesheet/fill.rb +32 -0
- data/lib/axlsx/stylesheet/font.rb +139 -0
- data/lib/axlsx/stylesheet/gradient_fill.rb +76 -0
- data/lib/axlsx/stylesheet/gradient_stop.rb +33 -0
- data/lib/axlsx/stylesheet/num_fmt.rb +63 -0
- data/lib/axlsx/stylesheet/pattern_fill.rb +66 -0
- data/lib/axlsx/stylesheet/styles.rb +298 -0
- data/lib/axlsx/stylesheet/table_style.rb +47 -0
- data/lib/axlsx/stylesheet/table_style_element.rb +71 -0
- data/lib/axlsx/stylesheet/table_styles.rb +39 -0
- data/lib/axlsx/stylesheet/xf.rb +138 -0
- data/lib/axlsx/util/constants.rb +220 -0
- data/lib/axlsx/util/parser.rb +43 -0
- data/lib/axlsx/util/parser.rb~ +6 -0
- data/lib/axlsx/util/simple_typed_list.rb +160 -0
- data/lib/axlsx/util/validators.rb +132 -0
- data/lib/axlsx/version.rb +4 -0
- data/lib/axlsx/workbook/#workbook.rb# +165 -0
- data/lib/axlsx/workbook/workbook.rb +160 -0
- data/lib/axlsx/workbook/worksheet/cell.rb +337 -0
- data/lib/axlsx/workbook/worksheet/row.rb +107 -0
- data/lib/axlsx/workbook/worksheet/worksheet.rb +279 -0
- metadata +93 -141
- data/examples/follow_20111202.xlsx +0 -0
- data/test/content_type/tc_content_type.rb +0 -81
- data/test/content_type/tc_default.rb +0 -40
- data/test/content_type/tc_override.rb +0 -40
- data/test/doc_props/tc_app.rb +0 -19
- data/test/doc_props/tc_core.rb +0 -34
- data/test/drawing/tc_axis.rb +0 -40
- data/test/drawing/tc_bar_3D_chart.rb +0 -66
- data/test/drawing/tc_bar_series.rb +0 -34
- data/test/drawing/tc_cat_axis.rb +0 -32
- data/test/drawing/tc_cat_axis_data.rb +0 -18
- data/test/drawing/tc_chart.rb +0 -73
- data/test/drawing/tc_drawing.rb +0 -80
- data/test/drawing/tc_graphic_frame.rb +0 -26
- data/test/drawing/tc_line_3d_chart.rb +0 -48
- data/test/drawing/tc_line_series.rb +0 -27
- data/test/drawing/tc_marker.rb +0 -45
- data/test/drawing/tc_one_cell_anchor.rb +0 -67
- data/test/drawing/tc_pic.rb +0 -71
- data/test/drawing/tc_picture_locking.rb +0 -73
- data/test/drawing/tc_pie_3D_chart.rb +0 -33
- data/test/drawing/tc_pie_series.rb +0 -35
- data/test/drawing/tc_scaling.rb +0 -37
- data/test/drawing/tc_ser_axis.rb +0 -31
- data/test/drawing/tc_series.rb +0 -24
- data/test/drawing/tc_series_title.rb +0 -34
- data/test/drawing/tc_title.rb +0 -34
- data/test/drawing/tc_two_cell_anchor.rb +0 -38
- data/test/drawing/tc_val_axis.rb +0 -25
- data/test/drawing/tc_val_axis_data.rb +0 -18
- data/test/drawing/tc_view_3D.rb +0 -55
- data/test/rels/tc_relationship.rb +0 -16
- data/test/rels/tc_relationships.rb +0 -27
- data/test/stylesheet/tc_border.rb +0 -38
- data/test/stylesheet/tc_border_pr.rb +0 -33
- data/test/stylesheet/tc_cell_alignment.rb +0 -77
- data/test/stylesheet/tc_cell_protection.rb +0 -30
- data/test/stylesheet/tc_cell_style.rb +0 -58
- data/test/stylesheet/tc_color.rb +0 -38
- data/test/stylesheet/tc_fill.rb +0 -19
- data/test/stylesheet/tc_font.rb +0 -114
- data/test/stylesheet/tc_gradient_fill.rb +0 -65
- data/test/stylesheet/tc_gradient_stop.rb +0 -32
- data/test/stylesheet/tc_num_fmt.rb +0 -31
- data/test/stylesheet/tc_pattern_fill.rb +0 -38
- data/test/stylesheet/tc_styles.rb +0 -52
- data/test/stylesheet/tc_table_style.rb +0 -37
- data/test/stylesheet/tc_table_style_element.rb +0 -37
- data/test/stylesheet/tc_table_styles.rb +0 -30
- data/test/stylesheet/tc_xf.rb +0 -121
- data/test/tc_package.rb +0 -68
- data/test/util/tc_simple_typed_list.rb +0 -66
- data/test/util/tc_validators.rb +0 -76
- data/test/workbook/tc_workbook.rb +0 -60
- data/test/workbook/worksheet/tc_cell.rb +0 -179
- data/test/workbook/worksheet/tc_row.rb +0 -36
- 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
|
+
|