现在的位置: 首页 > 综合 > 正文

ODFDOM for Java:简化文档及其数据的程序控制

2014年03月27日 ⁄ 综合 ⁄ 共 11183字 ⁄ 字号 评论关闭

近期要考虑构建一个支持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 文档分四步:

  1. 加载已有 ODF 文档或创建 ODF 文档。
  2. 向 ODF 文档插入内容。
  3. 为信息的不同部分设置样式。
  4. 保存文档。

ODFDOM 现在提供一些 API,借助它们,可以轻松完成这四步主要操作。

在本小节中,我们将演示一个简单用例:读取一个 XML 文件并输出到 ODF 文档。

作为文本文件,content.xml 层级如下(见清单 1):

  • 第一个元素是 <office:body>,它是文档根目录的子元素。
  • 下一级是 <office:text> 元素,它表示所有要保存在输出文档中的文档元素。
  • 在 <office:body> 之前的 <office:automatic-styles> 是文档根目录的另一个子元素,用于保存元素的各种样式信息。


<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 包:

  • OdfTextDocument textDocument:对应于一个文本文件(odt)对象。
  • OdfFileDom contentDom:对应于 content.xml 的 XML 文档对象。
  • OdfFileDom stylesDom:对应于 styles.xml 的 XML 文档对象。

文本文件对象可以使用 API 来获取内容对象(content.xml)和样式对象(styles.xml):

  • OdfFileDom contentDom = textDocument.getContentDom()
  • OdfFileDom stylesDom = textDocument.getStylesDom()

ODFDOM 还提供多个对象来表示各种内容和样式元素:

  • OdfOfficeAutomaticStyles contentAutoStyles:对应 content.xml 中 <office:automatic-styles>。
  • OdfOfficeStyles stylesOfficeStyles:对应 styles.xml 中 <office:styles>。
  • OdfOfficeText officeText:对应 content.xml 中 <office:text>。

用 ODFDOM API 获取内容元素对象和文本元素对象也很容易:

  • OdfOfficeAutomaticStyles autoStyles = textDocument.getContentDom (). GetAutomaticStyles ()
  • OdfOfficeStyles styles = textDocument.getDocumentStyles ();
  • OdfOfficeText text = (OfficeText) textDocument.getContentRoot ();

ODFDOM 还提供一个 API 来对 ODF 文档进行文件级操作,例如,可以用来创建文本文件、加载已有文件以及保存文件:

  • OdfTextDocument odtDoc = OdfTextDocument.newTextDocument (); / / 创建文本文件
  • OdfTextDocument odtDoc = (OdfTextDocument) OdfDocument.loadDocument ( "text.odt"); / / 加载已有文本文件
  • odtDoc.save ( "text.odt") / / 保存文件

当然,文本文件没有丰富的样式属性也不行,如文本字体、段落布局、项目符号。因此,ODFDOM 也有处理这些样式属性的方法:

  • OdfStyle style = odtDoc.getDocumentStyles (). getStyle ( "myStyle", 
    OdfStyleFamily.Paragraph); 
    / / 从文本文件获取用户定义的样式(style)对象
  • style.setProperty (OdfStyleTextProperties.FontWeight, "bold"); 
    / / 将样式设置为特定属性值

设置文本样式属性的代码如清单 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:table> 是表的根元素,表内容的所有元素都是它的子元素
  • <table:column> 指定电子表格的宽度,是默认的样式定义
  • <table:row> 表示表格行,由多个 <table:cell> 元素组成

对于 <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>

其中:

  • OdfTable 对象表示电子表格的表。
  • OdfTableColumn 用来指定电子表格的列,列数通过 TableNumberColumnsRepeatedAttribute 属性值设置。
  • OdfTableRow 用来表示表格行。一行由一个或多个 OdfTableCell 对象组成。
  • OdfTableCell 是组成表格元素的单元,每个单元格对象可以用来放值、段落和其他文本内容;通常需要设置三个属性值:
    • OfficeValueAttribute
    • OfficeValueTypeAttribute
    • TextContent

OfficeValueTypeAttribute 用来指定保存数据的类型(字符、数字、日期、时间、公式等);OfficeValueAttribute 用来保存值;TextContent 用来保存用户看到的值。

现在创建一个三行四列的表格来演示如何对表特性使用 ODFDOM API 操作(见清单 9):

  1. 创建 OdfSpreadsheetDocument 电子表格对象。
  2. 得到 OdfOfficeSpreadsheet 根。
  3. 创建 OdfTable 对象。
  4. 创建 <table:column> 元素,设置 table:number-columns-repeated 属性为表中列数。
  5. 使用循环为每一行创建 <table:row> 元素。
  6. 使用循环为每行中的单元格创建 <table:cell> 元素,并填入值。
  7. 保存电子表格。

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),如下所示:

  1. 用 getStylesDom () 取得 OdfAutomaticStyles 对象。
  2. 创建对应类的对象。
  3. 设置样式对象的具体格式。

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):

  1. 创建 OdfStyle 对象,它的层次是表单元格。
  2. 用 getStyleNameAttribute () 取得日期和数字样式名。
  3. 用 setStyleDataStyleNameAttribute() 设置日期和数字样式名为单元格样式对象的 style:data-stylename 属性。
  4. 将单元格样式应用到具体的单元格。

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:presentation> 电子演示文档的根元素。
  • <draw:page> 是 <office:presentation> 的子元素,表示一张幻灯片。只有图像元素才能保存在 <draw:page> 中,因此文本元素,如 <text:title>,必须先放在 <draw:frame> 下,然后才能放到幻灯片中。
  • <style:master-page> 是一个通用模板页,是 <style:master-style>. Styles 的子元素,例如背景,在母版页中设置,每张幻灯片都可关联到一个母版页;从而,可对其应用对应的模板。


<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):

  1. 创建 OdfPresentationDocument 对象。
  2. 获取 OdfOfficePresentation 对象。
  3. 获取 OdfOfficeStyles 对象,它表示文档样式元素;如果该元素不存在,就创建一个。
  4. 创建 <style:page-layout> 元素;该元素在 OdfOfficeAutomaticStyle 元素下,因此可以使用 getAutomaticStyles () 得到。
  5. 使用 OdfPresentationDocument 类的 getOfficeMasterStyles() 方法获取 OdfOfficeMasterStyles 对象。
  6. 创建 OdfStyleMasterPage 对象,其中我们必须指定母版页名称和布局样式名称。(可以使用第 4 步中的布局样式名称。)
  7. 演示文档由多个幻灯片组成,所以下一步是用 newDrawPageElement 方法(MasterPageStyleName)创建 OdfDrawPage 对象,这里我们可以指定母版页。调用该方法后,母版页就应用到新幻灯片。
  8. 由于只有图形元素能保存到 <draw:page> 中,因此我们需要创建 OdfDrawFrame 对象来添加内容。
  9. 我们创建两个 OdfDrawFrame 对象。一个用来保存标题; 另一个用来保存图片。

至此,演示文档已创建完成,现在可以保存为 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"); 
				

 

 

 

 

抱歉!评论已关闭.