 | 级别: 初级 Stefanus Wiguna , 顾问软件工程师, IBM
2009 年 8 月 03 日 利用 Mozilla Cross Platform Component Object Model (XPCOM) 框架,您可以将现有的 XML 内容动态导出到一个 OpenOffice 文档。这个过程对转换机制所支持的其他类型的内容,例如 XSLT,同样有效。在本文中,了解一个简便经济的服务器端解决方案的替代方案。
简介
OpenOffice 是一种备受欢迎的、可替代目前市场上那些价格不菲的商业办公软件套装的 —免费— 开源产品。如果没有适当的工具,创建内容或将现有内容导出到 OpenOffice 是件很费神的事。
通常,动态创建一个 OpenOffice 文档或将现有内容导出到一个 OpenOffice 文档会涉及下面的操作:
- 一个在服务器上运行的 Web 应用程序,用来将内容转变成 OpenDocument Format
(ODF)。
- 将生成的内容压缩成 OpenOffice 归档文件。
- 将文件保存在某个地方或提供给用户。
但倘若您无法访问服务器端应用程序,那该怎么办? 或者您只是想通过将这个过程转移到客户机来减小服务器负荷,又该怎么办?
 | | 本文不准备对 XPCOM、Firefox Extension、OpenOffice、OpenDocument Format 和 XSLT 做深入的探讨。更多这方面的内容,请参见 参考资料。 |
|
在本文中,我们将学习如何使用 Mozilla 中的 Cross Platform Component Object Model
(XPCOM) 框架来提高应用程序开发的速度。本文还提供了一些示例来说明如何用 Firefox Extension 中的 XPCOM 组件动态地将现有 XML 内容导出到 OpenOffice 文档。此外,能够导出的不仅仅局限于 XML;只要受转换机制支持(例如,Extensible Stylesheet Language Transformations
(XSLT)),其他类型的内容也可以这么处理。
在将内容动态地导出到 OpenOffice 前,我们先介绍一下构建一个 OpenOffice 文档的要求是什么。
OpenOffice 文档
OpenOffice 文档实际上是一个压缩文件,其中包括一组用来描述这个文档不同部分(例如,内容和类型)的 XML 文件、一个清单和一个缩略图。
若将一个 OpenOffice 文本文档 (.odt) 重命名为一个使用 .zip 扩展名的文件,然后解压缩这个文件,得到的文件结构将如图 1 所示。
图 1.
OpenOffice 文档结构
表 1 简单介绍了这些文件。
表 1. OpenOffice
文档文件
| 文件 | 描述 |
|---|
| content.xml | 包括此文档的实际内容。 |
|---|
| meta.xml | 包含元数据信息,例如创建日期和作者。 | | styles.xml | 为段落、字符等定义格式化类型。 |
要创建一个新的 OpenOffice 文档,最简单或许也是最安全的方法就是编辑一个现有的文档。在本文中,我们将只更新保存有文档实际内容的 content.xml 文件。content.xml 如图 2 所示。
图 2.
OpenOffice 文档的 content.xml
对于我们的示例,我们不妨将实际内容之前的一节称为内容头、实际内容之后的一节称为内容尾。实际内容自身作为内容体。每构建一个新的 OpenOffice 文档,我们都将使用这几个部分。
准备开发环境
要向 OpenOffice 导出内容,需要在 Firefox Extension 开发环境中做些准备。由于我们需要通过编辑一个现有文件来创建一个新的 OpenOffice 文档,因此您需要做以下事情:
- 将
图 1 中的所有文件包括在 Firefox Extension 包内。我们需要用这些文件来构建新的 OpenOffice 文档。
- 将这些文件保存在 Firefox Extension 的 chrome 文件夹外。这是为了确保这些文件在扩展打包期间不被压缩,以便在后面可以很容易地检索到它们。
下面是一个如何将文件保存进 Firefox Extension 的示例。
图 3. Firefox Extension 结构中的 OpenOffice 文档文件
所有文件都可以保存到同一个文件夹中。当创建 OpenOffice 文档时,可以将每个文件放到归档文件内的恰当位置。xml2odt.xsl 是一个定制的样式表文件,我们将使用这个文件来将现有内容转换为 ODF。
下一步是设置几个变量和常量,用于整个 OpenOffice 文档生成过程中的文件操作,例如定位和检索文件。清单 1 是一个示例。我们需要使用 nsIExtensionManager
XPCOM 接口来设置变量和常量。
清单 1. 设置变量和常量
var eid = "export2OO"; // extension id
var em = Components.classes["@mozilla.org/extensions/manager;1"]
.getService(Components.interfaces.nsIExtensionManager);
var oopath = "export/oo/"; // path to the OpenOffice document files
var ooxslt = "xml2odt.xsl";
const PR_WRONLY = 0x02;
const PR_RDWR = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_APPEND = 0x10;
const PR_TRUNCATE = 0x20;
const PR_USEC_PER_MSEC = 1000;
const time = Date.now();
|
只要能够被 Firefox Extension 访问到,本文中的这些代码可以被放到任何地方。通常,可以将代码保存到 chrome 文件夹中的一个 JavaScript 文件 (.js),并将其包括进 Firefox Extension 的主 XUL 文件,如下所示。
清单 2. 将代码包含进 Firefox Extension
<overlay id="export2OO"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript;version=1.7"
src="chrome://export2OO/content/export.js" />
|
至此开发环境已经准备完毕,下面就要进入正题了。
以编程的方式构建一个 OpenOffice 文档
要将现有内容动态导出到一个 OpenOffice 文档,涉及到如下三个主要任务:
将现有内容转换为 OpenDocument 格式
用 Extensible
Stylesheet Language Transformations (XSLT),可以很容易地将现有内容转换为 ODF。例如,清单 3 显示了 XML 格式的一些内容。
清单 3. XML 格式的内容
<h1>This is Heading 1</h1>
<h3>This is Heading 3</h3>
|
清单 4 显示了 ODF 格式的同样的内容。
清单 4. ODF 格式的内容
<text:h xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
text:style="Heading1" text:outline-level="1">
This is Heading 1
</text:h>
<text:h xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
text:style="Heading3" text:outline-level="3">
This is Heading 3
</text:h>
|
ODF 规范提供了关于定义和格式化样式的所有信息(参见 参考资料)。一旦样式表文件准备就绪,就可以在 Firefox Extension 环境中进行格式转换了。使用扩展管理器,定位用来进行格式转换的样式表文件。清单 5 是一个示例。
清单 5. 定位 XSLT 文件
// locate stylesheet file
var xslfile = em.getInstallLocation(eid).getItemFile(eid, oopath + ooxslt);
|
接下来,加载这个样式表文件。
清单 6. 加载 XSLT 文件
var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
var xsldata = "";
fstream.init(xslfile, -1, 0, 0);
sstream.init(fstream);
var tmp = sstream.read(4096);
while (tmp.length > 0) {
xsldata += tmp;
tmp = sstream.read(4096);
}
sstream.close();
fstream.close();
var parser = new DOMParser();
var xsldoc = parser.parseFromString(xsldata,"text/xml");
|
可以用这个样式表来将内容转换为 ODF。在下面的示例中,xmldoc 是包含现有内容的 XML 文档。
清单 7. 用 XSLT 将内容转换为 ODF
var xsltprocessor = new XSLTProcessor();
xsltprocessor.importStylesheet(xsldoc);
var newcontentdoc = xsltprocessor.transformToFragment(xmldoc,document);
|
现在,可以用新内容更新 content.xml 了。要制作一个有效的 OpenOffice content.xml,需要包含恰当的内容头和内容尾(参见 图 2)。
清单 8. 用新内容更新 content.xml
// prepare new content
var contentbody = (new XMLSerializer()).serializeToString(newcontentdoc);
var newcontent = xmlheader + contenthead + contentbody + contenttail
// update content.xml
var contentfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "content.xml");
var fstream = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
fstream.init(contentfile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0666, 0);
fstream.write(newcontent, newcontent.length);
fstream.close();
|
现在,content.xml 已经更新好了。在下面的章节,我们将构建一个新的 OpenOffice 文档。
构建 OpenOffice 文档
要构建一个新的 OpenOffice 文档,需要创建一个新的 OpenOffice 归档文件并将 content.xml 和其他请求文件添加到这个归档文件中。本文的这个示例使用 nsIZipWriter XPCOM 接口来达到这个目的。
首先是要创建这个归档文件并将它保存到用户机器上的某个地方。下面的示例将这个新文件保存到了桌面区域,如清单 9 所示。根据要求,可能还需要创建一个临时文件或将它保存到其他地方。
清单 9. 创建一个新的 OpenOffice 文档归档文件
var odtfile = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("Desk", Components.interfaces.nsIFile);
odtfile.append("new.odt");
odtfile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
var zipWriter = Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter");
var odt = new zipWriter ();
odt.open(odtfile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE | PR_APPEND); |
表 2 显示了一些可代替 Desk 的常用值。
表 2. 用来保存文件的常见目录
| 文件 | 描述 |
|---|
| ProfD | Firefox Profile 目录 |
|---|
| Desk | Desktop 目录(例如,Linux® 上的 ~/Desktop,Windows® 上的 C:\Documents 和 Settings\username\Desktop)
| | Home | 操作系统根目录(例如,Linux 上的 /root,Windows 上的 C:\) | | TmpD | 操作系统临时目录
|
第二步是将所需的 OpenOffice 文档文件添加到这个归档文件。要记住,需要将每个文件添加到 图 1 所示的这个结构中的恰当位置。
清单 10. 将 OpenOffice 文档添加到归档文件
// locate file
var contentfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "content.xml");
// add file to archive
odt.addEntryFile("content.xml",
Components.interfaces.nsIZipWriter.COMPRESSION_NONE,
contentfile, false);
// create directory
odt.addEntryDirectory("META-INF", time * PR_USEC_PER_MSEC, false);
// locate file
var manifestfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "manifest.xml");
// add file to specific directory in archive
odt.addEntryFile("META-INF/manifest.xml",
Components.interfaces.nsIZipWriter.COMPRESSION_NONE,
manifestfile, false);
odt.addEntryDirectory("Thumbnails", time * PR_USEC_PER_MSEC, false);
var thumbfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "thumbnail.png");
odt.addEntryFile("Thumbnails/thumbnail.png",
Components.interfaces.nsIZipWriter.COMPRESSION_NONE,
thumbfile, false);
var metafile = em.getInstallLocation(eid).getItemFile(eid, oopath + "meta.xml");
odt.addEntryFile("meta.xml",
Components.interfaces.nsIZipWriter.COMPRESSION_NONE,
metafile, false);
var mimetypefile = em.getInstallLocation(eid).getItemFile(eid, oopath + "mimetype");
odt.addEntryFile("mimetype",
Components.interfaces.nsIZipWriter.COMPRESSION_NONE,
mimetypefile, false);
var settingsfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "settings.xml");
odt.addEntryFile("settings.xml",
Components.interfaces.nsIZipWriter.COMPRESSION_NONE,
settingsfile, false);
var stylesfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "styles.xml");
odt.addEntryFile("styles.xml",
Components.interfaces.nsIZipWriter.COMPRESSION_NONE,
stylesfile, false);
// close the archive
odt.close();
|
现在新的 OpenOffice 文档已经创建完成,可以启动它了。
启动 OpenOffice
文档
启动可随意进行,因为新文档已经被保存到桌面上。新文档可以随时被手动打开,也可以将它用作其他过程的输入。
如果文件关联设置得恰当,那么下面示例中的代码将会自动打开 OpenOffice 中新创建的文档。
清单 11. 自动地启动 OpenOffice 文档
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var odtURL = ios.newFileURI(odt);
location.href = odtURL.spec;
|
如果文件关联尚未设置,会出现类似如下所示的一个提示窗口。
图 4. 打开 OpenOffice
文档
这就是所有操作步骤。
当构建并安装了 Firefox Extension 后,本文中的这些代码将为您在客户机上提供 Export to OpenOffice 功能。
结束语
XPCOM 的丰富库可以加快应用程序的开发速度,并让您可以在客户端做以前只能在服务器端做的事。您现在就有了服务器端解决方案的一个简便经济的替代方案了。
参考资料 学习
获得产品和技术
讨论
关于作者  | 
|  | Stefanus Wiguna 是位于美国北卡罗来纳州 Research Triangle Park 的 IBM Software
Group 的一名顾问软件工程师。他是一名 Principal Certified Lotus
Professional,在使用 Lotus、Web 2.0 和 Java 技术进行解决方案开发和集成方面具有 10 多年的经验。 |
对本文的评价
|  |