简体中文 繁體中文 English 日本語 Deutsch 한국 사람 بالعربية TÜRKÇE português คนไทย Français

站内搜索

搜索

活动公告

11-02 12:46
10-23 09:32
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,将及时处理!
10-23 09:31
10-23 09:28
通知:签到时间调整为每日4:00(东八区)
10-23 09:26

XSL FO文档索引制作全指南从基础入门到高级应用技巧详解如何利用这一强大工具创建专业文档索引提升信息检索效率

3万

主题

349

科技点

3万

积分

大区版主

木柜子打湿

积分
31898

三倍冰淇淋无人之境【一阶】财Doro小樱(小丑装)立华奏以外的星空【二阶】⑨的冰沙

发表于 2025-9-10 19:10:01 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

XSL FO(Extensible Stylesheet Language Formatting Objects)是一种强大的标记语言,专门用于格式化XML数据,特别适合创建高质量的打印文档和PDF文件。在专业文档制作过程中,索引是一个至关重要的组成部分,它不仅能显著提升信息检索效率,还能大大改善用户体验。本文将全面介绍如何使用XSL FO创建专业级的文档索引,从基础概念到高级应用技巧,帮助读者掌握这一强大工具,提升文档的专业性和可用性。

XSL FO基础

什么是XSL FO?

XSL FO是W3C推荐的一种标准,用于描述文档的格式和布局。它是XSL(Extensible Stylesheet Language)的一部分,专门负责文档的呈现层面。与HTML或CSS不同,XSL FO更加关注打印媒体和分页文档的精确控制。

XSL FO文档结构

一个基本的XSL FO文档由以下部分组成:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
  3.   <fo:layout-master-set>
  4.     <!-- 定义页面布局 -->
  5.   </fo:layout-master-set>
  6.   
  7.   <fo:page-sequence master-reference="main">
  8.     <fo:flow flow-name="xsl-region-body">
  9.       <!-- 文档内容 -->
  10.     </fo:flow>
  11.   </fo:page-sequence>
  12. </fo:root>
复制代码

XSL FO处理流程

XSL FO文档通常通过以下流程生成:

1. 创建源XML文档
2. 编写XSLT样式表将XML转换为XSL FO
3. 使用XSL FO处理器(如Apache FOP或RenderX)将XSL FO转换为PDF或其他格式

索引的基本概念

什么是索引?

索引是文档末尾的一个有序列表,包含重要术语、概念和它们在文档中出现的位置。良好的索引应该:

• 全面:覆盖文档中的重要内容
• 精确:准确指向相关内容
• 一致:使用统一的术语和格式
• 用户友好:考虑到用户的检索习惯

索引的类型

常见的索引类型包括:

1. 主题索引:按主题或概念组织
2. 作者索引:按作者姓名组织
3. 图表索引:列出所有图表及其位置
4. 法律文档索引:按法规、案例等组织

使用XSL FO创建简单索引

准备工作

在创建索引之前,我们需要:

1. 在源XML文档中标记索引条目
2. 创建XSLT样式表提取这些条目
3. 在XSL FO中格式化索引

标记索引条目

首先,在源XML文档中添加索引标记:
  1. <book>
  2.   <chapter>
  3.     <title>Introduction</title>
  4.     <para>This is about <index-term term="XSL FO"/>XSL FO formatting.</para>
  5.     <para>More text about <index-term term="Formatting Objects"/>Formatting Objects.</para>
  6.   </chapter>
  7. </book>
复制代码

创建基本索引XSLT

接下来,创建XSLT样式表来提取和格式化索引:
  1. <xsl:stylesheet version="1.0"
  2.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3.   xmlns:fo="http://www.w3.org/1999/XSL/Format">
  4.   
  5.   <!-- 提取所有索引条目 -->
  6.   <xsl:key name="index-terms" match="index-term" use="@term"/>
  7.   
  8.   <!-- 主模板 -->
  9.   <xsl:template match="/">
  10.     <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
  11.       <fo:layout-master-set>
  12.         <fo:simple-page-master master-name="main" page-height="29.7cm" page-width="21cm">
  13.           <fo:region-body margin="2cm"/>
  14.         </fo:simple-page-master>
  15.       </fo:layout-master-set>
  16.       
  17.       <fo:page-sequence master-reference="main">
  18.         <fo:flow flow-name="xsl-region-body">
  19.           <!-- 处理文档内容 -->
  20.           <xsl:apply-templates select="book"/>
  21.          
  22.           <!-- 生成索引 -->
  23.           <fo:block break-before="page" font-size="18pt" font-weight="bold" margin-bottom="12pt">
  24.             Index
  25.           </fo:block>
  26.           <xsl:call-template name="generate-index"/>
  27.         </fo:flow>
  28.       </fo:page-sequence>
  29.     </fo:root>
  30.   </xsl:template>
  31.   
  32.   <!-- 生成索引的模板 -->
  33.   <xsl:template name="generate-index">
  34.     <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  35.       <xsl:sort select="@term"/>
  36.       <fo:block margin-bottom="6pt">
  37.         <xsl:value-of select="@term"/>
  38.         <xsl:text>, </xsl:text>
  39.         <xsl:for-each select="key('index-terms', @term)">
  40.           <xsl:value-of select="ancestor::chapter/title"/>
  41.           <xsl:if test="position() != last()">, </xsl:if>
  42.         </xsl:for-each>
  43.       </fo:block>
  44.     </xsl:for-each>
  45.   </xsl:template>
  46.   
  47.   <!-- 处理其他元素 -->
  48.   <xsl:template match="chapter">
  49.     <fo:block break-before="page" font-size="16pt" font-weight="bold" margin-bottom="10pt">
  50.       <xsl:value-of select="title"/>
  51.     </fo:block>
  52.     <xsl:apply-templates select="para"/>
  53.   </xsl:template>
  54.   
  55.   <xsl:template match="para">
  56.     <fo:block margin-bottom="8pt">
  57.       <xsl:apply-templates/>
  58.     </fo:block>
  59.   </xsl:template>
  60.   
  61.   <xsl:template match="index-term">
  62.     <!-- 索引术语在正文中不显示 -->
  63.     <xsl:apply-templates/>
  64.   </xsl:template>
  65. </xsl:stylesheet>
复制代码

这个基本示例会创建一个简单的索引,列出所有标记的术语及其出现的章节。但我们可以进一步改进和完善它。

索引的高级功能

分组索引条目

在专业索引中,通常需要将相关术语分组。我们可以通过修改XSLT来实现这一点:
  1. <xsl:template name="generate-index">
  2.   <!-- 按首字母分组 -->
  3.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  4.     <xsl:sort select="@term"/>
  5.     <xsl:variable name="current-letter" select="substring-upper(@term, 1, 1)"/>
  6.     <xsl:if test="not(preceding-sibling::index-term[substring-upper(@term, 1, 1) = $current-letter])">
  7.       <fo:block font-weight="bold" margin-top="12pt" margin-bottom="6pt">
  8.         <xsl:value-of select="$current-letter"/>
  9.       </fo:block>
  10.     </xsl:if>
  11.    
  12.     <fo:block margin-left="12pt" margin-bottom="6pt">
  13.       <xsl:value-of select="@term"/>
  14.       <xsl:text>, </xsl:text>
  15.       <xsl:for-each select="key('index-terms', @term)">
  16.         <xsl:value-of select="ancestor::chapter/title"/>
  17.         <xsl:if test="position() != last()">, </xsl:if>
  18.       </xsl:for-each>
  19.     </fo:block>
  20.   </xsl:for-each>
  21. </xsl:template>
复制代码

多级索引

对于复杂文档,可能需要多级索引:
  1. <!-- 在源XML中添加多级索引标记 -->
  2. <para>This is about <index-term term="XSL FO" subterm="basic concepts"/>XSL FO basic concepts.</para>
  3. <para>More text about <index-term term="XSL FO" subterm="advanced features"/>XSL FO advanced features.</para>
  4. <!-- 修改XSLT以处理多级索引 -->
  5. <xsl:key name="index-terms" match="index-term" use="@term"/>
  6. <xsl:key name="subterms" match="index-term" use="concat(@term, '::', @subterm)"/>
  7. <xsl:template name="generate-index">
  8.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  9.     <xsl:sort select="@term"/>
  10.     <xsl:variable name="current-term" select="@term"/>
  11.    
  12.     <!-- 主术语 -->
  13.     <fo:block margin-bottom="6pt">
  14.       <xsl:value-of select="@term"/>
  15.       <xsl:text>, </xsl:text>
  16.       <xsl:for-each select="key('index-terms', @term)[not(@subterm)]">
  17.         <xsl:value-of select="ancestor::chapter/title"/>
  18.         <xsl:if test="position() != last()">, </xsl:if>
  19.       </xsl:for-each>
  20.     </fo:block>
  21.    
  22.     <!-- 子术语 -->
  23.     <xsl:for-each select="//index-term[@term = $current-term and @subterm][generate-id() = generate-id(key('subterms', concat(@term, '::', @subterm))[1])]">
  24.       <xsl:sort select="@subterm"/>
  25.       <fo:block margin-left="12pt" margin-bottom="6pt">
  26.         <xsl:value-of select="@subterm"/>
  27.         <xsl:text>, </xsl:text>
  28.         <xsl:for-each select="key('subterms', concat(@term, '::', @subterm))">
  29.           <xsl:value-of select="ancestor::chapter/title"/>
  30.           <xsl:if test="position() != last()">, </xsl:if>
  31.         </xsl:for-each>
  32.       </fo:block>
  33.     </xsl:for-each>
  34.   </xsl:for-each>
  35. </xsl:template>
复制代码

交叉引用

索引中的交叉引用可以帮助用户找到相关术语:
  1. <!-- 在源XML中添加交叉引用 -->
  2. <index-term term="Formatting Objects" see="XSL FO"/>
  3. <!-- 修改XSLT以处理交叉引用 -->
  4. <xsl:template name="generate-index">
  5.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  6.     <xsl:sort select="@term"/>
  7.     <fo:block margin-bottom="6pt">
  8.       <xsl:value-of select="@term"/>
  9.       <xsl:choose>
  10.         <xsl:when test="@see">
  11.           <xsl:text> see </xsl:text>
  12.           <xsl:value-of select="@see"/>
  13.         </xsl:when>
  14.         <xsl:otherwise>
  15.           <xsl:text>, </xsl:text>
  16.           <xsl:for-each select="key('index-terms', @term)">
  17.             <xsl:value-of select="ancestor::chapter/title"/>
  18.             <xsl:if test="position() != last()">, </xsl:if>
  19.           </xsl:for-each>
  20.         </xsl:otherwise>
  21.       </xsl:choose>
  22.     </fo:block>
  23.   </xsl:for-each>
  24. </xsl:template>
复制代码

页码范围

当术语在连续多页出现时,使用页码范围更为简洁:
  1. <xsl:template name="generate-index">
  2.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  3.     <xsl:sort select="@term"/>
  4.     <fo:block margin-bottom="6pt">
  5.       <xsl:value-of select="@term"/>
  6.       <xsl:text>, </xsl:text>
  7.       
  8.       <!-- 获取并排序页码 -->
  9.       <xsl:variable name="pages">
  10.         <xsl:for-each select="key('index-terms', @term)">
  11.           <page>
  12.             <xsl:value-of select="ancestor::chapter/title"/>
  13.           </page>
  14.         </xsl:for-each>
  15.       </xsl:variable>
  16.       
  17.       <!-- 输出页码或范围 -->
  18.       <xsl:call-template name="format-page-ranges">
  19.         <xsl:with-param name="pages" select="$pages"/>
  20.       </xsl:call-template>
  21.     </fo:block>
  22.   </xsl:for-each>
  23. </xsl:template>
  24. <xsl:template name="format-page-ranges">
  25.   <xsl:param name="pages"/>
  26.   <xsl:param name="position" select="1"/>
  27.   
  28.   <xsl:if test="$position &lt;= count($pages/page)">
  29.     <xsl:variable name="current" select="$pages/page[$position]"/>
  30.     <xsl:variable name="next" select="$pages/page[$position + 1]"/>
  31.    
  32.     <xsl:choose>
  33.       <xsl:when test="$next and $next = $current + 1">
  34.         <!-- 开始一个范围 -->
  35.         <xsl:value-of select="$current"/>
  36.         <xsl:text>–</xsl:text>
  37.         
  38.         <!-- 找到范围的结束 -->
  39.         <xsl:call-template name="find-range-end">
  40.           <xsl:with-param name="pages" select="$pages"/>
  41.           <xsl:with-param name="start" select="$position + 1"/>
  42.           <xsl:with-param name="expected" select="$current + 2"/>
  43.         </xsl:call-template>
  44.       </xsl:when>
  45.       <xsl:otherwise>
  46.         <xsl:value-of select="$current"/>
  47.         <xsl:if test="$position &lt; count($pages/page)">
  48.           <xsl:text>, </xsl:text>
  49.         </xsl:if>
  50.       </xsl:otherwise>
  51.     </xsl:choose>
  52.    
  53.     <!-- 处理下一页 -->
  54.     <xsl:call-template name="format-page-ranges">
  55.       <xsl:with-param name="pages" select="$pages"/>
  56.       <xsl:with-param name="position" select="$position + 1"/>
  57.     </xsl:call-template>
  58.   </xsl:if>
  59. </xsl:template>
  60. <xsl:template name="find-range-end">
  61.   <xsl:param name="pages"/>
  62.   <xsl:param name="start"/>
  63.   <xsl:param name="expected"/>
  64.   
  65.   <xsl:choose>
  66.     <xsl:when test="$start &lt;= count($pages/page) and $pages/page[$start] = $expected">
  67.       <!-- 继续查找 -->
  68.       <xsl:call-template name="find-range-end">
  69.         <xsl:with-param name="pages" select="$pages"/>
  70.         <xsl:with-param name="start" select="$start + 1"/>
  71.         <xsl:with-param name="expected" select="$expected + 1"/>
  72.       </xsl:call-template>
  73.     </xsl:when>
  74.     <xsl:otherwise>
  75.       <!-- 输出范围结束 -->
  76.       <xsl:value-of select="$pages/page[$start - 1]"/>
  77.       <xsl:if test="$start &lt;= count($pages/page)">
  78.         <xsl:text>, </xsl:text>
  79.       </xsl:if>
  80.       
  81.       <!-- 继续处理剩余页码 -->
  82.       <xsl:call-template name="format-page-ranges">
  83.         <xsl:with-param name="pages" select="$pages"/>
  84.         <xsl:with-param name="position" select="$start"/>
  85.       </xsl:call-template>
  86.     </xsl:otherwise>
  87.   </xsl:choose>
  88. </xsl:template>
复制代码

索引样式定制

字体和间距

自定义索引的字体和间距可以提升可读性:
  1. <xsl:template name="generate-index">
  2.   <fo:block font-family="Arial, sans-serif" font-size="10pt">
  3.     <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  4.       <xsl:sort select="@term"/>
  5.       <fo:block margin-bottom="4pt" text-indent="-12pt" start-indent="12pt">
  6.         <fo:inline font-weight="bold">
  7.           <xsl:value-of select="@term"/>
  8.         </fo:inline>
  9.         <fo:inline>
  10.           <xsl:text>, </xsl:text>
  11.           <xsl:for-each select="key('index-terms', @term)">
  12.             <xsl:value-of select="ancestor::chapter/title"/>
  13.             <xsl:if test="position() != last()">, </xsl:if>
  14.           </xsl:for-each>
  15.         </fo:inline>
  16.       </fo:block>
  17.     </xsl:for-each>
  18.   </fo:block>
  19. </xsl:template>
复制代码

分栏布局

对于大型索引,使用分栏布局可以节省空间并提高可读性:
  1. <fo:flow flow-name="xsl-region-body">
  2.   <!-- 处理文档内容 -->
  3.   <xsl:apply-templates select="book"/>
  4.   
  5.   <!-- 生成索引 -->
  6.   <fo:block break-before="page" font-size="18pt" font-weight="bold" margin-bottom="12pt">
  7.     Index
  8.   </fo:block>
  9.   
  10.   <fo:block-container>
  11.     <fo:block font-family="Arial, sans-serif" font-size="10pt">
  12.       <fo:table table-layout="fixed" width="100%">
  13.         <fo:table-column column-width="50%"/>
  14.         <fo:table-column column-width="50%"/>
  15.         <fo:table-body>
  16.           <fo:table-row>
  17.             <fo:table-cell>
  18.               <fo:block>
  19.                 <!-- 第一列索引 -->
  20.                 <xsl:call-template name="generate-index-column">
  21.                   <xsl:with-param name="terms" select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])][position() mod 2 = 1]"/>
  22.                 </xsl:call-template>
  23.               </fo:block>
  24.             </fo:table-cell>
  25.             <fo:table-cell>
  26.               <fo:block>
  27.                 <!-- 第二列索引 -->
  28.                 <xsl:call-template name="generate-index-column">
  29.                   <xsl:with-param name="terms" select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])][position() mod 2 = 0]"/>
  30.                 </xsl:call-template>
  31.               </fo:block>
  32.             </fo:table-cell>
  33.           </fo:table-row>
  34.         </fo:table-body>
  35.       </fo:table>
  36.     </fo:block>
  37.   </fo:block-container>
  38. </fo:flow>
  39. <xsl:template name="generate-index-column">
  40.   <xsl:param name="terms"/>
  41.   <xsl:for-each select="$terms">
  42.     <xsl:sort select="@term"/>
  43.     <fo:block margin-bottom="4pt" text-indent="-12pt" start-indent="12pt">
  44.       <fo:inline font-weight="bold">
  45.         <xsl:value-of select="@term"/>
  46.       </fo:inline>
  47.       <fo:inline>
  48.         <xsl:text>, </xsl:text>
  49.         <xsl:for-each select="key('index-terms', @term)">
  50.           <xsl:value-of select="ancestor::chapter/title"/>
  51.           <xsl:if test="position() != last()">, </xsl:if>
  52.         </xsl:for-each>
  53.       </fo:inline>
  54.     </fo:block>
  55.   </xsl:for-each>
  56. </xsl:template>
复制代码

制表符和对齐

使用制表符可以确保页码对齐:
  1. <xsl:template name="generate-index">
  2.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  3.     <xsl:sort select="@term"/>
  4.     <fo:block margin-bottom="4pt" text-align-last="justify">
  5.       <fo:inline font-weight="bold">
  6.         <xsl:value-of select="@term"/>
  7.       </fo:inline>
  8.       <fo:leader leader-pattern="dots"/>
  9.       <fo:inline>
  10.         <xsl:for-each select="key('index-terms', @term)">
  11.           <xsl:value-of select="ancestor::chapter/title"/>
  12.           <xsl:if test="position() != last()">, </xsl:if>
  13.         </xsl:for-each>
  14.       </fo:inline>
  15.     </fo:block>
  16.   </xsl:for-each>
  17. </xsl:template>
复制代码

自动化索引生成

使用XSLT自动标记索引条目

对于大型文档,手动标记每个索引条目可能很耗时。我们可以编写XSLT自动识别并标记潜在索引条目:
  1. <xsl:stylesheet version="1.0"
  2.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.   
  4.   <!-- 定义应该被索引的术语列表 -->
  5.   <xsl:variable name="index-terms" select="document('index-terms.xml')/terms/term"/>
  6.   
  7.   <!-- 处理文本节点,查找索引术语 -->
  8.   <xsl:template match="text()">
  9.     <xsl:param name="text" select="."/>
  10.     <xsl:choose>
  11.       <!-- 检查是否有匹配的索引术语 -->
  12.       <xsl:when test="$index-terms[contains($text, .)]">
  13.         <xsl:call-template name="process-text">
  14.           <xsl:with-param name="text" select="$text"/>
  15.         </xsl:call-template>
  16.       </xsl:when>
  17.       <xsl:otherwise>
  18.         <xsl:value-of select="$text"/>
  19.       </xsl:otherwise>
  20.     </xsl:choose>
  21.   </xsl:template>
  22.   
  23.   <!-- 处理文本,添加索引标记 -->
  24.   <xsl:template name="process-text">
  25.     <xsl:param name="text"/>
  26.    
  27.     <!-- 找到最早出现的索引术语 -->
  28.     <xsl:variable name="earliest-term">
  29.       <xsl:for-each select="$index-terms[contains($text, .)]">
  30.         <term pos="{string-length(substring-before($text, .))}" value="{.}"/>
  31.       </xsl:for-each>
  32.     </xsl:variable>
  33.    
  34.     <xsl:variable name="first-term" select="$earliest-term/term[@pos = min($earliest-term/term/@pos)][1]"/>
  35.    
  36.     <!-- 输出术语前的文本 -->
  37.     <xsl:value-of select="substring($text, 1, $first-term/@pos)"/>
  38.    
  39.     <!-- 添加索引标记 -->
  40.     <index-term term="{$first-term/@value}">
  41.       <xsl:value-of select="$first-term/@value"/>
  42.     </index-term>
  43.    
  44.     <!-- 处理剩余文本 -->
  45.     <xsl:call-template name="process-text">
  46.       <xsl:with-param name="text" select="substring-after($text, $first-term/@value)"/>
  47.     </xsl:call-template>
  48.   </xsl:template>
  49.   
  50.   <!-- 复制其他元素 -->
  51.   <xsl:template match="@*|node()">
  52.     <xsl:copy>
  53.       <xsl:apply-templates select="@*|node()"/>
  54.     </xsl:copy>
  55.   </xsl:template>
  56. </xsl:stylesheet>
复制代码

使用正则表达式识别索引术语

更高级的方法是使用正则表达式识别索引术语:
  1. <xsl:stylesheet version="2.0"
  2.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3.   xmlns:xs="http://www.w3.org/2001/XMLSchema"
  4.   xmlns:my="http://example.com/my"
  5.   exclude-result-prefixes="xs my">
  6.   
  7.   <!-- 定义索引术语的正则表达式模式 -->
  8.   <xsl:variable name="index-pattern" select="'\b(XSL FO|Formatting Objects|XML|XPath)\b'"/>
  9.   
  10.   <!-- 处理文本节点 -->
  11.   <xsl:template match="text()">
  12.     <xsl:analyze-string select="." regex="{$index-pattern}">
  13.       <xsl:matching-substring>
  14.         <index-term term="{.}">
  15.           <xsl:value-of select="."/>
  16.         </index-term>
  17.       </xsl:matching-substring>
  18.       <xsl:non-matching-substring>
  19.         <xsl:value-of select="."/>
  20.       </xsl:non-matching-substring>
  21.     </xsl:analyze-string>
  22.   </xsl:template>
  23.   
  24.   <!-- 复制其他元素 -->
  25.   <xsl:template match="@*|node()">
  26.     <xsl:copy>
  27.       <xsl:apply-templates select="@*|node()"/>
  28.     </xsl:copy>
  29.   </xsl:template>
  30. </xsl:stylesheet>
复制代码

性能优化

处理大型文档索引

对于大型文档,索引生成可能会变得很慢。以下是一些优化技巧:

1. 使用键(key)提高效率:
  1. <xsl:key name="index-terms" match="index-term" use="@term"/>
复制代码

1. 分阶段处理:
  1. <xsl:stylesheet version="1.0"
  2.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3.   xmlns:fo="http://www.w3.org/1999/XSL/Format"
  4.   xmlns:idx="http://example.com/index"
  5.   exclude-result-prefixes="idx">
  6.   
  7.   <!-- 第一阶段:收集索引条目 -->
  8.   <xsl:variable name="index-entries">
  9.     <idx:entries>
  10.       <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  11.         <xsl:sort select="@term"/>
  12.         <idx:entry term="{@term}">
  13.           <xsl:for-each select="key('index-terms', @term)">
  14.             <idx:location>
  15.               <xsl:value-of select="ancestor::chapter/title"/>
  16.             </idx:location>
  17.           </xsl:for-each>
  18.         </idx:entry>
  19.       </xsl:for-each>
  20.     </idx:entries>
  21.   </xsl:variable>
  22.   
  23.   <!-- 第二阶段:生成文档和索引 -->
  24.   <xsl:template match="/">
  25.     <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
  26.       <!-- 文档内容 -->
  27.       <fo:layout-master-set>
  28.         <fo:simple-page-master master-name="main" page-height="29.7cm" page-width="21cm">
  29.           <fo:region-body margin="2cm"/>
  30.         </fo:simple-page-master>
  31.       </fo:layout-master-set>
  32.       
  33.       <fo:page-sequence master-reference="main">
  34.         <fo:flow flow-name="xsl-region-body">
  35.           <!-- 处理文档内容 -->
  36.           <xsl:apply-templates select="book"/>
  37.          
  38.           <!-- 生成索引 -->
  39.           <fo:block break-before="page" font-size="18pt" font-weight="bold" margin-bottom="12pt">
  40.             Index
  41.           </fo:block>
  42.           <xsl:apply-templates select="$index-entries"/>
  43.         </fo:flow>
  44.       </fo:page-sequence>
  45.     </fo:root>
  46.   </xsl:template>
  47.   
  48.   <!-- 处理索引条目 -->
  49.   <xsl:template match="idx:entries">
  50.     <fo:block font-family="Arial, sans-serif" font-size="10pt">
  51.       <xsl:apply-templates select="idx:entry"/>
  52.     </fo:block>
  53.   </xsl:template>
  54.   
  55.   <xsl:template match="idx:entry">
  56.     <fo:block margin-bottom="4pt" text-indent="-12pt" start-indent="12pt">
  57.       <fo:inline font-weight="bold">
  58.         <xsl:value-of select="@term"/>
  59.       </fo:inline>
  60.       <fo:inline>
  61.         <xsl:text>, </xsl:text>
  62.         <xsl:for-each select="idx:location">
  63.           <xsl:value-of select="."/>
  64.           <xsl:if test="position() != last()">, </xsl:if>
  65.         </xsl:for-each>
  66.       </fo:inline>
  67.     </fo:block>
  68.   </xsl:template>
  69.   
  70.   <!-- 处理其他元素 -->
  71.   <xsl:template match="chapter">
  72.     <fo:block break-before="page" font-size="16pt" font-weight="bold" margin-bottom="10pt">
  73.       <xsl:value-of select="title"/>
  74.     </fo:block>
  75.     <xsl:apply-templates select="para"/>
  76.   </xsl:template>
  77.   
  78.   <xsl:template match="para">
  79.     <fo:block margin-bottom="8pt">
  80.       <xsl:apply-templates/>
  81.     </fo:block>
  82.   </xsl:template>
  83.   
  84.   <xsl:template match="index-term">
  85.     <!-- 索引术语在正文中不显示 -->
  86.     <xsl:apply-templates/>
  87.   </xsl:template>
  88. </xsl:stylesheet>
复制代码

1. 使用XSLT 2.0或更高版本:XSLT 2.0及更高版本提供了更强大的功能和更好的性能。

内存优化

对于非常大的文档,内存可能成为问题。以下是一些减少内存使用的技巧:

1. 使用SAX解析器:SAX解析器比DOM解析器更节省内存。
2. 分块处理:将文档分成较小的块处理:

使用SAX解析器:SAX解析器比DOM解析器更节省内存。

分块处理:将文档分成较小的块处理:
  1. <xsl:stylesheet version="2.0"
  2.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3.   xmlns:fo="http://www.w3.org/1999/XSL/Format"
  4.   xmlns:xs="http://www.w3.org/2001/XMLSchema"
  5.   exclude-result-prefixes="xs">
  6.   
  7.   <!-- 使用collection()函数处理多个文件 -->
  8.   <xsl:template name="process-large-document">
  9.     <xsl:for-each select="collection('docs/?select=*.xml')">
  10.       <xsl:apply-templates select="."/>
  11.     </xsl:for-each>
  12.   </xsl:template>
  13. </xsl:stylesheet>
复制代码

1. 使用流处理:XSLT 3.0支持流处理,可以处理非常大的文件而不会耗尽内存。

实际案例研究

技术手册索引

技术手册通常需要详细的索引,包括术语、命令、函数等:
  1. <!-- 示例:技术手册索引条目 -->
  2. <para>The <index-term term="configure" subterm="command"/>configure command
  3. is used to set system parameters. See also
  4. <index-term term="setup" subterm="process" see="configure"/>setup process.</para>
  5. <!-- 技术手册索引XSLT -->
  6. <xsl:template name="generate-technical-index">
  7.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  8.     <xsl:sort select="@term"/>
  9.     <fo:block margin-bottom="6pt">
  10.       <fo:inline font-weight="bold" font-family="Courier New, monospace">
  11.         <xsl:value-of select="@term"/>
  12.       </fo:inline>
  13.       <xsl:choose>
  14.         <xsl:when test="@see">
  15.           <xsl:text> see </xsl:text>
  16.           <fo:inline font-family="Courier New, monospace">
  17.             <xsl:value-of select="@see"/>
  18.           </fo:inline>
  19.         </xsl:when>
  20.         <xsl:otherwise>
  21.           <xsl:text>, </xsl:text>
  22.           <xsl:for-each select="key('index-terms', @term)[not(@subterm)]">
  23.             <xsl:value-of select="ancestor::chapter/title"/>
  24.             <xsl:if test="position() != last()">, </xsl:if>
  25.           </xsl:for-each>
  26.         </xsl:otherwise>
  27.       </xsl:choose>
  28.     </fo:block>
  29.    
  30.     <!-- 子术语 -->
  31.     <xsl:for-each select="//index-term[@term = current()/@term and @subterm][generate-id() = generate-id(key('subterms', concat(@term, '::', @subterm))[1])]">
  32.       <xsl:sort select="@subterm"/>
  33.       <fo:block margin-left="12pt" margin-bottom="6pt">
  34.         <fo:inline font-family="Courier New, monospace">
  35.           <xsl:value-of select="@subterm"/>
  36.         </fo:inline>
  37.         <xsl:choose>
  38.           <xsl:when test="@see">
  39.             <xsl:text> see </xsl:text>
  40.             <fo:inline font-family="Courier New, monospace">
  41.               <xsl:value-of select="@see"/>
  42.             </fo:inline>
  43.           </xsl:when>
  44.           <xsl:otherwise>
  45.             <xsl:text>, </xsl:text>
  46.             <xsl:for-each select="key('subterms', concat(@term, '::', @subterm))">
  47.               <xsl:value-of select="ancestor::chapter/title"/>
  48.               <xsl:if test="position() != last()">, </xsl:if>
  49.             </xsl:for-each>
  50.           </xsl:otherwise>
  51.         </xsl:choose>
  52.       </fo:block>
  53.     </xsl:for-each>
  54.   </xsl:for-each>
  55. </xsl:template>
复制代码

法律文档索引

法律文档索引需要精确引用和特殊的格式:
  1. <!-- 示例:法律文档索引条目 -->
  2. <para>According to <index-term term="Copyright Act" section="107(3)"/>Copyright Act § 107(3),
  3. fair use includes the purpose and character of the use. See also
  4. <index-term term="Fair Use" subterm="four factors" see="Copyright Act, 107(3)"/>.</para>
  5. <!-- 法律文档索引XSLT -->
  6. <xsl:template name="generate-legal-index">
  7.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  8.     <xsl:sort select="@term"/>
  9.     <fo:block margin-bottom="6pt">
  10.       <fo:inline font-weight="bold" font-style="italic">
  11.         <xsl:value-of select="@term"/>
  12.       </fo:inline>
  13.       <xsl:choose>
  14.         <xsl:when test="@see">
  15.           <xsl:text> see </xsl:text>
  16.           <fo:inline font-style="italic">
  17.             <xsl:value-of select="@see"/>
  18.           </fo:inline>
  19.         </xsl:when>
  20.         <xsl:otherwise>
  21.           <xsl:text>, </xsl:text>
  22.           <xsl:for-each select="key('index-terms', @term)[not(@subterm)]">
  23.             <xsl:value-of select="ancestor::chapter/title"/>
  24.             <xsl:if test="@section">
  25.               <xsl:text> §</xsl:text>
  26.               <xsl:value-of select="@section"/>
  27.             </xsl:if>
  28.             <xsl:if test="position() != last()">, </xsl:if>
  29.           </xsl:for-each>
  30.         </xsl:otherwise>
  31.       </xsl:choose>
  32.     </fo:block>
  33.    
  34.     <!-- 子术语 -->
  35.     <xsl:for-each select="//index-term[@term = current()/@term and @subterm][generate-id() = generate-id(key('subterms', concat(@term, '::', @subterm))[1])]">
  36.       <xsl:sort select="@subterm"/>
  37.       <fo:block margin-left="12pt" margin-bottom="6pt">
  38.         <fo:inline font-style="italic">
  39.           <xsl:value-of select="@subterm"/>
  40.         </fo:inline>
  41.         <xsl:choose>
  42.           <xsl:when test="@see">
  43.             <xsl:text> see </xsl:text>
  44.             <fo:inline font-style="italic">
  45.               <xsl:value-of select="@see"/>
  46.             </fo:inline>
  47.           </xsl:when>
  48.           <xsl:otherwise>
  49.             <xsl:text>, </xsl:text>
  50.             <xsl:for-each select="key('subterms', concat(@term, '::', @subterm))">
  51.               <xsl:value-of select="ancestor::chapter/title"/>
  52.               <xsl:if test="@section">
  53.                 <xsl:text> §</xsl:text>
  54.                 <xsl:value-of select="@section"/>
  55.               </xsl:if>
  56.               <xsl:if test="position() != last()">, </xsl:if>
  57.             </xsl:for-each>
  58.           </xsl:otherwise>
  59.         </xsl:choose>
  60.       </fo:block>
  61.     </xsl:for-each>
  62.   </xsl:for-each>
  63. </xsl:template>
复制代码

学术论文索引

学术论文索引可能需要包括作者、主题和方法等多种索引:
  1. <!-- 示例:学术论文索引条目 -->
  2. <para>As <index-term type="author" name="Smith"/>Smith (2020) argues,
  3. <index-term type="subject" term="qualitative research"/>qualitative research
  4. provides deeper insights into <index-term type="method" term="interviews"/>interviews.</para>
  5. <!-- 学术论文索引XSLT -->
  6. <xsl:key name="author-index" match="index-term[@type='author']" use="@name"/>
  7. <xsl:key name="subject-index" match="index-term[@type='subject']" use="@term"/>
  8. <xsl:key name="method-index" match="index-term[@type='method']" use="@term"/>
  9. <xsl:template name="generate-academic-index">
  10.   <!-- 作者索引 -->
  11.   <fo:block font-size="14pt" font-weight="bold" margin-top="12pt" margin-bottom="8pt">
  12.     Author Index
  13.   </fo:block>
  14.   <xsl:for-each select="//index-term[@type='author'][generate-id() = generate-id(key('author-index', @name)[1])]">
  15.     <xsl:sort select="@name"/>
  16.     <fo:block margin-bottom="4pt">
  17.       <xsl:value-of select="@name"/>
  18.       <xsl:text>, </xsl:text>
  19.       <xsl:for-each select="key('author-index', @name)">
  20.         <xsl:value-of select="ancestor::chapter/title"/>
  21.         <xsl:if test="position() != last()">, </xsl:if>
  22.       </xsl:for-each>
  23.     </fo:block>
  24.   </xsl:for-each>
  25.   
  26.   <!-- 主题索引 -->
  27.   <fo:block font-size="14pt" font-weight="bold" margin-top="12pt" margin-bottom="8pt">
  28.     Subject Index
  29.   </fo:block>
  30.   <xsl:for-each select="//index-term[@type='subject'][generate-id() = generate-id(key('subject-index', @term)[1])]">
  31.     <xsl:sort select="@term"/>
  32.     <fo:block margin-bottom="4pt">
  33.       <xsl:value-of select="@term"/>
  34.       <xsl:text>, </xsl:text>
  35.       <xsl:for-each select="key('subject-index', @term)">
  36.         <xsl:value-of select="ancestor::chapter/title"/>
  37.         <xsl:if test="position() != last()">, </xsl:if>
  38.       </xsl:for-each>
  39.     </fo:block>
  40.   </xsl:for-each>
  41.   
  42.   <!-- 方法索引 -->
  43.   <fo:block font-size="14pt" font-weight="bold" margin-top="12pt" margin-bottom="8pt">
  44.     Method Index
  45.   </fo:block>
  46.   <xsl:for-each select="//index-term[@type='method'][generate-id() = generate-id(key('method-index', @term)[1])]">
  47.     <xsl:sort select="@term"/>
  48.     <fo:block margin-bottom="4pt">
  49.       <xsl:value-of select="@term"/>
  50.       <xsl:text>, </xsl:text>
  51.       <xsl:for-each select="key('method-index', @term)">
  52.         <xsl:value-of select="ancestor::chapter/title"/>
  53.         <xsl:if test="position() != last()">, </xsl:if>
  54.       </xsl:for-each>
  55.     </fo:block>
  56.   </xsl:for-each>
  57. </xsl:template>
复制代码

常见问题和解决方案

问题1:索引条目排序不正确

问题:索引条目没有按字母顺序正确排序,特别是涉及特殊字符或不同语言时。

解决方案:使用适当的排序规则和语言设置:
  1. <xsl:sort select="@term" lang="en" data-type="text" collation="http://www.w3.org/2013/collation/UCA?lang=en"/>
复制代码

问题2:页码引用不准确

问题:索引中的页码与实际内容位置不匹配。

解决方案:确保在最终生成阶段使用正确的页码引用,可能需要两遍处理:
  1. <!-- 第一遍:收集所有索引条目 -->
  2. <xsl:variable name="raw-index">
  3.   <xsl:for-each select="//index-term">
  4.     <entry term="{@term}" page="{generate-id()}"/>
  5.   </xsl:for-each>
  6. </xsl:variable>
  7. <!-- 第二遍:将生成的页码与索引条目匹配 -->
  8. <xsl:template name="generate-index">
  9.   <xsl:for-each select="$raw-index/entry[generate-id() = generate-id(key('index-terms', @term)[1])]">
  10.     <xsl:sort select="@term"/>
  11.     <fo:block margin-bottom="4pt">
  12.       <xsl:value-of select="@term"/>
  13.       <xsl:text>, </xsl:text>
  14.       <xsl:for-each select="key('index-terms', @term)">
  15.         <xsl:variable name="page-id" select="@page"/>
  16.         <xsl:value-of select="id($page-id)/@page-number"/>
  17.         <xsl:if test="position() != last()">, </xsl:if>
  18.       </xsl:for-each>
  19.     </fo:block>
  20.   </xsl:for-each>
  21. </xsl:template>
复制代码

问题3:索引条目重复

问题:同一术语在索引中出现多次,而不是合并为一个条目。

解决方案:确保使用键(key)正确分组相同的术语:
  1. <xsl:key name="index-terms" match="index-term" use="normalize-space(translate(@term, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))"/>
  2. <xsl:template name="generate-index">
  3.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', normalize-space(translate(@term, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')))[1])]">
  4.     <xsl:sort select="normalize-space(translate(@term, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))"/>
  5.     <!-- 其余索引生成代码 -->
  6.   </xsl:for-each>
  7. </xsl:template>
复制代码

问题4:索引格式不一致

问题:索引中的术语格式不一致,有些使用大写,有些使用小写。

解决方案:在生成索引时统一术语格式:
  1. <xsl:template name="generate-index">
  2.   <xsl:for-each select="//index-term[generate-id() = generate-id(key('index-terms', @term)[1])]">
  3.     <xsl:sort select="@term"/>
  4.     <fo:block margin-bottom="4pt">
  5.       <!-- 首字母大写,其余小写 -->
  6.       <xsl:value-of select="concat(upper-case(substring(@term, 1, 1)), lower-case(substring(@term, 2)))"/>
  7.       <xsl:text>, </xsl:text>
  8.       <!-- 其余代码 -->
  9.     </fo:block>
  10.   </xsl:for-each>
  11. </xsl:template>
复制代码

问题5:索引生成速度慢

问题:对于大型文档,索引生成过程非常缓慢。

解决方案:优化XSLT代码并使用更高效的处理器:
  1. <!-- 使用XSLT 2.0或更高版本,并优化键的使用 -->
  2. <xsl:stylesheet version="2.0"
  3.   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4.   xmlns:fo="http://www.w3.org/1999/XSL/Format">
  5.   
  6.   <!-- 使用更高效的键定义 -->
  7.   <xsl:key name="index-terms" match="index-term" use="@term"/>
  8.   
  9.   <!-- 使用xsl:for-each-group而不是generate-id()检查 -->
  10.   <xsl:template name="generate-index">
  11.     <xsl:for-each-group select="//index-term" group-by="@term">
  12.       <xsl:sort select="@term"/>
  13.       <fo:block margin-bottom="4pt">
  14.         <xsl:value-of select="@term"/>
  15.         <xsl:text>, </xsl:text>
  16.         <xsl:value-of select="current-group()/ancestor::chapter/title" separator=", "/>
  17.       </fo:block>
  18.     </xsl:for-each-group>
  19.   </xsl:template>
  20. </xsl:stylesheet>
复制代码

结论和最佳实践

XSL FO提供了创建专业文档索引的强大功能。通过掌握本文介绍的技术和技巧,您可以创建出结构清晰、格式统一、易于使用的专业索引,显著提升文档的信息检索效率和用户体验。

最佳实践总结

1. 规划索引结构:在开始之前,仔细规划索引的结构和层次,确保它符合用户的需求和期望。
2. 使用一致的术语:确保索引中使用一致的术语和格式,避免混淆。
3. 考虑用户需求:从用户的角度思考,他们可能会如何搜索信息,并相应地组织索引。
4. 自动化索引生成:尽可能使用自动化工具生成索引,减少手动工作量和错误。
5. 测试和验证:在最终发布前,彻底测试索引,确保所有条目准确无误且格式一致。
6. 优化性能:对于大型文档,使用适当的优化技术确保索引生成过程高效。
7. 保持更新:随着文档的更新,及时更新索引,确保其始终准确反映文档内容。

规划索引结构:在开始之前,仔细规划索引的结构和层次,确保它符合用户的需求和期望。

使用一致的术语:确保索引中使用一致的术语和格式,避免混淆。

考虑用户需求:从用户的角度思考,他们可能会如何搜索信息,并相应地组织索引。

自动化索引生成:尽可能使用自动化工具生成索引,减少手动工作量和错误。

测试和验证:在最终发布前,彻底测试索引,确保所有条目准确无误且格式一致。

优化性能:对于大型文档,使用适当的优化技术确保索引生成过程高效。

保持更新:随着文档的更新,及时更新索引,确保其始终准确反映文档内容。

通过遵循这些最佳实践,您可以充分利用XSL FO的强大功能,创建出专业、高效、用户友好的文档索引,为读者提供卓越的信息检索体验。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

频道订阅

频道订阅

加入社群

加入社群

联系我们|TG频道|RSS

Powered by Pixtech

© 2025 Pixtech Team.