XQuery/DocBook 到 Microsoft Word
您想要从 DocBook 文件创建 Microsoft Word 文档。
构建高质量的 DocBook 到 MS-Word .docx 转换有两个步骤。
- 创建一个 docx 生成器,该生成器使用所有正确组件组装一个 zip 文件
- 创建一个类型切换转换,将每个 DocBook 元素转换为相应的 Open Office XML 格式
在这篇文章中,我们将使用 Microsoft Open_Packaging_Conventions (OPC) 格式创建 zip 文件。OPC 文件可以使用任何桌面解压缩程序打开,但必须以编程方式创建,以确保特定文件放置在正确的顺序。我们将创建几个小的 XQuery 函数,这些函数将从输入 DocBook 5 文件中提取关键元素,并生成 Open Office XML 规范中使用的 XML 文件。然后,我们将使用单个 generate-docx() 函数将所有组件组装到一个 zip 文件中。
另一个转换将遵循非常相似的模式。
本节向您展示使用 XQuery 生成 zip 文件的过程。这取决于您是否拥有一个 zip 函数,该函数允许您指定输出文件的每个组件,并且专门要求输出按文档顺序排列。
输出是一个 zip 文件,包含以下内容
- [Content_Types].xml - 根目录中的单个 XML 文件。此文件必须放在 zip 文件集合中的第一位。
- _rels - 一个文件夹,其中包含单个.rels文件,该文件是一个包含文件之间关系的 XML 文件
- docProps - 一个包含文档属性文件的文件夹。这些通常是 app.xml 和 core.xml 文件
- word - 一个包含所有 word 内容和两个子文件夹的文件夹。典型内容包括
- _rels 文件夹,其中包含单个文件,例如 document.xml.rels
- theme 文件夹,其中包含单个文件,例如 theme1.xml
- document.xml
- fontTable.xml
- settings.xml
- styles.xml
- webSettings.xml
compression:zip( $entries, true() ) 函数接受两个参数。第一个是一系列<entries>元素,每个元素代表我们要创建的每个文件或集合。
以下是构建主[Content_Types].xml文件的条目。
<entry name="[Content_Types].xml" type="xml" method="store">{doc(concat($db2docx:template-collection, '/content-types-template.xml'))}</entry>
以下是如何将文件放在 _rels 文件夹中并使用 .rels 文件名的示例
<entry name="_rels/.rels" type="xml" method="store">{doc(concat($db2docx:template-collection, '/dot-rels.xml'))}</entry>
因此,为了构建 docx 文件,我们使用 compression:zip() 函数“组装”每个<entry>元素,然后使用正确的 MIME 类型和文件名将二进制流返回到 Web 浏览器。此文件将被下载,然后您就可以使用 MS Word 打开它。
declare function db2docx:generate-docx($docbook-input-document as node(), $filename as xs:string) {
(: this has a sequence of <entry> elements that is used by the zip function :)
let $entries :=
(
db2docx:content-type-entry(),
...
db2docx:root-rels())
return
(
response:set-header("Content-Disposition", concat("attachment; filename=", concat($filename, '.docx')))
,
response:stream-binary(
compression:zip( $entries, true() ),
'application/zip',
concat($filename, '.docx')
)
)
};
DocBook 文件非常易于使用,因为整个文档可以存储在一个文件中。DocX 有许多小文件,这些文件存储在 zip 存档中的许多不同位置。
以下是一些示例
核心属性元素是您可能在书籍或文章的书目条目中看到的标准Dublin Core 元数据元素。
<cp:coreProperties xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Dublin Core Metadata Elements -->
<dc:title>Converting DocBook to DocX Format</dc:title>
<dc:subject>Horror Stories</dc:subject>
<dc:creator>Dan McCreary</dc:creator>
<cp:keywords>XML, Docbook, DocX, Conversion, Transformation, TypeSwitch</cp:keywords>
<dc:description>How to convert DocBook to DocX</dc:description>
<cp:lastModifiedBy>Dan McCreary</cp:lastModifiedBy>
<cp:revision>1</cp:revision>
<dcterms:created xsi:type="dcterms:W3CDTF">2012-05-04T13:35:00Z</dcterms:created>
<dcterms:modified xsi:type="dcterms:W3CDTF">2012-05-04T13:35:00Z</dcterms:modified>
</cp:coreProperties>
以下是一个 XQuery 函数示例,该函数将填充应用程序属性 XML 文件中的节数。
declare function db2docx:app-properties($docbook-input-document as node()) {
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"
xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
<SectionCount>{count($docbook-input-document//sect1)}</SectionCount>
<Application>DocBook 5 to Microsoft Open Office XML (DocX) Converter by Dan McCreary of Kelly-McCreary & Associates</Application>
<DocSecurity>0</DocSecurity>
<Lines>1</Lines>
<Paragraphs>1</Paragraphs>
<ScaleCrop>false</ScaleCrop>
<Company>Kelly-McCreary & Associates</Company>
<LinksUpToDate>false</LinksUpToDate>
<CharactersWithSpaces>12</CharactersWithSpaces>
<SharedDoc>false</SharedDoc>
<HyperlinksChanged>false</HyperlinksChanged>
<AppVersion>0.1</AppVersion>
</Properties>
};
将您的 DocBook 元素映射到 Open Office XML 格式将根据您使用的 DocBook 元素以及 Word 模板结构而有所不同。本教程示例将演示以下元素的映射
- article
- article title
- sect1
- sect 1 title
- para
- figure
我们将从一个 DocBook 5 章开始,它有两个级别 1 节,每个节有两个级别 2 子节,每个子节有两个段落。
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0">
<title>Chapter Title</title>
<subtitle>Chapter Subtitle</subtitle>
<para>This is the body introductory text of the chapter</para>
<sect1>
<title>Level 1 Title 1</title>
<subtitle>Section 1 Subtitle</subtitle>
<para>This is the text of the first paragraph of the first level 1 article section.</para>
<para>This is the text of the second paragraph of the first level 1 article section.</para>
<sect2>
<title>Level 2 Title 1.1</title>
<para>This is the text of the first paragraph of the first level 2 article sub-section.</para>
<para>This is the text of the second paragraph of the first level 2 article sub-section.</para>
</sect2>
<sect2>
<title>Level 2 Title 1.2</title>
<para>This is the text of the first paragraph of the second level 2 article sub-section.</para>
<para>This is the text of the second paragraph of the second level 2 article sub-section.</para>
</sect2>
</sect1>
<sect1>
<title>Level 1 Title 2</title>
<subtitle>Section 1 Subtitle</subtitle>
<para>This is the text of the first paragraph of the first level 1 article section.</para>
<para>This is the text of the second paragraph of the first level 1 article section.</para>
<sect2>
<title>Section 2 Title 2.1</title>
<para>This is the text of the first paragraph of the first level 2 article sub-section.</para>
<para>This is the text of the second paragraph of the first level 2 article sub-section.</para>
</sect2>
<sect2>
<title>Section 2 Title 2.2</title>
<para>This is the text of the first paragraph of the second level 2 article sub-section.</para>
<para>This is the text of the second paragraph of the second level 2 article sub-section.</para>
</sect2>
</sect1>
</chapter>
Open Office XML 使用复杂的 XML 结构来存储文本主体。段落被分解为“运行”,然后在这些运行元素中包含文本。以下结构是示例
<w:document xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"
xmlns:w10="urn:schemas-microsoft-com:office:word"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006">
<w:body>
<w:p>
<w:r>
<w:t>Hello World!</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
现在我们准备深入研究元素逐元素的转换。
结构如下
declare function db2docx:main($content as node()*) as item()* {
for $node in $content
(: let $log := util:log-system-out(concat('In main with ', count($node//node()), ' elements')) :)
return
typeswitch($node)
case text() return $node
(:
case element(article) return db2h:article($node)
case element(book) return db2h:book($node) :)
(: each of these is responsible for handling its own title, paragraphs and subsections :)
case element(db:chapter) return db2docx:chapter($node)
case element(db:sect1) return db2docx:sect1($node)
case element(db:sect2) return db2docx:sect2($node)
....
default return db2docx:null()
};
将您的 DocBook 元素映射到 Open Office XML 格式将根据您使用的 DocBook 元素以及 Word 模板结构而有所不同。本教程示例将演示以下元素的映射
- article
- article title
- sect1
- sect 1 title
- para
- figure
- 等等
主要“调度”函数将到达每个高级元素的节点。然后它将转到专门与该元素相关的函数。通常,函数名称与元素名称相同。
在转换的每个级别,您都输入了需要的元素数据,然后为每个子元素调用主函数。这使您可以专门输入已知存在的结构,并避免必须根据您在树中的位置查找元素的上下文。例如,标题元素在章、sect1 和 sect2 节中始终使用。当您到达标题元素时,您可以查找父元素名称,但通常更容易在刚到达的节中输入元素。
declare function db2docx:sect1($sect1 as node()) as node()* {
(
<!-- sect1 -->,
<w:p>
<w:pPr>
<w:pStyle w:val="Heading1"/>
</w:pPr>
<w:r>
<w:t>{$sect1/db:title/text()}</w:t>
</w:r>
</w:p>,
db2docx:main($sect1/db:para),
db2docx:main($sect1/db:sect2)
)
};
DocBook 图表具有以下示例结构
<figure>
<title>Figure Caption</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/my-image.png" scale="50" contentwidth="500"/>
</imageobject>
</mediaobject>
</figure>
在上面的示例中,我们将所有文章的图片存储在 images 集合中,直接存储在存储主文章 XML 文件的集合中。我们还将图片缩放到原始尺寸的 50% 或将内容宽度设置为固定像素数。
以下是 docx 格式图片的等效结构
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="1714286" cy="514286"/>
<wp:effectExtent l="19050" t="0" r="214" b="0"/>
<wp:docPr id="1" name="Picture 0" descr="nosql-logo.png"/>
<wp:cNvGraphicFramePr>
<a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
noChangeAspect="1"/>
</wp:cNvGraphicFramePr>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData
uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic
xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="my-image-file.png"/>
<pic:cNvPicPr/>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId4" cstate="print"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr>
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="1714286" cy="514286"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
二进制图片必须放置在 word/media 集合中。
Microsoft 文档还为每个段落、运行和文本包含大量的修订属性或“RSIDS”。当有多个作者进行更改并且需要使用修订审阅系统跟踪更改时,会使用这些属性。通过为文本的每个组件分配随机 ID 号,可以更方便地查看跟踪的更改。
<w:p w:rsidR="00D910F7" w:rsidRDefault="00CB02EF" w:rsidP="00CB02EF">
<w:pPr>
<w:pStyle w:val="Title"/>
</w:pPr>
<w:r>
<w:t>Document Title</w:t>
</w:r>
</w:p>
<w:p w:rsidR="00CB02EF" w:rsidRDefault="00CB02EF" w:rsidP="00CB02EF">
<w:pPr>
<w:pStyle w:val="Heading1"/>
</w:pPr>
<w:r>
<w:t>I am heading</w:t>
</w:r>
</w:p>
<w:p w:rsidR="00CB02EF" w:rsidRDefault="00CB02EF">
<w:r>
<w:t>This is the body text for a paragraph.</w:t>
</w:r>
</w:p>
您可以通过转到 Microsoft Word 选项,然后转到信任中心,然后选择“隐私设置”(尽管这与隐私无关)并取消选中“存储随机数以提高合并准确性”来禁用 RSIDS 的生成。