openxml-xlsx 0.1.2 → 0.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -4
- data/example +4 -3
- data/lib/openxml-xlsx.rb +1 -0
- data/lib/openxml/xlsx.rb +30 -0
- data/lib/{xlsx → openxml/xlsx}/elements.rb +4 -2
- data/lib/openxml/xlsx/elements/alignment.rb +22 -0
- data/lib/openxml/xlsx/elements/border.rb +28 -0
- data/lib/openxml/xlsx/elements/border_component.rb +21 -0
- data/lib/openxml/xlsx/elements/border_style.rb +22 -0
- data/lib/openxml/xlsx/elements/cell.rb +96 -0
- data/lib/openxml/xlsx/elements/defined_name.rb +13 -0
- data/lib/openxml/xlsx/elements/font.rb +22 -0
- data/lib/openxml/xlsx/elements/implied_number_format.rb +7 -0
- data/lib/openxml/xlsx/elements/indexed_color.rb +13 -0
- data/lib/openxml/xlsx/elements/number_format.rb +47 -0
- data/lib/openxml/xlsx/elements/pattern_fill.rb +20 -0
- data/lib/openxml/xlsx/elements/relationship.rb +20 -0
- data/lib/openxml/xlsx/elements/row.rb +37 -0
- data/lib/openxml/xlsx/elements/style.rb +29 -0
- data/lib/openxml/xlsx/elements/table_column.rb +8 -0
- data/lib/openxml/xlsx/elements/theme_color.rb +13 -0
- data/lib/openxml/xlsx/package.rb +52 -0
- data/lib/{xlsx → openxml/xlsx}/parts.rb +4 -2
- data/lib/openxml/xlsx/parts/shared_strings.rb +28 -0
- data/lib/openxml/xlsx/parts/stylesheet.rb +80 -0
- data/lib/openxml/xlsx/parts/table.rb +36 -0
- data/lib/openxml/xlsx/parts/workbook.rb +71 -0
- data/lib/openxml/xlsx/parts/worksheet.rb +90 -0
- data/lib/openxml/xlsx/version.rb +5 -0
- data/{xlsx.gemspec → openxml-xlsx.gemspec} +5 -5
- metadata +37 -36
- data/lib/xlsx.rb +0 -28
- data/lib/xlsx/elements/alignment.rb +0 -20
- data/lib/xlsx/elements/border.rb +0 -26
- data/lib/xlsx/elements/border_component.rb +0 -19
- data/lib/xlsx/elements/border_style.rb +0 -20
- data/lib/xlsx/elements/cell.rb +0 -94
- data/lib/xlsx/elements/defined_name.rb +0 -11
- data/lib/xlsx/elements/font.rb +0 -20
- data/lib/xlsx/elements/implied_number_format.rb +0 -5
- data/lib/xlsx/elements/indexed_color.rb +0 -11
- data/lib/xlsx/elements/number_format.rb +0 -45
- data/lib/xlsx/elements/pattern_fill.rb +0 -18
- data/lib/xlsx/elements/relationship.rb +0 -18
- data/lib/xlsx/elements/row.rb +0 -35
- data/lib/xlsx/elements/style.rb +0 -27
- data/lib/xlsx/elements/table_column.rb +0 -6
- data/lib/xlsx/elements/theme_color.rb +0 -11
- data/lib/xlsx/package.rb +0 -50
- data/lib/xlsx/parts/shared_strings.rb +0 -26
- data/lib/xlsx/parts/stylesheet.rb +0 -78
- data/lib/xlsx/parts/table.rb +0 -34
- data/lib/xlsx/parts/workbook.rb +0 -66
- data/lib/xlsx/parts/worksheet.rb +0 -88
- data/lib/xlsx/version.rb +0 -3
@@ -0,0 +1,37 @@
|
|
1
|
+
module OpenXml
|
2
|
+
module Xlsx
|
3
|
+
module Elements
|
4
|
+
class Row
|
5
|
+
attr_reader :worksheet, :number, :height, :hidden, :cells
|
6
|
+
|
7
|
+
def initialize(worksheet, options={})
|
8
|
+
@worksheet = worksheet
|
9
|
+
@number = options.fetch(:number)
|
10
|
+
@height = options[:height]
|
11
|
+
@hidden = options[:hidden]
|
12
|
+
@cells = []
|
13
|
+
|
14
|
+
Array(options[:cells]).each do |attributes|
|
15
|
+
add_cell attributes
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_cell(attributes)
|
20
|
+
cells.push Xlsx::Elements::Cell.new(self, attributes)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_xml(xml)
|
24
|
+
attributes = {"r" => number}
|
25
|
+
attributes.merge!("ht" => height, "customHeight" => 1) if height
|
26
|
+
attributes.merge!("hidden" => 1) if hidden
|
27
|
+
xml.row(attributes) do
|
28
|
+
cells.each do |cell|
|
29
|
+
cell.to_xml(xml)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module OpenXml
|
2
|
+
module Xlsx
|
3
|
+
module Elements
|
4
|
+
class Style < Struct.new(:format_id, :font_id, :fill_id, :border_id, :alignment)
|
5
|
+
|
6
|
+
def initialize(format_id=0, font_id=0, fill_id=0, border_id=0, alignment=nil)
|
7
|
+
super format_id || 0, font_id || 0, fill_id || 0, border_id || 0, alignment
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_xml(xml)
|
11
|
+
attributes = {
|
12
|
+
numFmtId: format_id,
|
13
|
+
fontId: font_id,
|
14
|
+
fillId: fill_id,
|
15
|
+
borderId: border_id }
|
16
|
+
attributes.merge!(applyNumberFormat: 1) if format_id > 0
|
17
|
+
attributes.merge!(applyFont: 1) if font_id > 0
|
18
|
+
attributes.merge!(applyFill: 1) if fill_id > 0
|
19
|
+
attributes.merge!(applyBorder: 1) if border_id > 0
|
20
|
+
attributes.merge!(applyAlignment: 1) if alignment
|
21
|
+
xml.xf(attributes) do
|
22
|
+
alignment.to_xml(xml) if alignment
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "openxml/package"
|
2
|
+
|
3
|
+
module OpenXml
|
4
|
+
module Xlsx
|
5
|
+
class Package < OpenXml::Package
|
6
|
+
attr_reader :xl_rels,
|
7
|
+
:shared_strings,
|
8
|
+
:stylesheet,
|
9
|
+
:workbook
|
10
|
+
|
11
|
+
|
12
|
+
content_types do
|
13
|
+
override "/xl/workbook.xml", TYPE_WORKBOOK
|
14
|
+
override "/xl/worksheets/sheet1.xml", TYPE_WORKSHEET
|
15
|
+
override "/xl/sharedStrings.xml", TYPE_SHARED_STRINGS
|
16
|
+
override "/xl/styles.xml", TYPE_STYLES
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
super
|
22
|
+
rels.add_relationship REL_DOCUMENT, "xl/workbook.xml"
|
23
|
+
|
24
|
+
@xl_rels = OpenXml::Parts::Rels.new([
|
25
|
+
{ "Type" => REL_SHARED_STRINGS, "Target" => "sharedStrings.xml" },
|
26
|
+
{ "Type" => REL_STYLES, "Target" => "styles.xml" }
|
27
|
+
])
|
28
|
+
@shared_strings = Xlsx::Parts::SharedStrings.new
|
29
|
+
@stylesheet = Xlsx::Parts::Stylesheet.new
|
30
|
+
@workbook = Xlsx::Parts::Workbook.new(self)
|
31
|
+
|
32
|
+
# docProps/app.xml
|
33
|
+
# docProps/core.xml
|
34
|
+
add_part "xl/_rels/workbook.xml.rels", xl_rels
|
35
|
+
# xl/calcChain.xml
|
36
|
+
add_part "xl/sharedStrings.xml", shared_strings
|
37
|
+
add_part "xl/styles.xml", stylesheet
|
38
|
+
# xl/theme/theme1.xml
|
39
|
+
add_part "xl/workbook.xml", workbook
|
40
|
+
end
|
41
|
+
|
42
|
+
def string_ref(string)
|
43
|
+
shared_strings.reference_of(string)
|
44
|
+
end
|
45
|
+
|
46
|
+
def style_ref(style)
|
47
|
+
stylesheet.reference_of(style)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module OpenXml
|
2
|
+
module Xlsx
|
3
|
+
module Parts
|
4
|
+
class SharedStrings < OpenXml::Part
|
5
|
+
attr_reader :strings
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@strings = Hash.new { |hash, key| hash[key] = hash.length }
|
9
|
+
end
|
10
|
+
|
11
|
+
def reference_of(string)
|
12
|
+
strings[string]
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_xml
|
16
|
+
build_standalone_xml do |xml|
|
17
|
+
xml.sst(xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main", uniqueCount: strings.length) do
|
18
|
+
strings.each do |string, i|
|
19
|
+
xml.si { xml.t(string) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module OpenXml
|
2
|
+
module Xlsx
|
3
|
+
module Parts
|
4
|
+
class Stylesheet < OpenXml::Part
|
5
|
+
include Xlsx::Elements
|
6
|
+
|
7
|
+
attr_reader :formats, :fonts, :fills, :borders, :styles
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@formats = []
|
11
|
+
@fonts = [Font.new("Calibri", 12)]
|
12
|
+
@fills = [PatternFill.new("none"), PatternFill.new("gray125")]
|
13
|
+
@borders = [Border.new]
|
14
|
+
@styles = [Style.new]
|
15
|
+
end
|
16
|
+
|
17
|
+
def reference_of(options={})
|
18
|
+
case format = options[:format]
|
19
|
+
when NumberFormat
|
20
|
+
options[:format_id] = Xlsx.index!(formats, format) + NUMBER_FORMAT_START_ID
|
21
|
+
when ImpliedNumberFormat
|
22
|
+
options[:format_id] = format.id
|
23
|
+
end
|
24
|
+
options[:font_id] = Xlsx.index!(fonts, options[:font]) if options.key? :font
|
25
|
+
options[:fill_id] = Xlsx.index!(fills, options[:fill]) if options.key? :fill
|
26
|
+
options[:border_id] = Xlsx.index!(borders, options[:border]) if options.key? :border
|
27
|
+
|
28
|
+
style = Style.new(
|
29
|
+
options.fetch(:format_id, 0),
|
30
|
+
options.fetch(:font_id, 0),
|
31
|
+
options.fetch(:fill_id, 0),
|
32
|
+
options.fetch(:border_id, 0),
|
33
|
+
options.fetch(:alignment, nil))
|
34
|
+
|
35
|
+
Xlsx.index!(styles, style)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_xml
|
39
|
+
build_standalone_xml do |xml|
|
40
|
+
xml.styleSheet(xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main", "xmlns:mc" => "http://schemas.openxmlformats.org/markup-compatibility/2006") do
|
41
|
+
xml.numFmts(count: formats.length) do
|
42
|
+
formats.each_with_index do |format, index|
|
43
|
+
format.to_xml(index + NUMBER_FORMAT_START_ID, xml)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
xml.fonts(count: fonts.length) do
|
48
|
+
fonts.each do |font|
|
49
|
+
font.to_xml(xml)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
xml.fills(count: fills.length) do
|
54
|
+
fills.each do |fill|
|
55
|
+
fill.to_xml(xml)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
xml.borders(count: borders.length) do
|
60
|
+
borders.each do |border|
|
61
|
+
border.to_xml(xml)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
xml.cellXfs(count: styles.length) do
|
66
|
+
styles.each do |style|
|
67
|
+
style.to_xml(xml)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
NUMBER_FORMAT_START_ID = 165.freeze
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module OpenXml
|
2
|
+
module Xlsx
|
3
|
+
module Parts
|
4
|
+
class Table < OpenXml::Part
|
5
|
+
attr_reader :id, :name, :ref, :columns
|
6
|
+
|
7
|
+
def initialize(id, name, ref, columns)
|
8
|
+
@id = id
|
9
|
+
@name = name
|
10
|
+
@ref = ref
|
11
|
+
@columns = columns
|
12
|
+
end
|
13
|
+
|
14
|
+
def filename
|
15
|
+
"table#{id}.xml"
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_xml
|
19
|
+
build_standalone_xml do |xml|
|
20
|
+
xml.table(xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
21
|
+
id: id, name: name, displayName: name, ref: ref, totalsRowShown: 0) do
|
22
|
+
xml.autoFilter ref: ref
|
23
|
+
xml.tableColumns(count: columns.length) do
|
24
|
+
columns.each_with_index do |column, index|
|
25
|
+
xml.tableColumn(id: index + 1, name: column.name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
xml.tableStyleInfo(name: "TableStyleLight6", showFirstColumn: 0, showLastColumn: 0, showRowStripes: 1, showColumnStripes: 0)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module OpenXml
|
2
|
+
module Xlsx
|
3
|
+
module Parts
|
4
|
+
class Workbook < OpenXml::Part
|
5
|
+
attr_reader :package, :worksheets, :tables, :defined_names
|
6
|
+
|
7
|
+
def initialize(package)
|
8
|
+
@package = package
|
9
|
+
@worksheets = []
|
10
|
+
@tables = []
|
11
|
+
@defined_names =[]
|
12
|
+
add_worksheet
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_worksheet
|
16
|
+
worksheet = Worksheet.new(self, worksheets.length + 1)
|
17
|
+
package.xl_rels.add_relationship(
|
18
|
+
REL_WORKSHEET,
|
19
|
+
"worksheets/sheet#{worksheet.index}.xml",
|
20
|
+
"rId#{worksheet.index}")
|
21
|
+
package.add_part "xl/worksheets/_rels/sheet#{worksheet.index}.xml.rels", worksheet.rels
|
22
|
+
package.add_part "xl/worksheets/sheet#{worksheet.index}.xml", worksheet
|
23
|
+
worksheets.push worksheet
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_table(table)
|
27
|
+
package.content_types.add_override "/xl/tables/#{table.filename}", TYPE_TABLE
|
28
|
+
package.add_part "xl/tables/#{table.filename}", table
|
29
|
+
tables.push table
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_defined_names(*defined_names)
|
33
|
+
defined_names.flatten.each do |attributes|
|
34
|
+
add_defined_name attributes
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_defined_name(attributes)
|
39
|
+
defined_names.push Xlsx::Elements::DefinedName.new(attributes[:name], attributes[:formula])
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_xml
|
43
|
+
build_standalone_xml do |xml|
|
44
|
+
xml.workbook(root_namespaces) {
|
45
|
+
xml.bookViews {
|
46
|
+
xml.workbookView
|
47
|
+
}
|
48
|
+
xml.sheets { worksheets.each { |worksheet|
|
49
|
+
xml.sheet(
|
50
|
+
"name" => worksheet.name,
|
51
|
+
"sheetId" => worksheet.index,
|
52
|
+
"r:id" => "rId#{worksheet.index}")
|
53
|
+
} }
|
54
|
+
xml.definedNames do
|
55
|
+
defined_names.each { |defined_name| defined_name.to_xml(xml) }
|
56
|
+
end if defined_names.any?
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def root_namespaces
|
64
|
+
{ "xmlns" => "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
65
|
+
"xmlns:r" => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' }
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module OpenXml
|
2
|
+
module Xlsx
|
3
|
+
module Parts
|
4
|
+
class Worksheet < OpenXml::Part
|
5
|
+
attr_reader :workbook, :index, :rows, :tables, :rels, :cell_ranges
|
6
|
+
|
7
|
+
def initialize(workbook, index)
|
8
|
+
@workbook = workbook
|
9
|
+
@index = index
|
10
|
+
@rows = []
|
11
|
+
@tables = []
|
12
|
+
@cell_ranges = []
|
13
|
+
@rels = OpenXml::Parts::Rels.new
|
14
|
+
@column_widths = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def column_widths(*args)
|
18
|
+
return @column_widths if args.none?
|
19
|
+
@column_widths = args.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_rows(*rows)
|
23
|
+
rows.flatten.each do |attributes|
|
24
|
+
add_row attributes
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_row(attributes)
|
29
|
+
rows.push Xlsx::Elements::Row.new(self, attributes)
|
30
|
+
end
|
31
|
+
|
32
|
+
def merge_cells(*ranges)
|
33
|
+
ranges.each { |range| cell_ranges.push range }
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_table(id, name, ref, columns)
|
37
|
+
table = Xlsx::Parts::Table.new(id, name, ref, columns)
|
38
|
+
rels.add_relationship(REL_TABLE, "../tables/#{table.filename}")
|
39
|
+
workbook.add_table table
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_xml
|
43
|
+
build_standalone_xml do |xml|
|
44
|
+
xml.worksheet(root_namespaces) do
|
45
|
+
xml.sheetViews do
|
46
|
+
xml.sheetView(showGridLines: 0, tabSelected: 1, workbookViewId: 0)
|
47
|
+
end
|
48
|
+
xml.sheetFormatPr(baseColWidth: 10, defaultColWidth: 13.33203125, defaultRowHeight: 20, customHeight: 1)
|
49
|
+
xml.cols do
|
50
|
+
column_widths.each do |column, width|
|
51
|
+
xml.col(min: column, max: column, width: width, customWidth: 1)
|
52
|
+
end
|
53
|
+
end if column_widths.any?
|
54
|
+
xml.sheetData do
|
55
|
+
rows.each { |row| row.to_xml(xml) }
|
56
|
+
end
|
57
|
+
xml.mergeCells(count: merge_cells.size) do
|
58
|
+
cell_ranges.each { |range| xml.mergeCell ref: range }
|
59
|
+
end if cell_ranges.any?
|
60
|
+
xml.pageMargins(left: 0.75, right: 0.75, top: 1, bottom: 1, header: 0.5, footer: 0.5)
|
61
|
+
xml.pageSetup(orientation: "portrait", horizontalDpi: 4294967292, verticalDpi: 4294967292)
|
62
|
+
xml.tableParts(count: tables.count) do
|
63
|
+
tables.each do |rel|
|
64
|
+
xml.tablePart("r:id" => rel.id)
|
65
|
+
end
|
66
|
+
end if tables.any?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def name
|
72
|
+
"Sheet#{index}"
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def root_namespaces
|
78
|
+
{ "xmlns" => "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
79
|
+
"xmlns:r" => "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
80
|
+
"xmlns:mc" => "http://schemas.openxmlformats.org/markup-compatibility/2006" }
|
81
|
+
end
|
82
|
+
|
83
|
+
def tables
|
84
|
+
rels.select { |rel| rel.type == REL_TABLE }
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|