【问题】
已经用xsltproc+fop实现了callout,在PDF和HTML中都可以正常显示了。
详见:【已解决】Docbook中的callout图片在programlisting中不显示 -> xsltproc不支持areaspec
但是PDF中的callout,却不能像HTML中一样可以点击实现跳转。
即,HTML中是可以通过点击callout图标,实现源码的位置和callou注释t的位置之间互相跳转的:
但是PDF中虽然是显示出来callout图片了,但是却不能点击实现跳转:
其中对应源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | < 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的那部分的内容,好像就只有这部分:
1 2 3 4 5 6 | < 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的相关处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | < 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文件中找到了对应的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | < 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中的:
1 | #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 > |
改为:
1 | #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中,添加了如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!--============================================================================ 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源码:
1 | < fo:block >< fo:external-graphic content-width = "7pt" width = "7pt" src = "url(images/system/callouts/1.svg)" /></ fo:block > |
也添加了fo:basic-link,改为:
1 | < 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,即可。
对应的源码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <!-- 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可以支持点击跳转,支持源码和注释间互相跳转,真的是费了九牛二虎之力啊。
最终的解决办法是,添加如下的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <!--============================================================================ 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的配置后,总的相关配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | <!--============================================================================ 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 > |