axlsx 1.1.3 → 1.2.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.
- data/.yardopts +5 -2
- data/CHANGELOG.md +60 -0
- data/LICENSE +1 -1
- data/README.md +115 -415
- data/Rakefile +11 -3
- data/examples/basic_charts.rb +54 -0
- data/examples/chart_colors.rb +88 -0
- data/examples/data_validation.rb +50 -0
- data/examples/example.rb +299 -155
- data/examples/example.xlsx +0 -0
- data/examples/example_streamed.xlsx +0 -0
- data/examples/extractive.pdf +0 -0
- data/examples/extractive.rb +45 -0
- data/examples/no-use_autowidth.xlsx +0 -0
- data/examples/scraping_html.rb +91 -0
- data/examples/shared_strings_example.xlsx +0 -0
- data/examples/sheet_protection.rb +10 -0
- data/examples/sheet_view.rb +34 -0
- data/examples/two_cell_anchor_image.rb +11 -0
- data/lib/axlsx/content_type/content_type.rb +2 -0
- data/lib/axlsx/content_type/default.rb +30 -15
- data/lib/axlsx/content_type/override.rb +29 -14
- data/lib/axlsx/doc_props/app.rb +161 -104
- data/lib/axlsx/doc_props/core.rb +9 -5
- data/lib/axlsx/drawing/ax_data_source.rb +26 -0
- data/lib/axlsx/drawing/axis.rb +92 -37
- data/lib/axlsx/drawing/bar_3D_chart.rb +49 -45
- data/lib/axlsx/drawing/bar_series.rb +25 -7
- data/lib/axlsx/drawing/cat_axis.rb +50 -22
- data/lib/axlsx/drawing/chart.rb +69 -56
- data/lib/axlsx/drawing/d_lbls.rb +100 -0
- data/lib/axlsx/drawing/drawing.rb +28 -12
- data/lib/axlsx/drawing/graphic_frame.rb +2 -1
- data/lib/axlsx/drawing/line_3D_chart.rb +3 -10
- data/lib/axlsx/drawing/line_series.rb +20 -4
- data/lib/axlsx/drawing/num_data.rb +52 -0
- data/lib/axlsx/drawing/num_data_source.rb +61 -0
- data/lib/axlsx/drawing/num_val.rb +32 -0
- data/lib/axlsx/drawing/pic.rb +44 -4
- data/lib/axlsx/drawing/pie_3D_chart.rb +10 -7
- data/lib/axlsx/drawing/pie_series.rb +18 -4
- data/lib/axlsx/drawing/scatter_chart.rb +2 -8
- data/lib/axlsx/drawing/scatter_series.rb +28 -2
- data/lib/axlsx/drawing/ser_axis.rb +1 -1
- data/lib/axlsx/drawing/series.rb +1 -1
- data/lib/axlsx/drawing/str_data.rb +42 -0
- data/lib/axlsx/drawing/str_val.rb +33 -0
- data/lib/axlsx/drawing/title.rb +21 -9
- data/lib/axlsx/drawing/two_cell_anchor.rb +46 -9
- data/lib/axlsx/drawing/view_3D.rb +44 -31
- data/lib/axlsx/drawing/vml_drawing.rb +42 -0
- data/lib/axlsx/drawing/vml_shape.rb +125 -0
- data/lib/axlsx/package.rb +44 -16
- data/lib/axlsx/rels/relationship.rb +3 -3
- data/lib/axlsx/rels/relationships.rb +1 -11
- data/lib/axlsx/stylesheet/gradient_fill.rb +1 -1
- data/lib/axlsx/stylesheet/styles.rb +1 -1
- data/lib/axlsx/stylesheet/table_style.rb +3 -3
- data/lib/axlsx/util/constants.rb +28 -0
- data/lib/axlsx/util/simple_typed_list.rb +15 -21
- data/lib/axlsx/util/validators.rb +81 -8
- data/lib/axlsx/version.rb +2 -7
- data/lib/axlsx/workbook/defined_name.rb +174 -0
- data/lib/axlsx/workbook/defined_names.rb +21 -0
- data/lib/axlsx/workbook/workbook.rb +65 -13
- data/lib/axlsx/workbook/worksheet/auto_filter.rb +34 -0
- data/lib/axlsx/workbook/worksheet/cell.rb +35 -7
- data/lib/axlsx/workbook/worksheet/col.rb +17 -1
- data/lib/axlsx/workbook/worksheet/cols.rb +20 -0
- data/lib/axlsx/workbook/worksheet/comment.rb +101 -0
- data/lib/axlsx/workbook/worksheet/comments.rb +85 -0
- data/lib/axlsx/workbook/worksheet/conditional_formattings.rb +25 -0
- data/lib/axlsx/workbook/worksheet/data_bar.rb +1 -1
- data/lib/axlsx/workbook/worksheet/data_validation.rb +245 -0
- data/lib/axlsx/workbook/worksheet/data_validations.rb +28 -0
- data/lib/axlsx/workbook/worksheet/dimension.rb +65 -0
- data/lib/axlsx/workbook/worksheet/merged_cells.rb +35 -0
- data/lib/axlsx/workbook/worksheet/page_setup.rb +113 -0
- data/lib/axlsx/workbook/worksheet/pane.rb +144 -0
- data/lib/axlsx/workbook/worksheet/print_options.rb +63 -0
- data/lib/axlsx/workbook/worksheet/protected_range.rb +46 -0
- data/lib/axlsx/workbook/worksheet/protected_ranges.rb +34 -0
- data/lib/axlsx/workbook/worksheet/row.rb +67 -12
- data/lib/axlsx/workbook/worksheet/selection.rb +111 -0
- data/lib/axlsx/workbook/worksheet/sheet_data.rb +25 -0
- data/lib/axlsx/workbook/worksheet/sheet_pr.rb +24 -0
- data/lib/axlsx/workbook/worksheet/sheet_protection.rb +224 -0
- data/lib/axlsx/workbook/worksheet/sheet_view.rb +379 -0
- data/lib/axlsx/workbook/worksheet/table.rb +3 -3
- data/lib/axlsx/workbook/worksheet/tables.rb +31 -0
- data/lib/axlsx/workbook/worksheet/worksheet.rb +377 -255
- data/lib/axlsx/workbook/worksheet/worksheet_comments.rb +57 -0
- data/lib/axlsx/workbook/worksheet/worksheet_drawing.rb +64 -0
- data/lib/axlsx.rb +30 -15
- data/test/content_type/tc_content_type.rb +1 -6
- data/test/doc_props/tc_app.rb +31 -1
- data/test/doc_props/tc_core.rb +3 -1
- data/test/drawing/tc_axis.rb +38 -3
- data/test/drawing/tc_bar_3D_chart.rb +12 -12
- data/test/drawing/tc_bar_series.rb +17 -13
- data/test/drawing/tc_cat_axis.rb +3 -3
- data/test/drawing/tc_cat_axis_data.rb +23 -13
- data/test/drawing/tc_chart.rb +29 -11
- data/test/drawing/tc_d_lbls.rb +47 -0
- data/test/drawing/tc_data_source.rb +17 -0
- data/test/drawing/tc_drawing.rb +11 -5
- data/test/drawing/tc_hyperlink.rb +4 -5
- data/test/drawing/tc_line_series.rb +9 -11
- data/test/drawing/tc_named_axis_data.rb +27 -0
- data/test/drawing/tc_num_data.rb +31 -0
- data/test/drawing/tc_num_val.rb +29 -0
- data/test/drawing/tc_pic.rb +22 -2
- data/test/drawing/tc_pie_3D_chart.rb +4 -8
- data/test/drawing/tc_pie_series.rb +11 -13
- data/test/drawing/tc_scatter_chart.rb +6 -1
- data/test/drawing/tc_scatter_series.rb +6 -5
- data/test/drawing/tc_series.rb +1 -1
- data/test/drawing/tc_str_data.rb +18 -0
- data/test/drawing/tc_str_val.rb +21 -0
- data/test/drawing/tc_title.rb +16 -0
- data/test/drawing/tc_two_cell_anchor.rb +2 -3
- data/test/drawing/tc_view_3D.rb +18 -18
- data/test/drawing/tc_vml_drawing.rb +25 -0
- data/test/drawing/tc_vml_shape.rb +100 -0
- data/test/rels/tc_relationship.rb +1 -0
- data/test/stylesheet/tc_gradient_fill.rb +8 -0
- data/test/stylesheet/tc_pattern_fill.rb +7 -1
- data/test/stylesheet/tc_styles.rb +3 -4
- data/test/stylesheet/tc_table_style.rb +8 -0
- data/test/stylesheet/tc_table_style_element.rb +10 -1
- data/test/tc_helper.rb +1 -0
- data/test/tc_package.rb +93 -17
- data/test/util/tc_simple_typed_list.rb +13 -0
- data/test/util/tc_validators.rb +95 -9
- data/test/workbook/tc_defined_name.rb +41 -0
- data/test/workbook/tc_workbook.rb +5 -3
- data/test/workbook/worksheet/table/tc_table.rb +3 -11
- data/test/workbook/worksheet/tc_cell.rb +30 -6
- data/test/workbook/worksheet/tc_col.rb +9 -0
- data/test/workbook/worksheet/tc_comment.rb +57 -0
- data/test/workbook/worksheet/tc_comments.rb +57 -0
- data/test/workbook/worksheet/tc_conditional_formatting.rb +44 -0
- data/test/workbook/worksheet/tc_data_bar.rb +1 -1
- data/test/workbook/worksheet/tc_data_validation.rb +265 -0
- data/test/workbook/worksheet/tc_page_setup.rb +138 -0
- data/test/workbook/worksheet/tc_pane.rb +94 -0
- data/test/workbook/worksheet/tc_print_options.rb +72 -0
- data/test/workbook/worksheet/tc_protected_range.rb +17 -0
- data/test/workbook/worksheet/tc_row.rb +36 -12
- data/test/workbook/worksheet/tc_selection.rb +94 -0
- data/test/workbook/worksheet/tc_sheet_protection.rb +117 -0
- data/test/workbook/worksheet/tc_sheet_view.rb +214 -0
- data/test/workbook/worksheet/tc_worksheet.rb +138 -48
- metadata +164 -52
- data/examples/example.csv +0 -1000
- data/lib/axlsx/drawing/cat_axis_data.rb +0 -34
- data/lib/axlsx/drawing/hlink_click.rb~ +0 -0
- data/lib/axlsx/drawing/hyperlink.rb~ +0 -64
- data/lib/axlsx/drawing/named_axis_data.rb +0 -36
- data/lib/axlsx/drawing/picture_locking.rb~ +0 -36
- data/lib/axlsx/drawing/val_axis_data.rb +0 -27
- data/lib/axlsx/util/cbf.rb +0 -333
- data/lib/axlsx/util/cfb.rb~ +0 -201
- data/lib/axlsx/util/font_tables.rb~ +0 -0
- data/lib/axlsx/util/ms_off_crypto.rb +0 -189
- data/lib/axlsx/util/ms_off_crypto.rb~ +0 -3
- data/lib/axlsx/util/ms_offcrypto.rb~ +0 -0
- data/lib/axlsx/util/parser.rb~ +0 -6
- data/lib/axlsx/util/storage.rb~ +0 -0
- data/lib/axlsx/workbook/shared_strings_table.rb~ +0 -69
- data/lib/axlsx/workbook/worksheet/cfvo.rb~ +0 -0
- data/lib/axlsx/workbook/worksheet/col.rb~ +0 -0
- data/lib/axlsx/workbook/worksheet/color_scale.rb~ +0 -46
- data/lib/axlsx/workbook/worksheet/data_bar.rb~ +0 -0
- data/lib/axlsx/workbook/worksheet/icon_set.rb~ +0 -95
- data/lib/axlsx/workbook/worksheet/shared_strings_table.rb~ +0 -0
- data/lib/axlsx/workbook/worksheet/table.rb~ +0 -97
- data/lib/schema/dc.xsd~ +0 -118
- data/lib/schema/dcterms.xsd~ +0 -331
- data/lib/schema/opc-coreProperties.xsd~ +0 -50
- data/test/drawing/tc_val_axis_data.rb +0 -17
- data/test/workbook/worksheet/table/tc_table.rb~ +0 -72
- data/test/workbook/worksheet/tc_cfvo.rb~ +0 -20
- data/test/workbook/worksheet/tc_col.rb~ +0 -10
- data/test/workbook/worksheet/tc_color_scale.rb~ +0 -0
- data/test/workbook/worksheet/tc_data_bar.rb~ +0 -0
- data/test/workbook/worksheet/tc_icon_set.rb~ +0 -0
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
module Axlsx
|
|
3
|
-
# The CatAxisData class serializes the category axis data for a chart
|
|
4
|
-
class CatAxisData < SimpleTypedList
|
|
5
|
-
|
|
6
|
-
# Create a new CatAxisData object
|
|
7
|
-
# @param [Array, SimpleTypedList] data the data for this category axis. This can be a simple array or a simple typed list of cells.
|
|
8
|
-
def initialize(data=[])
|
|
9
|
-
super Object
|
|
10
|
-
@list.concat data if data.is_a?(Array)
|
|
11
|
-
data.each { |i| @list << i } if data.is_a?(SimpleTypedList)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Serializes the object
|
|
15
|
-
# @param [String] str
|
|
16
|
-
# @return [String]
|
|
17
|
-
def to_xml_string(str = '')
|
|
18
|
-
str << '<c:cat>'
|
|
19
|
-
str << '<c:strRef>'
|
|
20
|
-
str << '<c:f>' << Axlsx::cell_range(@list) << '</c:f>'
|
|
21
|
-
str << '<c:strCache>'
|
|
22
|
-
str << '<c:ptCount val="' << size.to_s << '"/>'
|
|
23
|
-
each_with_index do |item, index|
|
|
24
|
-
v = item.is_a?(Cell) ? item.value.to_s : item
|
|
25
|
-
str << '<c:pt idx="' << index.to_s << '"><c:v>' << v << '</c:v></c:pt>'
|
|
26
|
-
end
|
|
27
|
-
str << '</c:strCache>'
|
|
28
|
-
str << '</c:strRef>'
|
|
29
|
-
str << '</c:cat>'
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
end
|
|
File without changes
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
module Axlsx
|
|
3
|
-
# a hlinkClick object adds a click behaviour to a drawing object.
|
|
4
|
-
# using the hlink_click method on a drawing object is the recommended way to manage hlinks
|
|
5
|
-
# @see Pic#link_to
|
|
6
|
-
class Hyperlink
|
|
7
|
-
|
|
8
|
-
# the type of hyper link defines the onclick or mouseover trigger.
|
|
9
|
-
# type must be one of :hlinkClick or :hlinkMouse
|
|
10
|
-
def type
|
|
11
|
-
@type ||= :hlinkClick
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# sets the type of the object.
|
|
15
|
-
# @param [Symbol] type one of :hlinkClick or hlinkMouse
|
|
16
|
-
def type(v) RestrictionValidator.validate "Hyperlink.type", [:hlinkClick, :hlinkMouse], v; @type = v end
|
|
17
|
-
|
|
18
|
-
# The destination of the hyperlink
|
|
19
|
-
# This value is actually stored in the drawings .rels. doc as a relationship.
|
|
20
|
-
attr_accessor :href
|
|
21
|
-
|
|
22
|
-
# The docs say:
|
|
23
|
-
# Specifies the URL when it has been determined by the generating application that the URL is invalid. That is the generating application can still store the URL but it is known that this URL is not correct.
|
|
24
|
-
attr_accessor :invalidUrl
|
|
25
|
-
|
|
26
|
-
# Specifies the target frame that is to be used when opening this hyperlink. When the hyperlink is activated this attribute is used to determine if a new window is launched for viewing or if an existing one can be used. If this attribute is omitted, than a new window is opened.
|
|
27
|
-
attr_accessor :tgtFrame
|
|
28
|
-
|
|
29
|
-
# Specifies the tooltip that should be displayed when the hyperlink text is hovered over with the mouse. If this attribute is omitted, than the hyperlink text itself can be displayed.
|
|
30
|
-
attr_accessor :tooltip
|
|
31
|
-
|
|
32
|
-
#Creates a hyperlink object
|
|
33
|
-
# parent must be a Pic for now, although I expect that other object support this tag and its cNvPr parent
|
|
34
|
-
# @param [Pic] parent
|
|
35
|
-
# @option [String] tooltip message shown when hyperlinked object is hovered over with mouse.
|
|
36
|
-
# @option [String] tgtFrame Target frame for opening hyperlink
|
|
37
|
-
# @option [String] invalidUrl supposedly use to store the href when we know it is an invalid resource.
|
|
38
|
-
# @option [String] href the target resource this hyperlink links to.
|
|
39
|
-
def initialize(parent, options=>{})
|
|
40
|
-
DataTypeValidator.validate "Hyperlink.parent", [Pic], parent
|
|
41
|
-
@parent = parent
|
|
42
|
-
options.each do |o|
|
|
43
|
-
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# The index of this hyperlink in the owning document's hyperlinks collection
|
|
48
|
-
# @returns [Integer]
|
|
49
|
-
def index
|
|
50
|
-
self.parent.anchor.drawing.hyperlinks.index(self)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# The relational ID for this hyperlink
|
|
54
|
-
# @returns [String]
|
|
55
|
-
def r:id
|
|
56
|
-
reference_count = self.parent.anchor.drawing.charts.size + self.parent.anchor.drawing.images.size + index
|
|
57
|
-
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def to_xml(xml)
|
|
61
|
-
xml.send(type,
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
# TODO: review cat, val and named access data to extend this and reduce replicated code.
|
|
3
|
-
module Axlsx
|
|
4
|
-
# The ValAxisData class manages the values for a chart value series.
|
|
5
|
-
class NamedAxisData < CatAxisData
|
|
6
|
-
|
|
7
|
-
# creates a new NamedAxisData Object
|
|
8
|
-
# @param [String] name The serialized node name for the axis data object
|
|
9
|
-
# @param [Array] The data to associate with the axis data object
|
|
10
|
-
def initialize(name, data=[])
|
|
11
|
-
super(data)
|
|
12
|
-
@name = name
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Serializes the object
|
|
16
|
-
# @param [String] str
|
|
17
|
-
# @return [String]
|
|
18
|
-
def to_xml_string(str = '')
|
|
19
|
-
str << '<c:' << @name.to_s << '>'
|
|
20
|
-
str << '<c:numRef>'
|
|
21
|
-
str << '<c:f>' << Axlsx::cell_range(@list) << '</c:f>'
|
|
22
|
-
str << '<c:numCache>'
|
|
23
|
-
str << '<c:formatCode>General</c:formatCode>'
|
|
24
|
-
str << '<c:ptCount val="' << size.to_s << '"/>'
|
|
25
|
-
each_with_index do |item, index|
|
|
26
|
-
v = item.is_a?(Cell) ? item.value.to_s : item
|
|
27
|
-
str << '<c:pt idx="' << index.to_s << '"><c:v>' << v << '</c:v></c:pt>'
|
|
28
|
-
end
|
|
29
|
-
str << '</c:numCache>'
|
|
30
|
-
str << '</c:numRef>'
|
|
31
|
-
str << '</c:' << @name.to_s << '>'
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
module Axlsx
|
|
2
|
-
# The picture locking class defines the locking properties for pictures in your workbook.
|
|
3
|
-
class PictureLocking
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
attr_reader :noGrp
|
|
7
|
-
attr_reader :noSelect
|
|
8
|
-
attr_reader :noRot
|
|
9
|
-
attr_reader :noChangeAspect
|
|
10
|
-
attr_reader :noMove
|
|
11
|
-
attr_reader :noResize
|
|
12
|
-
attr_reader :noEditPoints
|
|
13
|
-
attr_reader :noAdjustHandles
|
|
14
|
-
attr_reader :noChangeArrowheads
|
|
15
|
-
attr_reader :noChangeShapeType
|
|
16
|
-
|
|
17
|
-
# Creates a new PictureLocking object
|
|
18
|
-
# @option options [Boolean] noGrp
|
|
19
|
-
# @option options [Boolean] noSelect
|
|
20
|
-
# @option options [Boolean] noRot
|
|
21
|
-
# @option options [Boolean] noChangeAspect
|
|
22
|
-
# @option options [Boolean] noMove
|
|
23
|
-
# @option options [Boolean] noResize
|
|
24
|
-
# @option options [Boolean] noEditPoints
|
|
25
|
-
# @option options [Boolean] noAdjustHandles
|
|
26
|
-
# @option options [Boolean] noChangeArrowheads
|
|
27
|
-
# @option options [Boolean] noChangeShapeType
|
|
28
|
-
def initialize(options={})
|
|
29
|
-
options.each do |o|
|
|
30
|
-
self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end
|
|
36
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
module Axlsx
|
|
3
|
-
# The ValAxisData class manages the values for a chart value series.
|
|
4
|
-
class ValAxisData < CatAxisData
|
|
5
|
-
|
|
6
|
-
# Serializes the object
|
|
7
|
-
# @param [String] str
|
|
8
|
-
# @return [String]
|
|
9
|
-
def to_xml_string(str = '')
|
|
10
|
-
str << '<c:val>'
|
|
11
|
-
str << '<c:numRef>'
|
|
12
|
-
str << '<c:f>' << Axlsx::cell_range(@list) << '</c:f>'
|
|
13
|
-
str << '<c:numCache>'
|
|
14
|
-
str << '<c:formatCode>General</c:formatCode>'
|
|
15
|
-
str << '<c:ptCount val="' << size.to_s << '"/>'
|
|
16
|
-
each_with_index do |item, index|
|
|
17
|
-
v = item.is_a?(Cell) ? item.value.to_s : item
|
|
18
|
-
str << '<c:pt idx="' << index.to_s << '"><c:v>' << v << '</c:v></c:pt>'
|
|
19
|
-
end
|
|
20
|
-
str << '</c:numCache>'
|
|
21
|
-
str << '</c:numRef>'
|
|
22
|
-
str << '</c:val>'
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
end
|
data/lib/axlsx/util/cbf.rb
DELETED
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
module Axlsx
|
|
3
|
-
|
|
4
|
-
# The Cfb class is a MS-OFF-CRYPTOGRAPHY specific OLE (MS-CBF) writer implementation. No attempt is made to re-invent the wheel for read/write of compound binary files.
|
|
5
|
-
class Cbf
|
|
6
|
-
|
|
7
|
-
# the serialization for the CBF FAT
|
|
8
|
-
FAT_PACKING = "s128"
|
|
9
|
-
|
|
10
|
-
# the serialization for the MS-OFF-CRYPTO version stream
|
|
11
|
-
VERSION_PACKING = 'l s30 l3'
|
|
12
|
-
|
|
13
|
-
# The serialization for the MS-OFF-CRYPTO dataspace map stream
|
|
14
|
-
DATA_SPACE_MAP_PACKING = 'l6 s16 l s25 x2'
|
|
15
|
-
|
|
16
|
-
# The serialization for the MS-OFF-CRYPTO strong encrytion data space stream
|
|
17
|
-
STRONG_ENCRYPTION_DATA_SPACE_PACKING = 'l3 s26'
|
|
18
|
-
|
|
19
|
-
# The serialization for the MS-OFF-CRYPTO primary stream
|
|
20
|
-
PRIMARY_PACKING = 'l3 s38 l s40 l3 x12 l'
|
|
21
|
-
|
|
22
|
-
# The cutoff size that determines if a stream should be in the mini-fat or the fat
|
|
23
|
-
MINI_CUTOFF = 4096
|
|
24
|
-
|
|
25
|
-
# The serialization for CBF header
|
|
26
|
-
HEADER_PACKING = "q x16 l s3 x10 l l x4 l*"
|
|
27
|
-
|
|
28
|
-
# Creates a new Cbf object based on the ms_off_crypto object provided.
|
|
29
|
-
# @param [MsOffCrypto] ms_off_crypto
|
|
30
|
-
def initialize(ms_off_crypto)
|
|
31
|
-
@file_name = ms_off_crypto.file_name
|
|
32
|
-
@ms_off_crypto = ms_off_crypto
|
|
33
|
-
create_storages
|
|
34
|
-
mini_fat_stream
|
|
35
|
-
mini_fat
|
|
36
|
-
fat
|
|
37
|
-
header
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# creates or returns the version storage
|
|
41
|
-
# @return [Storage]
|
|
42
|
-
def version
|
|
43
|
-
@version ||= create_version
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# returns the data space map storage
|
|
47
|
-
# @return [Storage]
|
|
48
|
-
def data_space_map
|
|
49
|
-
@data_space_map ||= create_data_space_map
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# returns the primary storage
|
|
53
|
-
# @return [Storgae]
|
|
54
|
-
def primary
|
|
55
|
-
@primary ||= create_primary
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# returns the summary information storage
|
|
59
|
-
# @return [Storage]
|
|
60
|
-
def summary_information
|
|
61
|
-
@summary_information ||= create_summary_information
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# returns the document summary information
|
|
65
|
-
# @return [Storage]
|
|
66
|
-
def document_summary_information
|
|
67
|
-
@document_summary_information ||= create_document_summary_information
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# returns the stream of data allocated in the fat
|
|
71
|
-
# @return [String]
|
|
72
|
-
def fat_stream
|
|
73
|
-
@fat_stream ||= create_fat_stream
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# returns the stream allocated in the mini fat.
|
|
77
|
-
# return [String]
|
|
78
|
-
def mini_fat_stream
|
|
79
|
-
@mini_fat_stream ||= create_mini_fat_stream
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# returns the mini fat
|
|
83
|
-
# return [String]
|
|
84
|
-
def mini_fat
|
|
85
|
-
@mini_fat ||= create_mini_fat
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# returns the fat
|
|
89
|
-
# @return [String]
|
|
90
|
-
def fat
|
|
91
|
-
@fat ||= create_fat
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# returns the CFB header
|
|
95
|
-
# @return [String]
|
|
96
|
-
def header
|
|
97
|
-
@header ||= create_header
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# returns the encryption info from the ms_off_crypt object provided during intialization
|
|
101
|
-
# @return [String] encryption info
|
|
102
|
-
def encryption_info
|
|
103
|
-
@ms_off_crypto.encryption_info
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# returns the encrypted package from the ms_off_crypt object provided during initalization
|
|
107
|
-
# @return [String] encrypted package
|
|
108
|
-
def encrypted_package
|
|
109
|
-
@ms_off_crypto.encrypted_package
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# writes the compound binary file to disk
|
|
113
|
-
def save
|
|
114
|
-
ole = File.open(@file_name, 'w')
|
|
115
|
-
ole << header
|
|
116
|
-
ole << fat
|
|
117
|
-
@storages.each { |s| ole << s.to_s }
|
|
118
|
-
ole << Array.new((512-(ole.pos % 512)), 0).pack('c*')
|
|
119
|
-
ole << mini_fat
|
|
120
|
-
ole << mini_fat_stream
|
|
121
|
-
ole << fat_stream
|
|
122
|
-
ole.close
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
private
|
|
126
|
-
|
|
127
|
-
# Generates the storages required for ms-office-cryptography cfb
|
|
128
|
-
def create_storages
|
|
129
|
-
@storages = []
|
|
130
|
-
@encryption_info = @ms_off_crypto.encryption_info
|
|
131
|
-
@encrypted_package = @ms_off_crypto.encrypted_package
|
|
132
|
-
|
|
133
|
-
@storages << Storage.new('EncryptionInfo', :data=>encryption_info, :left=>3, :right=>11) # example shows right child. do we need the summary info????
|
|
134
|
-
@storages << Storage.new('EncryptedPackage', :data=>encrypted_package, :color=>Storage::COLORS[:red])
|
|
135
|
-
@storages << Storage.new([6].pack("c")+"DataSpaces", :child=>5, :modified =>129685612740945580, :created=>129685612740819979)
|
|
136
|
-
@storages << version
|
|
137
|
-
@storages << data_space_map
|
|
138
|
-
@storages << Storage.new('DataSpaceInfo', :right=>8, :child=>7, :created=>129685612740828880,:modified=>129685612740831800)
|
|
139
|
-
@storages << strong_encryption_data_space
|
|
140
|
-
@storages << Storage.new('TransformInfo', :color => Storage::COLORS[:red], :child=>9, :created=>129685612740834130, :modified=>129685612740943959)
|
|
141
|
-
@storages << Storage.new('StrongEncryptionTransform', :child=>10, :created=>129685612740834169, :modified=>129685612740942280)
|
|
142
|
-
@storages << primary
|
|
143
|
-
# @storages << summary_information
|
|
144
|
-
# @storages << document_summary_information
|
|
145
|
-
|
|
146
|
-
# we do this at the end as we need to build the minifat stream to determine the size. #HOWEVER - it looks like the size should not include the padding?
|
|
147
|
-
@storages.unshift Storage.new('Root Entry', :type=>Storage::TYPES[:root], :color=>Storage::COLORS[:red], :child=>1, :data => mini_fat_stream)
|
|
148
|
-
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# generates the mini fat stream
|
|
152
|
-
# @return [String]
|
|
153
|
-
def create_mini_fat_stream
|
|
154
|
-
mfs = []
|
|
155
|
-
@storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each_with_index do |stream, index|
|
|
156
|
-
puts "#{stream.name.pack('c*')}: #{stream.data.size}"
|
|
157
|
-
mfs.concat stream.data
|
|
158
|
-
mfs.concat Array.new(64 - (mfs.size % 64), 0) if mfs.size % 64 > 0
|
|
159
|
-
puts "mini fat stream size: #{mfs.size}"
|
|
160
|
-
end
|
|
161
|
-
mfs.concat(Array.new(512 - (mfs.size % 512), 0))
|
|
162
|
-
mfs.pack 'c*'
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# generates the fat stream.
|
|
166
|
-
# @return [String]
|
|
167
|
-
def create_fat_stream
|
|
168
|
-
mfs = []
|
|
169
|
-
@storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size >= MINI_CUTOFF}.each_with_index do |stream, index|
|
|
170
|
-
mfs.concat stream.data
|
|
171
|
-
mfs.concat Array.new(512 - (mfs.size % 512), 0) if mfs.size % 512 > 0
|
|
172
|
-
end
|
|
173
|
-
mfs.pack 'c*'
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# creates the mini fat
|
|
177
|
-
# @return [String]
|
|
178
|
-
def create_mini_fat
|
|
179
|
-
v_mf = []
|
|
180
|
-
@storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each do |stream|
|
|
181
|
-
allocate_stream(v_mf, stream, 64)
|
|
182
|
-
end
|
|
183
|
-
v_mf.concat Array.new(128 - v_mf.size, -1)
|
|
184
|
-
v_mf.pack 'l*'
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# creates the fat
|
|
188
|
-
# @return [String]
|
|
189
|
-
def create_fat
|
|
190
|
-
v_fat = [-3]
|
|
191
|
-
# storages four per sector, allocation forces directories to start at sector ID 0
|
|
192
|
-
allocate_stream(v_fat, @storages, 4)
|
|
193
|
-
# fat entry for minifat
|
|
194
|
-
allocate_stream(v_fat, 0, 512)
|
|
195
|
-
# fat entry for minifat stream
|
|
196
|
-
@storages[0].sector = v_fat.size
|
|
197
|
-
allocate_stream(v_fat, mini_fat_stream, 512)
|
|
198
|
-
# fat entries for encrypted package storage
|
|
199
|
-
# what to do about DIFAT for larger packages...
|
|
200
|
-
if @encrypted_package.size > (109 - v_fat.size) * 512
|
|
201
|
-
raise ArgumentError, "Your package is too big!"
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
if @encrypted_package.size >= MINI_CUTOFF
|
|
205
|
-
allocate_stream(v_fat, @encrypted_package, 512)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
v_fat.concat Array.new(128 - v_fat.size, -1) if v_fat.size < 128 #pack in unused sectors
|
|
209
|
-
v_fat.pack 'l*'
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# Creates the version storage
|
|
213
|
-
# @return [Storage]
|
|
214
|
-
def create_version
|
|
215
|
-
v_stream= [60, "Microsoft.Container.DataSpaces".bytes.to_a, 1, 1, 1].flatten!.pack VERSION_PACKING
|
|
216
|
-
Storage.new('Version', :data=>v_stream, :size=>v_stream.size)
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
# returns the strong encryption data space storage
|
|
220
|
-
# @return [Storgae]
|
|
221
|
-
def strong_encryption_data_space
|
|
222
|
-
@strong_encryption_data_space ||= create_strong_encryption_data_space
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Creates the data space map storage
|
|
226
|
-
# @return [Storgae]
|
|
227
|
-
def create_data_space_map
|
|
228
|
-
v_stream = [8,1,104, 1,0, 32, "EncryptedPackage".bytes.to_a, 50, "StrongEncryptionDataSpace".bytes.to_a].flatten!.pack DATA_SPACE_MAP_PACKING
|
|
229
|
-
Storage.new('DataSpaceMap', :data=>v_stream, :left => 4, :right => 6, :size=>v_stream.size)
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
# creates the stron encryption data space storage
|
|
234
|
-
# @return [Storgae]
|
|
235
|
-
def create_strong_encryption_data_space
|
|
236
|
-
v_stream = [8,1,50,"StrongEncryptionTransform".bytes.to_a,0].flatten.pack STRONG_ENCRYPTION_DATA_SPACE_PACKING
|
|
237
|
-
Storage.new("StrongEncryptionDataSpace", :data=>v_stream, :size => v_stream.size)
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# creates the primary storage
|
|
241
|
-
# @return [Storgae]
|
|
242
|
-
def create_primary
|
|
243
|
-
v_stream = [88,1,76,"{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}".bytes.to_a].flatten
|
|
244
|
-
v_stream.concat [78, "Microsoft.Container.EncryptionTransform".bytes.to_a,0,1,1,1,4].flatten
|
|
245
|
-
v_stream = v_stream.pack PRIMARY_PACKING
|
|
246
|
-
Storage.new([6].pack("c")+"Primary", :data=>v_stream)
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
# creates the summary information storage
|
|
251
|
-
# @return [Storage]
|
|
252
|
-
def create_summary_information
|
|
253
|
-
v_stream = []
|
|
254
|
-
v_stream.concat [0xFEFF, 0x0000, 0x030A, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000]
|
|
255
|
-
v_stream.concat [0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0xE085, 0x9FF2]
|
|
256
|
-
v_stream.concat [0xF94F, 0x6810, 0xAB91, 0x0800, 0x2B27, 0xB3D9, 0x3000, 0x0000]
|
|
257
|
-
v_stream.concat [0xAC00, 0x0000, 0x0700, 0x0000, 0x0100, 0x0000, 0x4000, 0x0000]
|
|
258
|
-
v_stream.concat [0x0400, 0x0000, 0x4800, 0x0000, 0x0800, 0x0000, 0x5800, 0x0000]
|
|
259
|
-
v_stream.concat [0x1200, 0x0000, 0x6800, 0x0000, 0x0C00, 0x0000, 0x8C00, 0x0000]
|
|
260
|
-
v_stream.concat [0x0D00, 0x0000, 0x9800, 0x0000, 0x1300, 0x0000, 0xA400, 0x0000]
|
|
261
|
-
v_stream.concat [0x0200, 0x0000, 0xE9FD, 0x0000, 0x1E00, 0x0000, 0x0800, 0x0000]
|
|
262
|
-
v_stream.concat [0x7261, 0x6E64, 0x796D, 0x0000, 0x1E00, 0x0000, 0x0800, 0x0000]
|
|
263
|
-
v_stream.concat [0x7261, 0x6E64, 0x796D, 0x0000, 0x1E00, 0x0000, 0x1C00, 0x0000]
|
|
264
|
-
v_stream.concat [0x4D69, 0x6372, 0x6F73, 0x6F66, 0x7420, 0x4D61, 0x6369, 0x6E74]
|
|
265
|
-
v_stream.concat [0x6F73, 0x6820, 0x4578, 0x6365, 0x6C00, 0x0000, 0x4000, 0x0000]
|
|
266
|
-
v_stream.concat [0x10AC, 0x5396, 0x60BC, 0xCC01, 0x4000, 0x0000, 0x40F4, 0xFDAF]
|
|
267
|
-
v_stream.concat [0x60BC, 0xCC01, 0x0300, 0x0000, 0x0100, 0x0000]
|
|
268
|
-
|
|
269
|
-
v_stream = v_stream.pack "s*"
|
|
270
|
-
|
|
271
|
-
Storage.new([5].pack('c')+"SummaryInformation", :data=>v_stream, :left => 2)
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
# creates the document summary information storage
|
|
276
|
-
# @return [Storage]
|
|
277
|
-
def create_document_summary_information
|
|
278
|
-
v_stream = []
|
|
279
|
-
v_stream.concat [0xFEFF, 0x0000, 0x030A, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000]
|
|
280
|
-
v_stream.concat [0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x02D5, 0xCDD5]
|
|
281
|
-
v_stream.concat [0x9C2E, 0x1B10, 0x9397, 0x0800, 0x2B2C, 0xF9AE, 0x3000, 0x0000]
|
|
282
|
-
v_stream.concat [0xCC00, 0x0000, 0x0900, 0x0000, 0x0100, 0x0000, 0x5000, 0x0000]
|
|
283
|
-
v_stream.concat [0x0F00, 0x0000, 0x5800, 0x0000, 0x1700, 0x0000, 0x6400, 0x0000]
|
|
284
|
-
v_stream.concat [0x0B00, 0x0000, 0x6C00, 0x0000, 0x1000, 0x0000, 0x7400, 0x0000]
|
|
285
|
-
v_stream.concat [0x1300, 0x0000, 0x7C00, 0x0000, 0x1600, 0x0000, 0x8400, 0x0000]
|
|
286
|
-
v_stream.concat [0x0D00, 0x0000, 0x8C00, 0x0000, 0x0C00, 0x0000, 0x9F00, 0x0000]
|
|
287
|
-
v_stream.concat [0x0200, 0x0000, 0xE9FD, 0x0000, 0x1E00, 0x0000, 0x0400, 0x0000]
|
|
288
|
-
v_stream.concat [0x0000, 0x0000, 0x0300, 0x0000, 0x0000, 0x0C00, 0x0B00, 0x0000]
|
|
289
|
-
v_stream.concat [0x0000, 0x0000, 0x0B00, 0x0000, 0x0000, 0x0000, 0x0B00, 0x0000]
|
|
290
|
-
v_stream.concat [0x0000, 0x0000, 0x0B00, 0x0000, 0x0000, 0x0000, 0x1E10, 0x0000]
|
|
291
|
-
v_stream.concat [0x0100, 0x0000, 0x0700, 0x0000, 0x5368, 0x6565, 0x7431, 0x000C]
|
|
292
|
-
v_stream.concat [0x1000, 0x0002, 0x0000, 0x001E, 0x0000, 0x0013, 0x0000, 0x00E3]
|
|
293
|
-
v_stream.concat [0x83AF, 0xE383, 0xBCE3, 0x82AF, 0xE382, 0xB7E3, 0x83BC, 0xE383]
|
|
294
|
-
v_stream.concat [0x8800, 0x0300, 0x0000, 0x0100, 0x0000, 0x0000]
|
|
295
|
-
v_stream = v_stream.pack 'c*'
|
|
296
|
-
Storage.new([5].pack('c')+"DocumentSummaryInformation", :data=>v_stream)
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
# Creates the header
|
|
300
|
-
# @return [String]
|
|
301
|
-
def create_header
|
|
302
|
-
header = []
|
|
303
|
-
header << -2226271756974174256 # identifier pack as q
|
|
304
|
-
header << 196670 # version pack as L
|
|
305
|
-
header << 65534 # byte order pack as s
|
|
306
|
-
header << 9 # sector shift
|
|
307
|
-
header << 6 # mini-sector shift
|
|
308
|
-
header << (fat.size/512.0).ceil # this is the number of FAT sectors in the file at index 6 pack as L
|
|
309
|
-
header << header.last # this is the first directory sector, index of 7 pack as L
|
|
310
|
-
header << MINI_CUTOFF # minfat cutoff pack as L
|
|
311
|
-
# MiniFat starts after directories
|
|
312
|
-
header << (fat.size/512.0).ceil + (@storages.size/4.0).ceil # this is the sector id for the first minifat index 10 pack as L
|
|
313
|
-
header << (mini_fat.size/512.0).ceil # minifat sector count index 11 pack as L
|
|
314
|
-
header << -2 # the first DIFAT - set to end of chain until we exceed a single FAT pack as L
|
|
315
|
-
header << 0 # number of DIFAT sectors, unless we go beyond 109 FAT sectors this will always be 0 pack as L
|
|
316
|
-
header << 0 # first FAT sector defined in the DIFAT pack as L
|
|
317
|
-
header.concat Array.new(108, -1) # Difat sectors pack as L108
|
|
318
|
-
header.pack(HEADER_PACKING)
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
# Allocates sector chains in a allocation table based on the sector size and stream provided
|
|
322
|
-
# If a storage obeject is provided, the starting sector value for the storage is updated based on the allocation performed here.
|
|
323
|
-
# @param [Array] table Allocation table array
|
|
324
|
-
# @param [Storage | String] stream
|
|
325
|
-
# @param [Integer] size The cutoff size for the stream.
|
|
326
|
-
def allocate_stream(table, stream, size)
|
|
327
|
-
stream.sector = table.size if stream.respond_to?(:sector)
|
|
328
|
-
((stream.size / size.to_f).ceil).times { table << table.size }
|
|
329
|
-
table[table.size-1] = -2 # this is the CBF chain terminator
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
end
|
|
333
|
-
end
|