2014年7月22日星期二

簡單 Microsoft Excel 2007+ OpenXML 格式

上次提及,利用 XML 來製作簡單的 Microsoft Excel 格式
但由於 Microsoft Excel 2003 或較舊的版本已經不再支援
Microsoft Office 文件亦會以 OpenXML 作為主要格式

Microsoft Office 2007 或以上所使用 OpenXML 其實與 OpenDocument Format 相似
事實上文件結構都是透過一組 XML 資料、樣式等檔案,再壓縮成 Zip 文件
Microsoft Excel 2007 的 OpenXML 的 Zip 最少要提供以下檔案
/[Content_Types].xml
/_rels/.rels
/xl/sharedStrings.xml
/xl/workbook.xml
/xl/_rels/workbook.xml.rels
/xl/worksheets/sheet1.xml

雖然都是以 XML 作為資料儲存,但 Microsoft Office OpenXML 比 OpenDocument Format XML 繁複,而且不容易理解

/[Content_Types].xml 的內容
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
    <Default Extension="bin" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings"/>
    <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
    <Default Extension="xml" ContentType="application/xml"/>
    <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
    <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
    <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
    <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
</Types>

/_rels/.rels 的內容
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
</Relationships>

/xl/workbook.xml 的內容
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
    <sheets>
        <sheet name="Sheet" sheetId="1" r:id="rId1"/>
    </sheets>
</workbook>

/xl/_rels/workbook.xml.rels 的內容
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
    <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
</Relationships>

/xl/sharedStrings.xml 的內容
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <si><‎t><![CDATA[data]]><‎/t></si>
</sst>


以上 5 個 XML 差不多是固定的內容,比較簡單的結構

/[Content_Types].xml
當中包含指定 workbook, worksheet, sharedStrings, style 等資料
若果需要增加更多 worksheet, sharedStrings, style 指向對應的檔案即可

/_rels/.rels
指定 workbook 的 XML ,一般情況只有一個 workbook 的 XML ,但留意 Id 必須是唯一

/xl/workbook.xml
若果過需要加入更多試算表,只需要加入 <sheet/> , Id 必須是唯一

/xl/_rels/workbook.xml.rels
workbook 所使用的 worksheet, sharedStrings, style , Id 必須是唯一
可以加入更多 worksheet, sharedStrings, style 資料

/xl/sharedStrings.xml
OpenXML 將計算表結構與內容資料分開
增加內容資料,只需要增加 <si><t><![CDATA[data]]></t></si> XML 資料
<![CDATA[data]]> 可以將 < 、 > 、 " 、 & 等 XML 特殊字元當作普通字元,避免 XML 出錯

最複雜是 worksheet 及 style 的 XML 格式

/xl/styles.xml 的內容
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
    <fonts>
        <font/>
    </fonts>
    <fills>
        <fill/>
        <fill/>
    </fills>
    <borders>
        <border/>
    </borders>
    <cellXfs>
        <xf/>
    </cellXfs>
</styleSheet>
style 主要分為 4 部分:fonts, fills, borders, cellXfs
fonts 為使用字型、大小等資料
fills 為資料格的顏色
borders 為資料格外框的顏色及風格
cellXfs 為對應資料格將會使用哪一種定義的 font, fill, border 等資料
但在下試驗時發現
fonts, border, cellXfs 必須有一個預設資料 及 fills 必須有兩個預設資料

font 的結構
<font>
    <charset val="136"/>
    <b/>
    <i/>
    <name val="Arial"/>
    <sz val="12"/>
    <color rgb="FF000000"/>
</font>
charset 字集,一般會用 136
b 粗體
i 斜體
name 字型, val 為字型名稱
sz 大小, val 為大小數值
color 文字顏色,rgb 為 16進制 顏色格式,前綴的 FF 為必寫資料

fill 的結構
<fill>
    <patternFill patternType="solid">
        <fgColor rgb="FFFFFFFF"/>
    </patternFill>
</fill>
patternType 填滿的類型,一般會用 solid 即是填滿整個資料格
fgColor 資料格顏色,rgb 為 16進制 顏色格式,前綴的 FF 為必寫資料

border 的結構
<border>
    <left style="thin">
        <color rgb="FF000000"/>
    </left>
    <right style="thin">
        <color rgb="FF000000"/>
    </right>
    <top style="thin">
        <color rgb="FF000000"/>
    </top>
    <bottom style="thin">
        <color rgb="FF000000"/>
    </bottom>
</border>
border 的結構好奇怪
必須依 left, right, top, bottom 的次序定義邊界,否則即使 XML 正確仍然會出錯
style 是邊界風格,主要有: thin (實線) 、 dotted (點線) 、 hair (虛線)
color 對應邊界的顏色,rgb 為 16進制 顏色格式,前綴的 FF 為必寫資料

xf 的結構
<xf quotePrefix="1" fontId="1" applyFont="1" fillId="2" applyFill="1" borderId="1" applyBorder="1" applyAlignment="1">
    <alignment vertical="top" horizontal="left"/>
</xf>
quotePrefix 是強制定義資料格為字串,即前置「'」
fontId 是 font 資料的 index
fillId 是 fill 資料的 index
borderId 是 border 資料的 index
index 由 0 開始
由於 fonts, border 必須有一個預設資料 及 fills 必須有兩個預設資料
因此,理論上 fontId 及 borderId 應該大於 0 , fillId 應該大於 1
vertical 垂直對齊,主要有: top (上) 、 center (中) 、 bottom (下)
horizontal 水平對齊,主要有: left (左) 、 center (中) 、 right (右)
applyFont, applyFill, applyBorder, applyAlignment 是對應各種操作,設定為 1 時為接受該項目

將樣式定義後,便開始製作試算表
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
    <sheetViews>
        <sheetView workbookViewId="0">
            <pane ySplit="1" xSplit="1" topLeftCell="B2" state="frozen"/>
        </sheetView>
    </sheetViews>
    <cols>
        <col min="1" max="1" width="12.5" customWidth="1"/>
    </cols>
    <sheetData>
        <row r="1" ht="75" customHeight="1">
            <c r="A1" s="1" t="s"><v>0</v></c>
            <c r="B1" s="2"/>
        </row>
    </sheetData>
</worksheet>
sheetView 的 pane 用來設定分割位置,但設定卻比想像複雜
state 為分割線的類型,一般為 frozen
ySplit 及 xSplit 分別為分割線的座標
topLeftCell 為分割後可捲動的開始位置,是指定資料格座標,即如例子的 B2
複雜在於欄座標的英文字需要計算

col 為欄的資料
customWidth 設定為 1 時,表示以自訂闊度來設定
width 為欄闊度
min 及 max 為設定欄的 index ,但此 index 以 1 為第 1 欄而不是 0 ,設定指定範圍的欄的資料

sheetData 同樣類似 HTML 的 table 結構
row 的 ht 是列的高度,而 customHeight 設定為 1 時,表示以自訂高度來設定
r 為列的 index ,此 index 以 1 為第 1 列而不是 0

c 又是一個比較奇怪的設定
c 是資料格
r 為資料格的 index ,是使用資料格本身的名字定義,例如 A1, B2 等……與分割位置的格式相同
s 為設定樣式的 index , 來自 /xl/styles.xml 中的 xf 的 index ,由 0 開始
由於第 xf 必須有一個預設資料,所以應該大於 0
t 為類型,s 即是設定資料格的類型為字串
當類型為字串時,用 v 所指定的是來自 /xl/sharedStrings.xml 的 si index ,由 0 開始
整個設定中最奇怪是 col 的 width 及 row 的 ht
width 及 ht 在例子中分別是 12.5 及 75 ,頗為奇怪的數值
原因是在下都以 100 為測試值
發現 width 為 100 時,欄闊為 800px ,而 ht 為 100 時列高為 133px
因此認為假如需要定義欄闊及列高 100px 時,必須將欄闊除以 8 及列乘除以 0.75 才能達到預期中的 px 值
(乘以 0.75 比除以 4/3 簡單一點吧 ?)
所以得出這種奇怪的數值

簡單例子
/xl/sharedStrings.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <si><‎t>row=1,column=A<‎/t></si>
    <si><‎t>row=1,column=B<‎/t></si>
    <si><‎t>row=1,column=C<‎/t></si>
    <si><‎t>row=2,column=A<‎/t></si>
    <si><‎t>row=2,column=B<‎/t></si>
    <si><‎t>row=2,column=C<‎/t></si>
    <si><‎t>row=3,column=A<‎/t></si>
    <si><‎t>row=3,column=B<‎/t></si>
    <si><‎t>row=3,column=C<‎/t></si>
</sst>

/xl/styles.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
    <fonts>
        <font/>
        <font>
            <charset val="136"/>
            <b/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FF000000"/>
        </font>
        <font>
            <charset val="136"/>
            <i/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FFffff00"/>
        </font>
        <font>
            <charset val="136"/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FF00ff00"/>
        </font>
        <font>
            <charset val="136"/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FFff0000"/>
        </font>
        <font>
            <charset val="136"/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FF00ffff"/>
        </font>
        <font>
            <charset val="136"/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FFff00ff"/>
        </font>
        <font>
            <charset val="136"/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FF0000ff"/>
        </font>
        <font>
            <charset val="136"/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FFffffff"/>
        </font>
        <font>
            <charset val="136"/>
            <name val="Arial"/>
            <sz val="8"/>
            <color rgb="FF000000"/>
        </font>
    </fonts>
    <fills>
        <fill/>
        <fill/>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FFffffff"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FF0000ff"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FFff00ff"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FF00ffff"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FFff0000"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FF00ff00"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FFffff00"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FF000000"/>
            </patternFill>
        </fill>
        <fill>
            <patternFill patternType="solid">
                <fgColor rgb="FFffffff"/>
            </patternFill>
        </fill>
    </fills>
    <borders>
        <border/>
        <border>
            <left style="hair">
                <color rgb="FF000000"/>
            </left>
            <right style="dotted">
                <color rgb="FF000000"/>
            </right>
            <top style="thin">
                <color rgb="FF000000"/>
            </top>
            <bottom style="thin">
                <color rgb="FF000000"/>
            </bottom>
        </border>
    </borders>
    <cellXfs>
        <xf/>
        <xf quotePrefix="1" fontId="1" applyFont="1" fillId="2" applyFill="1" applyAlignment="1">
            <alignment vertical="top" horizontal="left"/>
        </xf>
        <xf quotePrefix="1" fontId="2" applyFont="1" fillId="3" applyFill="1" applyAlignment="1">
            <alignment vertical="top" horizontal="center"/>
        </xf>
        <xf quotePrefix="1" fontId="3" applyFont="1" fillId="4" applyFill="1" applyAlignment="1">
            <alignment vertical="top" horizontal="right"/>
        </xf>
        <xf quotePrefix="1" fontId="4" applyFont="1" fillId="5" applyFill="1" applyAlignment="1">
            <alignment vertical="center" horizontal="left"/>
        </xf>
        <xf quotePrefix="1" fontId="5" applyFont="1" fillId="6" applyFill="1" borderId="1" applyBorder="1" applyAlignment="1">
            <alignment vertical="center" horizontal="center"/>
        </xf>
        <xf quotePrefix="1" fontId="6" applyFont="1" fillId="7" applyFill="1" applyAlignment="1">
            <alignment vertical="center" horizontal="right"/>
        </xf>
        <xf quotePrefix="1" fontId="7" applyFont="1" fillId="8" applyFill="1" applyAlignment="1">
            <alignment vertical="bottom" horizontal="left"/>
        </xf>
        <xf quotePrefix="1" fontId="8" applyFont="1" fillId="9" applyFill="1" applyAlignment="1">
            <alignment vertical="bottom" horizontal="center"/>
        </xf>
        <xf quotePrefix="1" fontId="9" applyFont="1" fillId="10" applyFill="1" applyAlignment="1">
            <alignment vertical="bottom" horizontal="right"/>
        </xf>
    </cellXfs>
</styleSheet>

/xl/worksheets/sheet1.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
    <sheetViews>
        <sheetView workbookViewId="0">
            <pane ySplit="1" xSplit="1" topLeftCell="B2" state="frozen"/>
        </sheetView>
    </sheetViews>
    <cols>
        <col min="1" max="1" width="12.5" customWidth="1"/>
        <col min="2" max="2" width="12.5" customWidth="1"/>
        <col min="3" max="3" width="12.5" customWidth="1"/>
    </cols>
    <sheetData>
        <row r="1" ht="75" customHeight="1">
            <c r="A1" s="1" t="s"><v>0</v></c>
            <c r="B1" s="2" t="s"><v>1</v></c>
            <c r="C1" s="3" t="s"><v>2</v></c>
        </row>
        <row r="2" ht="75" customHeight="1">
            <c r="A2" s="4" t="s"><v>3</v></c>
            <c r="B2" s="5" t="s"><v>4</v></c>
            <c r="C2" s="6" t="s"><v>5</v></c>
        </row>
        <row r="3" ht="75" customHeight="1">
            <c r="A3" s="7" t="s"><v>6</v></c>
            <c r="B3" s="8" t="s"><v>7</v></c>
            <c r="C3" s="9" t="s"><v>8</v></c>
        </row>
    </sheetData>
</worksheet>



這麼繁複的 OpenXML 格式其實是在下使用自製的程式所創建

沒有留言 :

發佈留言