💪

[XSLT 3.0][XSL-FO] xsl:tryで入力がおかしい場合にXSL-FO変換時にPDF注釈にを付ける

に公開

https://adventar.org/calendars/11635
XSLT 3.0デバッグまわり Advent Calendar 2025 の7日目です。

xsl:tryを使うとエラーがあるときも処理を復帰できますが、ではどういうときに便利なのかを例示します。
まあ今回の例についてはif式を使っている通り、xsl:tryで囲わなくとも成立するんですけれども。でもxsl:assertでエラーコード吐かせてxsl:catchするのであればアリかもしれません。

入力XML

<doc>
    <table border="dashed">
        <tr>
            <td>aaa</td>
            <td>bbbb</td>
        </tr>
        <tr>...</tr>
        ...
    </table>
    <table border="daaaashed">
    ...
    </table>
</doc>

テーブルの罫線のスタイルについて、@borderで指定するようになっています。
こういう、入力の値をダイレクトに使ってしまう設計はあまり良くありません。
さっそく2番目のテーブルでは@border="daaaashed"となっているように、スペルミスなどがその後の処理に波及してしまいます。
が、今回はこれでいきます。

スタイルシート


    <xsl:function name="my:gen-err" >
        <xsl:sequence 
            select="xs:QName('my:borderErr') 
                    => fn:error('invalid border attribute error')"/>
    </xsl:function>
    
    <xsl:template match="table">
        <fo:table border-width="0.3pt" border-color="black" space-after="2rem">
            <xsl:try>
                <xsl:attribute name="border-style"
                    select="if (@border => fn:exists() 
                    and (some $style in ('dashed', 'solid', 'dotted') 
                    satisfies ( string(@border) eq $style ))
                    ) 
                    then (@border) else (my:gen-err())"/>
                   <xsl:catch errors="my:borderErr">
                       <xsl:attribute name="border" select="'solid'"/>
                       <xsl:attribute name="axf:annotation-type" select="'Text'"/>
                       <xsl:attribute name="axf:annotation-position-horizontal"
                           select="'-2rem'"/>
                        <xsl:attribute name="axf:annotation-type" select="'Text'"/>
                       <xsl:attribute name="axf:annotation-contents"
                       select="$err:description || 
                       '@border ' || string(@border) || ' is invalid.' ||
                       ' Using fallback'"    
                       />
                   </xsl:catch>
            </xsl:try>
                       <fo:table-body>
                           <xsl:apply-templates/>
                       </fo:table-body>
        </fo:table>
    </xsl:template>

xsl:functionfn:error()のラッパを作成しています。

xsl:try内のxsl:attribute/@name="border-style"で、入力のtable/@borderを取り込んでいます。
しかし@borderがなかったり、XSL-FOの@border-styleで指定できない値であると後の工程で失敗するので、先に作ったラッパ関数でエラーを上げています。

エラーが出てxsl:catchに移行するときはxsl:tryの内容は破棄されているので、@border-style属性自体も一旦無かったことになります。今回はxsl:catchで書き直してしまっているのであまり意味がありませんが。

素のXSL-FOだとPDF注釈をあまり使えないため、Antenn House Formatterの拡張仕様を使用しています。axfのprefixのものです。
で、フォールバックの処理をしつつ結果出力物にどんなエラーだったかを伝える内容をPDFテキスト注釈(コメント)として出力させます。

それをXSL-FO処理系(Antenna House XSL Formatter)でPDFに出力した結果が次の通り。

上のテーブルはdashedが反映され、下のテーブルは値が不正なのでフォールバックのsolidで線が引かれた上でPDF注釈で説明が付加されています。

標準仕様だけで類似したPDF上でのエラー表示は実現できるか?

同じ形はまず無理という前提で。

PDF注釈をXFDF(PDFフォーム、注釈仕様のFDFのXML形式)としてxsl:result-documentなどで別に出した上で、XFDFをAcrobat Readerなどから出力後のPDFに取り込ませるということができます。とはいえ致命的なのは、XSL-FOプロセッサが処理するまでページ上のどこにこの箇所が来ることになるのか分からないため、位置と紐付いた可視性が与えられないことになります。

ユニークなエラーIDを生成してPDF上のテキストとして表示されるようにして、XFDF用の情報にも紐付けておき、後から別のソフトでエラーIDとPDFのページ番号の対応を取るシステムを作ってXFDF側に反映する、までやれば何とか同等になるでしょうか。

参考資料

https://www.antenna.co.jp/AHF/help/ja/ahf-ext.html#axf.annotation-contents

組版・ドキュメンテーション勉強会

Discussion