最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【全部解决】Docbook生成的PDF中callout不能点击跳转(而HTML中却可以)

Docbook crifan 2409浏览 0评论

【问题】

已经用xsltproc+fop实现了callout,在PDF和HTML中都可以正常显示了。

详见:【已解决】Docbook中的callout图片在programlisting中不显示 -> xsltproc不支持areaspec

但是PDF中的callout,却不能像HTML中一样可以点击实现跳转。

即,HTML中是可以通过点击callout图标,实现源码的位置和callou注释t的位置之间互相跳转的:

HTML can click to jump

但是PDF中虽然是显示出来callout图片了,但是却不能点击实现跳转:

pdf can not click to jump

其中对应源码如下:

<programlistingco>
    <programlisting language="c">
......
#define NAND_MFR_TOSHIBA	0x98
#define NAND_MFR_SAMSUNG	0xec<co id="co.mfr_samsung_id" linkends="co.note.mfr_samsung_id" />
......
    </programlisting>
    <calloutlist>
        <callout id="co.note.mfr_samsung_id" arearefs="co.mfr_samsung_id" >
            <para>我们最常读到的生产厂家的id:0xEC,就是对应这里的Samsung,表示此款nand flash是三星家的。</para>
        </callout>
    </calloutlist>
</programlistingco>

【解决过程】

1.去官网中找了关于FO的xsl配置参数:

Part 2. FO Parameter Reference

但是也没找到,如何可以控制co去生成链接的参数。

2.参考:

http://www.sagehill.net/docbookxsl/AnnotateListing.html

去把co的id改为xml:id,看看是否有效果。

结果没效果。

3.也看到上面哪里提到了,对应的fo的xsl配置文件,对于callout的话,是:

docbook-xsl-ns-1.76.1\fo\callout.xsl

也找到了对应负责给callout编号部分的内容。

其中也看到一些关于coref中有linkend的部分,但是关于对于co的话,如何生成链接,以支持点击callout图片实现跳转的功能,还是没搞懂。

4.不过,在callout.xsl中,处理co的那部分的内容,好像就只有这部分:

<xsl:template match="d:co">
  <fo:inline>
    <xsl:call-template name="anchor"/>
    <xsl:apply-templates select="." mode="callout-bug"/>
  </fo:inline>
</xsl:template>

其中,用anchor去生成对应的链接。但是具体内部逻辑还是没搞懂。

5.后来去找了相关的html的xsl文件:

docbook-xsl-ns-1.76.1\html\callout.xsl

然后发现其中的确有对于co中的id的相关处理:

<xsl:template match="d:co" name="co">
  <!-- Support a single linkend in HTML -->
  <xsl:variable name="targets" select="key('id', @linkends)"/>
  <xsl:variable name="target" select="$targets[1]"/>
  <xsl:choose>
    <xsl:when test="$target">
      <a>
        <xsl:apply-templates select="." mode="common.html.attributes"/>
        <xsl:if test="@id or @xml:id">
          <xsl:attribute name="name">
            <xsl:value-of select="(@id|@xml:id)[1]"/>
          </xsl:attribute>
        </xsl:if>
        <xsl:attribute name="href">
          <xsl:call-template name="href.target">
            <xsl:with-param name="object" select="$target"/>
          </xsl:call-template>
        </xsl:attribute>
        <xsl:apply-templates select="." mode="callout-bug"/>
      </a>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="anchor"/>
      <xsl:apply-templates select="." mode="callout-bug"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

其中会给对应的co,根据对应的id,添加对应的href,这样使得html中可以实现点击callout bug图片,实现跳转。

而上面贴出来的对应的fo的callout.xsl中,就没有对应处理,看来问题就出在这里了。

不过暂时对于如何添加代码,以实现支持fo内部的跳转,还是需要去额外学习一下xslt的语法才可以。

6.关于callout,官方的解释:Callouts,已经的确是足够详细了,只可惜,没有说到后期处理的问题,如何才能让xsltproc+fop所生成的fo中支持双向引用跳转。

7.后来实在没辙了,只能想办法,自己找其他办法。

想到了,可以尝试去参考目前pdf中已有的一些点击实现跳转的内容,看看其内部的fo的xslt的源码是如何的,然后看看是否可以仿照着去给co添加对应的配置。

然后就找到了pdf中目录的某个链接:pdf中已有的 目录中的链接

在fo文件中找到了对应的源码:

<fo:block text-align-last="justify" text-align="start" end-indent="24pt" last-line-end-indent="-24pt">
<fo:inline keep-with-next.within-line="always">
    <fo:basic-link internal-destination="ch01_some_jargon">
        1. 看此文之前,一些有必要先解释的术语
    </fo:basic-link>
</fo:inline>
<fo:inline keep-together.within-line="always">
    <fo:leader leader-pattern="dots" leader-pattern-width="3pt" leader-alignment="reference-area" keep-with-next.within-line="always"/>
    <fo:basic-link internal-destination="ch01_some_jargon">
        <fo:page-number-citation ref-id="ch01_some_jargon"/>
    </fo:basic-link>
 </fo:inline>
</fo:block>

然后照葫芦画瓢,去将原先fo中的:

#define NAND_MFR_SAMSUNG	0xec<fo:inline id="co.mfr_samsung_id"><fo:external-graphic content-width="7pt" width="7pt" src="url(images/system/callouts/1.svg)"/></fo:inline>

改为:

#define NAND_MFR_SAMSUNG	0xec<fo:inline id="co.mfr_samsung_id"><fo:basic-link internal-destination="co.note.mfr_samsung_id"><fo:external-graphic content-width="7pt" width="7pt" src="url(images/system/callouts/1.svg)"/></fo:basic-link></fo:inline>

然后用此fo去生成的pdf,就可以实现点击callout图片跳转到对应callout注释内容的位置了:

手动添加link后 pdf中也可以实现点击跳转了

所以,剩下的事情,就是去如何写好xslt的语句,实现添加此对应的fo:basic-link,即可。

8.经过一番照葫芦画瓢的折腾,在自己的fo的xsl配置文件docbook_crl.xsl中,添加了如下配置:

<!--============================================================================
callout setting
=============================================================================-->
<!-- from docbook-xsl-ns-1.76.1\fo\callout.xsl -->
<xsl:template match="d:co">
    <fo:inline>
        <fo:basic-link>
            <xsl:if test="@linkends">
              <xsl:attribute name="internal-destination">
                <xsl:value-of select="@linkends"/>
              </xsl:attribute>
            </xsl:if>
            
            <xsl:call-template name="anchor"/>
            <xsl:apply-templates select="." mode="callout-bug"/>
        </fo:basic-link>
    </fo:inline>
</xsl:template>

然后成功的实现了,在源码中的callout图片,可以点击实现跳转到对应的callout注释的位置了。

但是callout注释部分的链接支持,暂时还不支持.等有空再看看是否能添加进来。

9.后来继续折腾,把原先callout注释部分的fo源码:

<fo:block><fo:external-graphic content-width="7pt" width="7pt" src="url(images/system/callouts/1.svg)"/></fo:block>

也添加了fo:basic-link,改为:

<fo:block><fo:basic-link internal-destination="co.mfr_samsung_id"><fo:external-graphic content-width="7pt" width="7pt" src="url(images/system/callouts/1.svg)"/></fo:basic-link></fo:block>

编出的pdf,实现了需要的效果,即,点击callout注释部分callout bug图片,也可以跳转到对应的源码中的位置了:

pdf中 点击callout注释的图片 也可以跳转到源码了

这样,通过手动修改fo文件源码,至少验证了此条路是通的,是可以实现pdf中的callout的双向的链接的,效果为:

pdf中 最终就实现了callout的双向链接支持了

然后就是去看看是否能改对应的xsl配置文件,添加对应的配置了。

注:关于internal-destination,找到一个对应的解释的:link to locations inside XSL FO,需要了解fo页内链接方面的知识的话,可以好好去看看。

 

10.然后就开始分析fo的源码所对应的xsl的配置是在哪里。

fo:external-graphic对应的配置,很容易找到,就是在callout.xsl中的:

<xsl:template name="callout-bug">

然后将其拷贝过来,照葫芦画瓢去给fo:external-graphic上层,包裹一个fo:basic-link,然后设置internal-destination的值,但却发现无法获得对应的arearefs的值。

然后折腾了半天,终于发现,其实此处的callout-bug,只是负责画出对应的callout bug的那个小图片,而对应的给此图片添加链接,即加fo:basic-link的话,是通过

<xsl:template match="d:programlistingco|d:screenco">

中使用

<xsl:apply-templates select="d:calloutlist"/>

去调用对应的calloutlist来处理的。

但是刚开始找了半天,一直没有找到对应的calloutlist是在哪里配置的。

后来终于找到了,是在与callout.xsl同路径下面的list.xsl中的

<xsl:template match="d:calloutlist">

然后其会通过

<xsl:apply-templates select="d:callout

                                |comment()[preceding-sibling::d:callout]

                                |processing-instruction()[preceding-sibling::d:callout]"/>

去调用对应的callout,对应配置是:

<xsl:template match="d:callout">

其中再去调用

<xsl:call-template name="callout.arearefs">

对应着:

<xsl:template name="callout.arearefs">

其中再调用具体的单个的arearef:

<xsl:call-template name="callout.arearef">

对应着

<xsl:template name="callout.arearef">

至此为止,才会用:

<xsl:apply-templates select="$target" mode="callout-bug"/>

去真正的会去调用上面的那个callout-bug,然后画出callout bug图片(或者unicode字符)的。

而对于想要给callout bug图片加链接,也就是在这里,添加对应的fo:basic-link,设置对应的属性internal-destination,即可。

对应的源码为:

<!-- from docbook-xsl-ns-1.76.1\fo\lists.xsl -->
<xsl:template name="callout.arearef">
  <xsl:param name="arearef"></xsl:param>
  <xsl:variable name="targets" select="key('id',$arearef)"/>
  <xsl:variable name="target" select="$targets[1]"/>

  <xsl:choose>
    <xsl:when test="count($target)=0">
      <xsl:value-of select="$arearef"/>
      <xsl:text>: ???</xsl:text>
    </xsl:when>
    <xsl:when test="local-name($target)='co'">
      <!-- added by crifan start -->
      <fo:basic-link>
        <xsl:attribute name="internal-destination">
          <xsl:value-of select="$arearef"/>
        </xsl:attribute>
        <!-- added by crifan end -->
        
        <xsl:apply-templates select="$target" mode="callout-bug"/>
      <!-- added by crifan start -->
      </fo:basic-link>
      <!-- added by crifan end -->
    </xsl:when>
    <xsl:when test="local-name($target)='areaset'">
      <xsl:call-template name="callout-bug">
        <xsl:with-param name="conum">
          <xsl:apply-templates select="$target" mode="conumber"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="local-name($target)='area'">
      <xsl:choose>
        <xsl:when test="$target/parent::d:areaset">
          <xsl:call-template name="callout-bug">
            <xsl:with-param name="conum">
              <xsl:apply-templates select="$target/parent::d:areaset"
                                   mode="conumber"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="callout-bug">
            <xsl:with-param name="conum">
              <xsl:apply-templates select="$target" mode="conumber"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>???</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

此时,最终生成的pdf中,callout注释部分的callout bug图片,才可以实现支持点击以跳转到对应的programlisting中源码的位置的。

【总结】

为了使得让pdf中的callout bug可以支持点击跳转,支持源码和注释间互相跳转,真的是费了九牛二虎之力啊。

最终的解决办法是,添加如下的配置:

<!--============================================================================
callout setting
=============================================================================-->
<!-- from docbook-xsl-ns-1.76.1\fo\callout.xsl -->
<xsl:template match="d:co">
    <!-- added by crifan start -->
    <fo:inline>
        <fo:basic-link>
            <xsl:if test="@linkends">
              <xsl:attribute name="internal-destination">
                <xsl:value-of select="@linkends"/>
              </xsl:attribute>
            </xsl:if>
    <!-- added by crifan end -->
    
            <xsl:call-template name="anchor"/>
            <xsl:apply-templates select="." mode="callout-bug"/>
        <!-- added by crifan start -->
        </fo:basic-link>
        <!-- added by crifan end -->
    </fo:inline>
</xsl:template>

<!-- from docbook-xsl-ns-1.76.1\fo\lists.xsl -->
<xsl:template name="callout.arearef">
  <xsl:param name="arearef"></xsl:param>
  <xsl:variable name="targets" select="key('id',$arearef)"/>
  <xsl:variable name="target" select="$targets[1]"/>

  <xsl:choose>
    <xsl:when test="count($target)=0">
      <xsl:value-of select="$arearef"/>
      <xsl:text>: ???</xsl:text>
    </xsl:when>
    <xsl:when test="local-name($target)='co'">
      <!-- added by crifan start -->
      <fo:basic-link>
        <xsl:attribute name="internal-destination">
          <xsl:value-of select="$arearef"/>
        </xsl:attribute>
        <!-- added by crifan end -->
        
        <xsl:apply-templates select="$target" mode="callout-bug"/>
      <!-- added by crifan start -->
      </fo:basic-link>
      <!-- added by crifan end -->
    </xsl:when>
    <xsl:when test="local-name($target)='areaset'">
      <xsl:call-template name="callout-bug">
        <xsl:with-param name="conum">
          <xsl:apply-templates select="$target" mode="conumber"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="local-name($target)='area'">
      <xsl:choose>
        <xsl:when test="$target/parent::d:areaset">
          <xsl:call-template name="callout-bug">
            <xsl:with-param name="conum">
              <xsl:apply-templates select="$target/parent::d:areaset"
                                   mode="conumber"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="callout-bug">
            <xsl:with-param name="conum">
              <xsl:apply-templates select="$target" mode="conumber"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>???</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

【后记】

后来发现,上述配置,对于coref还是无效的,所以,又去折腾了下,添加了相应配置,以支持coref和所指向的co之间的链接引用。

原先配置,加上对应coref的配置后,总的相关配置如下:

<!--============================================================================
callout setting
=============================================================================-->
<!-- from docbook-xsl-ns-1.76.1\fo\callout.xsl -->
<xsl:template match="d:co">
    <!-- added by crifan start -->
    <fo:inline>
        <fo:basic-link>
            <xsl:if test="@linkends">
              <xsl:attribute name="internal-destination">
                <xsl:value-of select="@linkends"/>
              </xsl:attribute>
            </xsl:if>
    <!-- added by crifan end -->
    
            <xsl:call-template name="anchor"/>
            <xsl:apply-templates select="." mode="callout-bug"/>
        <!-- added by crifan start -->
        </fo:basic-link>
        <!-- added by crifan end -->
    </fo:inline>
</xsl:template>


<xsl:template match="d:coref">
  <!-- tricky; this relies on the fact that we can process the "co" that's -->
  <!-- "over there" as if it were "right here" -->

  <xsl:variable name="co" select="key('id', @linkend)"/>
  <xsl:choose>
    <xsl:when test="not($co)">
      <xsl:message>
        <xsl:text>Error: coref link is broken: </xsl:text>
        <xsl:value-of select="@linkend"/>
      </xsl:message>
    </xsl:when>
    <xsl:when test="local-name($co) != 'co'">
      <xsl:message>
        <xsl:text>Error: coref doesn't point to a co: </xsl:text>
        <xsl:value-of select="@linkend"/>
      </xsl:message>
    </xsl:when>
    <xsl:otherwise>
      <fo:inline>
        <!-- added by crifan start -->
        <fo:basic-link>
            <xsl:if test="@linkend">
              <xsl:attribute name="internal-destination">
                <xsl:value-of select="@linkend"/>
              </xsl:attribute>
            </xsl:if>
        <!-- added by crifan end -->
            <xsl:call-template name="anchor"/>
            <xsl:apply-templates select="$co" mode="callout-bug"/>
        <!-- added by crifan start -->
        </fo:basic-link>
        <!-- added by crifan end -->
      </fo:inline>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- from docbook-xsl-ns-1.76.1\fo\lists.xsl -->
<xsl:template name="callout.arearef">
  <xsl:param name="arearef"></xsl:param>
  <xsl:variable name="targets" select="key('id',$arearef)"/>
  <xsl:variable name="target" select="$targets[1]"/>

  <xsl:choose>
    <xsl:when test="count($target)=0">
      <xsl:value-of select="$arearef"/>
      <xsl:text>: ???</xsl:text>
    </xsl:when>
    <xsl:when test="local-name($target)='co'">
      <!-- added by crifan start -->
      <fo:basic-link>
        <xsl:attribute name="internal-destination">
          <xsl:value-of select="$arearef"/>
        </xsl:attribute>
        <!-- added by crifan end -->
        
        <xsl:apply-templates select="$target" mode="callout-bug"/>
      <!-- added by crifan start -->
      </fo:basic-link>
      <!-- added by crifan end -->
    </xsl:when>
    <xsl:when test="local-name($target)='areaset'">
      <xsl:call-template name="callout-bug">
        <xsl:with-param name="conum">
          <xsl:apply-templates select="$target" mode="conumber"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="local-name($target)='area'">
      <xsl:choose>
        <xsl:when test="$target/parent::d:areaset">
          <xsl:call-template name="callout-bug">
            <xsl:with-param name="conum">
              <xsl:apply-templates select="$target/parent::d:areaset"
                                   mode="conumber"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="callout-bug">
            <xsl:with-param name="conum">
              <xsl:apply-templates select="$target" mode="conumber"/>
            </xsl:with-param>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>???</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

转载请注明:在路上 » 【全部解决】Docbook生成的PDF中callout不能点击跳转(而HTML中却可以)

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
82 queries in 0.165 seconds, using 22.09MB memory