require 'cgi'
require 'tmpdir'
require 'zip/zip'
module Ruport
# This class provides Excel output for Ruport's Table controllers.
# It can export to format :
# * Excel 2003 (use spreadsheet/excel gems)
# * Excel 2003 XML
# * Excel 2007 OpenXML
#
# === Rendering Options
# * worksheet_name : Name of the Worksheet
# * Renders
# * xls => Excel 2003 (If spreadsheet/excel no exist use xml format instead)
# * xlsx => Excel 2007 OpenXML
# * xlsxml => Excel 2003 XML
#
class Formatter::XLS < Formatter
renders :xls, :for => [Controller::Row, Controller::Table]
def initialize
Ruport.quiet {
require 'spreadsheet/excel'
}
end
def prepare_table
@xls_row = 0
@tempfile = Tempfile.new('output.xls')
@workbook = Spreadsheet::Excel.new(@tempfile.path)
@worksheet = @workbook.add_worksheet(options.worksheet_name || 'Ruport')
@header_style = options.header_style || @workbook.add_format(:bold => 1, :size => 12)
end
def build_table_header
if options.show_table_headers
table_row { build_cells(data.column_names, @header_style) }
end
end
def build_table_body
data.each do |r|
table_row { build_cells(r) }
end
end
def build_row
table_row{ build_cells(data.to_a) }
end
def table_row
yield
@xls_row += 1
end
def build_cells(values, style = nil)
col = 0
values.each do |value|
if style
@worksheet.write(@xls_row, col, value, style)
else
@worksheet.write(@xls_row, col, value)
end
col += 1
end
end
def finalize_table
@workbook.close
options.io =
if options.tempfile
@tempfile
else
File.read(@tempfile.path)
end
end
end
# Excel 2007 OpenXML
class Formatter::XLSX < Formatter
BLANK_XLSX = File.join(Ruport::Util::BASEDIR, 'example', 'data', 'blank.xlsx')
renders :xlsx, :for => [ Controller::Row, Controller::Table]
def initialize
end
def prepare_table
@xls_row = 0
output << %{
}
data.column_names.size.times {
output << %{ }
}
output << %{}
@strings = []
end
def build_table_header
if options.show_table_headers
table_row { build_cells(data.column_names, 'Heading') }
end
end
def build_table_body
data.each do |r|
table_row { build_cells(r) }
end
end
def build_row
table_row{ build_cells(data.to_a) }
end
def table_row
output << %{ \n}
yield
output << %{
\n}
@xls_row += 1
end
def get_cell_name(row, col)
name = ((col % 26) + 65).chr + row.to_s
name = ((col / 26) + 65).chr + name if (col / 26 != 0)
name
end
def build_cells(values, style = '')
col = 0
values.each do |value|
value = CGI.escapeHTML(value.to_s)
id = @strings.length
@strings.push(value)
output << %{
#{id}
}
col += 1
end
end
def build_strings_file
out = ''
out << %{\n}
@strings.each {|val|
out << %{ #{val}\n}
}
out << %{\n}
out
end
def finalize_table
output << %{
}
@tempfile = Tempfile.new('output.xlsx')
File.open(BLANK_XLSX) { |bo|
@tempfile.print(bo.read(1024)) until bo.eof?
}
@tempfile.close
zip = Zip::ZipFile.open(@tempfile.path)
zip.get_output_stream('xl/worksheets/sheet1.xml') do |cxml|
cxml.write(output)
end
zip.get_output_stream('xl/sharedStrings.xml') do |cxml|
cxml.write(build_strings_file)
end
workbook = %{
}
zip.get_output_stream('xl/workbook.xml') do |cxml|
cxml.write(workbook)
end
zip.close
options.io =
if options.tempfile
@tempfile
else
File.read(@tempfile.path)
end
end
end
# Excel 2003 XML
class Formatter::XLSXML < Formatter
renders :xlsxml, :for => [ Controller::Row, Controller::Table]
def prepare_table
output << %{
}
data.column_names.size.times {
output << %{}
}
end
def build_table_header
if options.show_table_headers
table_row { build_cells(data.column_names, 'Heading') }
end
end
def build_table_body
data.each do |r|
table_row { build_cells(r) }
end
end
def build_row
table_row{ build_cells(data.to_a) }
end
def table_row
output << %{ \n}
yield
output << %{
\n}
end
def build_cells(values, style = '')
values.each do |value|
value = CGI.escapeHTML(value.to_s)
if style.length > 0
output << %{ \n}
else
output << %{ | \n}
end
output << %{ #{value}\n}
output << %{ | \n}
end
end
def finalize_table
output << %{ |
}
@tempfile = Tempfile.new('output.xls')
@tempfile.print(output)
@tempfile.close();
options.io =
if options.tempfile
@tempfile
else
File.read(@tempfile.path)
end
end
end
end