環境
rubyXL 3.4.6
なんでエクセルファイルが死んでしまうん?
印刷範囲は workbook.xml
内の definedNames
要素に、定義名が _xlnm.Print_Area
として全シート分定義されます
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook>
<definedNames>
<definedName name="_xlnm.Print_Area" localSheetId="0">シート1!$A$1:$I$53</definedName>
<definedName name="_xlnm.Print_Area" localSheetId="1">シート2!$A$1:$I$53</definedName>
<definedName name="_xlnm.Print_Area" localSheetId="2">シート3!$A$1:$I$53</definedName>
<definedName name="_xlnm.Print_Area" localSheetId="3">シート4!$A$1:$I$53</definedName>
<definedName name="_xlnm.Print_Area" localSheetId="4">シート5!$A$1:$I$53</definedName>
</definedNames>
</workbook>
また、 app.xml
の TitlesOfParts
要素に、 シート名!印刷範囲 の形で記録されます。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties>
<TitlesOfParts>
<vt:vector baseType="lpstr" size="10">
<vt:lpstr>シート1</vt:lpstr>
<vt:lpstr>シート2</vt:lpstr>
<vt:lpstr>シート3</vt:lpstr>
<vt:lpstr>シート4</vt:lpstr>
<vt:lpstr>シート5</vt:lpstr>
<vt:lpstr>シート1!Print_Area</vt:lpstr>
<vt:lpstr>シート2!Print_Area</vt:lpstr>
<vt:lpstr>シート3!Print_Area</vt:lpstr>
<vt:lpstr>シート4!Print_Area</vt:lpstr>
<vt:lpstr>シート5!Print_Area</vt:lpstr>
</vt:vector>
</TitlesOfParts>
</Properties>
しかし、悲しいことに rubyXL では definedName
要素の name
属性の値がそのまま TitlesOfParts
に設定されてしまうのでした。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties>
<TitlesOfParts>
<vt:vector baseType="lpstr" size="10">
<vt:lpstr>シート1</vt:lpstr>
<vt:lpstr>シート2</vt:lpstr>
<vt:lpstr>シート3</vt:lpstr>
<vt:lpstr>シート4</vt:lpstr>
<vt:lpstr>シート5</vt:lpstr>
<vt:lpstr>_xlnm.Print_Area</vt:lpstr>
<vt:lpstr>_xlnm.Print_Area</vt:lpstr>
<vt:lpstr>_xlnm.Print_Area</vt:lpstr>
<vt:lpstr>_xlnm.Print_Area</vt:lpstr>
<vt:lpstr>_xlnm.Print_Area</vt:lpstr>
</vt:vector>
</TitlesOfParts>
</Properties>
ではどうするか
モンキーパッチ!
require 'rubyXL'
module RubyXL::DocumentPropertiesFilePatch
XLNM_PATTERN = /^_xlnm\./
def before_write_xml
workbook = root.workbook
self.heading_pairs = RubyXL::VectorValue.new(:vt_vector => RubyXL::Vector.new(:base_type => 'variant'))
self.titles_of_parts = RubyXL::VectorValue.new(:vt_vector => RubyXL::Vector.new(:base_type => 'lpstr'))
worksheets = chartsheets = 0
workbook.worksheets.each { |sheet|
add_part_title(sheet.sheet_name)
case sheet
when RubyXL::Worksheet then worksheets += 1
when RubyXL::Chartsheet then chartsheets += 1
end
}
add_parts_count('Worksheets', worksheets) if worksheets > 0
add_parts_count('Charts', chartsheets) if chartsheets > 0
if workbook.defined_names then
add_parts_count('Named Ranges', workbook.defined_names.size)
# workbook.defined_names.each { |defined_name| add_part_title(defined_name.name) }
xlnm_defined_names = workbook.defined_names.select {|v| v.name =~ XLNM_PATTERN }
defined_names_without_xlnm = workbook.defined_names - xlnm_defined_names
if xlnm_defined_names.present? then
xlnm_defined_names.each do |defined_name|
sheet_name = /^(.*)!/.match(defined_name.reference)&.values_at(1)&.first
local_sheet_id = workbook.worksheets.index {|v| v.sheet_name == sheet_name }&.to_i if sheet_name.present?
if local_sheet_id.present? then
defined_name.local_sheet_id = local_sheet_id
name = defined_name.name.gsub(XLNM_PATTERN, "#{sheet_name}!")
add_part_title(name)
else
defined_names_without_xlnm << defined_name
end
end
end
defined_names_without_xlnm.each { |defined_name| add_part_title(defined_name.name) }
end
true
end
end
RubyXL::DocumentPropertiesFile.prepend RubyXL::DocumentPropertiesFilePatch
おまけ
下記は、実ファイルを気合で書き換えた場合の diff です
@@ -54,6 +54,8 @@ module RubyXL
end
private :add_part_title
+ XLNM_PATTERN = /^_xlnm\./
+
def before_write_xml
workbook = root.workbook
@@ -75,7 +77,28 @@ module RubyXL
if workbook.defined_names then
add_parts_count('Named Ranges', workbook.defined_names.size)
- workbook.defined_names.each { |defined_name| add_part_title(defined_name.name) }
+ # workbook.defined_names.each { |defined_name| add_part_title(defined_name.name) }
+
+ xlnm_defined_names = workbook.defined_names.select {|v| v.name =~ XLNM_PATTERN }
+ defined_names_without_xlnm = workbook.defined_names - xlnm_defined_names
+
+ if xlnm_defined_names.present? then
+ xlnm_defined_names.each do |defined_name|
+ sheet_name = /^(.*)!/.match(defined_name.reference)&.values_at(1)&.first
+ local_sheet_id = workbook.worksheets.index {|v| v.sheet_name == sheet_name }&.to_i if sheet_name.present?
+ if local_sheet_id.present? then
+ defined_name.local_sheet_id = local_sheet_id
+
+ name = defined_name.name.gsub(XLNM_PATTERN, "#{sheet_name}!")
+ add_part_title(name)
+ else
+ defined_names_without_xlnm << defined_name
+ end
+ end
+ end
+
+ defined_names_without_xlnm.each { |defined_name| add_part_title(defined_name.name) }
end
true
ホントはプルリク出したい。