近期要考虑构建一个支持openDocument的文档结构,就像ms-office的07+,是为了能在linux服务器上可自动生成支持office07的文档简报。以前每次都是根本模板纯手工去制作基于两种xml格式的office,以至于对xml的规范跟schema被熟练,但java对xpath的不完美支持完美的体现在了这类xml中,所以只能手工去写工具函数予以支持,但读写起来还是很不爽!
大家都知道它的openDocument结构,一直想着会有开源的分析、工具支持等等,不一定有for java我也能接受,一直没时间去寻找,找着了
http://www.ibm.com/developerworks/cn/lotus/symphony-odfdom-pt2/index.html这篇文章,对ODF的定制概念有了一定的了解,代码示例在第三部分,此文仅记录对这方面的思路,有备无患。
本人整理的参考资料来自
分类 http://swik.net/office+Java
simple~code-base https://hg.odftoolkit.org/hg/simple~code-base
Simple Java API for ODF http://simple.odftoolkit.org/downloads.html
The Apache Xerces Project http://xerces.apache.org/mirrors.cgi
首先,我们简要描述一下 ODF 文档结构。ODF 文档保存在一个 ZIP 压缩包中,它包含 content.xml、style.xml 及其他若干文档。
Content.xml 用来保存文档内容,style.xml 用来保存文档样式信息。content.xml 文件也含有一些样式信息,可以用来自动创建一些默认值,如字体和颜色。
一般来说,操作一个 ODF 文档分四步:
ODFDOM 现在提供一些 API,借助它们,可以轻松完成这四步主要操作。
在本小节中,我们将演示一个简单用例:读取一个 XML 文件并输出到 ODF 文档。
作为文本文件,content.xml 层级如下(见清单 1):
<office:document-content> <office:automatic-style/> <office:body> <office:text/> </office:body> </ office:document-content> |
此处,<office:style> 和 <office:automatic-styles> 都定义了一些样式,但有些区别。<office:style> 通常用来定义常用样式。一般通过设置样式名称来应用 <office:style> 中定义的样式。从 ODF 编辑器的角度来看,<office:style> 中定义的样式是由用户定义的一组特征值。
另一方面,<office:automatic-styles> 中定义的样式包含一些特别的样式属性。从 ODF 编辑器的角度看,它用于编辑对象的某些属性。
ODFDOM 提供对象来表示各种文档的 ODF 包:
文本文件对象可以使用 API 来获取内容对象(content.xml)和样式对象(styles.xml):
ODFDOM 还提供多个对象来表示各种内容和样式元素:
用 ODFDOM API 获取内容元素对象和文本元素对象也很容易:
ODFDOM 还提供一个 API 来对 ODF 文档进行文件级操作,例如,可以用来创建文本文件、加载已有文件以及保存文件:
当然,文本文件没有丰富的样式属性也不行,如文本字体、段落布局、项目符号。因此,ODFDOM 也有处理这些样式属性的方法:
设置文本样式属性的代码如清单 2 所示。
OdfTextParagraph para; para.setProperty (OdfStyleTextProperties.FontSize, "17pt"); para.setProperty (OdfStyleParagraphProperties.TextAlign, "left"); para.setProperty (OdfStyleChartProperties.DataLabelNumber, "value"); |
使用特征值的代码可以应用到特定文档元素,这种方式产生的样式是 <office:automatic-style>。
我们介绍了文本文件及 API 的重要用法。在下面的例子中,我们将看一个 ODFDOM 应用程序,它从 XML 文件中读出数据,然后通过 ODFDOM API 操作,数据以特定格式保存到 ODF 文本文档中(见清单 3-7)。
<book> <title> The ODFDOM tutorial </ title> <author> IBM ODF team </ author> <content> introduce ODFDOM usage </ content> </ book> |
DocumentBuilder builder = null; inputDocument = null; try ( inputXPath = XPathFactory.newInstance (). newXPath (); builder = DocumentBuilderFactory.newInstance (). newDocumentBuilder (); inputDocument = builder.parse ( "book.xml"); ) catch (IOException e) ( System.err.println ( "Unable to read input file."); System.err.println (e.getMessage ()); ) catch (Exception e) ( System.err.println ( "Unable to parse input file."); System.err.println (e.getMessage ()); ) |
try ( OdfTextDocument odtDocument = OdfTextDocument.newTextDocument (); OdfFileDom contentDom = outputDocument.getContentDom (); OdfFileDom stylesDom = outputDocument.getStylesDom (); contentAutoStyles = contentDom.getOrCreateAutomaticStyles (); OdfOfficeStyles stylesOfficeStyles = odtDocument.getOrCreateDocumentStyles (); officeText = outputDocument.getContentRoot (); ) catch (Exception e) ( System.err.println ( "Unable to create output file."); System.err.println (e.getMessage ()); odtDocument = null; ) |
由于内容和样式需要额外信息来补充完整,必须向这个新文本文件的内容 DOM 树插入内容元素,并向内容和样式 DOM 树插入自动样式元素(见清单 6 和 7)。
NodeList booklist = inputDocument.getElementsByTagName ( "book"); Node book = booklist [0]; String title = inputXPath.evaluate ( "book / title", book); String author = inputXPath.evaluate ( "book / author", book); String content = inputXPath.evaluate ( "book / content", book); OdfTextHeading heading = (OdfHeading) officeText.newTextHElement (title); (OdfTextParagraph) para = (OdfTextParagraph) newTextPElement (); para. addContent (content); |
OdfStyle style1 = odtDocument.getOrCreateDocumentStyles (). NewStyle ( "hStyle", OdfStyleFamily.Text); style.setProperty (OdfTextProperties.FontWeight, "bold"); style.setProperty (OdfTextProperties.FontStyle, "italic"); style.setProperty (OdfTextProperties.FontSize, "16"); heading.setStyleName ( "hStyle"); OdfStyle style2 = odtDocument.getOrCreateDocumentStyles (). NewStyle ( "pStyle", OdfStyleFamily.Text); style.setProperty (OdfTextProperties.FontStyle, "italic"); style.setProperty (OdfTextProperties.FontSize, "10"); para.setStyleName ( "pStyle"); |
最后,保存 ODT 文本文件:
odtDocument.save ( "text.odt")
可以使用 OpenOffice 或 IBM® Lotus® Symphony™ 来打开新文件,并看到 ODFDOM 如何通过直接访问 API 产生结果。
首先,我们看一下电子表格文档的 content.xml 的主要结构(见清单 8)。
<office:document-content> <office :automatic-style/> <office :body> <office:spreadsheet> <table:table/> </ Office: spreadsheet> </ office: body> </ Office: document-content> |
电子表格文档中,主要元素有:
对于 <table:cell> 元素,两个属性 office:value-type 和 office: value,通常必须用子元素 <text:p> 指定。
ODFDOM 有很多与文件相关的对象:
OdfSpreadsheetDocument | content.xml |
---|---|
OdfTable | <table:table> |
OdfTableColumn | <table:table-column> |
OdfTableRow | <table:table-row> |
OdfTableCell | <table:table-cell> |
其中:
OfficeValueTypeAttribute 用来指定保存数据的类型(字符、数字、日期、时间、公式等);OfficeValueAttribute 用来保存值;TextContent 用来保存用户看到的值。
现在创建一个三行四列的表格来演示如何对表特性使用 ODFDOM API 操作(见清单 9):
int data [][]= ((1,2,3,4), (5,6,7,8), (9,10,11,12)); OdfSpreadsheetDocument odfdoc = OdfSpreadsheetDocument.newSpreadsheetDocument (); OdfOfficeSpreadsheet spreadsheet = odfdoc.getContentRoot (); OdfTable table = (OdfTable) spreadsheet.newTableTableElement (); OdfTableColumn column = (OdfTableColumn) table.newTableTableColumnElement (); column.setTableNumberColumnsRepeatedAttribute (new Integer (4)); for (int i = 0; i <3; i) ( OdfTableRow row = (OdfTableRow) table.newTableTableRowElement (); / / row.setStyleName ( "ro1"); for (int j = 0; j <4; j) ( OdfTableCell cell = (OdfTableCell) row.newTableTableCellElement (); cell.setOfficeValueAttribute (new Double (data [i] [j])); cell.setOfficeValueTypeAttribute ( "float"); cell.setTextContent ((new Double (data [i] [j]). toString ())); ) ) Odfdoc.save (ResourceUtilities.createTestResource ( "table3R4C.ods")); |
表特性经常包含日期和时间值,这些值的表示在各国家和地区会不一样。ODFDOM 不仅提供合适的方法来设置日期和时间格式,还有一些特别的类用于这种格式。
例如,OdfNumberDateStyle 用于处理日期格式,OdfNumberTimeStyle 用来处理时间格式,OdfNumberStyle 用来处理数字格式。
这些样式元素可以放在 <office:automatic-styles> 元素下(见清单 10),如下所示:
OdfOfficeAutomaticStyles autoStyles = odfdoc.getStylesDom (). GetAutomaticStyles (); OdfNumberDateStyle dataStyle = (OdfNumberDateStyle) autoStyles.newNumberDateStyleElement ( "numberDateStyle"); dataStyle.buildFromFormat ( "yyyy-MM-dd"); OdfNumberTimeStyle timeStyle = (OdfNumberTimeStyle) autoStyles.newNumberTimeStyleElement ( "numberTimeStyle"); timeStyle.buildFromFormat ( "hh: mm: ss"); OdfNumberStyle numberStyle = (OdfNumberStyle) autoStyles.newNumberNumberStyleElement ( "numberStyle"); numberStyle.buildFromFormat ( "# 0.00"); |
然后指定单元格样式,然后将其和数字样式对象关联到单元格样式对象(见清单 11):
Cell style for date cells: OdfStyle style; style = autoStyles.newStyle (OdfStyleFamily.TableCell); String dataCellStyleName = style.getStyleNameAttribute (); style.setStyleDataStyleNameAttribute ( "numberDateStyle"); cell.setStyleName (dataCellStyleName); And for time cells: style = autoStyles.newStyle (OdfStyleFamily.TableCell); String timeCellStyleName = style.getStyleNameAttribute (); style.setStyleDataStyleNameAttribute ( "numberTimeStyle"); cell.setStyleName (timeCellStyleName); And for the temperatures: style = autoStyles.newStyle (OdfStyleFamily.TableCell); String numberCellStyleName = style.getStyleNameAttribute (); style.setStyleDataStyleNameAttribute ( "numberStyle"); cell.setStyleName (numberCellStyleNam); |
本例中,创建了一个简单的电子表格,但实际上,电子表格可能会相当复杂。例如,可能有跨行和跨列的单元格,以及大量样式和嵌入对象和媒体的应用程序。
这些复杂的特性可以通过 ODFDOM API 实现,尽管代码会很复杂。但随着 ODFDOM 发展,这些复杂的电子表格的创建会变得越来越简单。
让我们从定义演示 content.xml 中的相关术语开始(见清单 12):
<office:document-content> <office :automatic-style/> <office :body> <office:presentation> <draw:page/> </ office: presentation> </ office: body> </ Office: document-content> |
在本小节中,我们将展示如何创建演示文件、插入幻灯片、列出标题、应用母版页模板,还有保存新幻灯片。
表 1 列举出代码中用到的 ODF 类。
ODF 类 | 用途 |
---|---|
OdfPresentationDocument | 演示文件 |
OdfStyleDom | Style DOM |
OdfOfficePresentation | <office:presentation> 元素 |
OdfOfficeStyles | <office:styles> 文档样式元素 |
OdfOfficeAutomaticStyle | <style:automatic-styles> 放在自动样式中 |
OdfStylePageLayout | <style:page-layout> 定义页面布局 |
OdfOfficeMasterStyles | <office:master-styles> 定义页面的母版样式 |
OdfStyleMasterPage | <style:master-page> 是 <office:master-styles> 子元素,用于定义主样式母版页 |
OdfDrawPage | <draw:page> 元素用于表示一个演示页面或一张幻灯片 |
OdfDrawFrame | <draw:frame> 是一个容器元素,其他元素放置其中 |
步骤如下(见清单 13):
至此,演示文档已创建完成,现在可以保存为 ODP 文档,并使用 OpenOffice 或 IBM Lotus Symphony 打开。
OdfPresentationDocument presentationDoc = OdfPresentationDocument.newPresentationDocument (); OdfOfficePresentation presentation1 = presentationDoc.getContentRoot (); presentationDoc.getOrCreateDocumentStyles (); presentationDoc.getStylesDom (). getAutomaticStyles (). newStylePageLayoutElement ( "PM01"); OdfOfficeMasterStyles officeMasterStyles = presentationDoc.getOfficeMasterStyles (); OdfStyleMasterPage masterPage = (OdfStyleMasterPage) officeMasterStyles. newStyleMasterPageElement ( "master-name-1", "PM01"); OdfDrawPage page4 = (OdfDrawPage) presentation1.newDrawPageElement ( "master-name-1"); OdfDrawFrame frame1 = (OdfDrawFrame) page4.newDrawFrameElement (); frame1.newDrawTextBoxElement (). setTextContent ( "title"); OdfDrawFrame frame2 = (OdfDrawFrame) page4.newDrawFrameElement (); frame2.newDrawImageElement (). setXlinkHrefAttribute ( "http://impage"); presentationDoc.save ( "presentation.odp"); |