OpenXML ファイルの整形

メモ.Microsoft Office 2007 (Word, Excel, PoerPoint) から,デフォルトの保存形式 (*.docx, *.xlsx, *.pptx) で保存されたファイルは,これまでのバイナリファイルではなく OpenXML と呼ばれる XML ベースのテキストで記述されています.この OpenXML ファイルが実際にどのような構造になっているのかを確認するのは簡単で,ファイル (*.docx, *.xlsx, *.pptx) を zip 解凍ソフトで解凍すれば OK です.

Office 2007形式のファイルも、従来と同様、文書1つに1つのファイルが作成される。しかし実際には、docx、xlsxなどという独自の拡張子が付いているものの、ファイルの実体はZIP形式で複数のファイルをアーカイブしたものだ。

試しに、Word 2007で保存した「*.docx」形式の文書ファイルを1つ用意して、拡張子を「*.zip」に変更してみよう。Windows XPVistaであれば、「*.zip」形式のアーカイブを直接開くことができるので、内容の確認は容易だ。拡張子を変更したものを開いてみると、1つの.docxファイル内部に複数のXML文書と画像データが含まれていることが分かる。

Office 2007新ファイル・フォーマット対策ガイド(1/4) - @IT

Microsoft Office 製品がどのようなファイル構造を用いているのかを解析する場合,これまでのバイナリファイル (*.doc, *.xls, *.ppt) よりも OpenXML ファイル (*.docx, *.xlsx, *.pptx) を見た方が解析しやすいのですが,各 Microsoft Office 製品が吐く OpenXML ファイルには改行コードもインデントもまったく存在しないため,人間が目で追うにはちょっときつい代物です.そんな訳で,Microsoft Office 製品が吐いた OpenXML ファイルを整形するためのコードを記述しました.

#!/bin/ruby -Ku

require 'rexml/document'

class FormatOpenXML
    def initialize()
        @level = 0
    end
    
    def run(element)
        indent = "\t" * @level
        print("#{indent}<")
        print("#{element.expanded_name}")
        element.attributes.each { |key, value|
            print(" #{key}=\"#{value}\"")
        }
        
        if (element.has_elements?)
            puts(">")
            element.each_element { |child|
                @level += 1
                self.run(child)
                @level -= 1
            }
            puts("#{indent}</#{element.expanded_name}>")
        elsif (element.has_text?)
            puts(">#{element.text}</#{element.expanded_name}>")
        else
            puts(" />")
        end
    end
end

document = REXML::Document.new(File.open(ARGV[0]))
puts(document.xml_decl)
FormatOpenXML.new.run(document.root)
before
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office 
テーマ"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" 
lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1>
<a:dk2><a:srgbClr val="1F497D"/></a:dk2><a:lt2><a:srgbClr val="EEECE1"/></a:lt2>
<a:accent1><a:srgbClr val="4F81BD"/></a:accent1><a:accent2><a:srgbClr val="C0504D"/>
</a:accent2><a:accent3><a:srgbClr val="9BBB59"/></a:accent3><a:accent4><a:srgbClr 
・・・
after
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<a:theme name="Office テーマ" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
	<a:themeElements>
		<a:clrScheme name="Office">
			<a:dk1>
				<a:sysClr val="windowText" lastClr="000000" />
			</a:dk1>
			<a:lt1>
				<a:sysClr val="window" lastClr="FFFFFF" />
			</a:lt1>
			<a:dk2>
				<a:srgbClr val="1F497D" />
			</a:dk2>
			<a:lt2>
				<a:srgbClr val="EEECE1" />
			</a:lt2>
			<a:accent1>
				<a:srgbClr val="4F81BD" />
			</a:accent1>
			<a:accent2>
				<a:srgbClr val="C0504D" />
			</a:accent2>
			<a:accent3>
				<a:srgbClr val="9BBB59" />
			</a:accent3>
			<a:accent4>
				<a:srgbClr val="8064A2" />
			</a:accent4>
・・・