🕌

TEI Processing Modelで実現する宣言的なマルチフォーマット変換

に公開

はじめに

TEI (Text Encoding Initiative) は人文学テキストのデジタル化において広く使われている標準規格です。本記事では、TEI P5で導入された Processing Model という機能を使って、TEI XMLから複数のフォーマット(HTML、LaTeX/PDF、EPUB3)への変換を実現した事例を紹介します。

https://www.tei-c.org/Vault/P5/3.0.0/doc/tei-p5-doc/en/html/TD.html#TDPM

対象プロジェクトは「校異源氏物語」で公開されているテキストを例とします。

https://kouigenjimonogatari.github.io/

背景

これまで、以下の記事で紹介したように、変換処理を個別に行ってきました。

ODD/RNGファイルのカスタマイズによる、使用するタグの限定

https://zenn.dev/nakamura196/articles/4b2dc135bbe731

XSLTを用いたHTMLへの変換

https://zenn.dev/nakamura196/articles/d0e74f0e3fc547

XSLTを用いたTeX/PDFへの変換

https://zenn.dev/nakamura196/articles/1e27334fa5ca45

EPUBへの変換

https://zenn.dev/nakamura196/articles/a8c55f6689ea89

それぞれの取り組みにおいて、個別の変換ルールなどを記載したファイルを作成する必要があり、その煩雑さが課題となっていました。

Processing Modelとは

Processing Modelは、TEI要素の変換ルールを宣言的に記述する仕組みです。従来は各出力フォーマット用に個別のXSLTを書く必要がありましたが、Processing Modelを使うことで:

  • ODDファイル内で変換ルールを定義できる
  • 複数の出力フォーマットに対応可能(web、latex、epubなど)
  • スキーマと変換ルールを一元管理できる

Processing Modelの構造

<elementSpec ident="persName" mode="change">
  <desc>Personal name</desc>
  <model>
    <!-- HTML output -->
    <modelSequence output="web">
      <model behaviour="inline">
        <outputRendition>span</outputRendition>
        <desc>Inline span for person name</desc>
      </model>
    </modelSequence>

    <!-- EPUB3 output -->
    <modelSequence output="epub">
      <model behaviour="inline">
        <outputRendition>span</outputRendition>
        <desc>Inline span for person name in EPUB3</desc>
      </model>
    </modelSequence>

    <!-- LaTeX output -->
    <modelSequence output="latex">
      <model behaviour="inline">
        <outputRendition>\person</outputRendition>
        <desc>Custom LaTeX command for person names</desc>
      </model>
    </modelSequence>
  </model>
</elementSpec>

主な要素:

  • elementSpec/@ident: 対象となるTEI要素名
  • modelSequence/@output: 出力モード (web, latex, epubなど)
  • model/@behaviour: 変換の振る舞い (inline, block, paragraph, break, omitなど)
  • outputRendition: 出力要素名またはコマンド

実装アーキテクチャ

本プロジェクトでは、関心の分離(Separation of Concerns) の原則に基づき、以下の2層構造を採用しました:

1. Processing Model層(自動生成)

ODDファイル内のProcessing Model定義から、基本的な要素変換ルールを自動生成:

odd_with_pm.odd (Processing Model定義)
  ↓ (odd_to_xslt.py --output-mode web)
tei_elements_html.xsl (HTML用基本変換)
  ↓ (odd_to_xslt.py --output-mode latex)
tei_elements_latex.xsl (LaTeX用基本変換)
  ↓ (odd_to_xslt.py --output-mode epub)
tei_elements_epub.xsl (EPUB3用基本変換)

2. Wrapper層(手動作成)

フォーマット固有の機能を実装:

  • HTML Wrapper (html_wrapper.xsl)

    • Mirador IIIF viewerの統合
    • JavaScript (ページナビゲーション、ハイライト)
    • Tailwind CSSスタイリング
    • 縦書き表示
    • メタデータモーダル
  • LaTeX Wrapper (tex_wrapper.xsl)

    • ltjtarticleドキュメントクラス
    • LuaLaTeX日本語対応
    • カスタムジオメトリ
    • カラーコマンド定義
  • EPUB3生成ツール (tei_to_epub.py)

    • EPUB構造ファイル生成 (container.xml, content.opf, nav.xhtml)
    • 縦書きCSS
    • ZIPパッケージング

実装手順

Step 1: ODDにProcessing Model定義を追加

<!-- seg要素の例 -->
<elementSpec ident="seg" mode="change">
  <desc>Text segment with optional correspondence link</desc>
  <model>
    <modelSequence output="web">
      <model behaviour="inline">
        <desc>Inline span with data attributes for JavaScript processing</desc>
      </model>
    </modelSequence>
    <modelSequence output="epub">
      <model behaviour="inline">
        <desc>Inline span for EPUB3</desc>
      </model>
    </modelSequence>
    <modelSequence output="latex">
      <model behaviour="paragraph">
        <desc>Paragraph with medium skip</desc>
      </model>
    </modelSequence>
  </model>
</elementSpec>

校異源氏物語では、以下の要素のProcessing Modelを定義:

  • seg: テキストセグメント(HTMLではインライン、LaTeXでは段落)
  • lb: 改行(HTMLでは<br/>、LaTeXでは省略)
  • pb: ページブレーク(HTMLではインラインマーカー、LaTeXでは省略)
  • persName: 人名(HTMLでは<span>、LaTeXでは\person{}コマンド)
  • placeName: 地名(HTMLでは<span>、LaTeXでは\place{}コマンド)
  • body, div, p: 構造要素

Step 2: XSLT生成ツールの作成

Processing ModelからXSLTを自動生成するPythonツール odd_to_xslt.py を開発:

class XSLTGeneratorBase(ABC):
    """XSLT生成の基底クラス"""

    @abstractmethod
    def generate_header(self) -> List[str]:
        """XSLTヘッダー生成"""
        pass

    @abstractmethod
    def _generate_inline(self, element, rendition, params):
        """inline behaviour処理"""
        pass

    # その他のbehaviour処理...

class HTMLGenerator(XSLTGeneratorBase):
    """HTML用XSLT生成"""
    # HTML固有の実装

class LaTeXGenerator(XSLTGeneratorBase):
    """LaTeX用XSLT生成"""
    # LaTeX固有の実装

class EPUBGenerator(HTMLGenerator):
    """EPUB3用XSLT生成(HTMLとほぼ同じ)"""
    # XHTML5準拠の実装

使用方法:

# HTML用
python3 odd_to_xslt.py --output-mode web odd_with_pm.odd tei_elements_html.xsl

# LaTeX用
python3 odd_to_xslt.py --output-mode latex odd_with_pm.odd tei_elements_latex.xsl

# EPUB3用
python3 odd_to_xslt.py --output-mode epub odd_with_pm.odd tei_elements_epub.xsl

Step 3: Wrapper XSLTの作成

生成されたXSLTをインポートし、フォーマット固有の機能を追加:

<!-- html_wrapper.xsl -->
<xsl:stylesheet version="2.0" ...>
  <!-- Processing Model生成XSLTをインポート -->
  <xsl:import href="tei_elements_html.xsl"/>

  <!-- ルートテンプレートをオーバーライド -->
  <xsl:template match="/">
    <xsl:apply-templates select="tei:TEI"/>
  </xsl:template>

  <!-- カスタムHTML文書構造 -->
  <xsl:template match="tei:TEI">
    <html>
      <head>
        <!-- Mirador, Tailwind CSS, カスタムスタイル -->
      </head>
      <body>
        <!-- ヘッダー、メタデータモーダル、メインコンテンツ、Miradorビューワー -->
        <script>
          // JavaScript for navigation, highlighting, etc.
        </script>
      </body>
    </html>
  </xsl:template>

  <!-- 特定要素のオーバーライド(必要に応じて) -->
  <xsl:template match="tei:pb">
    <!-- IIIF Canvas IDとの連携 -->
  </xsl:template>
</xsl:stylesheet>

Step 4: 変換実行

各フォーマットへの変換:

# HTML生成
saxon -xsl:html_wrapper.xsl -s:01.xml -o:01.html

# LaTeX/PDF生成
saxon -xsl:tex_wrapper.xsl -s:01.xml -o:01.tex
lualatex -interaction=nonstopmode 01.tex

# EPUB3生成
python3 tei_to_epub.py --xsl=tei_elements_epub.xsl 01.xml 01.epub

出力結果

1つのTEI XMLファイル(01.xml)から、3つのフォーマットを生成:

フォーマット ファイルサイズ 特徴
HTML 115KB Mirador IIIFビューワー統合、縦書き、インタラクティブナビゲーション
PDF 201KB (8ページ) LuaLaTeX日本語組版、横長レイアウト、カラー表示
EPUB3 14KB 縦書き電子書籍、XHTML5準拠

HTML

PDF

EPUB3

実装の利点

1. 保守性の向上

  • Processing Modelの変更が容易: ODDを修正してXSLTを再生成するだけ
  • 要素変換とプレゼンテーションの分離: 基本変換とインタラクティブ機能が独立
  • 一元管理: スキーマと変換ルールがODDに集約

2. 再利用性

  • 基本変換XSLTの再利用: 他のプロジェクトでも利用可能
  • Wrapperのカスタマイズ: プロジェクト固有の要件に対応

3. 宣言的記述

  • 可読性: Processing Modelは命令型XSLTより理解しやすい
  • ドキュメント化: <desc>でルールの意図を明示

4. 一貫性

  • 複数フォーマット間の一貫性: 同じODDから生成
  • スキーマと実装の同期: 定義と実装が分離しない

課題と解決策: Processing Modelの実行環境

TEI Publisherなど、Processing Modelを直接実行できるツールは限られています。

今回の取り組みでは、カスタムXSLT生成ツール (odd_to_xslt.py) を開発し、Processing ModelからXSLTスケルトンを生成しています。

まとめ

TEI Processing Modelを使用することで:

  1. 宣言的保守しやすい変換ルールを記述できる
  2. 複数フォーマット(HTML、LaTeX/PDF、EPUB3)を一元管理できる
  3. 関心の分離により、基本変換とフォーマット固有機能を独立して管理できる
  4. 再利用性が高く、他のTEIプロジェクトにも適用可能

校異源氏物語プロジェクトでは、この approach により:

  • 1つのODDファイルから3種類の出力フォーマットを生成
  • インタラクティブなWebビューワー(Mirador統合)
  • PDF(LuaLaTeX日本語組版)
  • 電子書籍フォーマット(縦書きEPUB3)

を実現しました。

参考資料

ソースコード

本記事で紹介したすべてのコードは、以下のリポジトリで公開しています:

root/
├── genji/
│   ├── odd_with_pm.odd              # Processing Model定義
│   ├── tei_elements_*.xsl           # 生成されたXSLT
│   ├── html_wrapper.xsl             # HTMLラッパー
│   ├── tex_wrapper.xsl              # LaTeXラッパー
│   └── README_processing_model.md   # 詳細ドキュメント
└── tools/
    ├── odd_to_xslt.py               # XSLT生成ツール
    └── tei_to_epub.py               # EPUB3生成ツール

Discussion