# 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&nbsp;-&nbsp;2019/07/31&nbsp;</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'>鄭央辰&nbsp;</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'>蔡長生&nbsp;</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'>游淳茹&nbsp;</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'>鄭央辰&nbsp;</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'>陳正雄&nbsp;</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'>陳正雄&nbsp;</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'>陳正雄&nbsp;</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> ``` 形成的表格如下圖: ![](https://i.imgur.com/Z7a6Tox.jpg) ### 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 報表生成 這個報表有合併欄位 ![](https://i.imgur.com/Z7a6Tox.jpg) 目前想到的解決方法,列出所要產生的各個欄位,分列去形成。 ```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; } } ```