# ReportX-ODF擴充
**專案連結**:[ReportX](http://192.168.1.136/roychou/ReportX)
ReportX 擴充可以支援儲存.odf相關格式
ODF包含以下五種格式:
– .odt:對應Word(.doc .docx)
– .ods:對應Excel(.xls .xlsx)
– .odp:對應PowerPoint(.ppt .pptx)
– .odg:對應Visio
– .odb:對應Access
## ODT格式
參考文件-[OpenDocument technical specification](https://en.wikipedia.org/wiki/OpenDocument_technical_specification#Format_internals)
OpenDocument文件通常由包含許多文件和目錄的zip檔組成,可拆解為以下檔案
* XML
* content.xml
* meta.xml
* style.xml
* setting.xml
* 其他檔案
* mimetype
* 資料夾
* META-INF
* manifest.xml
* Thumbnails(縮圖)
* thumbnail.png
以下為主要相關檔案的簡介
* content.xml
最重要的檔案,記錄文檔的實際內容,可單獨存在形成Odt。
* style.xml
記錄文件樣式以及格式,也有一部分樣式會在content.xml中,可結合進content成一個檔案。
* meta.xml
記錄文件數據,例如作者、最後修改者、日期。
* setting.xml
記錄基本係數
## 組成Odt的檔案格式
參考文件-[OASIS OpenDocument](http://docs.oasis-open.org/office/v1.1/OS/OpenDocument-v1.1-html/OpenDocument-v1.1.html#14.1.1.Style%20Mappings|outline)
文件說明組成Odt的content.xml 格式
## 實驗
* doc 更改副檔名 odt: 不可行
* doc 更名成content.xml 壓縮成 odt: 不可行
* odt由xml組成,文字格式不合。
## Word 與 Odt檔的組合差異
Word 形成方法,以類似html語法所組成,形成檔案只需要寫入txt檔,更改附檔名成doc,即可完成,無須壓縮。
Odt 形成方法,採取XML的形式,有一套準則,去表明該如何組成其文件,完成檔案須命名為content.xml,並且需要進行壓縮,才可形成檔案。
**Word語法格式:**
```xml
<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:w="urn:schemas-microsoft-com:office:word"
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"
xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf-8">
<meta name=ProgId content=Word.Document>
<meta name=Generator content="Microsoft Word 12 (filtered)">
<meta name=Originator content="Microsoft Word 12">
<title>知識統計表</title>
<!--[if gte mso 9]><xml>
<o:DocumentProperties>
<o:Subject>IQS Report</o:Subject>
<o:Author>IQS Technology Inc.</o:Author>
<o:Version>12.00</o:Version>
</o:DocumentProperties>
</xml><![endif]-->
<style>
<!--
@font-face
{font-family:新細明體;
panose-1:2 2 5 0 0 0 0 0 0 0;}
@font-face
{font-family:"Times New Roman";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Times New Roman;
panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
{font-family:標楷體;
panose-1:3 0 5 9 0 0 0 0 0 0;}
@font-face
{font-family:"\@新細明體";
panose-1:2 2 5 0 0 0 0 0 0 0;}
@font-face
{font-family:"\@標楷體";
panose-1:3 0 5 9 0 0 0 0 0 0;}
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
margin-bottom:.0001pt;
font-size:9.0pt;
font-family:"Times New Roman","sans-serif";}
p.MsoHeader, li.MsoHeader, div.MsoHeader
{mso-style-link:"頁首 字元";
margin:0cm;
margin-bottom:.0001pt;
layout-grid-mode:char;
font-size:10.0pt;
font-family:"Times New Roman","sans-serif";}
p.MsoFooter, li.MsoFooter, div.MsoFooter
{mso-style-link:"頁尾 字元";
margin:0cm;
margin-bottom:.0001pt;
layout-grid-mode:char;
font-size:10.0pt;
font-family:"Times New Roman","sans-serif";}
span.a
{mso-style-name:"頁首 字元";
mso-style-link:頁首;}
span.a0
{mso-style-name:"頁尾 字元";
mso-style-link:頁尾;}
p
{padding:0cm 0cm 0cm 0cm;
margin:0cm 0.1cm 0cm 0.1cm;
}
td
{font-size:9.0pt;
font-family:新細明體;
padding:0cm 0cm 0cm 0cm;
}
.FmtDate
{mso-number-format:"yyyy\/ mm\/ dd\\ hh\:mm\:ss";}
@page Section1
{size:595.3pt 841.9pt;
font-size:9.0pt;
font-family:新細明體;
margin:2.0cm 2.0cm 2.0cm 2.0cm;}
div.Section1
{page:Section1;}
-->
</style>
</head>
<body lang=ZH-TW style='text-justify-trim:punctuation'>
<div class=Section1>
<p class=MsoNormal align=center style='text-align:center;font-size:18.0pt;font-family:標楷體;margin:0 0 8pt 0'>知識統計表</p>
<p style='text-align:center;font-size:14.0pt;font-family:"Times New Roman","serif";margin:0 0 4pt 0'>2019/07/01 - 2019/07/31 </p>
<p class=MsoNormal align=right style='text-align:right;word-break:break-all;font-size:8.0pt;font-family:"Times New Roman","serif";margin:0 0 0 0'>製表人:brucechen<br />製表時間:2019/08/26 16:38:36</p>
<p style='text-align:left;font-size:12.0pt;font-family:"Times New Roman","serif";margin:0 0 4pt 0'>※ 新增知識 (共 7 筆)</p>
<div align=center>
<table class=MsoNormalTable border=1 cellspacing=0 cellpadding=0 width="100%" style='width:100.0%;border-collapse:collapse;border:none'>
<thead><tr>
<td width=45 style='width:33.75pt;background:#EAF1FD;padding:0cm 5.4pt 0cm 5.4pt'>編號</td>
<td width=132 style='width:99.25pt;background:#EAF1FD;padding:0cm 5.4pt 0cm 5.4pt'>知識目錄</td>
<td width=283 style='width:212.6pt;background:#EAF1FD;padding:0cm 5.4pt 0cm 5.4pt'>知識標題</td>
<td width=104 style='width:78.0pt;background:#EAF1FD;padding:0cm 5.4pt 0cm 5.4pt'>建立時間</td>
<td width=92 style='width:69.1pt;background:#EAF1FD;padding:0cm 5.4pt 0cm 5.4pt'>建立人員</td>
</tr></thead>
<td style='text-align:center'>7310</td>
<td style='text-align:left'>公司部門-公告文件區\品牌服務事業部BSBU\品牌經營服務處(2210)</td>
<td style='text-align:left'>2019年7月 Epson商品銷售活動及售價表通知</td>
<td style='text-align:center'>2019/07/01 10:06</td>
<td style='text-align:center'>鄭央辰 </td>
</tr>
<td style='text-align:center'>7311</td>
<td style='text-align:left'>維修知識區\EPSON 愛普生\04-商用傳真複合機\WF-M5799_M5299\技術通報</td>
<td style='text-align:left'>WF-M5299_M5799_C5290_C5790_C579R 滾輪組異常</td>
<td style='text-align:center'>2019/07/11 13:47</td>
<td style='text-align:center'>蔡長生 </td>
</tr>
<td style='text-align:center'>7312</td>
<td style='text-align:left'>公司規章辦法\規章總表</td>
<td style='text-align:left'>規章總表</td>
<td style='text-align:center'>2019/07/12 15:18</td>
<td style='text-align:center'>游淳茹 </td>
</tr>
<td style='text-align:center'>7313</td>
<td style='text-align:left'>公司部門-公告文件區\品牌服務事業部BSBU\品牌經營服務處(2210)</td>
<td style='text-align:left'>2019 7月大圖新產品教育訓練講義</td>
<td style='text-align:center'>2019/07/15 10:32</td>
<td style='text-align:center'>鄭央辰 </td>
</tr>
<td style='text-align:center'>7314</td>
<td style='text-align:left'>維修知識區\EPSON 愛普生\03-相片複合機\XP-4101\Driver or Firmware</td>
<td style='text-align:left'>XP-4101 韌體FWG510TL_MB13J6</td>
<td style='text-align:center'>2019/07/15 12:03</td>
<td style='text-align:center'>陳正雄 </td>
</tr>
<td style='text-align:center'>7315</td>
<td style='text-align:left'>維修知識區\EPSON 愛普生\02-噴墨印表機\XP-15010\Driver or Firmware</td>
<td style='text-align:left'>XP-15010 韌體 -FWG150TL_NW01J7</td>
<td style='text-align:center'>2019/07/15 12:08</td>
<td style='text-align:center'>陳正雄 </td>
</tr>
<td style='text-align:center'>7316</td>
<td style='text-align:left'>維修知識區\EPSON 愛普生\04-商用傳真複合機\WF-2831\Driver or Firmware</td>
<td style='text-align:left'>WF-2831 韌體 FWG511TL_AL13J6</td>
<td style='text-align:center'>2019/07/15 12:14</td>
<td style='text-align:center'>陳正雄 </td>
</tr>
</table>
<p style='text-align:left;margin:0 0 0 0'>※編號前方若有「◎」,則表示該知識已失效。</p>
</div>
<br>
</div>
</body>
</html>
```
## S5 報表組成
### ODT檔案 xml 組成
為了不求產生多個檔案壓縮,所以只壓縮一個content.xml來形成odt檔
content.xml的基本架構
``` xml=
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<office:document-content xmlns:anim='urn:oasis:names:tc:opendocument:xmlns:animation:1.0' xmlns:chart='urn:oasis:names:tc:opendocument:xmlns:chart:1.0' xmlns:config='urn:oasis:names:tc:opendocument:xmlns:config:1.0' xmlns:db='urn:oasis:names:tc:opendocument:xmlns:database:1.0' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:dr3d='urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0' xmlns:draw='urn:oasis:names:tc:opendocument:xmlns:drawing:1.0' xmlns:fo='urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0' xmlns:form='urn:oasis:names:tc:opendocument:xmlns:form:1.0' xmlns:grddl='http://www.w3.org/2003/g/data-view#' xmlns:math='http://www.w3.org/1998/Math/MathML' xmlns:meta='urn:oasis:names:tc:opendocument:xmlns:meta:1.0' xmlns:number='urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0' xmlns:office='urn:oasis:names:tc:opendocument:xmlns:office:1.0' xmlns:presentation='urn:oasis:names:tc:opendocument:xmlns:presentation:1.0' xmlns:script='urn:oasis:names:tc:opendocument:xmlns:script:1.0' xmlns:smil='urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0' xmlns:style='urn:oasis:names:tc:opendocument:xmlns:style:1.0' xmlns:svg='urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0' xmlns:table='urn:oasis:names:tc:opendocument:xmlns:table:1.0' xmlns:text='urn:oasis:names:tc:opendocument:xmlns:text:1.0' xmlns:xforms='http://www.w3.org/2002/xforms' xmlns:xhtml='http://www.w3.org/1999/xhtml' xmlns:xlink='http://www.w3.org/1999/xlink' office:version='1.2'>
```
接著會是字體的格式語法
``` xml =
<office:font-face-decls>
<style:font-face style:name='Calibri' svg:font-family='Calibri' style:font-family-generic='swiss' style:font-pitch='variable' svg:panose-1='2 15 5 2 2 2 4 3 2 4'/>
<style:font-face style:name='新細明體' svg:font-family='新細明體' style:font-family-generic='roman' style:font-pitch='variable' svg:panose-1='2 2 5 0 0 0 0 0 0 0'/>
<style:font-face style:name='Times New Roman' svg:font-family='Times New Roman' style:font-family-generic='roman' style:font-pitch='variable' svg:panose-1='2 2 6 3 5 4 5 2 3 4'/>
<style:font-face style:name='標楷體' svg:font-family='標楷體' style:font-family-generic='script' style:font-pitch='fixed' svg:panose-1='3 0 5 9 0 0 0 0 0 0'/>
<style:font-face style:name='Calibri Light' svg:font-family='Calibri Light' style:font-family-generic='swiss' style:font-pitch='variable' svg:panose-1='2 15 3 2 2 2 4 3 2 4'/>
</office:font-face-decls>
```
再來則會是文件的樣式設定(類似於css)
一開始的最外層
``` xml =
<office:automatic-styles></office:automatic-styles>
```
以下是樣式的語法
不同屬性值代表設定文件的不同處之樣式屬性
例如:文字會用text、表格用table
* paragraph-properties 設定段落
* text-properties 設定文字
* table-properties 設定表格
* table-cell-properties 設定表格欄位
* table-column 設定欄
* table-row 設定列
* page-layout-properties 設定整個文件頁面(從style.xml轉移而來)
**完整樣式程式碼:**
``` xml
<office:automatic-styles>
<!--字體相關設定-->
<style:style style:name='Title' style:parent-style-name='內文' style:master-page-name='MP0' style:family='paragraph'>
<style:paragraph-properties fo:break-before='page' fo:text-align='center' fo:margin-bottom='0.1111in'/>
<style:text-properties style:font-name='標楷體' style:font-name-asian='標楷體' fo:font-size='18pt' style:font-size-asian='18pt' style:font-size-complex='18pt'/>
</style:style>
<style:style style:name='User' style:parent-style-name='內文' style:family='paragraph'>
<style:paragraph-properties style:line-break='normal' fo:text-align='end'/>
<style:text-properties fo:font-size='8pt' style:font-size-asian='8pt' style:font-size-complex='8pt'/>
</style:style>
<!--表格 欄設定-->
<style:style style:name='TableColumn4' style:family='table-column'>
<style:table-column-properties style:column-width='0.543in'/>
</style:style>
<style:style style:name='TableColumn5' style:family='table-column'>
<style:table-column-properties style:column-width='4.2069in'/>
</style:style>
<style:style style:name='TableColumn6' style:family='table-column'>
<style:table-column-properties style:column-width='0.968in'/>
</style:style>
<style:style style:name='TableColumn7' style:family='table-column'>
<style:table-column-properties style:column-width='0.9638in'/>
</style:style>
<!--表格 本體相關設定-->
<style:style style:name='Table' style:family='table'>
<style:table-properties style:width='6.6819in' fo:margin-left='0in' table:align='center'/>
</style:style>
<style:style style:name='TitleTableRow' style:family='table-row'>
<!--表格 列相關設定-->
<style:table-row-properties/>
</style:style>
<style:style style:name='TableCell' style:family='table-cell'>
<!--表格 欄位相關設定-->
<style:table-cell-properties fo:border='0.0104in outset #000000' fo:background-color='#EAF1FD' style:writing-mode='lr-tb' style:vertical-align='middle' fo:padding-top='0in' fo:padding-left='0.075in' fo:padding-bottom='0in' fo:padding-right='0.075in'/>
</style:style>
<style:style style:name='ContentTableCell' style:family='table-cell'>
<style:table-cell-properties fo:border='0.0104in outset #000000' fo:background-color='#EAF1FD' style:writing-mode='lr-tb' style:vertical-align='middle' fo:padding-top='0in' fo:padding-left='0in' fo:padding-bottom='0in' fo:padding-right='0in'/>
</style:style>
<style:style style:name='Data' style:parent-style-name='內文' style:family='paragraph'>
<style:paragraph-properties fo:text-align='center'/>
<style:text-properties style:font-name='新細明體' style:font-name-complex='新細明體'/>
</style:style>
<style:style style:name='SequenceDataTableCell' style:family='table-cell'>
<style:table-cell-properties fo:border='0.0104in outset #000000' style:writing-mode='lr-tb' style:vertical-align='middle' fo:padding-top='0in' fo:padding-left='0in' fo:padding-bottom='0in' fo:padding-right='0in'/>
</style:style>
<style:style style:name='ContentDataTableCell' style:family='table-cell'>
<style:table-cell-properties fo:border='0.0104in outset #000000' style:writing-mode='lr-tb' style:vertical-align='middle' fo:padding-top='0in' fo:padding-left='0.0416in' fo:padding-bottom='0in' fo:padding-right='0in'/>
</style:style>
<style:style style:name='KnowledgeDataTableCell' style:family='table-cell'>
<style:table-cell-properties fo:border='0.0104in outset #000000' style:writing-mode='lr-tb' style:vertical-align='middle' fo:padding-top='0in' fo:padding-left='0in' fo:padding-bottom='0in' fo:padding-right='0.0416in'/>
</style:style>
<style:style style:name='KnowledgeData' style:parent-style-name='內文' style:family='paragraph'>
<style:paragraph-properties fo:text-align='end' fo:margin-right='0.1666in'/>
<style:text-properties style:font-name='新細明體' style:font-name-complex='新細明體'/>
</style:style>
<style:style style:name='MarkContentData' style:parent-style-name='內文' style:family='paragraph'>
<style:text-properties style:font-name='新細明體' style:font-name-complex='新細明體' fo:color='#FF0000'/>
</style:style>
<style:style style:name='ContentData' style:parent-style-name='內文' style:family='paragraph'>
<style:text-properties style:font-name='新細明體' style:font-name-complex='新細明體'/>
</style:style>
<!-- style.xml 設定 -->
<!-- 此段放在 <office:automatic-styles>內-->
<style:page-layout style:name='PL0'>
<style:page-layout-properties fo:page-width='8.268in' fo:page-height='11.693in' style:print-orientation='portrait' fo:margin-top='0.7875in' fo:margin-left='0.7875in' fo:margin-bottom='0.7875in' fo:margin-right='0.7875in' style:num-format='1' style:writing-mode='lr-tb'>
<style:footnote-sep style:width='0.007in' style:rel-width='33%' style:color='#000000' style:line-style='solid' style:adjustment='left'/>
</style:page-layout-properties>
</style:page-layout>
</office:automatic-styles>
<!-- 此段放在 <office:automatic-styles>外-->
<office:master-styles>
<style:master-page style:name='MP0' style:page-layout-name='PL0'/>
</office:master-styles>
```
接著是資料主體
``` xml
<office:body>
<office:text text:use-soft-page-breaks='true'>
<!-- 文字段落 -->
<text:p text:style-name='Title'>知識數量統計表</text:p>
<text:p text:style-name='User'>
製表人:林家弘<text:line-break/>製表時間:2019/09/03 05:52:04
</text:p>
<!-- 表格 -->
<table:table table:style-name='Table'>
<!-- 表格欄 -->
<table:table-columns>
<table:table-column table:style-name='TableColumn4'/>
<table:table-column table:style-name='TableColumn5'/>
<table:table-column table:style-name='TableColumn6'/>
<table:table-column table:style-name='TableColumn7'/>
</table:table-columns>
<!-- 表格列 -->
<table:table-row table:style-name='TitleTableRow '>
<!-- 表格欄位 -->
<table:table-cell table:style-name='TableCell'>
<text:p text:style-name="Data" >順序</text:p>
</table:table-cell>
<table:table-cell table:style-name='TableCell'>
<text:p text:style-name="Data" >知識目錄</text:p>
</table:table-cell>
<table:table-cell table:style-name='TableCell'>
<text:p text:style-name="Data" >有效知識</text:p>
</table:table-cell>
<table:table-cell table:style-name='TableCell'>
<text:p text:style-name="Data" >無效知識</text:p>
</table:table-cell>
</table:table-row>
<table:table-row table:style-name='TitleTableRow '>
<table:table-cell table:style-name='SequenceDataTableCell'>
<text:p text:style-name="Data" >1</text:p>
</table:table-cell>
<table:table-cell table:style-name='ContentDataTableCell'>
<text:p text:style-name="MarkContentData" >◎目錄0</text:p>
</table:table-cell>
<table:table-cell table:style-name='KnowledgeDataTableCell'>
<text:p text:style-name="KnowledgeData" >8</text:p>
</table:table-cell>
<table:table-cell table:style-name='KnowledgeDataTableCell'>
<text:p text:style-name="KnowledgeData" >4</text:p>
</table:table-cell>
</table:table-row>
<table:table-row table:style-name='TitleTableRow '>
<!-- table:number-columns-spanned 合併欄位 2代表 2格 -->
<table:table-cell table:style-name='ContentTableCell' table:number-columns-spanned="2" >
<text:p text:style-name="Data" >合計</text:p>
</table:table-cell>
<!--table:covered-table-cell 用來填補空缺 若有spanned 則可以不必寫 -->
<table:covered-table-cell/>
<table:table-cell table:style-name='ContentTableCell'>
<text:p text:style-name='KnowledgeData'>8</text:p>
</table:table-cell>
<table:table-cell table:style-name='ContentTableCell'>
<text:p text:style-name='KnowledgeData'>4</text:p>
</table:table-cell>
</table:table-row>
</table:table>
<text:p text:style-name='ContentData'/>
</office:text>
</office:body>
</office:document-content>
```
以上組成一個xml,即可將檔案壓縮成odt的格式。
表格組成注意事項
``<table:table-columns>``代表欄,
裡面有幾個 ``<table:table-column/>``
也要有幾個``<table:table-cell></table:table-cell>``
那如果要合併欄位 可用
``<table:table-cell table:number-columns-spanned="2">`` 來做橫向的合併欄位
若是想要垂直合併欄位,則是 `` <table:covered-table-cell/>``
**以下範例:**
```xml
<!--這裡有12個column -->
<table:table-columns>
<table:table-column table:style-name='TableColumn5'/>
<table:table-column table:style-name='TableColumn6'/>
<table:table-column table:style-name='TableColumn7'/>
<table:table-column table:style-name='TableColumn8'/>
<table:table-column table:style-name='TableColumn9'/>
<table:table-column table:style-name='TableColumn10'/>
<table:table-column table:style-name='TableColumn11'/>
<table:table-column table:style-name='TableColumn12'/>
<table:table-column table:style-name='TableColumn13'/>
<table:table-column table:style-name='TableColumn14'/>
<table:table-column table:style-name='TableColumn15'/>
<table:table-column table:style-name='TableColumn16'/>
</table:table-columns>
<!-- 照理而言要有12個table-cell 但是 他每個cell都有rows-spanned 總和加起來是12這樣也不會使表格出錯-->
<table:table-row table:style-name='TableRow '>
<table:table-cell table:style-name='HeaderTableCell' table:number-rows-spanned='2'>
<text:p text:style-name="CenterWord" >順序</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell' table:number-rows-spanned='2'>
<text:p text:style-name="CenterWord" >審核起始日</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell' table:number-columns-spanned='3'>
<text:p text:style-name="CenterWord" >新增知識</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell' table:number-columns-spanned='3'>
<text:p text:style-name="CenterWord" >修改知識</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell' table:number-columns-spanned='3'>
<text:p text:style-name="CenterWord" >刪除知識</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell' table:number-rows-spanned='2'>
<text:p text:style-name="CenterWord" >合計</text:p>
</table:table-cell>
</table:table-row>
<table:table-row table:style-name='TableRow '>
<!--這裡 <table:covered-table-cell/> 用了三組 + 九組<table:table-cell>-->
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:table-cell table:style-name='HeaderTableCell'>
<text:p text:style-name="CenterWord" >審核中</text:p>
</table:table-cell>
<table:table-cell table:style-name='FooterTableCell'>
<text:p text:style-name="CenterWord" >生效</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell'>
<text:p text:style-name="CenterWord" >退件</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell'>
<text:p text:style-name="CenterWord" >審核中</text:p>
</table:table-cell>
<table:table-cell table:style-name='FooterTableCell'>
<text:p text:style-name="CenterWord" >生效</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell'>
<text:p text:style-name="CenterWord" >退件</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell'>
<text:p text:style-name="CenterWord" >審核中</text:p>
</table:table-cell>
<table:table-cell table:style-name='FooterTableCell'>
<text:p text:style-name="CenterWord" >生效</text:p>
</table:table-cell>
<table:table-cell table:style-name='HeaderTableCell'>
<text:p text:style-name="CenterWord" >退件</text:p>
</table:table-cell>
<table:covered-table-cell/>
</table:table-row>
```
形成的表格如下圖:

### Amount
產生Amount報表 odt 流程
``` csharp=
public void AmountReportTest()
{
//生成假資料
Random r = new Random();
ModelKnowledgeAmount[] data = new ModelKnowledgeAmount[1];
for (int i = 1 - 1; i >= 0; i--)
{
int num = r.Next(0, 30);
int numw = r.Next(0, 5);
string knowledge = "";
string s = Guid.NewGuid().ToString("N");
if (num % 2 == 0)
{
knowledge = "◎目錄" + i;
}
else
{
knowledge = "目錄" + i;
}
ModelKnowledgeAmount tmp = new ModelKnowledgeAmount
{
sequence = i + 1,
knowledge = knowledge,
correctAmount = num,
wrongAmount = numw,
};
data[i] = tmp;
}
var datetime = DateTime.Now.ToString("yyyyMMddhhmmss");
string[] cols = new string[4];
cols[0] = "順序";
cols[1] = "知識目錄";
cols[2] = "有效知識";
cols[3] = "無效知識";
string title = "知識數量統計表";
//組成xml資料
Report Rpt = new Report();
Amount AmountRes = Rpt.AmountReport(data, cols, title, DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"), "林家弘", true);
string res = AmountRes.render();
//產生xml
if (File.Exists("content.xml"))
File.Delete("content.xml");
File.AppendAllText("content.xml", res); // 檔案存在 路徑: D:\CSharp\ReportX\ReportXTests2\bin\Debug
Assert.IsNotNull(res);
//壓縮xml 產生odt
if (File.Exists("content.xml"))
{
string inputFile = @"content.xml";
string outputFile = @"./Amount(" + datetime + ").odt";
byte[] buffer = new byte[4096];
using (var output = new FileStream(outputFile, FileMode.Create, FileAccess.Write))
using (var input = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
using (var zip = new ZipOutputStream(output))
{
ZipEntry entry = new ZipEntry(inputFile);
entry.DateTime = DateTime.Now;
zip.PutNextEntry(entry);
int readLength;
do
{
readLength = input.Read(buffer, 0, buffer.Length);
if (readLength > 0)
{
zip.Write(buffer, 0, readLength);
}
} while (readLength > 0);
}
}
}
```
從組成xml 開始
AmountReport
這裡主要進行的是產生content.xml資料主體的
``` csharp=
public AmountReport AmountReport<T>(T[] data, string[] cols, string title,string dateTime, string Creator, bool end = false)
{
AmountReport file = new AmountReport(typeof(T));
if (cols.Length > 0)
{
file.setcut(cols);
}
file.setTile(title);
file.setCreator(Creator);
file.setCreatedDate(dateTime);
file.setColumn();
file.setData(data);
if (end) //如果要顯示結算筆數 end =true;
{
file.setsum(data);
}
return file;
}
```
逐條來解釋其作用
首先宣告AmountReport物件,執行時,也順便將客製化的樣式做了設定,
``` csharp=
string customCSS = @"客製化樣式資料,由於基於排版,這裡的內容可參考上面的完整樣式程式碼";
public AmountReport(Type model) : base(model)
{
setCustomStyle(customCSS);
}
```
傳入欲顯示的欄位
setcut(cols)
``` csharp=
//amountreport:amount
public void setcut(string[] cut)
{
changecut(cut);
}
```
將顯示的標題欄位塞進陣列中
``` csharp=
//amount
public void changecut(string[] cut)
{
newcols = cut;
var intersectResult = oldcols.Intersect(newcols);
cols = intersectResult.ToArray();
amount.colNum = cols.Length;
}
```
接著設定表格名稱、製表時間、製表人
``` csharp=
//amountreport:amount
public void setTile(string title)
{
setTitle(sheetName: title);
}
public void setCreator(string creator)
{
setTitle(author: creator);
}
public void setCreatedDate(string dateTime)
{
setTitle(dateTime: dateTime);
}
```
帶入AmountModel中,之後進行組合
``` csharp=
//amount
public void setTitle(string author = null, string company = null, string sheetName = null, string dateTime = null)
{
if (author != null) amount.author = author;
if (company != null) amount.company = company;
if (sheetName != null) amount.sheetName = sheetName;
if (dateTime != null) amount.datetime = dateTime;
}
```
然後設定欄位
``` csharp=
//amountreport:amount
public void setColumn()
{
ModelTR col = appendRow(cols);
foreach (ModelTD td in col.tds)
td.className = "column";
}
```
appendRow 開始製作表格的每一列資料
判斷 value是否為null
若是null 則為欄位標題,不是則依序抓取資料再判定是否存在並加入
``` csharp=
//amount
public ModelTR appendRow(params object[] data)
{
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
foreach (object cell in data)
{
ModelTD td = new ModelTD();
var value = cell.GetType().GetProperty("value");
if (value == null)
{
td.data = cell.ToString();
}
else
{
var colspan = cell.GetType().GetProperty("colspan");
var rowspan = cell.GetType().GetProperty("rowspan");
var fontSize = cell.GetType().GetProperty("fontSize");
var align = cell.GetType().GetProperty("align");
var bold = cell.GetType().GetProperty("bold");
var style = cell.GetType().GetProperty("style");
var className = cell.GetType().GetProperty("className");
if (value != null) td.data = value.GetValue(cell, null);
if (colspan != null) td.colspan = (int)colspan.GetValue(cell, null);
if (rowspan != null) td.rowspan = (int)rowspan.GetValue(cell, null);
if (fontSize != null) td.fontSize = fontSize.GetValue(cell, null).ToString();
if (align != null) td.align = align.GetValue(cell, null).ToString();
if (bold != null) td.bold = true;
if (style != null) td.style = style.GetValue(cell, null).ToString();
if (className != null) td.className = className.GetValue(cell, null).ToString();
}
tr.tds.Add(td);
}
trs.Add(tr);
return tr;
}
```
最後,加入表格內資料
``` csharp=
//amountreport:amount
public void setData<T>(T[] data)
{
appendTable(data);
}
```
appendTable 先取出屬性,對應欄位名稱,排序且塞入資料。
``` csharp=
//amount
public void appendTable<T>(T[] data, string trStyle = null, string className = null)
{
foreach (T tuple in data)
{
ModelTD[] tds = new ModelTD[cols.Length];
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
tr.style = trStyle;
tr.className = className;
foreach (var prop in tuple.GetType().GetProperties())
{
try
{
Present attr = prop.GetCustomAttribute<Present>();
if (attr == null) continue;
int colinx = Array.IndexOf(cols, attr.getName());
object value = prop.GetValue(tuple, null);
tds[colinx] = new ModelTD()
{
col = attr.getName(),
data = value
};
}
catch (Exception)
{
continue;
}
}
foreach (ModelTD td in tds)
tr.tds.Add(td);
trs.Add(tr);
}
}
```
如果還有總結,需記得AmountReport 帶入的參數中,end要為true
有牽扯到資料的總和的話會先在此算出,接著透過appendRow新增資料(appendRow上面已提過)
``` csharp=
//amountreport:amount
public void setsum<T>(T[] data) //總筆數
{
int sum_correct = 0;
int sum_wrong = 0;
string lastRowStyle = "TotalCell"; //預設CSS
string lastClassName = "Data";
foreach (T item in data)
{
foreach (var prop in item.GetType().GetProperties())
{
switch (prop.Name)
{
case "correctAmount":
sum_correct += (int)prop.GetValue(item, null);
break;
case "wrongAmount":
sum_wrong += (int)prop.GetValue(item, null);
break;
default:
break;
}
}
}
appendRow(new { colspan = getColCount() - 2, style = lastRowStyle, className = lastClassName, value = "合計" },
new { value = sum_correct, className = lastClassName },
new { value = sum_wrong, className = lastClassName });//統計資料數
}
```
當所有資料都設置完後,再來就是將這些資料組合成xml
string res = AmountRes.render();
``` csharp=
//amount
public string render(int? width = null)
{
amount.body = new ViewBodyAmount(trs, width);
ViewAmount report = new ViewAmount(amount);
return report.render();
}
```
設定amount.body的值,將用來組成
```csharp=
public ViewBodyAmount(List<ModelTR> model, int? width = null)
{
this.model = model;
this.width = width;
}
```
ViewAmount report = new ViewAmount(amount);
將各段的xml語法集合成字串,將匯入content.xml
``` csharp=
public class ViewAmount
{
private ModelAmount m;
public ViewAmount(ModelAmount model)
{
m = model;
}
public string render()
{
string style = m.style.render(),
body = m.body.render();
return string.Format(template, m.author, m.company, m.sheetName,m.datetime, style, body);
}
string template =
@"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<office:document-content xmlns:anim='urn:oasis:names:tc:opendocument:xmlns:animation:1.0' xmlns:chart='urn:oasis:names:tc:opendocument:xmlns:chart:1.0' xmlns:config='urn:oasis:names:tc:opendocument:xmlns:config:1.0' xmlns:db='urn:oasis:names:tc:opendocument:xmlns:database:1.0' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:dr3d='urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0' xmlns:draw='urn:oasis:names:tc:opendocument:xmlns:drawing:1.0' xmlns:fo='urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0' xmlns:form='urn:oasis:names:tc:opendocument:xmlns:form:1.0' xmlns:grddl='http://www.w3.org/2003/g/data-view#' xmlns:math='http://www.w3.org/1998/Math/MathML' xmlns:meta='urn:oasis:names:tc:opendocument:xmlns:meta:1.0' xmlns:number='urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0' xmlns:office='urn:oasis:names:tc:opendocument:xmlns:office:1.0' xmlns:presentation='urn:oasis:names:tc:opendocument:xmlns:presentation:1.0' xmlns:script='urn:oasis:names:tc:opendocument:xmlns:script:1.0' xmlns:smil='urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0' xmlns:style='urn:oasis:names:tc:opendocument:xmlns:style:1.0' xmlns:svg='urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0' xmlns:table='urn:oasis:names:tc:opendocument:xmlns:table:1.0' xmlns:text='urn:oasis:names:tc:opendocument:xmlns:text:1.0' xmlns:xforms='http://www.w3.org/2002/xforms' xmlns:xhtml='http://www.w3.org/1999/xhtml' xmlns:xlink='http://www.w3.org/1999/xlink' office:version='1.2'>
{4}
<office:body>
<office:text text:use-soft-page-breaks='true'>
<text:p text:style-name='Title'>{2}</text:p>
<text:p text:style-name='User'>
製表人:{0}<text:line-break/>製表時間:{3}
</text:p>
<table:table table:style-name='Table'>
<table:table-columns>
<table:table-column table:style-name='TableColumn4'/>
<table:table-column table:style-name='TableColumn5'/>
<table:table-column table:style-name='TableColumn6'/>
<table:table-column table:style-name='TableColumn7'/>
</table:table-columns>
{5}
</table:table>
<text:p text:style-name='ContentData'/>
</office:text>
</office:body>
</office:document-content>";
}
```
` style = m.style.render()` 組成樣式主體
` body = m.body.render();` 組成資料主體
``` csharp=
//body.render
public class ViewBodyAmount
{
private List<ModelTR> model;
public MemberInfo[] modeli;
private int? width; // if not set, keep it is auto
public ViewBodyAmount(List<ModelTR> model, int? width = null)
{
this.model = model;
this.width = width;
}
//body = m.body.render();
public string render()
{
string table_width = width == null ? "" : string.Format("width={0}", width),
trs = "";
foreach (ModelTR tr in model)
{
string tr_className = tr.className == null ? "" : string.Format("class=\"{0}\" ", tr.className),
tr_customStyle = tr.style ?? "",
tr_style = string.Format("style=\"{0}\" ", tr_customStyle) + tr_className,
tds = ""
;
if (tr.tds != null)
{
foreach (ModelTD td in tr.tds)
{
if (td == null) continue;
string attributes = "",
text_style = "",
td_style = "",
table_cell = "",
className = td.className == null ? "" : td.className,
data = td.data == null ? "" : td.data.ToString();
if (td.className == "column")
{
attributes += string.Format("table:style-name='TableCell'");
className = "Data";
}
if (td.className == null)
{
switch (td.col)
{
case "順序":
attributes += string.Format("table:style-name='SequenceDataTableCell'");
className = "Data";
break;
case "知識目錄":
attributes += string.Format("table:style-name='ContentDataTableCell'");
className = "ContentData";
var test = td.data.ToString().Substring(0, 1);
if (test == "◎")
{
className = "MarkContentData";
}
else
{
className = "ContentData";
}
break;
case "有效知識":
attributes += string.Format("table:style-name='KnowledgeDataTableCell'");
className = "KnowledgeData";
break;
case "無效知識":
attributes += string.Format("table:style-name='KnowledgeDataTableCell'");
className = "KnowledgeData";
break;
default:
attributes += string.Format("table:style-name='SequenceDataTableCell'");
className = "Data";
break;
}
}
if (td.className == "Data")
{
if (td.style != null)
{
attributes += string.Format("table:style-name='ContentTableCell' table:number-columns-spanned='2' ");
for (int i = 1; i < td.colspan; i++)
{
table_cell += "<table:covere-table-cell/>";
}
className = "Data";
}
else
{
attributes += string.Format("table:style-name='ContentTableCell'");
className = "KnowledgeData";
}
}
if (td_style == null)
attributes += string.Format("table:style-name=\"{0}\" ", td_style);
text_style += string.Format("text:style-name=\"{0}\" ", className);
tds += string.Format(template_td, attributes, text_style, data, table_cell);
}
}
trs += string.Format(template_tr, tr_style, tds);
}
return string.Format(template, trs, table_width);
}
string template = @"{0}";
string template_td = "<table:table-cell {0}><text:p {1}>{2}</text:p></table:table-cell>{3} ";
string template_tr = "<table:table-row table:style-name='TitleTableRow '>{1}</table:table-row>";
}
```
patchCSS用來寫入字體相關樣式
costomCSS為客製樣式,在宣告物件時就已經將數值帶入
```csharp=
//style.render
public class ViewStyleAmount
{
private string costomCSS = "";
private string patchCSS = "";
public ViewStyleAmount()
{
patchCSS = @" <office:font-face-decls>
<style:font-face style:name='Calibri' svg:font-family='Calibri' style:font-family-generic='swiss' style:font-pitch='variable' svg:panose-1='2 15 5 2 2 2 4 3 2 4'/>
<style:font-face style:name='新細明體' svg:font-family='新細明體' style:font-family-generic='roman' style:font-pitch='variable' svg:panose-1='2 2 5 0 0 0 0 0 0 0'/>
<style:font-face style:name='Times New Roman' svg:font-family='Times New Roman' style:font-family-generic='roman' style:font-pitch='variable' svg:panose-1='2 2 6 3 5 4 5 2 3 4'/>
<style:font-face style:name='標楷體' svg:font-family='標楷體' style:font-family-generic='script' style:font-pitch='fixed' svg:panose-1='3 0 5 9 0 0 0 0 0 0'/>
<style:font-face style:name='Calibri Light' svg:font-family='Calibri Light' style:font-family-generic='swiss' style:font-pitch='variable' svg:panose-1='2 15 3 2 2 2 4 3 2 4'/>
</office:font-face-decls>";
}
public void setCustomCSS(string costomCSS)
{
this.costomCSS = costomCSS;
}
public string render()
{
string format_all_css = string.Format(global_css, patchCSS, costomCSS);
return string.Format(template, format_all_css);
}
string template = @"{0}";
string global_css = @"
{0}
{1}
";
}
```
最後,形成content.xml,再壓縮即完成odt檔。
```csharp=
File.AppendAllText("content.xml", res);
```
### KBStatic 報表生成
大致上流程與Amount報表組成差不多,這個報表多了要顯示時間區間
這兩個參數帶入產出報表當月的前一個月的起始日
**beforeMouthOneDay
beforeMouthLastDay**
```csharp=
KBStatic KBSRes = Rpt.KBStaticReport(data, cols, title, DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"),
beforeMouthOneDay, beforeMouthLastDay, "林家弘");
```
```csharp=
file.setCreatedDayRange(firstday, lastdday);
```
```csharp=
public void setCreatedDayRange(string firstday,string lastday)
{
setTitle(dateRange: firstday+" - "+lastday);
}
```
```csharp=
public void setTitle(string author = null, string company = null, string sheetName = null, string dateTime = null, string dateRange = null)
{
if (author != null) kbs.author = author;
if (company != null) kbs.company = company;
if (sheetName != null) kbs.sheetName = sheetName;
if (dateTime != null) kbs.datetime = dateTime;
if (dateRange != null) kbs.dateRange = dateRange;
}
```
並且這個報表沒有帶入end參數,並沒有總和。
### SignStatus 報表生成
這個報表有合併欄位

目前想到的解決方法,列出所要產生的各個欄位,分列去形成。
```csharp=
string[] cols = new string[15];
cols[0] = "順序";
cols[1] = "審核起始日";
cols[2] = "新增知識";
cols[3] = "修改知識";
cols[4] = "刪除知識";
cols[5] = "合計";
cols[6] = "新增知識審核";
cols[7] = "新增知識生效";
cols[8] = "新增知識退件";
cols[9] = "修改知識審核";
cols[10] = "修改知識生效";
cols[11] = "修改知識退件";
cols[12] = "刪除知識審核";
cols[13] = "刪除知識生效";
cols[14] = "刪除知識退件";
string title = "知識統計表";
Report Rpt = new Report();
SignStatus SSRes = Rpt.SignStatusReport(data, cols, title, DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"), beforeMouthOneDay, beforeMouthLastDay, "林家弘", true);
```
setColumn()
setSecondColun()
去形成兩排的表頭欄位
```csharp=
public SignStatusReport SignStatusReport<T>(T[] data, string[] cols, string title, string dateTime, string firstday, string lastdday, string Creator, bool end = false)
{
SignStatusReport file = new SignStatusReport(typeof(T));
if (cols.Length > 0)
{
file.setcut(cols);
}
file.setTile(title);
file.setCreator(Creator);
file.setCreatedDate(dateTime);
file.setCreatedDayRange(firstday, lastdday);
file.setColumn();
file.setSecondColumn();
file.setData(data); ;
if (end) //如果要顯示結算筆數 end =true;
{
file.setsum(data);
}
return file;
}
```
setColumn()
給予className="column"
```csharp=
public void setColumn()
{
ModelTR col = appendRow(cols);
foreach (ModelTD td in col.tds)
td.className = "column";
}
```
setSecondColumn()
給予className="secondColumn"
```csharp=
public void setSecondColumn()
{
ModelTR col = appendRow(cols);
foreach (ModelTD td in col.tds)
td.className = "secondColumn";
}
```
在後面組成時
再去判斷組成欄位
```csharp=
//ViewBodySignStatus
if (td.className == "column")
{
switch (td.data)
{
case "順序":
attributes += string.Format("table:style-name='HeaderTableCell' table:number-rows-spanned='2'");
className = "CenterWord";
break;
case "審核起始日":
attributes += string.Format("table:style-name='HeaderTableCell' table:number-rows-spanned='2'");
className = "CenterWord";
break;
case "新增知識":
attributes += string.Format("table:style-name='HeaderTableCell' table:number-columns-spanned='3'");
className = "CenterWord";
table_cell += "<table:covered-table-cell/>";
table_cell += "<table:covered-table-cell/>";
break;
case "修改知識":
attributes += string.Format("table:style-name='HeaderTableCell' table:number-columns-spanned='3'");
className = "CenterWord";
table_cell += "<table:covered-table-cell/>";
table_cell += "<table:covered-table-cell/>";
break;
case "刪除知識":
attributes += string.Format("table:style-name='HeaderTableCell' table:number-columns-spanned='3'");
className = "CenterWord";
table_cell += "<table:covered-table-cell/>";
table_cell += "<table:covered-table-cell/>";
break;
case "合計":
attributes += string.Format("table:style-name='HeaderTableCell' table:number-rows-spanned='2'");
className = "CenterWord";
break;
default:
continue;
}
}
if (td.className == "secondColumn")
{
switch (td.data)
{
case "順序":
tds += " <table:covered-table-cell/>";
continue;
case "審核起始日":
tds += " <table:covered-table-cell/>";
continue;
case "合計":
tds += " <table:covered-table-cell/>";
continue;
case "新增知識審核":
attributes += string.Format("table:style-name='HeaderTableCell'");
className = "CenterWord";
data = "審核中";
break;
case "新增知識生效":
attributes += string.Format("table:style-name='FooterTableCell'");
className = "CenterWord";
data = "生效";
break;
case "新增知識退件":
attributes += string.Format("table:style-name='HeaderTableCell'");
className = "CenterWord";
data = "退件";
break;
case "修改知識審核":
attributes += string.Format("table:style-name='HeaderTableCell'");
className = "CenterWord";
data = "審核中";
break;
case "修改知識生效":
attributes += string.Format("table:style-name='FooterTableCell'");
className = "CenterWord";
data = "生效";
break;
case "修改知識退件":
attributes += string.Format("table:style-name='HeaderTableCell'");
className = "CenterWord";
data = "退件";
break;
case "刪除知識審核":
attributes += string.Format("table:style-name='HeaderTableCell'");
className = "CenterWord";
data = "審核中";
break;
case "刪除知識生效":
attributes += string.Format("table:style-name='FooterTableCell'");
className = "CenterWord";
data = "生效";
break;
case "刪除知識退件":
attributes += string.Format("table:style-name='HeaderTableCell'");
className = "CenterWord";
data = "退件";
break;
default:
continue;
}
}
```
其餘部分,大多數皆相同。
## Method
### `AmountReport`
| 名稱 | 類型 | 描述 |
| -------- | -------- | -------- |
| AmountReport<T>(T[] data, string[] cols, string title,string dateTime, string Creator, bool end = false)| AmountReport | 組合成報表相關資料 |
| Args | 描述 |
| -------- | -------- |
| data | 表格內資料 |
| cols | 欲顯示的表格標頭|
| title | 標題|
|dateTime | 文件建立時間|
|Creator | 建立者|
|end | 判斷是否要形成總結,預設為 `NULL`|
### `setcut`
| 名稱 | 類型 | 描述 |
| -------- | -------- | -------- |
| setcut(cols)| void | 傳入欲顯示欄位標題之陣列 |
| Args | 描述 |
| -------- | -------- |
| cols | 表格欄位資料,陣列 |
### `setTile`
| 名稱 | 類型 | 描述 |
| -------- | -------- | -------- |
| setTile(title)| void | 傳入標題名稱 |
| Args | 描述 |
| -------- | -------- |
| title | 標題名稱 |
### `setCreator`
| 名稱 | 類型 | 描述 |
| -------- | -------- | -------- |
| setCreator(Creator)| void | 傳入製作者名稱 |
| Args | 描述 |
| -------- | -------- |
| Creator | 製作者名稱 |
### `setCreatedDate`
| 名稱 | 類型 | 描述 |
| -------- | -------- | -------- |
| setCreatedDate(dateTime)| void | 傳入製作日期 |
| Args | 描述 |
| -------- | -------- |
| dateTime | 製作日期 |
### `setData`
| 名稱 | 類型 | 描述 |
| -------- | -------- | -------- |
| setData(data)| void | 傳入表格內資料 |
| Args | 描述 |
| -------- | -------- |
| data | 表格資料 |
### `setsum`
| 名稱 | 類型 | 描述 |
| -------- | -------- | -------- |
| setsum(data)| void | 傳入表格資料計算出資料總和 |
| Args | 描述 |
| -------- | -------- |
| data | 表格資料 |
### 其他method
| 名稱 | 類型 | 描述 |
| -------- | -------- | -------- |
| getColCount()| int | 回傳表格欄數 |
|render(int? width) |string |回傳完整的xml字串,`width` 為表格寬度,可顯示為空值|
| render() | string |組成各段xml片段|
|changecut(string[] cut)|void|塞入欲顯示欄位的陣列進入新的陣列中|
|setWord(string author = null, string company = null, string sheetName = null)|void |設定表格資料|
|appendFullRow(string data, string trStyle = null, string className = null)|ModelTR |將表格資料塞入ModelTR,用以之後使用,主要為塞入文件相關資料。`trStyle`給予表格樣式語法,預設為null。`className` 給予資料className設定,預設為null|
|appendRow(params object[] data)|ModelTR|將表格資料塞入ModelTR,用以之後使用,主要為塞入表格標題以及表格資料。|
|appendTable<T>(T[] data, string trStyle = null, string className = null)|void|將表格資料塞入ModelTR,用以之後使用,主要為塞入表格內容資料|
## DataTable 資料型態 形成odt
dataTable的資料組成 塞了6種欄位資料
欲顯示的資料有4種 col
```csharp=
DataTable dtTable = new DataTable("test");
DataRow row;
// 建立欄位
DataColumn[] colss ={
new DataColumn("ID",typeof(int)),
new DataColumn("標題",typeof(string)),
new DataColumn("姓名",typeof(string)),
new DataColumn("編號",typeof(decimal)),
new DataColumn("資料",typeof(string)),
new DataColumn("電話",typeof(string))
};
dtTable.Columns.AddRange(colss);
// 新增資料到DataTable
for (int i = 1; i <= 10; i++)
{
string a = Guid.NewGuid().ToString("N");
row = dtTable.NewRow();
row["ID"] = i;
row["標題"] = "測試 " + i.ToString();
row["姓名"] = "SOL_" + i;
row["編號"] = "123";
row["資料"] = a.ToString();
row["電話"] = "0923456789";
dtTable.Rows.Add(row);
}
//
string[] cols = new string[6];
cols[0] = "姓名";
cols[1] = "資料";
cols[2] = "ID";
cols[3] = "電話";
//多帶入dtTable
Odt odtRes = Rpt.OdtResponse(data, cols, title,DateTime.Now.AddDays(-1), DateTime.Now, "林家弘", true,dtTable);
```
```csharp=
public OdtReport OdtResponse<T>(T[] data, string[] cols, string title, DateTime starting, DateTime ending, string Creator, bool end = false)
{
OdtReport orp = new OdtReport(typeof(T));
if (cols.Length > 0)
{
orp.setcut(cols);
}
orp.setTile(title);
orp.setDate(starting, ending);
orp.setCreator(Creator);
orp.setCreatedDate();
orp.setColumn();
orp.setData(data);
if (end) //如果要顯示結算筆數 end =true;
{
orp.setsum(data);
}
return orp;
}
```
```csharp=
public void setData (DataTable data)
{
appendTable(data);
}
```
將dataTable資料 抓取欄位值(column)跟欲顯示欄位(cols)對應,找出顯示欄位,並與資料作對應。
```csharp=
public void appendTable(DataTable data, string trStyle = null, string className = null)
{
for (int i = 0; i < data.Rows.Count; i++)
{
ModelTD[] tds = new ModelTD[cols.Length];
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
tr.style = trStyle;
tr.className = className;
foreach (var prop in data.Columns)
{
try
{
var column = prop;
int colinx = Array.IndexOf(cols, column.ToString());
if (colinx == -1) continue;
var value = data.Rows[i][column.ToString()];
tds[colinx] = new ModelTD()
{
data = value
};
}
catch (Exception)
{
continue;
}
}
foreach (ModelTD td in tds)
tr.tds.Add(td);
trs.Add(tr);
}
}
```
data.select().count() 算出總比數
```csharp=
public void setsum(DataTable data) //總筆數
{
string lastRowStyle = "TotalCell"; //預設CSS
string lastClassName = "Word";
appendRow(new { value = data.Select().Count(), colspan = getColCount() - 1, style = lastRowStyle, className = lastClassName });//統計資料數
}
```
## Refactor Code
### interface
全通用標準報表規格
```csharp=
namespace ReportX.Rep.Common
{
public interface IReportX
{
string render(int? width = null);
void changecut(string[] cut);
void setCustomStyle(string css);
ModelTR appendFullRow(string data, string trStyle = null, string className = null);
ModelTR appendRow(params object[] data);
void appendTable<T>(T[] data, string trStyle = null, string className = null);
void appendTable(DataTable data, string trStyle = null, string className = null);
void setData(string author = null, string company = null, string sheetName = null, string dateTime = null, string dateRange = null);
int getColCount();
}
}
```
### abstract
#### openoffice
各個檔案皆需要制定各自必要規格
> render()
> changecut()
> setCustomStyle()
> appendFullRow()
> setData()
```csharp=
namespace ReportX.Rep.Common
{
public abstract class AbsOpenOffice: IReportX
{
protected abstract string[] oldcols { get; set; }
protected abstract string[] newcols { get; set; }
public abstract string[] cols { get; set; }
protected abstract List<ModelTR> trs { get; }
public abstract string render( int? width = null);
public abstract void changecut(string[] cut);
public abstract void setCustomStyle(string css);
public abstract ModelTR appendFullRow(string data, string trStyle = null, string className = null);
public abstract void setData(string author = null, string company = null, string sheetName = null, string dateTime = null, string dateRange = null);
public ModelTR appendRow(params object[] data)
{
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
foreach (object cell in data)
{
ModelTD td = new ModelTD();
var value = cell.GetType().GetProperty("value");
if (value == null)
{
td.data = cell.ToString();
}
else
{
var colspan = cell.GetType().GetProperty("colspan");
var rowspan = cell.GetType().GetProperty("rowspan");
var fontSize = cell.GetType().GetProperty("fontSize");
var align = cell.GetType().GetProperty("align");
var bold = cell.GetType().GetProperty("bold");
var style = cell.GetType().GetProperty("style");
var className = cell.GetType().GetProperty("className");
if (value != null) td.data = value.GetValue(cell, null);
if (colspan != null) td.colspan = (int)colspan.GetValue(cell, null);
if (rowspan != null) td.rowspan = (int)rowspan.GetValue(cell, null);
if (fontSize != null) td.fontSize = fontSize.GetValue(cell, null).ToString();
if (align != null) td.align = align.GetValue(cell, null).ToString();
if (bold != null) td.bold = true;
if (style != null) td.style = style.GetValue(cell, null).ToString();
if (className != null) td.className = className.GetValue(cell, null).ToString();
}
tr.tds.Add(td);
}
trs.Add(tr);
return tr;
}
public void appendTable<T>(T[] data, string trStyle = null, string className = null)
{
foreach (T tuple in data)
{
ModelTD[] tds = new ModelTD[cols.Length];
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
tr.style = trStyle;
tr.className = className;
foreach (var prop in tuple.GetType().GetProperties())
{
try
{
Present attr = prop.GetCustomAttribute<Present>();
if (attr == null) continue;
var asdf = attr.getName();
int colinx = Array.IndexOf(cols, attr.getName());
object value = prop.GetValue(tuple, null);
tds[colinx] = new ModelTD()
{
col = attr.getName(),
data = value
};
}
catch (Exception)
{
continue;
}
}
foreach (ModelTD td in tds)
tr.tds.Add(td);
trs.Add(tr);
}
}
public void appendTable(DataTable data, string trStyle = null, string className = null)
{
for (int i = 0; i < data.Rows.Count; i++)
{
ModelTD[] tds = new ModelTD[cols.Length];
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
tr.style = trStyle;
tr.className = className;
foreach (var prop in data.Columns)
{
try
{
var test = prop;
int colinx = Array.IndexOf(cols, test.ToString());
if (colinx == -1) continue;
var value = data.Rows[i][test.ToString()];
tds[colinx] = new ModelTD()
{
data = value
};
}
catch (Exception)
{
continue;
}
}
foreach (ModelTD td in tds)
tr.tds.Add(td);
trs.Add(tr);
}
}
public int getColCount()
{
return cols.Length;
}
public void CreateMeta(string type)
{
var str = "";
switch (type)
{
case "odt":
str = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><manifest:manifest xmlns:manifest='urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'><manifest:file-entry manifest:full-path='/' manifest:media-type='application/vnd.oasis.opendocument.text'/><manifest:file-entry manifest:full-path='content.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='settings.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='styles.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='meta.xml' manifest:media-type='text/xml'/></manifest:manifest>";
break;
case "ods":
str = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><manifest:manifest xmlns:manifest='urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'><manifest:file-entry manifest:full-path='/' manifest:media-type='application/vnd.oasis.opendocument.spreadsheet'/><manifest:file-entry manifest:full-path='styles.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='content.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='meta.xml' manifest:media-type='text/xml'/></manifest:manifest>";
break;
default:
break;
}
string dirPath = @".\META-INF";
if (Directory.Exists(dirPath))
{
if (File.Exists("META-INF/manifest.xml"))
File.Delete("META-INF/manifest.xml");
File.AppendAllText("META-INF/manifest.xml", str);
}
else
{
Directory.CreateDirectory(dirPath);
if (File.Exists("META-INF/manifest.xml"))
File.Delete("META-INF/manifest.xml");
File.AppendAllText("META-INF/manifest.xml", str);
}
}
}
}
```
#### office
各個檔案皆需要制定各自必要規格
> changecut()
> setCustomStyle()
> appendFullRow()
> setData()
由於多載,用virtual 由各個加以定義
>render()
```csharp=
namespace ReportX.Rep.Common
{
public abstract class AbsOffice: IReportX
{
protected abstract string[] oldcols { get; set; }
protected abstract string[] newcols { get; set; }
public abstract string[] cols { get; set; }
protected abstract List<ModelTR> trs { get; }
public virtual string render(int? width = null, string File_type = null)
{
throw new NotImplementedException();
}
public virtual string render(int? width = null)
{
throw new NotImplementedException();
}
public abstract void changecut(string[] cut);
public abstract void setCustomStyle(string css);
public abstract ModelTR appendFullRow(string data, string trStyle = null, string className = null);
public abstract void setData(string author = null, string company = null, string sheetName = null, string dateTime = null, string dateRange = null);
public ModelTR appendRow(params object[] data)
{
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
foreach (object cell in data)
{
ModelTD td = new ModelTD();
var value = cell.GetType().GetProperty("value");
if (value == null)
{
td.data = cell.ToString();
}
else
{
var colspan = cell.GetType().GetProperty("colspan");
var rowspan = cell.GetType().GetProperty("rowspan");
var fontSize = cell.GetType().GetProperty("fontSize");
var align = cell.GetType().GetProperty("align");
var bold = cell.GetType().GetProperty("bold");
var style = cell.GetType().GetProperty("style");
var className = cell.GetType().GetProperty("className");
if (value != null) td.data = value.GetValue(cell, null);
if (colspan != null) td.colspan = (int)colspan.GetValue(cell, null);
if (rowspan != null) td.rowspan = (int)rowspan.GetValue(cell, null);
if (fontSize != null) td.fontSize = fontSize.GetValue(cell, null).ToString();
if (align != null) td.align = align.GetValue(cell, null).ToString();
if (bold != null) td.bold = true;
if (style != null) td.style = style.GetValue(cell, null).ToString();
if (className != null) td.className = className.GetValue(cell, null).ToString();
}
tr.tds.Add(td);
}
trs.Add(tr);
return tr;
}
public void appendTable<T>(T[] data, string trStyle = null, string className = null)
{
foreach (T tuple in data)
{
ModelTD[] tds = new ModelTD[cols.Length];
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
tr.style = trStyle;
tr.className = className;
foreach (var prop in tuple.GetType().GetProperties())
{
try
{
Present attr = prop.GetCustomAttribute<Present>();
if (attr == null) continue;
var asdf = attr.getName();
int colinx = Array.IndexOf(cols, attr.getName());
object value = prop.GetValue(tuple, null);
tds[colinx] = new ModelTD()
{
col = attr.getName(),
data = value
};
}
catch (Exception)
{
continue;
}
}
foreach (ModelTD td in tds)
tr.tds.Add(td);
trs.Add(tr);
}
}
public void appendTable(DataTable data, string trStyle = null, string className = null)
{
for (int i = 0; i < data.Rows.Count; i++)
{
ModelTD[] tds = new ModelTD[cols.Length];
ModelTR tr = new ModelTR();
tr.tds = new List<ModelTD>();
tr.style = trStyle;
tr.className = className;
foreach (var prop in data.Columns)
{
try
{
var test = prop;
int colinx = Array.IndexOf(cols, test.ToString());
if (colinx == -1) continue;
var value = data.Rows[i][test.ToString()];
tds[colinx] = new ModelTD()
{
data = value
};
}
catch (Exception)
{
continue;
}
}
foreach (ModelTD td in tds)
tr.tds.Add(td);
trs.Add(tr);
}
}
public int getColCount()
{
return cols.Length;
}
}
}
```
### class
各種格式 設定各自的報表名稱
>getsheetName()
```csharp=
public string getsheetName()
{
return excel.sheetName;
}
```
### ReportCreator
建立標準建立方法
>ReportCreator<T>
```csharp=
public class ReportCreator<T> where T : IReportX
{
static T report { get; set; }
public MemberInfo[] modeli;
protected Type type { get; set; }
protected string[] oldcols { get; set; }
protected string[] newcols { get; set; }
protected List<ModelTR> trs { get; }
public static string[] cols { get; set; }
public string sheetName { get; set; }
private ModelExcel excel;
private ModelWord word;
private ModelOdt odt;
public ReportCreator(Type type)
{
List<MemberInfo> list_cols = new List<MemberInfo>();
modeli = type.GetMembers();
foreach (var member in type.GetMembers())
{
Present attr = member.GetCustomAttribute<Present>();
if (attr == null) continue;
int MetadataToken = member.MetadataToken,
inserted_index = 0;
// sory by MetadataToken (declaration)
for (int i = 0; i < list_cols.Count; i++)
{
inserted_index = i;
if (MetadataToken < list_cols[i].MetadataToken) break;
inserted_index = i + 1;
}
list_cols.Insert(inserted_index, member);
}
string[] str_cols = new string[list_cols.Count]; //取得標題數量
for (int i = 0; i < list_cols.Count; i++)
str_cols[i] = list_cols[i].GetCustomAttribute<Present>().getName();//取得標題名稱
oldcols = str_cols; //舊的陣列
cols = str_cols;
report = (T)Activator.CreateInstance(typeof(T), type);
}
public string render(int? width = null)
{
return report.render(width);
}
public void setDate(DateTime from, DateTime? to = null, string type = null)
{
if (from == null) return;
if (to == null) to = DateTime.Now;
string date_start = Convert.ToDateTime(from).ToString("yyyy/MM/dd"),
date_end = Convert.ToDateTime(to).ToString("yyyy/MM/dd");
switch (type)
{
case "Word":
report.appendFullRow(string.Format("{0} - {1}", date_start, date_end), null, "r-header-date");
break;
case "Excel":
report.appendFullRow(string.Format("{0} - {1}", date_start, date_end), null, "r-header-date");
break;
case "Odt":
report.appendFullRow(string.Format("{0} - {1}", date_start, date_end), "TableCellData", "TitleDateWord");
break;
case "Ods":
report.appendFullRow(string.Format("{0} - {1}", date_start, date_end), "TableCellData", "TitleDateWord");
break;
default:
break;
}
}
public void setCreator(string creator, string type = null)
{
report.setData(author: creator);
switch (type)
{
case "Word":
report.appendFullRow(string.Format("製表人:{0}", creator), null, "r-header-secondary");
break;
case "Excel":
report.appendFullRow(string.Format("製表人:{0}", creator), null, "r-header-secondary");
break;
case "Odt":
report.appendFullRow(string.Format("製表人:{0}", creator), "TableCellData", "TitleTimeWord");
break;
case "Ods":
report.appendFullRow(string.Format("製表人:{0}", creator), "TableCellData", "TitleTimeWord");
break;
default:
break;
}
}
public void setTile(string title, string type = null)
{
report.setData(sheetName: title);
switch (type)
{
case "Word":
report.appendFullRow(title, null, "r-header-title");
break;
case "Excel":
report.appendFullRow(title, null, "r-header-title");
break;
case "Odt":
report.appendFullRow(title, "TableCellData", "Title");
break;
case "Ods":
report.appendFullRow(title, "TableCellData", "Title");
break;
default:
break;
}
}
public void setCreatedDate(string type = null)
{
string now = Convert.ToDateTime(DateTime.Now).ToString("yyyy/MM/dd hh:mm:tt");
switch (type)
{
case "Word":
report.appendFullRow(string.Format("製表時間:{0}", now), null, "r-header-secondary");
break;
case "Excel":
report.appendFullRow(string.Format("製表時間:{0}", now), null, "r-header-secondary");
break;
case "Odt":
report.appendFullRow(string.Format("製表時間:{0}", now), "TableCellData", "TitleTimeWord");
break;
case "Ods":
report.appendFullRow(string.Format("製表時間:{0}", now), "TableCellData", "TitleTimeWord");
break;
default:
break;
}
}
public void setColumn()
{
var intersectResult = oldcols.Intersect(newcols);
cols = intersectResult.ToArray();
ModelTR col = report.appendRow(cols);
foreach (ModelTD td in col.tds)
td.className = "column";
}
public void setData<T>(T[] data)
{
report.appendTable(data);
}
public void setsum<T>(T[] data, string type) //總筆數
{
string lastRowStyle = "";
string lastClassName = "";
switch (type)
{
case "Word":
lastRowStyle = "background-color:#DDD;-webkit-print-color-adjust: exact;"; //預設CSS
report.appendRow(new { value = "總筆數", colspan = report.getColCount() - 1, style = lastRowStyle }, data.Length);//統計資料數
break;
case "Excel":
lastRowStyle = "background-color:#DDD;-webkit-print-color-adjust: exact;"; //預設CSS
report.appendRow(new { value = "總筆數", colspan = report.getColCount() - 1, style = lastRowStyle }, data.Length);//統計資料數
break;
case "Odt":
lastRowStyle = "TotalCell"; //預設CSS
lastClassName = "Word";
report.appendRow(new { value = data.Length, colspan = report.getColCount() - 1, style = lastRowStyle, className = lastClassName });//統計資料數 break;
break;
case "Ods":
lastRowStyle = "TotalCell"; //預設CSS
lastClassName = "Word";
report.appendRow(new { value = data.Length, colspan = report.getColCount() - 1, style = lastRowStyle, className = lastClassName });//統計資料數
break;
default:
break;
}
}
// 傳入欲顯示欄位標題 之陣列
public void setcut(string[] cut)
{
newcols = cut;
report.changecut(cut);
}
public void CreateMeta(string type)
{
var str = "";
switch (type)
{
case "odt":
str = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><manifest:manifest xmlns:manifest='urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'><manifest:file-entry manifest:full-path='/' manifest:media-type='application/vnd.oasis.opendocument.text'/><manifest:file-entry manifest:full-path='content.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='settings.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='styles.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='meta.xml' manifest:media-type='text/xml'/></manifest:manifest>";
break;
case "ods":
str = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><manifest:manifest xmlns:manifest='urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'><manifest:file-entry manifest:full-path='/' manifest:media-type='application/vnd.oasis.opendocument.spreadsheet'/><manifest:file-entry manifest:full-path='styles.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='content.xml' manifest:media-type='text/xml'/><manifest:file-entry manifest:full-path='meta.xml' manifest:media-type='text/xml'/></manifest:manifest>";
break;
default:
break;
}
string dirPath = @".\META-INF";
if (Directory.Exists(dirPath))
{
if (File.Exists("META-INF/manifest.xml"))
File.Delete("META-INF/manifest.xml");
File.AppendAllText("META-INF/manifest.xml", str);
}
else
{
Directory.CreateDirectory(dirPath);
if (File.Exists("META-INF/manifest.xml"))
File.Delete("META-INF/manifest.xml");
File.AppendAllText("META-INF/manifest.xml", str);
}
}
public int getColCount()
{
return cols.Length;
}
}
```