xlsx_writer 0.1.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/LICENSE +20 -0
- data/README.markdown +74 -0
- data/Rakefile +13 -0
- data/lib/xlsx_writer.rb +32 -0
- data/lib/xlsx_writer/autofilter.rb +7 -0
- data/lib/xlsx_writer/cell.rb +173 -0
- data/lib/xlsx_writer/document.rb +82 -0
- data/lib/xlsx_writer/generators/app.erb +13 -0
- data/lib/xlsx_writer/generators/app.rb +7 -0
- data/lib/xlsx_writer/generators/content_types.erb +14 -0
- data/lib/xlsx_writer/generators/content_types.rb +7 -0
- data/lib/xlsx_writer/generators/doc_props.erb +3 -0
- data/lib/xlsx_writer/generators/doc_props.rb +7 -0
- data/lib/xlsx_writer/generators/image.rb +68 -0
- data/lib/xlsx_writer/generators/rels.erb +6 -0
- data/lib/xlsx_writer/generators/rels.rb +7 -0
- data/lib/xlsx_writer/generators/sheet.rb +105 -0
- data/lib/xlsx_writer/generators/sheet_rels.erb +4 -0
- data/lib/xlsx_writer/generators/sheet_rels.rb +17 -0
- data/lib/xlsx_writer/generators/styles.erb +43 -0
- data/lib/xlsx_writer/generators/styles.rb +7 -0
- data/lib/xlsx_writer/generators/vml_drawing.erb +6 -0
- data/lib/xlsx_writer/generators/vml_drawing.rb +7 -0
- data/lib/xlsx_writer/generators/vml_drawing_rels.erb +6 -0
- data/lib/xlsx_writer/generators/vml_drawing_rels.rb +7 -0
- data/lib/xlsx_writer/generators/workbook.erb +12 -0
- data/lib/xlsx_writer/generators/workbook.rb +7 -0
- data/lib/xlsx_writer/generators/workbook_rels.erb +7 -0
- data/lib/xlsx_writer/generators/workbook_rels.rb +7 -0
- data/lib/xlsx_writer/header_footer.rb +116 -0
- data/lib/xlsx_writer/page_setup.rb +43 -0
- data/lib/xlsx_writer/row.rb +42 -0
- data/lib/xlsx_writer/utils.rb +34 -0
- data/lib/xlsx_writer/xml.rb +44 -0
- metadata +116 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Dee Zsombor, Justin Beck, Seamus Abshere
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# xlsx_writer
|
2
|
+
|
3
|
+
Writes (doesn't read or modify) XLSX files.
|
4
|
+
|
5
|
+
## Credit
|
6
|
+
|
7
|
+
Based on the [original simple\_xlsx\_writer gem](https://github.com/harvesthq/simple_xlsx_writer) and [patches by mumboe](https://github.com/mumboe/simple_xlsx_writer)
|
8
|
+
|
9
|
+
Then I tore it down and rebuilt it:
|
10
|
+
|
11
|
+
* no longer constructs everything in a single zipstream... instead writes the individual files to /tmp and then zips them together
|
12
|
+
* absolute minimum XML - went through every line, testing to see if I could remove it
|
13
|
+
* no more block format - this was more appropriate when it was constructed as a zipstream
|
14
|
+
|
15
|
+
Features not present in simple_xlsx_writer:
|
16
|
+
|
17
|
+
* opinionated, non-customizable styles - Arial 10pt, left-aligned text and dates, right-aligned numbers and currency
|
18
|
+
* autofilter based on a cell range
|
19
|
+
* header and footer, with support for images (.emf only) and page numbers
|
20
|
+
* fits columns to text
|
21
|
+
|
22
|
+
## Example
|
23
|
+
|
24
|
+
require 'xlsx_writer'
|
25
|
+
|
26
|
+
doc = XlsxWriter::Document.new
|
27
|
+
|
28
|
+
sheet1 = doc.add_sheet("People")
|
29
|
+
|
30
|
+
# DATA
|
31
|
+
|
32
|
+
sheet1.add_row([
|
33
|
+
"DoB",
|
34
|
+
"Name",
|
35
|
+
"Occupation",
|
36
|
+
"Salary",
|
37
|
+
"Citations"
|
38
|
+
])
|
39
|
+
sheet1.add_row([
|
40
|
+
Date.parse("July 31, 1912"),
|
41
|
+
"Milton Friedman",
|
42
|
+
"Economist / Statistician",
|
43
|
+
{:type => :Currency, :value => 10_000},
|
44
|
+
500_000
|
45
|
+
])
|
46
|
+
sheet1.add_autofilter 'A1:E1'
|
47
|
+
|
48
|
+
# FORMATTING
|
49
|
+
|
50
|
+
doc.page_setup.top = 1.5
|
51
|
+
doc.header.right.contents = 'Corporate Reporting'
|
52
|
+
doc.footer.left.contents = 'Confidential'
|
53
|
+
doc.footer.right.contents = :page_x_of_y
|
54
|
+
|
55
|
+
# if you really need images in header/footer: do it in Excel, save, unzip the xlsx... get the .emf files, "cropleft" (if necessary), etc. from there
|
56
|
+
|
57
|
+
left_header_image = doc.add_image('image1.emf', 118, 107)
|
58
|
+
left_header_image.croptop = '11025f'
|
59
|
+
left_header_image.cropleft = '9997f'
|
60
|
+
center_footer_image = doc.add_image('image2.emf', 116, 36)
|
61
|
+
doc.header.left.contents = left_header_image
|
62
|
+
doc.footer.center.contents = [ 'Powered by ', center_footer_image ]
|
63
|
+
doc.page_setup.header = 0
|
64
|
+
doc.page_setup.footer = 0
|
65
|
+
|
66
|
+
# OUTPUT
|
67
|
+
|
68
|
+
# You should move the file to where you want it
|
69
|
+
require 'fileutils'
|
70
|
+
::FileUtils.mv doc.path, 'myfile.xlsx'
|
71
|
+
|
72
|
+
# don't forget
|
73
|
+
doc.cleanup
|
74
|
+
|
data/Rakefile
ADDED
data/lib/xlsx_writer.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
3
|
+
module XlsxWriter
|
4
|
+
def self.gem_dir
|
5
|
+
::File.join ::File.dirname(__FILE__), 'xlsx_writer'
|
6
|
+
end
|
7
|
+
|
8
|
+
autoload :Cell, "#{gem_dir}/cell"
|
9
|
+
autoload :Document, "#{gem_dir}/document"
|
10
|
+
autoload :Row, "#{gem_dir}/row"
|
11
|
+
autoload :Utils, "#{gem_dir}/utils"
|
12
|
+
autoload :Xml, "#{gem_dir}/xml"
|
13
|
+
autoload :HeaderFooter, "#{gem_dir}/header_footer"
|
14
|
+
autoload :Autofilter, "#{gem_dir}/autofilter"
|
15
|
+
autoload :PageSetup, "#{gem_dir}/page_setup"
|
16
|
+
|
17
|
+
# manual
|
18
|
+
autoload :Sheet, "#{gem_dir}/generators/sheet"
|
19
|
+
autoload :SheetRels, "#{gem_dir}/generators/sheet_rels"
|
20
|
+
autoload :Image, "#{gem_dir}/generators/image"
|
21
|
+
|
22
|
+
# generators
|
23
|
+
autoload :App, "#{gem_dir}/generators/app"
|
24
|
+
autoload :ContentTypes, "#{gem_dir}/generators/content_types"
|
25
|
+
autoload :DocProps, "#{gem_dir}/generators/doc_props"
|
26
|
+
autoload :Rels, "#{gem_dir}/generators/rels"
|
27
|
+
autoload :Styles, "#{gem_dir}/generators/styles"
|
28
|
+
autoload :Workbook, "#{gem_dir}/generators/workbook"
|
29
|
+
autoload :WorkbookRels, "#{gem_dir}/generators/workbook_rels"
|
30
|
+
autoload :VmlDrawing, "#{gem_dir}/generators/vml_drawing"
|
31
|
+
autoload :VmlDrawingRels, "#{gem_dir}/generators/vml_drawing_rels"
|
32
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'fast_xs'
|
2
|
+
|
3
|
+
module XlsxWriter
|
4
|
+
class Cell
|
5
|
+
class << self
|
6
|
+
# TODO make a class for this
|
7
|
+
def excel_type(calculated_type)
|
8
|
+
case calculated_type
|
9
|
+
when :String
|
10
|
+
:inlineStr
|
11
|
+
when :Number, :Date, :Currency
|
12
|
+
:n
|
13
|
+
when :Boolean
|
14
|
+
:b
|
15
|
+
else
|
16
|
+
raise ::ArgumentError, "Unknown cell type #{k}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO make a class for this
|
21
|
+
def excel_style_number(calculated_type)
|
22
|
+
case calculated_type
|
23
|
+
when :String
|
24
|
+
0
|
25
|
+
when :Number
|
26
|
+
3
|
27
|
+
when :Currency
|
28
|
+
1
|
29
|
+
when :Date
|
30
|
+
2
|
31
|
+
when :Boolean
|
32
|
+
0 # todo
|
33
|
+
else
|
34
|
+
raise ::ArgumentError, "Unknown cell type #{k}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def excel_column_letter(i)
|
39
|
+
result = []
|
40
|
+
while i >= 26 do
|
41
|
+
result << ABC[i % 26]
|
42
|
+
i /= 26
|
43
|
+
end
|
44
|
+
result << ABC[result.empty? ? i : i - 1]
|
45
|
+
result.reverse.join
|
46
|
+
end
|
47
|
+
|
48
|
+
def excel_string(value)
|
49
|
+
value.to_s.fast_xs
|
50
|
+
end
|
51
|
+
|
52
|
+
def excel_number(value)
|
53
|
+
str = value.to_s.dup
|
54
|
+
unless str =~ /\A[0-9\.\-]*\z/
|
55
|
+
raise ::ArgumentError, %{Bad value "#{value}" Only numbers and dots (.) allowed in number fields}
|
56
|
+
end
|
57
|
+
str.fast_xs
|
58
|
+
end
|
59
|
+
|
60
|
+
alias :excel_currency :excel_number
|
61
|
+
|
62
|
+
# doesn't necessarily work for times yet
|
63
|
+
JAN_1_1900 = ::Time.parse('1900-01-01')
|
64
|
+
def excel_date(value)
|
65
|
+
if value.is_a?(::String)
|
66
|
+
((::Time.parse(str) - JAN_1_1900) / 86_400).round
|
67
|
+
elsif value.respond_to?(:to_date)
|
68
|
+
(value.to_date - JAN_1_1900.to_date).to_i
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def excel_boolean(value)
|
73
|
+
value ? 1 : 0
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
ABC = ('A'..'Z').to_a
|
78
|
+
|
79
|
+
attr_reader :row
|
80
|
+
attr_reader :data
|
81
|
+
|
82
|
+
def initialize(row, data)
|
83
|
+
@row = row
|
84
|
+
@data = data.is_a?(::Hash) ? data.symbolize_keys : data
|
85
|
+
end
|
86
|
+
|
87
|
+
# width = Truncate([{Number of Characters} * {Maximum Digit Width} + {5 pixel padding}]/{Maximum Digit Width}*256)/256
|
88
|
+
# Using the Calibri font as an example, the maximum digit width of 11 point font size is 7 pixels (at 96 dpi). In fact, each digit is the same width for this font. Therefore if the cell width is 8 characters wide, the value of this attribute shall be Truncate([8*7+5]/7*256)/256 = 8.7109375.
|
89
|
+
MAX_DIGIT_WIDTH = 5
|
90
|
+
MAX_REASONABLE_WIDTH = 75
|
91
|
+
def pixel_width
|
92
|
+
@pixel_width ||= [
|
93
|
+
((character_width.to_f*MAX_DIGIT_WIDTH+5)/MAX_DIGIT_WIDTH*256)/256,
|
94
|
+
MAX_REASONABLE_WIDTH
|
95
|
+
].min
|
96
|
+
end
|
97
|
+
|
98
|
+
DATE_LENGTH = 'YYYY-MM-DD'.length
|
99
|
+
BOOLEAN_LENGTH = 'FALSE'.length
|
100
|
+
def character_width
|
101
|
+
@character_width ||= case calculated_type
|
102
|
+
when :String
|
103
|
+
value.to_s.length
|
104
|
+
when :Number
|
105
|
+
# -1000000.5
|
106
|
+
len = value.round(2).to_s.length
|
107
|
+
len += 1 if value < 0
|
108
|
+
len
|
109
|
+
when :Currency
|
110
|
+
# (1,000,000.50)
|
111
|
+
len = value.round(2).to_s.length + ::Math.log(value.abs, 1_000).floor
|
112
|
+
len += 2 if value < 0
|
113
|
+
len
|
114
|
+
when :Date
|
115
|
+
DATE_LENGTH
|
116
|
+
when :Boolean
|
117
|
+
BOOLEAN_LENGTH
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def unstyled?
|
122
|
+
!styled?
|
123
|
+
end
|
124
|
+
|
125
|
+
def styled?
|
126
|
+
data.is_a?(::Hash)
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_xml
|
130
|
+
if value.blank?
|
131
|
+
%{<c r="#{excel_column_letter}#{row.ndx}" s="0" t="inlineStr" />}
|
132
|
+
elsif excel_type == :inlineStr
|
133
|
+
%{<c r="#{excel_column_letter}#{row.ndx}" s="#{excel_style_number}" t="#{excel_type}"><is><t>#{excel_value}</t></is></c>}
|
134
|
+
else
|
135
|
+
%{<c r="#{excel_column_letter}#{row.ndx}" s="#{excel_style_number}" t="#{excel_type}"><v>#{excel_value}</v></c>}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# 0 -> A (zero based!)
|
140
|
+
def excel_column_letter
|
141
|
+
Cell.excel_column_letter row.cells.index(self)
|
142
|
+
end
|
143
|
+
|
144
|
+
# detect dates here, even if we're not styled
|
145
|
+
def excel_type
|
146
|
+
Cell.excel_type calculated_type
|
147
|
+
end
|
148
|
+
|
149
|
+
def excel_style_number
|
150
|
+
Cell.excel_style_number calculated_type
|
151
|
+
end
|
152
|
+
|
153
|
+
def calculated_type
|
154
|
+
@calculated_type ||= if styled?
|
155
|
+
data[:type]
|
156
|
+
elsif value.is_a?(::Date)
|
157
|
+
:Date
|
158
|
+
elsif value.is_a?(::Numeric)
|
159
|
+
:Number
|
160
|
+
else
|
161
|
+
:String
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def value
|
166
|
+
styled? ? data[:value] : data
|
167
|
+
end
|
168
|
+
|
169
|
+
def excel_value
|
170
|
+
Cell.send "excel_#{calculated_type.to_s.underscore}", value
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module XlsxWriter
|
4
|
+
class Document
|
5
|
+
class << self
|
6
|
+
def auto
|
7
|
+
::Dir[::File.expand_path('../generators/*.rb', __FILE__)].map do |path|
|
8
|
+
XlsxWriter.const_get ::File.basename(path, '.rb').camelcase
|
9
|
+
end.reject do |klass|
|
10
|
+
klass.const_defined?(:AUTO) and klass.const_get(:AUTO) == false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_sheet(name)
|
16
|
+
raise ::RuntimeError, "Can't add sheet, already generated!" if generated?
|
17
|
+
sheet = Sheet.new self, name
|
18
|
+
sheets << sheet
|
19
|
+
sheet
|
20
|
+
end
|
21
|
+
|
22
|
+
def page_setup
|
23
|
+
@page_setup ||= PageSetup.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def header_footer
|
27
|
+
@header_footer ||= HeaderFooter.new self
|
28
|
+
end
|
29
|
+
|
30
|
+
delegate :header, :footer, :to => :header_footer
|
31
|
+
|
32
|
+
def add_image(path, width, height)
|
33
|
+
raise ::RuntimeError, "Can't add image, already generated!" if generated?
|
34
|
+
image = Image.new self, path, width, height
|
35
|
+
images << image
|
36
|
+
image
|
37
|
+
end
|
38
|
+
|
39
|
+
def path
|
40
|
+
generate unless generated?
|
41
|
+
@path
|
42
|
+
end
|
43
|
+
|
44
|
+
def cleanup
|
45
|
+
::File.unlink(@path) if ::File.exist?(@path)
|
46
|
+
::FileUtils.rm_rf(@staging_dir) if ::File.exist?(@staging_dir)
|
47
|
+
@path = nil
|
48
|
+
@staging_dir = nil
|
49
|
+
@generated = false
|
50
|
+
end
|
51
|
+
|
52
|
+
def sheets #:nodoc:
|
53
|
+
@sheets ||= []
|
54
|
+
end
|
55
|
+
|
56
|
+
def images
|
57
|
+
@images ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
def staging_dir
|
61
|
+
@staging_dir ||= Utils.tmp_path
|
62
|
+
::FileUtils.mkdir_p @staging_dir
|
63
|
+
@staging_dir
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def generate
|
69
|
+
sheets.each(&:generate)
|
70
|
+
images.each(&:generate)
|
71
|
+
Document.auto.each do |part|
|
72
|
+
part.new(self).generate
|
73
|
+
end
|
74
|
+
@path = Utils.zip staging_dir
|
75
|
+
@generated = true
|
76
|
+
end
|
77
|
+
|
78
|
+
def generated?
|
79
|
+
@generated == true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
|
3
|
+
<HeadingPairs>
|
4
|
+
<vt:vector size="2" baseType="variant">
|
5
|
+
<vt:variant>
|
6
|
+
<vt:lpstr>Worksheets</vt:lpstr>
|
7
|
+
</vt:variant>
|
8
|
+
<vt:variant>
|
9
|
+
<vt:i4>1</vt:i4>
|
10
|
+
</vt:variant>
|
11
|
+
</vt:vector>
|
12
|
+
</HeadingPairs>
|
13
|
+
</Properties>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
3
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
4
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
5
|
+
<Default Extension="vml" ContentType="application/vnd.openxmlformats-officedocument.vmlDrawing"/>
|
6
|
+
<Default Extension="emf" ContentType="image/x-emf"/>
|
7
|
+
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
|
8
|
+
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
|
9
|
+
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
|
10
|
+
<% document.sheets.each do |sheet| %>
|
11
|
+
<Override PartName="<%= sheet.absolute_path %>" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
|
12
|
+
<% end %>
|
13
|
+
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
|
14
|
+
</Types>
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
3
|
+
</cp:coreProperties>
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
module XlsxWriter
|
3
|
+
class Image < ::Struct.new(:document, :original_path, :width, :height, :lcr, :croptop, :cropleft)
|
4
|
+
|
5
|
+
AUTO = false
|
6
|
+
|
7
|
+
def to_xml
|
8
|
+
<<-EOS
|
9
|
+
<v:shape id="#{id}" o:spid="#{o_spid}" type="#_x0000_t75" style="position:absolute;margin-left:0;margin-top:0;width:#{width}pt;height:#{height}pt;z-index:1">
|
10
|
+
<v:imagedata o:relid="#{rid}" o:title="#{o_title}" croptop=#{croptop} cropleft=#{cropleft}/>
|
11
|
+
<o:lock v:ext="edit" rotation="t"/>
|
12
|
+
</v:shape>
|
13
|
+
EOS
|
14
|
+
end
|
15
|
+
|
16
|
+
def croptop
|
17
|
+
self[:croptop] || 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def cropleft
|
21
|
+
self[:cropleft] || 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def id
|
25
|
+
if lcr
|
26
|
+
lcr.image_id
|
27
|
+
else
|
28
|
+
o_spid #?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate
|
33
|
+
::FileUtils.cp original_path, staging_path
|
34
|
+
end
|
35
|
+
|
36
|
+
def ndx
|
37
|
+
document.images.index(self) + 1
|
38
|
+
end
|
39
|
+
|
40
|
+
def rid
|
41
|
+
"rId#{ndx}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def o_title
|
45
|
+
::File.basename(original_path)
|
46
|
+
end
|
47
|
+
|
48
|
+
def o_spid
|
49
|
+
"_x0000_s#{1025+ndx}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def absolute_path
|
53
|
+
"/#{relative_path}"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def relative_path
|
59
|
+
"xl/media/image#{ndx}.emf"
|
60
|
+
end
|
61
|
+
|
62
|
+
def staging_path
|
63
|
+
p = ::File.join document.staging_dir, relative_path
|
64
|
+
::FileUtils.mkdir_p ::File.dirname(p)
|
65
|
+
p
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
3
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="/xl/workbook.xml"/>
|
4
|
+
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="/docProps/core.xml"/>
|
5
|
+
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="/docProps/app.xml"/>
|
6
|
+
</Relationships>
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'fast_xs'
|
2
|
+
|
3
|
+
module XlsxWriter
|
4
|
+
class Sheet < Xml
|
5
|
+
class << self
|
6
|
+
def excel_name(value)
|
7
|
+
str = value.to_s.dup
|
8
|
+
str.gsub! '/', '' # remove forward slashes
|
9
|
+
str.gsub! /\s+/, '' # compress "inner" whitespace
|
10
|
+
str.strip! # trim whitespace from ends
|
11
|
+
str.fast_xs
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
AUTO = false
|
16
|
+
|
17
|
+
attr_reader :name
|
18
|
+
|
19
|
+
def initialize(document, name)
|
20
|
+
@document = document
|
21
|
+
@name = Sheet.excel_name name
|
22
|
+
end
|
23
|
+
|
24
|
+
def ndx
|
25
|
+
document.sheets.index(self) + 1
|
26
|
+
end
|
27
|
+
|
28
|
+
# +1 because styles.xml occupies the first spot
|
29
|
+
def rid
|
30
|
+
"rId#{ndx + 1}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def relative_path
|
34
|
+
"xl/worksheets/sheet#{ndx}.xml"
|
35
|
+
end
|
36
|
+
|
37
|
+
def absolute_path
|
38
|
+
"/#{relative_path}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def autofilters
|
42
|
+
@autofilters ||= []
|
43
|
+
end
|
44
|
+
|
45
|
+
# specify range like "A1:C1"
|
46
|
+
def add_autofilter(range)
|
47
|
+
raise ::RuntimeError, "Can't add autofilter, already generated!" if generated?
|
48
|
+
autofilters << Autofilter.new(range)
|
49
|
+
end
|
50
|
+
|
51
|
+
def rows
|
52
|
+
@rows ||= []
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_row(data)
|
56
|
+
raise ::RuntimeError, "Can't add row, already generated!" if generated?
|
57
|
+
row = Row.new self, data
|
58
|
+
rows << row
|
59
|
+
row
|
60
|
+
end
|
61
|
+
|
62
|
+
# override Xml method to save memory
|
63
|
+
def generate
|
64
|
+
@path = staging_path
|
65
|
+
::File.open(@path, 'wb') do |out|
|
66
|
+
to_file out
|
67
|
+
end
|
68
|
+
Utils.unix2dos @path
|
69
|
+
SheetRels.new(document, self).generate
|
70
|
+
@generated = true
|
71
|
+
end
|
72
|
+
|
73
|
+
delegate :header_footer, :page_setup, :to => :document
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# not using ERB to save memory
|
78
|
+
def to_file(f)
|
79
|
+
f.puts <<-EOS
|
80
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
81
|
+
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
82
|
+
<cols>
|
83
|
+
EOS
|
84
|
+
(0..max_length-1).each do |x|
|
85
|
+
f.puts %{<col min="#{x+1}" max="#{x+1}" width="#{max_cell_width(x)}" bestFit="1" customWidth="1" />}
|
86
|
+
end
|
87
|
+
f.puts %{</cols>}
|
88
|
+
f.puts %{<sheetData>}
|
89
|
+
rows.each { |row| f.puts row.to_xml }
|
90
|
+
f.puts %{</sheetData>}
|
91
|
+
autofilters.each { |autofilter| f.puts autofilter.to_xml }
|
92
|
+
f.puts page_setup.to_xml
|
93
|
+
f.puts header_footer.to_xml
|
94
|
+
f.puts %{</worksheet>}
|
95
|
+
end
|
96
|
+
|
97
|
+
def max_length
|
98
|
+
rows.max_by { |row| row.length }.length
|
99
|
+
end
|
100
|
+
|
101
|
+
def max_cell_width(x)
|
102
|
+
rows.max_by { |row| row.cell_width(x) }.cell_width(x)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
3
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" Target="/xl/drawings/vmlDrawing1.vml"/>
|
4
|
+
</Relationships>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module XlsxWriter
|
2
|
+
class SheetRels < Xml
|
3
|
+
|
4
|
+
AUTO = false
|
5
|
+
|
6
|
+
attr_reader :sheet
|
7
|
+
|
8
|
+
def initialize(document, sheet)
|
9
|
+
@document = document
|
10
|
+
@sheet = sheet
|
11
|
+
end
|
12
|
+
|
13
|
+
def relative_path
|
14
|
+
"xl/worksheets/_rels/sheet#{sheet.ndx}.xml.rels"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
3
|
+
<fonts count="1">
|
4
|
+
<font>
|
5
|
+
<sz val="10"/>
|
6
|
+
<name val="Arial"/>
|
7
|
+
</font>
|
8
|
+
</fonts>
|
9
|
+
|
10
|
+
<fills count="1">
|
11
|
+
<fill />
|
12
|
+
</fills>
|
13
|
+
|
14
|
+
<borders count="1">
|
15
|
+
<border />
|
16
|
+
</borders>
|
17
|
+
|
18
|
+
<cellStyleXfs count="2">
|
19
|
+
<!-- general -->
|
20
|
+
<xf />
|
21
|
+
<!-- currency -->
|
22
|
+
<xf builtinId="4" />
|
23
|
+
</cellStyleXfs>
|
24
|
+
|
25
|
+
<cellXfs count="4">
|
26
|
+
<!-- general -->
|
27
|
+
<xf numFmtId="0" fontId="0">
|
28
|
+
<alignment vertical="top" horizontal="left" />
|
29
|
+
</xf>
|
30
|
+
<!-- currency (really accounting) -->
|
31
|
+
<xf numFmtId="39" fontId="0" xfId="1">
|
32
|
+
<alignment vertical="top" horizontal="right" />
|
33
|
+
</xf>
|
34
|
+
<!-- date -->
|
35
|
+
<xf numFmtId="14" fontId="0">
|
36
|
+
<alignment vertical="top" horizontal="left" />
|
37
|
+
</xf>
|
38
|
+
<!-- number -->
|
39
|
+
<xf numFmtId="0" fontId="0">
|
40
|
+
<alignment vertical="top" horizontal="right" />
|
41
|
+
</xf>
|
42
|
+
</cellXfs>
|
43
|
+
</styleSheet>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:mv="http://macVmlSchemaUri">
|
3
|
+
<% document.images.each do |image| %>
|
4
|
+
<%= image.to_xml %>
|
5
|
+
<% end %>
|
6
|
+
</xml>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
3
|
+
<% document.images.each do |image| %>
|
4
|
+
<Relationship Id="<%= image.rid %>" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="<%= image.absolute_path %>"/>
|
5
|
+
<% end %>
|
6
|
+
</Relationships>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
3
|
+
<workbookPr date1904="0" />
|
4
|
+
<bookViews>
|
5
|
+
<workbookView xWindow="0" yWindow="0" windowWidth="22667" windowHeight="17000" tabRatio="500"/>
|
6
|
+
</bookViews>
|
7
|
+
<sheets>
|
8
|
+
<% document.sheets.each do |sheet| %>
|
9
|
+
<sheet name="<%= sheet.name %>" sheetId="<%= sheet.ndx %>" r:id="<%= sheet.rid %>"/>
|
10
|
+
<% end %>
|
11
|
+
</sheets>
|
12
|
+
</workbook>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
3
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="/xl/styles.xml"/>
|
4
|
+
<% document.sheets.each do |sheet| %>
|
5
|
+
<Relationship Id="<%= sheet.rid %>" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="<%= sheet.absolute_path %>"/>
|
6
|
+
<% end %>
|
7
|
+
</Relationships>
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module XlsxWriter
|
2
|
+
class HeaderFooter < ::Struct.new(:document, :header, :footer)
|
3
|
+
def header
|
4
|
+
self[:header] ||= H.new self
|
5
|
+
end
|
6
|
+
|
7
|
+
def footer
|
8
|
+
self[:footer] ||= F.new self
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_xml
|
12
|
+
lines = []
|
13
|
+
lines << %{<headerFooter>}
|
14
|
+
lines << header.to_xml
|
15
|
+
lines << footer.to_xml
|
16
|
+
lines << %{</headerFooter>}
|
17
|
+
if header.has_image? or footer.has_image?
|
18
|
+
lines << %{<legacyDrawingHF r:id="rId1"/>}
|
19
|
+
end
|
20
|
+
lines.join("\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
class HF < ::Struct.new(:header_footer, :left, :center, :right)
|
24
|
+
def left
|
25
|
+
self[:left] ||= L.new self
|
26
|
+
end
|
27
|
+
|
28
|
+
def center
|
29
|
+
self[:center] ||= C.new self
|
30
|
+
end
|
31
|
+
|
32
|
+
def right
|
33
|
+
self[:right] ||= R.new self
|
34
|
+
end
|
35
|
+
|
36
|
+
def hf
|
37
|
+
self.class.name.demodulize
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_xml
|
41
|
+
%{<#{tag}>#{parts.map(&:to_s).join}</#{tag}>}
|
42
|
+
end
|
43
|
+
|
44
|
+
def parts
|
45
|
+
[left,center,right].select(&:present?)
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_image?
|
49
|
+
parts.any?(&:has_image?)
|
50
|
+
end
|
51
|
+
|
52
|
+
class LCR < ::Struct.new(:hf, :contents)
|
53
|
+
FONT = %{"Arial,Regular"}
|
54
|
+
SIZE = 10
|
55
|
+
|
56
|
+
def present?
|
57
|
+
contents.present?
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_image?
|
61
|
+
::Array.wrap(contents).any? { |v| v.is_a?(XlsxWriter::Image) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def lcr
|
65
|
+
self.class.name.demodulize
|
66
|
+
end
|
67
|
+
|
68
|
+
def image_id
|
69
|
+
[ lcr, hf.hf ].join
|
70
|
+
end
|
71
|
+
|
72
|
+
def render
|
73
|
+
out = case contents
|
74
|
+
when :page_x_of_y
|
75
|
+
'Page &P of &N'
|
76
|
+
when ::Array
|
77
|
+
contents.map do |v|
|
78
|
+
case v
|
79
|
+
when XlsxWriter::Image
|
80
|
+
v.lcr = self
|
81
|
+
'&G'
|
82
|
+
else
|
83
|
+
v
|
84
|
+
end
|
85
|
+
end.join
|
86
|
+
when XlsxWriter::Image
|
87
|
+
contents.lcr = self
|
88
|
+
'&G'
|
89
|
+
else
|
90
|
+
contents
|
91
|
+
end
|
92
|
+
"K000000#{out}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_s
|
96
|
+
[ '', lcr, FONT, SIZE, render ].join('&')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class L < LCR; end
|
101
|
+
class C < LCR; end
|
102
|
+
class R < LCR; end
|
103
|
+
end
|
104
|
+
|
105
|
+
class H < HF
|
106
|
+
def tag
|
107
|
+
'oddHeader'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
class F < HF
|
111
|
+
def tag
|
112
|
+
'oddFooter'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module XlsxWriter
|
2
|
+
class PageSetup < ::Struct.new(:top, :right, :bottom, :left, :header, :footer, :orientation, :vertical_dpi, :horizontal_dpi)
|
3
|
+
def top
|
4
|
+
self[:top] || 1.0
|
5
|
+
end
|
6
|
+
|
7
|
+
def right
|
8
|
+
self[:right] || 0.75
|
9
|
+
end
|
10
|
+
|
11
|
+
def bottom
|
12
|
+
self[:bottom] || 1.0
|
13
|
+
end
|
14
|
+
|
15
|
+
def left
|
16
|
+
self[:left] || 0.75
|
17
|
+
end
|
18
|
+
|
19
|
+
def header
|
20
|
+
self[:header] || 0.5
|
21
|
+
end
|
22
|
+
|
23
|
+
def footer
|
24
|
+
self[:footer] || 0.5
|
25
|
+
end
|
26
|
+
|
27
|
+
def orientation
|
28
|
+
self[:orientation] || 'landscape'
|
29
|
+
end
|
30
|
+
|
31
|
+
def vertical_dpi
|
32
|
+
self[:vertical_dpi] || 4294967292
|
33
|
+
end
|
34
|
+
|
35
|
+
def horizontal_dpi
|
36
|
+
self[:horizontal_dpi] || 4294967292
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_xml
|
40
|
+
%{<pageMargins left="#{left}" right="#{right}" top="#{top}" bottom="#{bottom}" header="#{header}" footer="#{footer}"/><pageSetup orientation="#{orientation}" horizontalDpi="#{horizontal_dpi}" verticalDpi="#{vertical_dpi}"/>}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module XlsxWriter
|
2
|
+
class Row
|
3
|
+
attr_reader :sheet
|
4
|
+
attr_reader :cells
|
5
|
+
|
6
|
+
def initialize(sheet, columns)
|
7
|
+
@sheet = sheet
|
8
|
+
@cells = columns.map do |column|
|
9
|
+
Cell.new self, column
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def ndx
|
14
|
+
sheet.rows.index(self) + 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def length
|
18
|
+
cells.length
|
19
|
+
end
|
20
|
+
|
21
|
+
def cell_width(x)
|
22
|
+
if cell = cells[x]
|
23
|
+
cell.pixel_width
|
24
|
+
else
|
25
|
+
0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_xml
|
30
|
+
ary = []
|
31
|
+
ary << %{<row r="#{ndx}">}
|
32
|
+
cells.each do |cell|
|
33
|
+
ary << cell.to_xml
|
34
|
+
end
|
35
|
+
ary << %{</row>}
|
36
|
+
ary.join
|
37
|
+
end
|
38
|
+
|
39
|
+
extend ::ActiveSupport::Memoizable
|
40
|
+
memoize :cell_width
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
require 'posix/spawn'
|
4
|
+
|
5
|
+
module XlsxWriter
|
6
|
+
module Utils
|
7
|
+
def self.tmp_path(basename = nil, extname = nil)
|
8
|
+
::Kernel.srand
|
9
|
+
::File.join ::Dir.tmpdir, "XlsxWriter-#{basename}#{::Kernel.rand(99999999)}#{extname ? ".#{extname}" : ''}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# zip -r -q #{filename} .
|
13
|
+
def self.zip(src_dir)
|
14
|
+
out_path = tmp_path('zip', 'zip')
|
15
|
+
child = ::POSIX::Spawn::Child.new 'zip', '--recurse-paths', out_path, '.', :chdir => src_dir
|
16
|
+
if child.success?
|
17
|
+
out_path
|
18
|
+
else
|
19
|
+
raise ::RuntimeError, child.err
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# use awk to convert [CR]LF to CRLF
|
24
|
+
def self.unix2dos(path)
|
25
|
+
out_path = tmp_path
|
26
|
+
::File.open(out_path, 'wb') do |out|
|
27
|
+
pid = ::POSIX::Spawn.spawn 'awk', '{ sub(/\r?$/,"\r"); print }', path, :out => out
|
28
|
+
::Process.waitpid pid
|
29
|
+
end
|
30
|
+
::FileUtils.mv out_path, path
|
31
|
+
path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module XlsxWriter
|
5
|
+
class Xml
|
6
|
+
attr_reader :document
|
7
|
+
|
8
|
+
def initialize(document)
|
9
|
+
@document = document
|
10
|
+
end
|
11
|
+
|
12
|
+
def path
|
13
|
+
generate unless generated?
|
14
|
+
@path
|
15
|
+
end
|
16
|
+
|
17
|
+
def generated?
|
18
|
+
@generated == true
|
19
|
+
end
|
20
|
+
|
21
|
+
def staging_path
|
22
|
+
p = ::File.join document.staging_dir, relative_path
|
23
|
+
::FileUtils.mkdir_p ::File.dirname(p)
|
24
|
+
p
|
25
|
+
end
|
26
|
+
|
27
|
+
def template_path
|
28
|
+
::File.expand_path "../generators/#{self.class.name.demodulize.underscore}.erb", __FILE__
|
29
|
+
end
|
30
|
+
|
31
|
+
def render
|
32
|
+
::ERB.new(::File.read(template_path), nil, '<>').result(binding)
|
33
|
+
end
|
34
|
+
|
35
|
+
def generate
|
36
|
+
@path = staging_path
|
37
|
+
::File.open(@path, 'wb') do |out|
|
38
|
+
out.write render
|
39
|
+
end
|
40
|
+
Utils.unix2dos @path
|
41
|
+
@generated = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xlsx_writer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dee Zsombor
|
9
|
+
- Justin Beck
|
10
|
+
- Seamus Abshere
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2011-12-06 00:00:00.000000000Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activesupport
|
18
|
+
requirement: &2166102660 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ! '>='
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *2166102660
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fast_xs
|
29
|
+
requirement: &2166101620 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.7.3
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *2166101620
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: posix-spawn
|
40
|
+
requirement: &2166100780 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
type: :runtime
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *2166100780
|
49
|
+
description: Writes XLSX files. Minimal XML and style. Supports autofilters and headers/footers
|
50
|
+
with images and page numbers.
|
51
|
+
email:
|
52
|
+
- seamus@abshere.net
|
53
|
+
executables: []
|
54
|
+
extensions: []
|
55
|
+
extra_rdoc_files: []
|
56
|
+
files:
|
57
|
+
- lib/xlsx_writer/autofilter.rb
|
58
|
+
- lib/xlsx_writer/cell.rb
|
59
|
+
- lib/xlsx_writer/document.rb
|
60
|
+
- lib/xlsx_writer/generators/app.erb
|
61
|
+
- lib/xlsx_writer/generators/app.rb
|
62
|
+
- lib/xlsx_writer/generators/content_types.erb
|
63
|
+
- lib/xlsx_writer/generators/content_types.rb
|
64
|
+
- lib/xlsx_writer/generators/doc_props.erb
|
65
|
+
- lib/xlsx_writer/generators/doc_props.rb
|
66
|
+
- lib/xlsx_writer/generators/image.rb
|
67
|
+
- lib/xlsx_writer/generators/rels.erb
|
68
|
+
- lib/xlsx_writer/generators/rels.rb
|
69
|
+
- lib/xlsx_writer/generators/sheet.rb
|
70
|
+
- lib/xlsx_writer/generators/sheet_rels.erb
|
71
|
+
- lib/xlsx_writer/generators/sheet_rels.rb
|
72
|
+
- lib/xlsx_writer/generators/styles.erb
|
73
|
+
- lib/xlsx_writer/generators/styles.rb
|
74
|
+
- lib/xlsx_writer/generators/vml_drawing.erb
|
75
|
+
- lib/xlsx_writer/generators/vml_drawing.rb
|
76
|
+
- lib/xlsx_writer/generators/vml_drawing_rels.erb
|
77
|
+
- lib/xlsx_writer/generators/vml_drawing_rels.rb
|
78
|
+
- lib/xlsx_writer/generators/workbook.erb
|
79
|
+
- lib/xlsx_writer/generators/workbook.rb
|
80
|
+
- lib/xlsx_writer/generators/workbook_rels.erb
|
81
|
+
- lib/xlsx_writer/generators/workbook_rels.rb
|
82
|
+
- lib/xlsx_writer/header_footer.rb
|
83
|
+
- lib/xlsx_writer/page_setup.rb
|
84
|
+
- lib/xlsx_writer/row.rb
|
85
|
+
- lib/xlsx_writer/utils.rb
|
86
|
+
- lib/xlsx_writer/xml.rb
|
87
|
+
- lib/xlsx_writer.rb
|
88
|
+
- LICENSE
|
89
|
+
- README.markdown
|
90
|
+
- Rakefile
|
91
|
+
homepage: http://github.com/seamusabshere/xlsx_writer
|
92
|
+
licenses: []
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.8.10
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Writes XLSX files. Minimal XML and style. Supports autofilters and headers/footers
|
115
|
+
with images and page numbers.
|
116
|
+
test_files: []
|