DITAのdir属性のテンプレート

DITAにはdir属性というのがあります.

3.4.1.6 localization-atts attribute group

2.1.3.9.2 The dir attribute

指定できる値は、ltr, rtl,lro,rloです.英語のように文字の進行方向が左から右である言語はltrです.アラビア語のように文字の進行方向が右から左である言語はrtlです.でもUnicodeでは左から右に書く言語の文字と右から左に書く言語の文字はコードポイントが分かれているので、通常はレンダリングするソフトウェア(Formatter、ブラウザ)が文字の進行方向を自動的に切り替えてくれます.わざわざdir属性を指定する必要はありません.

The dir attribute provides direction about how processors should render bidirectional text. Languages such as Arabic, Hebrew, Farsi, Urdu, and Yiddish have text written from right to left. Numerics and embedded sections of Western language text, however, are written from left to right. Some multilingual documents also contain a mixture of text segments in two directions. This attribute specifies how such text should be rendered to a reader.

でも時として、レンダリング結果がおかしくなってしまう場合があります.

When embedding a right-to-left text run inside a left-to-right text run (or vice-versa), the default direction may provide incorrect results based on the rendering mechanism, especially if the embedded text run includes punctuation that is located at one end of the embedded text run. 

たとえばアラビア語の文書にピリオド入りの英語の会社名を入れる場合もそうなります.アラビア語の文書では通常XSL-FOの場合writing-mode="rtl"を指定します.そうすると

<ph>Acme Corporation Ltd.</ph>

は、以下のようにレンダリングされてしまいます.

.Acme Corporation Ltd

これを直すには、オーサリングで

<ph dir="ltr">Acme Corporation Ltd.</ph>

として明示的に約物文字の進行方向を指定します.

こうしてスタイルシートでは@dirが指定されていたら

<fo:bidi-override direction="ltr" unicode-bidi="embed">Acme Corporation Ltd.</fo:bidi-override>

を生成します.Formatterのレンダリング結果は以下のようになります.

Acme Corporation Ltd.

この場合unicode-bidi="embed"は、約物の位置に影響を及ぼします.

さてこのようなdir属性ですがDITA Open Toolkitのプラグインを@dirでgrepしてみると、XHTMLプラグインだけがこれをインプリメントしていました.

[DITA-OT1.8\plugins\org.dita.xhtml\xsl\xslhtml\dita2htmlImpl.xsl(3305)]

<!-- If an element has @dir, copy it to the output -->
<xsl:template match="@dir">
  <xsl:attribute name="dir"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>

XHTMLはブラウザがdir属性を処理してくれるので単なる属性のコピーで済んでしまうみたいです.

ではXSL-FOの場合はどうなるのでしょう?以下が私の考えてみたテンプレートです.XSLT2.0だと<xsl:next-match/>を使ってバッチリ書けます.でもまだ試作品です.ご了解ください.

    <!-- 
     function: dir属性の処理
     param: なし
     return: fo:bidi-override
     note: fo:bidi-overrideは%inlineなので%blockが期待されるコンテキストで
                このテンプレートがマッチするのはまずいです.例えばfo:flowの子要素.
                またテーブルなどで構造が決まっていいるものもダメです.
                colspec,thead,tbody,row,entry,sthead,strow,stentryなど
                これらにマッチしてfo:bidi-overrideを生成すると不正なFOになります.
                これをプロテクトするためahf:isBidiOverrideAllowedElem(.)でチェックしています.
     -->
    <xsl:template match="*[exists(@dir)][ahf:isBidiOverrideAllowedElem(.)]" priority="10">
        <xsl:variable name="dir" as="xs:string" select="string(@dir)"/>
        <xsl:variable name="unicodeBidi" as="xs:string">
            <xsl:choose>
                <xsl:when test="$dir = ('ltr','rtl')">
                    <xsl:sequence select="'embed'"/>
                </xsl:when>
                <xsl:when test="$dir = ('lro','rlo')">
                    <xsl:sequence select="'bidi-override'"/>
                </xsl:when>
                <xsl:otherwise>
                    <!--ほんとはこのケースはありえない-->
                    <xsl:sequence select="'normal'"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:variable name="direction" as="xs:string">
            <xsl:choose>
                <xsl:when test="$dir = ('ltr','rtl')">
                    <xsl:sequence select="$dir"/>
                </xsl:when>
                <xsl:when test="$dir eq 'lro'">
                    <xsl:sequence select="'ltr'"/>
                </xsl:when>
                <xsl:when test="$dir eq 'rlo'">
                    <xsl:sequence select="'rtl'"/>
                </xsl:when>
                <xsl:otherwise>
                    <!--ほんとはこのケースはありえない-->
                    <xsl:sequence select="'inherit'"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <fo:bidi-override unicode-bidi="{$unicodeBidi}" direction="{$direction}">
            <xsl:next-match/>
        </fo:bidi-override>
    </xsl:template>

    <!-- 
     function: fo:bidi-overrideを要素の親として生成して良いか否かの判定
     param: prmElem
     return: xs:boolaen
     note: topicはfo:flowの直下に来る場合がありえるのでfalse()にしてあります.
                その他テーブル関係の要素は構造が決まっているのでダメです.
     -->
    <xsl:function name="ahf:isBidiOverrideAllowedElem" as="xs:boolean">
        <xsl:param name="prmElem" as="element()"/>
        <xsl:variable name="class" as="xs:string" select="string($prmElem/@class)"/>
        <xsl:choose>
            <xsl:when test="contains($class,' topic/colspec ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:when test="contains($class,' topic/thead ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:when test="contains($class,' topic/tbody ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:when test="contains($class,' topic/row ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:when test="contains($class,' topic/entry ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:when test="contains($class,' topic/sthead ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:when test="contains($class,' topic/strow ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:when test="contains($class,' topic/stentry ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:when test="contains($class,' topic/topic ')">
                <xsl:sequence select="false()"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="true()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>

問題はfo:bidi-overrideがXSL-FOの仕様の%inlineに該当することです.

6.2 Formatting Object Content

つまり%blockが期待されるコンテキストではfo:bidi-overrideは組版時にエラーになります.またテーブルのように要素構造が決まっていてfo:bidi-overrideを書ける余地がないコンテキストでも出現すると同様にエラーになります.このあたりは完璧にエラーをブロックする手段はないと思います.運用で逃げるしかないでしょう.

しかしDITA-OTのプラグインでdir属性を処理しているのがXHTMLプラグインだけというのはちょっと驚きました.まだDITAでアラビア語というのはポピュラーではないのでしょうか?