【问题】
已经用xsltproc+fop实现了callout,在PDF和HTML中都可以正常显示了。
详见:【已解决】Docbook中的callout图片在programlisting中不显示 -> xsltproc不支持areaspec
但是PDF中的callout,却不能像HTML中一样可以点击实现跳转。
即,HTML中是可以通过点击callout图标,实现源码的位置和callou注释t的位置之间互相跳转的:
但是PDF中虽然是显示出来callout图片了,但是却不能点击实现跳转:
其中对应源码如下:
<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添加对应的配置。
在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注释内容的位置了:
所以,剩下的事情,就是去如何写好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图片,也可以跳转到对应的源码中的位置了:
这样,通过手动修改fo文件源码,至少验证了此条路是通的,是可以实现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>