# -*- encoding: utf-8 -*- # frozen_string_literal: true require 'delegate' require 'write_xlsx/package/xml_writer_simple' module Writexlsx class Sheets < DelegateClass(Array) include Writexlsx::Utility BASE_NAME = { sheet: 'Sheet', chart: 'Chart' } # :nodoc: def initialize super([]) end def chartsheet_count chartsheets.count end def sheetname_count count - chartname_count end def chartname_count chartsheet_count end def make_and_check_sheet_chart_name(type, name) count = sheet_chart_count(type) name = "#{BASE_NAME[type]}#{count + 1}" unless ptrue?(name) check_valid_sheetname(name) name end def write_sheets(writer) writer.tag_elements('sheets') do id_num = 1 each do |sheet| write_sheet(writer, sheet, id_num) id_num += 1 end end end def write_worksheet_files(package_dir) dir = "#{package_dir}/xl/worksheets" worksheets.each_with_index do |sheet, index| write_sheet_files(dir, sheet, index) end end def write_chartsheet_files(package_dir) dir = "#{package_dir}/xl/chartsheets" chartsheets.each_with_index do |sheet, index| write_sheet_files(dir, sheet, index) end end def write_vml_files(package_dir) dir = "#{package_dir}/xl/drawings" index = 1 each do |sheet| next if !sheet.has_vml? and !sheet.has_header_vml? FileUtils.mkdir_p(dir) if sheet.has_vml? vml = Package::Vml.new vml.set_xml_writer("#{dir}/vmlDrawing#{index}.vml") vml.assemble_xml_file( sheet.vml_data_id, sheet.vml_shape_id, sheet.sorted_comments, sheet.buttons_data ) index += 1 end next unless sheet.has_header_vml? vml = Package::Vml.new vml.set_xml_writer("#{dir}/vmlDrawing#{index}.vml") vml.assemble_xml_file( sheet.vml_header_id, sheet.vml_header_id * 1024, [], [], sheet.header_images_data ) write_vml_drawing_rels_files(package_dir, sheet, index) index += 1 end end def write_comment_files(package_dir) self.select { |sheet| sheet.has_comments? } .each_with_index do |sheet, index| FileUtils.mkdir_p("#{package_dir}/xl/drawings") sheet.comments.set_xml_writer("#{package_dir}/xl/comments#{index + 1}.xml") sheet.comments.assemble_xml_file end end def write_table_files(package_dir) unless tables.empty? dir = "#{package_dir}/xl/tables" FileUtils.mkdir_p(dir) tables.each_with_index do |table, index| table.set_xml_writer("#{dir}/table#{index + 1}.xml") table.assemble_xml_file end end end def write_chartsheet_rels_files(package_dir) write_sheet_rels_files_base( chartsheets, "#{package_dir}/xl/chartsheets/_rels", 'sheet' ) end def write_drawing_rels_files(package_dir) dir = "#{package_dir}/xl/drawings/_rels" index = 0 each do |sheet| index += 1 if !sheet.drawing_links[0].empty? || sheet.has_shapes? next if sheet.drawing_links[0].empty? FileUtils.mkdir_p(dir) rels = Package::Relationships.new sheet.drawing_links.each do |drawing_datas| drawing_datas.each do |drawing_data| rels.add_document_relationship(*drawing_data) end end # Create the .rels file such as /xl/drawings/_rels/sheet1.xml.rels. rels.set_xml_writer("#{dir}/drawing#{index}.xml.rels") rels.assemble_xml_file end end def write_vml_drawing_rels_files(package_dir, worksheet, index) # Create the drawing .rels dir. dir = "#{package_dir}/xl/drawings/_rels" FileUtils.mkdir_p(dir) rels = Package::Relationships.new worksheet.vml_drawing_links.each do |drawing_data| rels.add_document_relationship(*drawing_data) end # Create the .rels file such as /xl/drawings/_rels/vmlDrawing1.vml.rels. rels.set_xml_writer("#{dir}/vmlDrawing#{index}.vml.rels") rels.assemble_xml_file end def write_worksheet_rels_files(package_dir) write_sheet_rels_files_base( worksheets, "#{package_dir}/xl/worksheets/_rels", 'sheet' ) end def write_sheet_rels_files_base(sheets, dir, body) sheets.each_with_index do |sheet, index| next if sheet.external_links.empty? FileUtils.mkdir_p(dir) rels = Package::Relationships.new sheet.external_links.each do |link_datas| link_datas.each do |link_data| rels.add_worksheet_relationship(*link_data) end end # Create the .rels file such as /xl/worksheets/_rels/sheet1.xml.rels. rels.set_xml_writer("#{dir}/#{body}#{index + 1}.xml.rels") rels.assemble_xml_file end end def tables inject([]) { |tables, sheet| tables + sheet.tables }.flatten end def tables_count tables.count end def index_by_name(sheetname) name = sheetname.sub(/^'/, '').sub(/'$/, '') collect { |sheet| sheet.name }.index(name) end def worksheets reject { |worksheet| worksheet.is_chartsheet? } end def chartsheets self.select { |worksheet| worksheet.is_chartsheet? } end def visible_first reject { |worksheet| worksheet.hidden? }.first end private def sheet_chart_count(type) case type when :sheet sheetname_count when :chart chartname_count end end def check_valid_sheetname(name) # Check that sheet name is <= 31. Excel limit. raise "Sheetname #{name} must be <= #{SHEETNAME_MAX} chars" if name.length > SHEETNAME_MAX # Check that sheetname doesn't contain any invalid characters invalid_char = %r{[\[\]:*?/\\]} raise 'Invalid character []:*?/\\ in worksheet name: ' + name if name =~ invalid_char # Check that sheetname doesn't start or end with an apostrophe. raise "Worksheet name #{name} cannot start or end with an " if name =~ /^'/ || name =~ /'$/ # Check that the worksheet name doesn't already exist since this is a fatal # error in Excel 97. The check must also exclude case insensitive matches. raise "apostropheWorksheet name '#{name}', with case ignored, is already used." unless is_sheetname_uniq?(name) end def is_sheetname_uniq?(name) each do |worksheet| return false if name.downcase == worksheet.name.downcase end true end def write_sheet_files(dir, sheet, index) FileUtils.mkdir_p(dir) sheet.set_xml_writer("#{dir}/sheet#{index + 1}.xml") sheet.assemble_xml_file end def write_sheet(writer, sheet, sheet_id) # :nodoc: attributes = [ ['name', sheet.name], ['sheetId', sheet_id] ] if sheet.hidden? attributes << %w[state hidden] elsif sheet.very_hidden? attributes << %w[state veryHidden] end attributes << r_id_attributes(sheet_id) writer.empty_tag('sheet', attributes) end end end