Pandoc の改行コード: Windows/Linux 差異の実装調査

Pandoc の HTML 出力における改行コードは、デフォルトでプラットフォーム依存です。 Windows では \r\n (CRLF)、Linux や macOS では \n (LF) が使用されます。ただし、この動作は --eol オプションで明示的に制御可能であり、HTML 出力特有の実装ではなく、全てのテキスト形式の出力に適用されるファイル出力レベルの機能として設計されています。重要なのは、HTML Writer モジュール自体には改行コード処理のロジックが含まれておらず、出力パイプラインの最終段階で変換が行われることです。この設計により、クロスプラットフォーム開発における一貫性の問題に対処しながら、多くのユーザーが期待する OS 標準の動作も維持しています。

1 実装アーキテクチャ: 出力段階での変換

Pandoc のソースコード調査により、改行コード処理が個別の Writer モジュールではなく、ファイル出力ハンドラで実装されていることが判明しました。具体的な処理フローは以下の通りです。

入力ドキュメントが Reader で Pandoc AST (抽象構文木) に変換され、フィルタや変換処理を経た後、HTML Writer (src/Text/Pandoc/Writers/HTML.hs) が AST 構造を HTML 文字列に変換します。この段階では、HTML Writer は標準的な \n (LF) を使用してコンテンツを生成します。その後、Output Handler が --eol 設定に基づいて改行コードを変換し、最終的にファイルに書き込まれます。

ソースコードのディレクトリ構造では、以下のファイルが改行コード処理に関連しています。

  • src/Text/Pandoc/Options.hs - WriterOptions データ構造を定義 (EOL 設定を含む)
  • src/Text/Pandoc/App/CommandLineOptions.hs - --eol を含むコマンドライン引数を解析
  • src/Text/Pandoc/App/OutputSettings.hs - 出力設定と改行コード変換を処理
  • src/Text/Pandoc/Writers/HTML.hs - HTML Writer 実装 (改行コードは直接処理しない)
  • src/Text/Pandoc/Writers/Shared.hs - Writer 共通ユーティリティ

この設計により、全ての出力フォーマット (HTML、Markdown、LaTeX、プレーンテキストなど) で統一的な改行コード処理が実現されています。

2 プラットフォーム検出と条件付きコンパイル

Pandoc は Haskell の条件付きコンパイルディレクティブを使用して Windows 環境を検出しています。ソースコード内には以下のようなパターンが見られます。

#ifdef _WINDOWS
import Data.List (isPrefixOf)
#endif

この #ifdef _WINDOWS ディレクティブにより、コンパイル時に Windows 特有のコードが組み込まれます。デフォルトの「native」モードでは、このプラットフォーム検出により、Windows では自動的に CRLF、Unix 系システムでは LF が使用される仕組みとなっています。

具体的な変換メカニズムは以下の通りです。

  1. Windows のデフォルト動作 - システムのテキストモードファイル I/O が自動的に \n\r\n に変換
  2. Linux/Unix のデフォルト動作 - \n がそのまま維持される
  3. 明示的な --eol=lf 指定 - 全プラットフォームで変換を抑制し、Unix 改行コードを維持
  4. 明示的な --eol=crlf 指定 - 全プラットフォームで Windows 改行コードを強制

3 –eol オプションの実装背景と歴史

この機能は段階的に開発されました。

3.1 2015 年 4 月 - Issue #2097「Surprisingly, line endings vary by OS..」

ユーザーから、Linux と Windows 間で同じ入力ファイルから生成された HTML 出力の改行コードが異なることで、Git リポジトリ内で不要な差分が発生する問題が報告されました。ユーザーは以下を期待していました。

  1. 全ての OS で常に LF 出力
  2. コマンドラインオプションで改行コードを選択・強制できる機能
  3. 入力ファイルの元の改行コードを保持する動作

3.2 2017 年 - Issue #3663「Add EOL writer option」

正式な機能リクエストが提出され、開発者コミュニティで議論されました。Issue 内の重要な引用は以下の通りです。

“OS 標準の EOL を使用することは、通常の使用では多くのユーザーが期待することかもしれません。しかし、クロスプラットフォームプロジェクトでは、このオプションにより標準を強制できるようになります。”

3.3 2017 年 11 月 - Pandoc 2.0 でリリース

Stefan Dresselhaus によって実装され、以下の機能が追加されました。

  • --eol=crlf|lf|native コマンドラインフラグ
  • Writer オプションによる改行コード制御
  • Issue #3663 および Issue #2097 への対応

4 –eol オプションの使用方法

現在の --eol オプションは以下の 3 つの値を受け付けます。

4.1 コマンドライン使用

pandoc input.md -o output.html --eol=lf

pandoc input.md -o output.html --eol=crlf

pandoc input.md -o output.html --eol=native
pandoc input.md -o output.html

4.2 デフォルトファイルでの設定

eol: lf  # または crlf、native

4.3 RMarkdown での使用

output:
  html_document:
    pandoc_args: --eol=lf

5 実際のユースケースと問題解決

5.1 クロスプラットフォーム開発での課題

5.1.1 Git リポジトリのノイズ問題

  • Windows と Linux 間で Git リポジトリを共有する開発者が、改行コードの違いによる不要な差分を経験
  • HTML ファイルが内容的には同一でも、異なる OS で再生成すると変更として検出される
  • 改行コードの違いが実際のコンテンツ変更を見えにくくする

5.1.1.1 解決策

ビルドスクリプトで --eol=lf を明示的に指定し、全ての開発者の環境で一貫した出力を生成します。

5.1.2 CI/CD パイプライン

  • 異なる OS のビルドサーバー間で一貫性のない出力
  • デプロイされるドキュメントの改行コードが環境によって異なる

5.1.2.1 解決策

CI 設定ファイルで --eol=lf (または組織の標準) を強制指定します。

5.1.3 RMarkdown との統合 (Issue #1657)

  • self_contained: no オプション使用時に --eol=lf の効果が失われる
  • R による後処理がシステムデフォルトの改行コードに戻してしまう

5.1.3.1 解決策

レンダリング後に手動で改行コード変換を実行、または Pandoc の後処理フックを使用します。

6 技術的実装の詳細

6.1 処理フロー

入力ドキュメント
     ↓
[Reader] AST に解析
     ↓
[Filters/Transforms] フィルタと変換処理
     ↓
[Writer] AST → テキスト変換 (\n 使用)
     ↓
[Output Handler] --eol 設定に基づいて改行コード変換
     ↓
ファイル出力 (プラットフォーム適切または指定された改行コード)

6.2 重要な設計原則

以下の設計原則に基づいて実装されています。

  1. HTML Writer 内でハードコードされていない - 改行コードは HTML Writer のソースコードにハードコードされていません
  2. デフォルトでプラットフォーム依存 - 指定がない限り OS 標準の改行コードを使用
  3. 設定可能 - --eol コマンドラインオプションで制御可能
  4. 出力段階で適用 - 変換は HTML 生成時ではなく、ファイル書き込み時に実行
  5. フォーマット非依存 - 同じメカニズムが全てのテキストベース出力フォーマットに適用

6.3 影響を受ける出力フォーマット

--eol オプションは以下のテキストベース出力フォーマットに影響します。

  • HTML (html、html5)
  • Markdown (全バリエーション)
  • LaTeX
  • プレーンテキスト
  • RST (reStructuredText)
  • その他のテキストベース形式

7 開発者コミュニティの視点

7.1 メンテナー (John MacFarlane) の見解

以下のように判断されています。

  • 元の「native」動作はほとんどのユースケースで妥当と判断
  • 機能リクエストはクロスプラットフォームワークフローにおいて正当なものとして受け入れられた
  • 実装は直接的で、Pandoc 2.0 で追加
  • デフォルトを「native」から変更する計画はなし

7.2 ユーザーコミュニティのフィードバック

以下のフィードバックが得られています。

  • 機能は公式マニュアルページで適切に文書化されている
  • ユーザーは適切に使用した場合、機能が期待通りに動作することを確認
  • --eol オプション自体にプラットフォーム固有のバグや問題は報告されていない
  • Stack Overflow やコミュニティディスカッションで広く認知されている

8 実践的な推奨事項

8.1 クロスプラットフォームプロジェクト向け

以下の対応を推奨します。

  1. ビルドスクリプトで明示的に設定 - --eol=lf を指定して一貫性を保証
  2. プロジェクト README で文書化 - 改行コード規則をドキュメントに記載
  3. Pandoc デフォルトファイルを使用 - チーム全体での一貫性のために検討
  4. Git 設定と併用 - .gitattributes と組み合わせて多層防御

推奨設定例 (.gitattributes) を以下に示します。

*.html text eol=lf
*.md text eol=lf

8.2 Git 統合での推奨

以下の対応を推奨します。

  1. Git にコミットするファイルには --eol=lf を使用 (Unix 規則が標準)
  2. .gitattributes と組み合わせて使用
  3. Git の autocrlf 設定だけに依存しない

8.3 単一プラットフォームプロジェクト向け

以下の対応を推奨します。

  • デフォルトの --eol=native が一般的に適切
  • クロスプラットフォームでファイルを共有しない限り、アクション不要

9 結論: 設計思想と実装の妥当性

Pandoc の改行コード処理は、バグではなく意図的な設計判断です。調査により以下が明らかになりました。

9.1 ソースコードレベルの実装

  1. 分離されたアーキテクチャ - 改行コード処理は HTML Writer (src/Text/Pandoc/Writers/HTML.hs) から分離され、出力ハンドラ (src/Text/Pandoc/App/OutputSettings.hs) で実装
  2. プラットフォーム検出 - Haskell の #ifdef _WINDOWS 条件付きコンパイルを使用
  3. 統一的な処理 - 全出力フォーマットで同じロジックを使用し、コード重複を回避
  4. 設定可能な動作 - WriterOptions データ構造に EOL 設定を含め、柔軟性を提供

9.2 設計の妥当性

  • デフォルトの「native」動作は大多数のユーザーの期待に沿っており、後方互換性を維持
  • --eol オプションにより、CI/CD パイプライン、Git リポジトリ管理、自動ドキュメント生成など、クロスプラットフォーム要件に対応
  • クリーンな実装により保守性が向上し、全ての出力フォーマットで一貫した動作を保証
  • 2015 年の問題報告から 2017 年の実装まで、コミュニティのフィードバックを反映した段階的な開発

9.3 実用上の影響

  • Windows では \r\n (CRLF) がデフォルト、Linux では \n (LF) がデフォルト
  • 両プラットフォームで --eol=lf または --eol=crlf を指定することで統一可能
  • HTML Writer 自体は改行コードを処理せず、出力段階で変換が行われる
  • この設計により、フォーマット固有のコードを変更せずに、全出力形式で改行コード制御が実現