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 << %{