突然の仕事の依頼です.それもお客さんからお金をもらえるわけではない、単なる自分の会社の宣伝用.それもワタクシの大嫌いなEPUBです.EPUBなんてちよっと.epubを解凍してみたら画像とCSSとXHTMLが入っているだけで、いろいろサンプルを見てみても対象デバイスがスマートフォンだったりするのでまるでデザインがギチギチに詰めてあって綺麗じゃないからです.特に読めればいいという感じで作った自炊のEPUBは見るだけでデザインにげんなりします.いやなのでいろいろケチをつけましたが、結局やる羽目になりました.
でも問題はいろいろありました.単一のXHTMLになっているので、これをまずなんとか分割することを考えなければならないのですが、
2.グルーピングをするのには、XSLT2.0のxsl:for-each-groupを使えばよい.でもこれをやるためには、html/bodyの下にグルーピング対象の要素(h2とか)が平坦に並んでいる必要がある.
3.リンクを解決しなければならない.今までは単一のXHTMLだったので<a href="#~">になっていたけれど、こんどは<a href="[分割後のXHTML]#~">としてやらないといけません.
4.EPUBでは"OEBPS"のフォルダの下に、それなりに整理して画像フォルダ、スタイルシート(CSS)フォルダ、XHTMLフォルダという感じで作ってやる.なので例えばXHTMLを"xhtml"画像を"image"というフォルダにするのだったら、image要素のsrc属性はsrc="../imag/[元のsrc属性]"というように加工してやらねばならない.
などなどいろいろありました.
まずグルーピングなんですが、W3CのXHTMLは<div class="div1">、<div class="div2">、<div class="div3">なんていう要素で階層化してあってbodyの下に「平坦」にグルーピング対象要素が並んでくれないのでこれらは前変換で削除するようにしました.
あとグルーピングですがカスタマイズできるよう、一個の関数でグルーピング条件をまとめてあります.
<xsl:template match="xhtm:body">
<xsl:for-each-group select="*"
group-starting-with="*[ahf:isGroupStarts(.)]">
...
<xsl:result-documet>で個別のXHTMLを作ります.
...
<xsl:for-each-group>
</xsl:template>
<xsl:for-each-group select="*"
group-starting-with="*[ahf:isGroupStarts(.)]">
...
<xsl:result-documet>で個別のXHTMLを作ります.
...
<xsl:for-each-group>
</xsl:template>
グループの最初の要素を決める関数は次のようなものです.これさえ変えればカスタマイズできるのが強みです.<h2>~<h3>~<h4>と並んでいるところは、<h2>や<h3>だけで1つのXHTMLを作らないように工夫してあります.
<xsl:function name="ahf:isGroupStarts" as="xs:boolean">
<xsl:param name="prmElem" as="element()"/>
<xsl:variable name="name" select="local-name($prmElem)" as="xs:string"/>
<xsl:variable name="class" select="string($prmElem/@class)" as="xs:string"/>
<xsl:choose>
<xsl:when test="$name eq 'div' and $class eq 'head'">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'div' and $class eq 'abstract'">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'div' and $class eq 'divtoc'">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'h2' and not($prmElem/ancestor::div[@class = 'head'])">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'h3' and $prmElem/preceding-sibling::*[1][local-name() ne 'h2']">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'h4' and $prmElem/preceding-sibling::*[1][local-name() ne 'h3']">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:param name="prmElem" as="element()"/>
<xsl:variable name="name" select="local-name($prmElem)" as="xs:string"/>
<xsl:variable name="class" select="string($prmElem/@class)" as="xs:string"/>
<xsl:choose>
<xsl:when test="$name eq 'div' and $class eq 'head'">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'div' and $class eq 'abstract'">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'div' and $class eq 'divtoc'">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'h2' and not($prmElem/ancestor::div[@class = 'head'])">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'h3' and $prmElem/preceding-sibling::*[1][local-name() ne 'h2']">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:when test="$name eq 'h4' and $prmElem/preceding-sibling::*[1][local-name() ne 'h3']">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
ファイルリストはこんな感じのただのテキストファイルです.
コピータスクはこんな感じです.
<copy todir="${epub.image.dir}">
<fileset dir="${xhtm2epub.input.dirname}" includesfile="${epub.image.list.file}"/>
</copy>
<fileset dir="${xhtm2epub.input.dirname}" includesfile="${epub.image.list.file}"/>
</copy>
結局XHTMLの分割/生成まで行きました.表示させてみると、リンクもちゃんと動くし画像も表示されます.で気がついたのですが、元のXHTMLの表示とちょっとだけ違うのです.それは、次のようなCSSによるものでした.